PCI change from git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git 
1f16dbb3b4ed8cd8a852f0cc90b78e71ed6cbc99
for drm-tip testing in i915 and xe.

Signed-off-by: Lucas De Marchi <[email protected]>
---
 Documentation/driver-api/pci/pci.rst        |   3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c  |  18 +-
 drivers/gpu/drm/i915/gt/intel_region_lmem.c |  24 +-
 drivers/gpu/drm/xe/xe_vram.c                |  58 +---
 drivers/pci/Makefile                        |   2 +-
 drivers/pci/iov.c                           |  25 +-
 drivers/pci/pci-sysfs.c                     |  19 +-
 drivers/pci/pci.c                           | 145 ---------
 drivers/pci/pci.h                           |  14 +-
 drivers/pci/rebar.c                         | 325 ++++++++++++++++++++
 drivers/pci/setup-bus.c                     | 126 ++++++--
 drivers/pci/setup-res.c                     |  78 -----
 include/linux/pci.h                         |  16 +-
 13 files changed, 469 insertions(+), 384 deletions(-)
 create mode 100644 drivers/pci/rebar.c

diff --git a/Documentation/driver-api/pci/pci.rst 
b/Documentation/driver-api/pci/pci.rst
index 59d86e8271986..99a1bbaaec5d0 100644
--- a/Documentation/driver-api/pci/pci.rst
+++ b/Documentation/driver-api/pci/pci.rst
@@ -37,6 +37,9 @@ PCI Support Library
 .. kernel-doc:: drivers/pci/slot.c
    :export:
 
+.. kernel-doc:: drivers/pci/rebar.c
+   :export:
+
 .. kernel-doc:: drivers/pci/rom.c
    :export:
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 86255c13fbb79..7810bbc33c73e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1678,9 +1678,9 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device 
*adev)
        int rbar_size = pci_rebar_bytes_to_size(adev->gmc.real_vram_size);
        struct pci_bus *root;
        struct resource *res;
+       int max_size, r;
        unsigned int i;
        u16 cmd;
-       int r;
 
        if (!IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
                return 0;
@@ -1726,30 +1726,26 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device 
*adev)
                return 0;
 
        /* Limit the BAR size to what is available */
-       rbar_size = min(fls(pci_rebar_get_possible_sizes(adev->pdev, 0)) - 1,
-                       rbar_size);
+       max_size = pci_rebar_get_max_size(adev->pdev, 0);
+       if (max_size < 0)
+               return 0;
+       rbar_size = min(max_size, rbar_size);
 
        /* Disable memory decoding while we change the BAR addresses and size */
        pci_read_config_word(adev->pdev, PCI_COMMAND, &cmd);
        pci_write_config_word(adev->pdev, PCI_COMMAND,
                              cmd & ~PCI_COMMAND_MEMORY);
 
-       /* Free the VRAM and doorbell BAR, we most likely need to move both. */
+       /* Tear down doorbell as resizing will release BARs */
        amdgpu_doorbell_fini(adev);
-       if (adev->asic_type >= CHIP_BONAIRE)
-               pci_release_resource(adev->pdev, 2);
 
-       pci_release_resource(adev->pdev, 0);
-
-       r = pci_resize_resource(adev->pdev, 0, rbar_size);
+       r = pci_resize_resource(adev->pdev, 0, rbar_size, 1 << 5);
        if (r == -ENOSPC)
                dev_info(adev->dev,
                         "Not enough PCI address space for a large BAR.");
        else if (r && r != -ENOTSUPP)
                dev_err(adev->dev, "Problem resizing BAR0 (%d).", r);
 
-       pci_assign_unassigned_bus_resources(adev->pdev->bus);
-
        /* When the doorbell or fb BAR isn't available we have no chance of
         * using the device.
         */
diff --git a/drivers/gpu/drm/i915/gt/intel_region_lmem.c 
b/drivers/gpu/drm/i915/gt/intel_region_lmem.c
index 890183de22775..a30060fd44292 100644
--- a/drivers/gpu/drm/i915/gt/intel_region_lmem.c
+++ b/drivers/gpu/drm/i915/gt/intel_region_lmem.c
@@ -20,16 +20,6 @@
 #include "gt/intel_gt_regs.h"
 
 #ifdef CONFIG_64BIT
-static void _release_bars(struct pci_dev *pdev)
-{
-       int resno;
-
-       for (resno = PCI_STD_RESOURCES; resno < PCI_STD_RESOURCE_END; resno++) {
-               if (pci_resource_len(pdev, resno))
-                       pci_release_resource(pdev, resno);
-       }
-}
-
 static void
 _resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size)
 {
@@ -37,9 +27,7 @@ _resize_bar(struct drm_i915_private *i915, int resno, 
resource_size_t size)
        int bar_size = pci_rebar_bytes_to_size(size);
        int ret;
 
-       _release_bars(pdev);
-
-       ret = pci_resize_resource(pdev, resno, bar_size);
+       ret = pci_resize_resource(pdev, resno, bar_size, 0);
        if (ret) {
                drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n",
                         resno, 1 << bar_size, ERR_PTR(ret));
@@ -63,16 +51,12 @@ static void i915_resize_lmem_bar(struct drm_i915_private 
*i915, resource_size_t
        current_size = roundup_pow_of_two(pci_resource_len(pdev, 
GEN12_LMEM_BAR));
 
        if (i915->params.lmem_bar_size) {
-               u32 bar_sizes;
-
-               rebar_size = i915->params.lmem_bar_size *
-                       (resource_size_t)SZ_1M;
-               bar_sizes = pci_rebar_get_possible_sizes(pdev, GEN12_LMEM_BAR);
-
+               rebar_size = i915->params.lmem_bar_size * 
(resource_size_t)SZ_1M;
                if (rebar_size == current_size)
                        return;
 
-               if (!(bar_sizes & BIT(pci_rebar_bytes_to_size(rebar_size))) ||
+               if (!pci_rebar_size_supported(pdev, GEN12_LMEM_BAR,
+                                             
pci_rebar_bytes_to_size(rebar_size)) ||
                    rebar_size >= roundup_pow_of_two(lmem_size)) {
                        rebar_size = lmem_size;
 
diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c
index 0e10da790cc5d..d50baefcd1248 100644
--- a/drivers/gpu/drm/xe/xe_vram.c
+++ b/drivers/gpu/drm/xe/xe_vram.c
@@ -25,39 +25,13 @@
 #include "xe_vram.h"
 #include "xe_vram_types.h"
 
-#define BAR_SIZE_SHIFT 20
-
-/*
- * Release all the BARs that could influence/block LMEMBAR resizing, i.e.
- * assigned IORESOURCE_MEM_64 BARs
- */
-static void release_bars(struct pci_dev *pdev)
-{
-       struct resource *res;
-       int i;
-
-       pci_dev_for_each_resource(pdev, res, i) {
-               /* Resource already un-assigned, do not reset it */
-               if (!res->parent)
-                       continue;
-
-               /* No need to release unrelated BARs */
-               if (!(res->flags & IORESOURCE_MEM_64))
-                       continue;
-
-               pci_release_resource(pdev, i);
-       }
-}
-
 static void resize_bar(struct xe_device *xe, int resno, resource_size_t size)
 {
        struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
        int bar_size = pci_rebar_bytes_to_size(size);
        int ret;
 
-       release_bars(pdev);
-
-       ret = pci_resize_resource(pdev, resno, bar_size);
+       ret = pci_resize_resource(pdev, resno, bar_size, 0);
        if (ret) {
                drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). 
Consider enabling 'Resizable BAR' support in your BIOS\n",
                         resno, 1 << bar_size, ERR_PTR(ret));
@@ -79,41 +53,37 @@ void xe_vram_resize_bar(struct xe_device *xe)
        resource_size_t current_size;
        resource_size_t rebar_size;
        struct resource *root_res;
-       u32 bar_size_mask;
+       int max_size, i;
        u32 pci_cmd;
-       int i;
 
        /* gather some relevant info */
        current_size = pci_resource_len(pdev, LMEM_BAR);
-       bar_size_mask = pci_rebar_get_possible_sizes(pdev, LMEM_BAR);
-
-       if (!bar_size_mask)
-               return;
 
        if (force_vram_bar_size < 0)
                return;
 
        /* set to a specific size? */
        if (force_vram_bar_size) {
-               u32 bar_size_bit;
+               rebar_size = pci_rebar_bytes_to_size(force_vram_bar_size *
+                                                    (resource_size_t)SZ_1M);
 
-               rebar_size = force_vram_bar_size * (resource_size_t)SZ_1M;
-
-               bar_size_bit = bar_size_mask & 
BIT(pci_rebar_bytes_to_size(rebar_size));
-
-               if (!bar_size_bit) {
+               if (!pci_rebar_size_supported(pdev, LMEM_BAR, rebar_size)) {
                        drm_info(&xe->drm,
-                                "Requested size: %lluMiB is not supported by 
rebar sizes: 0x%x. Leaving default: %lluMiB\n",
-                                (u64)rebar_size >> 20, bar_size_mask, 
(u64)current_size >> 20);
+                                "Requested size: %lluMiB is not supported by 
rebar sizes: 0x%llx. Leaving default: %lluMiB\n",
+                                (u64)pci_rebar_size_to_bytes(rebar_size) >> 20,
+                                pci_rebar_get_possible_sizes(pdev, LMEM_BAR),
+                                (u64)current_size >> 20);
                        return;
                }
 
-               rebar_size = 1ULL << (__fls(bar_size_bit) + BAR_SIZE_SHIFT);
-
+               rebar_size = pci_rebar_size_to_bytes(rebar_size);
                if (rebar_size == current_size)
                        return;
        } else {
-               rebar_size = 1ULL << (__fls(bar_size_mask) + BAR_SIZE_SHIFT);
+               max_size = pci_rebar_get_max_size(pdev, LMEM_BAR);
+               if (max_size < 0)
+                       return;
+               rebar_size = pci_rebar_size_to_bytes(max_size);
 
                /* only resize if larger than current */
                if (rebar_size <= current_size)
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 67647f1880fb8..f3c81c892786e 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_PCI)              += access.o bus.o probe.o host-bridge.o \
                                   remove.o pci.o pci-driver.o search.o \
-                                  rom.o setup-res.o irq.o vpd.o \
+                                  rebar.o rom.o setup-res.o irq.o vpd.o \
                                   setup-bus.o vc.o mmap.o devres.o
 
 obj-$(CONFIG_PCI)              += msi/
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 77dee43b78583..00784a60ba80b 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -158,8 +158,7 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, 
int resno)
        return dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)];
 }
 
-void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
-                              resource_size_t size)
+void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size)
 {
        if (!pci_resource_is_iov(resno)) {
                pci_warn(dev, "%s is not an IOV resource\n",
@@ -167,7 +166,8 @@ void pci_iov_resource_set_size(struct pci_dev *dev, int 
resno,
                return;
        }
 
-       dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)] = size;
+       resno = pci_resource_num_to_vf_bar(resno);
+       dev->sriov->barsz[resno] = pci_rebar_size_to_bytes(size);
 }
 
 bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
@@ -1339,29 +1339,16 @@ EXPORT_SYMBOL_GPL(pci_sriov_configure_simple);
  */
 int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size)
 {
-       u32 sizes;
-       int ret;
-
        if (!pci_resource_is_iov(resno))
                return -EINVAL;
 
        if (pci_iov_is_memory_decoding_enabled(dev))
                return -EBUSY;
 
-       sizes = pci_rebar_get_possible_sizes(dev, resno);
-       if (!sizes)
-               return -ENOTSUPP;
-
-       if (!(sizes & BIT(size)))
+       if (!pci_rebar_size_supported(dev, resno, size))
                return -EINVAL;
 
-       ret = pci_rebar_set_size(dev, resno, size);
-       if (ret)
-               return ret;
-
-       pci_iov_resource_set_size(dev, resno, pci_rebar_size_to_bytes(size));
-
-       return 0;
+       return pci_rebar_set_size(dev, resno, size);
 }
 EXPORT_SYMBOL_GPL(pci_iov_vf_bar_set_size);
 
@@ -1380,7 +1367,7 @@ EXPORT_SYMBOL_GPL(pci_iov_vf_bar_set_size);
 u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs)
 {
        u64 vf_len = pci_resource_len(dev, resno);
-       u32 sizes;
+       u64 sizes;
 
        if (!num_vfs)
                return 0;
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 9d6f74bd95f8c..cb512bf0df7cc 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1587,7 +1587,7 @@ static ssize_t __resource_resize_show(struct device *dev, 
int n, char *buf)
        pci_config_pm_runtime_get(pdev);
 
        ret = sysfs_emit(buf, "%016llx\n",
-                        (u64)pci_rebar_get_possible_sizes(pdev, n));
+                        pci_rebar_get_possible_sizes(pdev, n));
 
        pci_config_pm_runtime_put(pdev);
 
@@ -1599,18 +1599,13 @@ static ssize_t __resource_resize_store(struct device 
*dev, int n,
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct pci_bus *bus = pdev->bus;
-       struct resource *b_win, *res;
        unsigned long size;
-       int ret, i;
+       int ret;
        u16 cmd;
 
        if (kstrtoul(buf, 0, &size) < 0)
                return -EINVAL;
 
-       b_win = pbus_select_window(bus, pci_resource_n(pdev, n));
-       if (!b_win)
-               return -EINVAL;
-
        device_lock(dev);
        if (dev->driver || pci_num_vf(pdev)) {
                ret = -EBUSY;
@@ -1632,15 +1627,7 @@ static ssize_t __resource_resize_store(struct device 
*dev, int n,
 
        pci_remove_resource_files(pdev);
 
-       pci_dev_for_each_resource(pdev, res, i) {
-               if (i >= PCI_BRIDGE_RESOURCES)
-                       break;
-
-               if (b_win == pbus_select_window(bus, res))
-                       pci_release_resource(pdev, i);
-       }
-
-       ret = pci_resize_resource(pdev, n, size);
+       ret = pci_resize_resource(pdev, n, size, 0);
 
        pci_assign_unassigned_bus_resources(bus);
 
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b14dd064006cc..aedf6a9932cea 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1823,32 +1823,6 @@ static void pci_restore_config_space(struct pci_dev 
*pdev)
        }
 }
 
-static void pci_restore_rebar_state(struct pci_dev *pdev)
-{
-       unsigned int pos, nbars, i;
-       u32 ctrl;
-
-       pos = pdev->rebar_cap;
-       if (!pos)
-               return;
-
-       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
-       nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
-
-       for (i = 0; i < nbars; i++, pos += 8) {
-               struct resource *res;
-               int bar_idx, size;
-
-               pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
-               bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
-               res = pci_resource_n(pdev, bar_idx);
-               size = pci_rebar_bytes_to_size(resource_size(res));
-               ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
-               ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
-               pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
-       }
-}
-
 /**
  * pci_restore_state - Restore the saved state of a PCI device
  * @dev: PCI device that we're dealing with
@@ -3687,125 +3661,6 @@ void pci_acs_init(struct pci_dev *dev)
        pci_enable_acs(dev);
 }
 
-void pci_rebar_init(struct pci_dev *pdev)
-{
-       pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
-}
-
-/**
- * pci_rebar_find_pos - find position of resize ctrl reg for BAR
- * @pdev: PCI device
- * @bar: BAR to find
- *
- * Helper to find the position of the ctrl register for a BAR.
- * Returns -ENOTSUPP if resizable BARs are not supported at all.
- * Returns -ENOENT if no ctrl register for the BAR could be found.
- */
-static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
-{
-       unsigned int pos, nbars, i;
-       u32 ctrl;
-
-       if (pci_resource_is_iov(bar)) {
-               pos = pci_iov_vf_rebar_cap(pdev);
-               bar = pci_resource_num_to_vf_bar(bar);
-       } else {
-               pos = pdev->rebar_cap;
-       }
-
-       if (!pos)
-               return -ENOTSUPP;
-
-       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
-       nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
-
-       for (i = 0; i < nbars; i++, pos += 8) {
-               int bar_idx;
-
-               pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
-               bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl);
-               if (bar_idx == bar)
-                       return pos;
-       }
-
-       return -ENOENT;
-}
-
-/**
- * pci_rebar_get_possible_sizes - get possible sizes for BAR
- * @pdev: PCI device
- * @bar: BAR to query
- *
- * Get the possible sizes of a resizable BAR as bitmask defined in the spec
- * (bit 0=1MB, bit 31=128TB). Returns 0 if BAR isn't resizable.
- */
-u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
-{
-       int pos;
-       u32 cap;
-
-       pos = pci_rebar_find_pos(pdev, bar);
-       if (pos < 0)
-               return 0;
-
-       pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
-       cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
-
-       /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
-       if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
-           bar == 0 && cap == 0x700)
-               return 0x3f00;
-
-       return cap;
-}
-EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
-
-/**
- * pci_rebar_get_current_size - get the current size of a BAR
- * @pdev: PCI device
- * @bar: BAR to set size to
- *
- * Read the size of a BAR from the resizable BAR config.
- * Returns size if found or negative error code.
- */
-int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
-{
-       int pos;
-       u32 ctrl;
-
-       pos = pci_rebar_find_pos(pdev, bar);
-       if (pos < 0)
-               return pos;
-
-       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
-       return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl);
-}
-
-/**
- * pci_rebar_set_size - set a new size for a BAR
- * @pdev: PCI device
- * @bar: BAR to set size to
- * @size: new size as defined in the spec (0=1MB, 31=128TB)
- *
- * Set the new size of a BAR as defined in the spec.
- * Returns zero if resizing was successful, error code otherwise.
- */
-int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
-{
-       int pos;
-       u32 ctrl;
-
-       pos = pci_rebar_find_pos(pdev, bar);
-       if (pos < 0)
-               return pos;
-
-       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
-       ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
-       ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
-       pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
-       return 0;
-}
-
 /**
  * pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
  * @dev: the PCI device
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4492b809094b5..a1e7dbeb0f2cc 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -421,8 +421,10 @@ enum pci_bar_type {
 struct device *pci_get_host_bridge_device(struct pci_dev *dev);
 void pci_put_host_bridge_device(struct device *dev);
 
+void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size);
+int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int 
size,
+                                      int exclude_bars);
 unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);
-int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res);
 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, 
resource_size_t add_size, resource_size_t align);
 
 int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
@@ -808,8 +810,7 @@ void pci_iov_update_resource(struct pci_dev *dev, int 
resno);
 resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
 void pci_restore_iov_state(struct pci_dev *dev);
 int pci_iov_bus_range(struct pci_bus *bus);
-void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
-                              resource_size_t size);
+void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size);
 bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev);
 static inline u16 pci_iov_vf_rebar_cap(struct pci_dev *dev)
 {
@@ -851,7 +852,7 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
        return 0;
 }
 static inline void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
-                                            resource_size_t size) { }
+                                            int size) { }
 static inline bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
 {
        return false;
@@ -1020,12 +1021,9 @@ static inline int acpi_get_rc_resources(struct device 
*dev, const char *hid,
 #endif
 
 void pci_rebar_init(struct pci_dev *pdev);
+void pci_restore_rebar_state(struct pci_dev *pdev);
 int pci_rebar_get_current_size(struct pci_dev *pdev, int bar);
 int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size);
-static inline u64 pci_rebar_size_to_bytes(int size)
-{
-       return 1ULL << (size + 20);
-}
 
 struct device_node;
 
diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c
new file mode 100644
index 0000000000000..7f6dece19138e
--- /dev/null
+++ b/drivers/pci/rebar.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI Resizable BAR Extended Capability handling.
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/ioport.h>
+#include <linux/log2.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+#include "pci.h"
+
+#define PCI_REBAR_MIN_SIZE     ((resource_size_t)SZ_1M)
+
+/**
+ * pci_rebar_bytes_to_size - Convert size in bytes to PCI BAR Size
+ * @bytes: size in bytes
+ *
+ * Convert size in bytes to encoded BAR Size in Resizable BAR Capability
+ * (PCIe r6.2, sec. 7.8.6.3).
+ *
+ * Return: encoded BAR Size as defined in the PCIe spec (0=1MB, 31=128TB)
+ */
+int pci_rebar_bytes_to_size(u64 bytes)
+{
+       int rebar_minsize = ilog2(PCI_REBAR_MIN_SIZE);
+
+       bytes = roundup_pow_of_two(bytes);
+
+       return max(ilog2(bytes), rebar_minsize) - rebar_minsize;
+}
+EXPORT_SYMBOL_GPL(pci_rebar_bytes_to_size);
+
+/**
+ * pci_rebar_size_to_bytes - Convert encoded BAR Size to size in bytes
+ * @size: encoded BAR Size as defined in the PCIe spec (0=1MB, 31=128TB)
+ *
+ * Return: BAR size in bytes
+ */
+resource_size_t pci_rebar_size_to_bytes(int size)
+{
+       return 1ULL << (size + ilog2(PCI_REBAR_MIN_SIZE));
+}
+EXPORT_SYMBOL_GPL(pci_rebar_size_to_bytes);
+
+void pci_rebar_init(struct pci_dev *pdev)
+{
+       pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+}
+
+/**
+ * pci_rebar_find_pos - find position of resize control reg for BAR
+ * @pdev: PCI device
+ * @bar: BAR to find
+ *
+ * Helper to find the position of the control register for a BAR.
+ *
+ * Return:
+ * * %-ENOTSUPP if resizable BARs are not supported at all,
+ * * %-ENOENT if no control register for the BAR could be found.
+ */
+static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
+{
+       unsigned int pos, nbars, i;
+       u32 ctrl;
+
+       if (pci_resource_is_iov(bar)) {
+               pos = pci_iov_vf_rebar_cap(pdev);
+               bar = pci_resource_num_to_vf_bar(bar);
+       } else {
+               pos = pdev->rebar_cap;
+       }
+
+       if (!pos)
+               return -ENOTSUPP;
+
+       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+       nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+
+       for (i = 0; i < nbars; i++, pos += 8) {
+               int bar_idx;
+
+               pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+               bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl);
+               if (bar_idx == bar)
+                       return pos;
+       }
+
+       return -ENOENT;
+}
+
+/**
+ * pci_rebar_get_possible_sizes - get possible sizes for Resizable BAR
+ * @pdev: PCI device
+ * @bar: BAR to query
+ *
+ * Get the possible sizes of a resizable BAR as bitmask.
+ *
+ * Return: A bitmask of possible sizes (bit 0=1MB, bit 31=128TB), or %0 if
+ *        BAR isn't resizable.
+ */
+u64 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
+{
+       int pos;
+       u32 cap;
+
+       pos = pci_rebar_find_pos(pdev, bar);
+       if (pos < 0)
+               return 0;
+
+       pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
+       cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
+
+       /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
+       if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
+           bar == 0 && cap == 0x700)
+               return 0x3f00;
+
+       return cap;
+}
+EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
+
+/**
+ * pci_rebar_size_supported - check if size is supported for BAR
+ * @pdev: PCI device
+ * @bar: BAR to check
+ * @size: encoded size as defined in the PCIe spec (0=1MB, 31=128TB)
+ *
+ * Return: %true if @bar is resizable and @size is supported, otherwise
+ *        %false.
+ */
+bool pci_rebar_size_supported(struct pci_dev *pdev, int bar, int size)
+{
+       u64 sizes = pci_rebar_get_possible_sizes(pdev, bar);
+
+       return BIT(size) & sizes;
+}
+EXPORT_SYMBOL_GPL(pci_rebar_size_supported);
+
+/**
+ * pci_rebar_get_max_size - get the maximum supported size of a BAR
+ * @pdev: PCI device
+ * @bar: BAR to query
+ *
+ * Get the largest supported size of a resizable BAR as a size.
+ *
+ * Return: the encoded maximum BAR size as defined in the PCIe spec
+ *        (0=1MB, 31=128TB), or %-NOENT on error.
+ */
+int pci_rebar_get_max_size(struct pci_dev *pdev, int bar)
+{
+       u64 sizes;
+
+       sizes = pci_rebar_get_possible_sizes(pdev, bar);
+       if (!sizes)
+               return -ENOENT;
+
+       return __fls(sizes);
+}
+EXPORT_SYMBOL_GPL(pci_rebar_get_max_size);
+
+/**
+ * pci_rebar_get_current_size - get the current size of a Resizable BAR
+ * @pdev: PCI device
+ * @bar: BAR to get the size from
+ *
+ * Read the current size of a BAR from the Resizable BAR config.
+ *
+ * Return: BAR Size if @bar is resizable (0=1MB, 31=128TB), or negative on
+ *         error.
+ */
+int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
+{
+       int pos;
+       u32 ctrl;
+
+       pos = pci_rebar_find_pos(pdev, bar);
+       if (pos < 0)
+               return pos;
+
+       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+       return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl);
+}
+
+/**
+ * pci_rebar_set_size - set a new size for a Resizable BAR
+ * @pdev: PCI device
+ * @bar: BAR to set size to
+ * @size: new size as defined in the PCIe spec (0=1MB, 31=128TB)
+ *
+ * Set the new size of a BAR as defined in the spec.
+ *
+ * Return: %0 if resizing was successful, or negative on error.
+ */
+int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
+{
+       int pos;
+       u32 ctrl;
+
+       pos = pci_rebar_find_pos(pdev, bar);
+       if (pos < 0)
+               return pos;
+
+       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+       ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+       ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
+       pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+
+       if (pci_resource_is_iov(bar))
+               pci_iov_resource_set_size(pdev, bar, size);
+
+       return 0;
+}
+
+void pci_restore_rebar_state(struct pci_dev *pdev)
+{
+       unsigned int pos, nbars, i;
+       u32 ctrl;
+
+       pos = pdev->rebar_cap;
+       if (!pos)
+               return;
+
+       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+       nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+
+       for (i = 0; i < nbars; i++, pos += 8) {
+               struct resource *res;
+               int bar_idx, size;
+
+               pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+               bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+               res = pci_resource_n(pdev, bar_idx);
+               size = pci_rebar_bytes_to_size(resource_size(res));
+               ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+               ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
+               pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+       }
+}
+
+static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev,
+                                                 int resno)
+{
+       u16 cmd;
+
+       if (pci_resource_is_iov(resno))
+               return pci_iov_is_memory_decoding_enabled(dev);
+
+       pci_read_config_word(dev, PCI_COMMAND, &cmd);
+
+       return cmd & PCI_COMMAND_MEMORY;
+}
+
+void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size)
+{
+       resource_size_t res_size = pci_rebar_size_to_bytes(size);
+       struct resource *res = pci_resource_n(dev, resno);
+
+       if (pci_resource_is_iov(resno))
+               res_size *= pci_sriov_get_totalvfs(dev);
+
+       resource_set_size(res, res_size);
+}
+
+/**
+ * pci_resize_resource - reconfigure a Resizable BAR and resources
+ * @dev: the PCI device
+ * @resno: index of the BAR to be resized
+ * @size: new size as defined in the spec (0=1MB, 31=128TB)
+ * @exclude_bars: a mask of BARs that should not be released
+ *
+ * Reconfigure @resno to @size and re-run resource assignment algorithm
+ * with the new size.
+ *
+ * Prior to resize, release @dev resources that share a bridge window with
+ * @resno.  This unpins the bridge window resource to allow changing it.
+ *
+ * The caller may prevent releasing a particular BAR by providing
+ * @exclude_bars mask, but this may result in the resize operation failing
+ * due to insufficient space.
+ *
+ * Return: 0 on success, or negative on error. In case of an error, the
+ *         resources are restored to their original places.
+ */
+int pci_resize_resource(struct pci_dev *dev, int resno, int size,
+                       int exclude_bars)
+{
+       struct pci_host_bridge *host;
+       int old, ret;
+
+       /* Check if we must preserve the firmware's resource assignment */
+       host = pci_find_host_bridge(dev->bus);
+       if (host->preserve_config)
+               return -ENOTSUPP;
+
+       if (pci_resize_is_memory_decoding_enabled(dev, resno))
+               return -EBUSY;
+
+       if (!pci_rebar_size_supported(dev, resno, size))
+               return -EINVAL;
+
+       old = pci_rebar_get_current_size(dev, resno);
+       if (old < 0)
+               return old;
+
+       ret = pci_rebar_set_size(dev, resno, size);
+       if (ret)
+               return ret;
+
+       ret = pci_do_resource_release_and_resize(dev, resno, size, 
exclude_bars);
+       if (ret)
+               goto error_resize;
+       return 0;
+
+error_resize:
+       pci_rebar_set_size(dev, resno, old);
+       return ret;
+}
+EXPORT_SYMBOL(pci_resize_resource);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 3645f392a9fd3..6e90f46f52afd 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -15,6 +15,7 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/bug.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -135,6 +136,9 @@ static void restore_dev_resource(struct pci_dev_resource 
*dev_res)
 {
        struct resource *res = dev_res->res;
 
+       if (WARN_ON_ONCE(res->parent))
+               return;
+
        res->start = dev_res->start;
        res->end = dev_res->end;
        res->flags = dev_res->flags;
@@ -2420,18 +2424,16 @@ 
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
  * release it when possible. If the bridge window contains assigned
  * resources, it cannot be released.
  */
-int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res)
+static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource 
*res,
+                                         struct list_head *saved)
 {
        unsigned long type = res->flags;
        struct pci_dev_resource *dev_res;
-       struct pci_dev *bridge;
-       LIST_HEAD(saved);
+       struct pci_dev *bridge = NULL;
        LIST_HEAD(added);
        LIST_HEAD(failed);
        unsigned int i;
-       int ret;
-
-       down_read(&pci_bus_sem);
+       int ret = 0;
 
        while (!pci_is_root_bus(bus)) {
                bridge = bus->self;
@@ -2443,9 +2445,9 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, 
struct resource *res)
 
                /* Ignore BARs which are still in use */
                if (!res->child) {
-                       ret = add_to_list(&saved, bridge, res, 0, 0);
+                       ret = add_to_list(saved, bridge, res, 0, 0);
                        if (ret)
-                               goto cleanup;
+                               return ret;
 
                        pci_release_resource(bridge, i);
                } else {
@@ -2459,10 +2461,8 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, 
struct resource *res)
                bus = bus->parent;
        }
 
-       if (list_empty(&saved)) {
-               up_read(&pci_bus_sem);
+       if (!bridge)
                return -ENOENT;
-       }
 
        __pci_bus_size_bridges(bridge->subordinate, &added);
        __pci_bridge_assign_resources(bridge, &added, &failed);
@@ -2470,49 +2470,107 @@ int pbus_reassign_bridge_resources(struct pci_bus 
*bus, struct resource *res)
                free_list(&added);
 
        if (!list_empty(&failed)) {
-               if (pci_required_resource_failed(&failed, type)) {
+               if (pci_required_resource_failed(&failed, type))
                        ret = -ENOSPC;
-                       goto cleanup;
-               }
-               /* Only resources with unrelated types failed (again) */
                free_list(&failed);
+               if (ret)
+                       return ret;
+
+               /* Only resources with unrelated types failed (again) */
        }
 
-       list_for_each_entry(dev_res, &saved, list) {
+       list_for_each_entry(dev_res, saved, list) {
+               struct pci_dev *dev = dev_res->dev;
+
                /* Skip the bridge we just assigned resources for */
-               if (bridge == dev_res->dev)
+               if (bridge == dev)
+                       continue;
+
+               if (!dev->subordinate)
                        continue;
 
-               bridge = dev_res->dev;
-               pci_setup_bridge(bridge->subordinate);
+               pci_setup_bridge(dev->subordinate);
        }
 
-       free_list(&saved);
-       up_read(&pci_bus_sem);
        return 0;
+}
 
-cleanup:
-       /* Restore size and flags */
-       list_for_each_entry(dev_res, &failed, list)
-               restore_dev_resource(dev_res);
-       free_list(&failed);
+int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int 
size,
+                                      int exclude_bars)
+{
+       struct resource *res = pci_resource_n(pdev, resno);
+       struct pci_dev_resource *dev_res;
+       struct pci_bus *bus = pdev->bus;
+       struct resource *b_win, *r;
+       LIST_HEAD(saved);
+       unsigned int i;
+       int ret = 0;
+
+       b_win = pbus_select_window(bus, res);
+       if (!b_win)
+               return -EINVAL;
 
+       pci_dev_for_each_resource(pdev, r, i) {
+               if (i >= PCI_BRIDGE_RESOURCES)
+                       break;
+
+               if (exclude_bars & BIT(i))
+                       continue;
+
+               if (b_win != pbus_select_window(bus, r))
+                       continue;
+
+               ret = add_to_list(&saved, pdev, r, 0, 0);
+               if (ret)
+                       goto restore;
+               pci_release_resource(pdev, i);
+       }
+
+       pci_resize_resource_set_size(pdev, resno, size);
+
+       if (!bus->self)
+               goto out;
+
+       down_read(&pci_bus_sem);
+       ret = pbus_reassign_bridge_resources(bus, res, &saved);
+       if (ret)
+               goto restore;
+
+out:
+       up_read(&pci_bus_sem);
+       free_list(&saved);
+       return ret;
+
+restore:
        /* Revert to the old configuration */
        list_for_each_entry(dev_res, &saved, list) {
                struct resource *res = dev_res->res;
+               struct pci_dev *dev = dev_res->dev;
 
-               bridge = dev_res->dev;
-               i = pci_resource_num(bridge, res);
+               i = pci_resource_num(dev, res);
+
+               if (res->parent) {
+                       release_child_resources(res);
+                       pci_release_resource(dev, i);
+               }
 
                restore_dev_resource(dev_res);
 
-               pci_claim_resource(bridge, i);
-               pci_setup_bridge(bridge->subordinate);
-       }
-       free_list(&saved);
-       up_read(&pci_bus_sem);
+               ret = pci_claim_resource(dev, i);
+               if (ret)
+                       continue;
 
-       return ret;
+               if (i < PCI_BRIDGE_RESOURCES) {
+                       const char *res_name = pci_resource_name(dev, i);
+
+                       pci_update_resource(dev, i);
+                       pci_info(dev, "%s %pR: old value restored\n",
+                                res_name, res);
+               }
+               if (dev->subordinate)
+                       pci_setup_bridge(dev->subordinate);
+       }
+       goto out;
 }
 
 void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index c3ba4ccecd433..e5fcadfc58b01 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -431,84 +431,6 @@ int pci_release_resource(struct pci_dev *dev, int resno)
 }
 EXPORT_SYMBOL(pci_release_resource);
 
-static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev,
-                                                 int resno)
-{
-       u16 cmd;
-
-       if (pci_resource_is_iov(resno))
-               return pci_iov_is_memory_decoding_enabled(dev);
-
-       pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
-       return cmd & PCI_COMMAND_MEMORY;
-}
-
-static void pci_resize_resource_set_size(struct pci_dev *dev, int resno,
-                                        int size)
-{
-       resource_size_t res_size = pci_rebar_size_to_bytes(size);
-       struct resource *res = pci_resource_n(dev, resno);
-
-       if (!pci_resource_is_iov(resno)) {
-               resource_set_size(res, res_size);
-       } else {
-               resource_set_size(res, res_size * pci_sriov_get_totalvfs(dev));
-               pci_iov_resource_set_size(dev, resno, res_size);
-       }
-}
-
-int pci_resize_resource(struct pci_dev *dev, int resno, int size)
-{
-       struct resource *res = pci_resource_n(dev, resno);
-       struct pci_host_bridge *host;
-       int old, ret;
-       u32 sizes;
-
-       /* Check if we must preserve the firmware's resource assignment */
-       host = pci_find_host_bridge(dev->bus);
-       if (host->preserve_config)
-               return -ENOTSUPP;
-
-       /* Make sure the resource isn't assigned before resizing it. */
-       if (!(res->flags & IORESOURCE_UNSET))
-               return -EBUSY;
-
-       if (pci_resize_is_memory_decoding_enabled(dev, resno))
-               return -EBUSY;
-
-       sizes = pci_rebar_get_possible_sizes(dev, resno);
-       if (!sizes)
-               return -ENOTSUPP;
-
-       if (!(sizes & BIT(size)))
-               return -EINVAL;
-
-       old = pci_rebar_get_current_size(dev, resno);
-       if (old < 0)
-               return old;
-
-       ret = pci_rebar_set_size(dev, resno, size);
-       if (ret)
-               return ret;
-
-       pci_resize_resource_set_size(dev, resno, size);
-
-       /* Check if the new config works by trying to assign everything. */
-       if (dev->bus->self) {
-               ret = pbus_reassign_bridge_resources(dev->bus, res);
-               if (ret)
-                       goto error_resize;
-       }
-       return 0;
-
-error_resize:
-       pci_rebar_set_size(dev, resno, old);
-       pci_resize_resource_set_size(dev, resno, old);
-       return ret;
-}
-EXPORT_SYMBOL(pci_resize_resource);
-
 int pci_enable_resources(struct pci_dev *dev, int mask)
 {
        u16 cmd, old_cmd;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index d1fdf81fbe1e4..4b7f4c08b5c7b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1419,16 +1419,16 @@ void pcibios_reset_secondary_bus(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int pci_release_resource(struct pci_dev *dev, int resno);
-static inline int pci_rebar_bytes_to_size(u64 bytes)
-{
-       bytes = roundup_pow_of_two(bytes);
 
-       /* Return BAR size as defined in the resizable BAR specification */
-       return max(ilog2(bytes), 20) - 20;
-}
+/* Resizable BAR related routines */
+int pci_rebar_bytes_to_size(u64 bytes);
+resource_size_t pci_rebar_size_to_bytes(int size);
+u64 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
+bool pci_rebar_size_supported(struct pci_dev *pdev, int bar, int size);
+int pci_rebar_get_max_size(struct pci_dev *pdev, int bar);
+int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size,
+                                    int exclude_bars);
 
-u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
-int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size);
 int pci_select_bars(struct pci_dev *dev, unsigned long flags);
 bool pci_device_is_present(struct pci_dev *pdev);
 void pci_ignore_hotplug(struct pci_dev *dev);
-- 
2.51.2


Reply via email to