PCI: dwc: Fix missing iATU setup when ECAM is enabled

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: f6fd357f7a ("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
This commit is contained in:
Krishna Chaitanya Chundru 2026-01-27 16:10:41 +01:00 committed by Manivannan Sadhasivam
parent b5dab9b38d
commit 43d324eeb0
2 changed files with 28 additions and 11 deletions

View 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;

View 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) {