Commit 43d324ee authored by Krishna Chaitanya Chundru's avatar Krishna Chaitanya Chundru Committed by Manivannan Sadhasivam
Browse files

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: f6fd357f ("PCI: dwc: Prepare the driver for enabling ECAM mechanism using iATU 'CFG Shift Feature'")
Reported-by: default avatarMaciej 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: default avatarKrishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Co-developed-by: default avatarNiklas Cassel <cassel@kernel.org>
Signed-off-by: default avatarNiklas Cassel <cassel@kernel.org>
[mani: used imperative tone]
Signed-off-by: default avatarManivannan Sadhasivam <mani@kernel.org>
Tested-by: default avatarMaciej W. Rozycki <macro@orcam.me.uk>
Reviewed-by: default avatarDamien Le Moal <dlemoal@kernel.org>
Reviewed-by: default avatarHans Zhang <zhanghuabing@ecosda.com>
Reviewed-by: default avatarFrank 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
parent b5dab9b3
Loading
Loading
Loading
Loading
+22 −11
Original line number Diff line number Diff line
@@ -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.
	 */
	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;
+6 −0
Original line number Diff line number Diff line
@@ -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) {