]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: dwc: Fix missing iATU setup when ECAM is enabled
authorKrishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Tue, 27 Jan 2026 15:10:41 +0000 (16:10 +0100)
committerManivannan Sadhasivam <mani@kernel.org>
Thu, 5 Feb 2026 12:53:30 +0000 (18:23 +0530)
When ECAM is enabled, the driver skipped calling dw_pcie_iatu_setup()
before configuring ECAM iATU entries. This left IO and MEM outbound
windows unprogrammed, resulting in broken IO transactions. Additionally,
dw_pcie_config_ecam_iatu() was only called during host initialization,
so ECAM-related iATU entries were not restored after suspend/resume,
leading to failures in configuration space access

To resolve these issues, move the ECAM iATU configuration to
dw_pcie_iatu_setup(), and invoke dw_pcie_iatu_setup() when ECAM is
enabled.

Furthermore, add error checks in dw_pcie_prog_outbound_atu() and
dw_pcie_prog_inbound_atu() such that an error is returned if the caller is
trying to program an iATU that is outside the number of iATUs supported by
the controller.

Fixes: f6fd357f7afb ("PCI: dwc: Prepare the driver for enabling ECAM mechanism using iATU 'CFG Shift Feature'")
Reported-by: Maciej W. Rozycki <macro@orcam.me.uk>
Closes: https://lore.kernel.org/all/alpine.DEB.2.21.2511280256260.36486@angie.orcam.me.uk/
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Co-developed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
[mani: used imperative tone]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Tested-by: Maciej W. Rozycki <macro@orcam.me.uk>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hans Zhang <zhanghuabing@ecosda.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Cc: stable+noautosel@kernel.org # depends on Clean up iATU index usage in dw_pcie_iatu_setup()
Link: https://patch.msgid.link/20260127151038.1484881-8-cassel@kernel.org
drivers/pci/controller/dwc/pcie-designware-host.c
drivers/pci/controller/dwc/pcie-designware.c

index 87e6a32dbb9ae84b8142b7192288acfd90734efa..bc2e08ec515ee8d87ffe6c654b7c180ab92257a4 100644 (file)
@@ -641,14 +641,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
        if (ret)
                goto err_free_msi;
 
-       if (pp->ecam_enabled) {
-               ret = dw_pcie_config_ecam_iatu(pp);
-               if (ret) {
-                       dev_err(dev, "Failed to configure iATU in ECAM mode\n");
-                       goto err_free_msi;
-               }
-       }
-
        /*
         * Allocate the resource for MSG TLP before programming the iATU
         * outbound window in dw_pcie_setup_rc(). Since the allocation depends
@@ -915,8 +907,21 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
         * NOTE: For outbound address translation, outbound iATU at index 0 is
         * reserved for CFG IOs (dw_pcie_other_conf_map_bus()), thus start at
         * index 1.
+        *
+        * If using ECAM, outbound iATU at index 0 and index 1 is reserved for
+        * CFG IOs.
         */
-       ob_iatu_index = 1;
+       if (pp->ecam_enabled) {
+               ob_iatu_index = 2;
+               ret = dw_pcie_config_ecam_iatu(pp);
+               if (ret) {
+                       dev_err(pci->dev, "Failed to configure iATU in ECAM mode\n");
+                       return ret;
+               }
+       } else {
+               ob_iatu_index = 1;
+       }
+
        resource_list_for_each_entry(entry, &pp->bridge->windows) {
                resource_size_t res_size;
 
@@ -985,8 +990,14 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
                         * be shared between I/O space and CFG IOs, by
                         * temporarily reconfiguring the iATU to CFG space, in
                         * order to do a CFG IO, and then immediately restoring
-                        * it to I/O space.
+                        * it to I/O space. This is only implemented when using
+                        * dw_pcie_other_conf_map_bus(), which is not the case
+                        * when using ECAM.
                         */
+                       if (pp->ecam_enabled) {
+                               dev_err(pci->dev, "Cannot add outbound window for I/O\n");
+                               return -ENOMEM;
+                       }
                        pp->cfg0_io_shared = true;
                }
        }
@@ -1157,7 +1168,7 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
         * the platform uses its own address translation component rather than
         * ATU, so we should not program the ATU here.
         */
-       if (pp->bridge->child_ops == &dw_child_pcie_ops) {
+       if (pp->bridge->child_ops == &dw_child_pcie_ops || pp->ecam_enabled) {
                ret = dw_pcie_iatu_setup(pp);
                if (ret)
                        return ret;
index 18331d9e85be30462277532afa71b5850438f32e..5741c09dde7f40487c6da6dfd66f7c8d96a74259 100644 (file)
@@ -532,6 +532,9 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
        u32 retries, val;
        u64 limit_addr;
 
+       if (atu->index >= pci->num_ob_windows)
+               return -ENOSPC;
+
        limit_addr = parent_bus_addr + atu->size - 1;
 
        if ((limit_addr & ~pci->region_limit) != (parent_bus_addr & ~pci->region_limit) ||
@@ -605,6 +608,9 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
        u64 limit_addr = pci_addr + size - 1;
        u32 retries, val;
 
+       if (index >= pci->num_ib_windows)
+               return -ENOSPC;
+
        if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
            !IS_ALIGNED(parent_bus_addr, pci->region_align) ||
            !IS_ALIGNED(pci_addr, pci->region_align) || !size) {