[PATCH v2 4/4] PCI: dwc: Fix missing iATU setup when ECAM is enabled

Niklas Cassel
2026-01-22T22:29:39Z

From: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>

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, the ECAM iATU configuration is moved into
dw_pcie_iatu_setup(), and dw_pcie_iatu_setup() is invoked when ECAM is
enabled.

Furthermore, error checks are added in dw_pcie_prog_outbound_atu() and
dw_pcie_prog_inbound_atu() so that an error is returned if trying to
programming an iATU that is outside the number of iATUs provided 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>
---
.../pci/controller/dwc/pcie-designware-host.c | 30 +++++++++++--------
drivers/pci/controller/dwc/pcie-designware.c | 6 ++++
2 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 76be24af7cfd..ef66a031f0bb 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -441,7 +441,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
/*
* Root bus under the host bridge doesn't require any iATU configuration
* as DBI region will be used to access root bus config space.
- * Immediate bus under Root Bus, needs type 0 iATU configuration and
+ * Immediate bus under Root Bus needs type 0 iATU configuration and
* remaining buses need type 1 iATU configuration.
*/
atu.index = 0;
@@ -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_to_use = 1;
+ if (pp->ecam_enabled) {
+ ob_iatu_index_to_use = 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_to_use = 1;
+ }
+
resource_list_for_each_entry(entry, &pp->bridge->windows) {
resource_size_t res_size;

@@ -1157,9 +1162,10 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
/*
* If the platform provides its own child bus config accesses, it means
* the platform uses its own address translation component rather than
- * ATU, so we should not program the ATU here.
+ * ATU, so we should not program the ATU here. If ECAM is enabled,
+ * config space access goes through ATU, so set up 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;
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 2fa9f6ee149e..225897c87c49 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -531,6 +531,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) ||
@@ -604,6 +607,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) {
--
2.52.0