mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:04:43 +01:00
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:
parent
b5dab9b38d
commit
43d324eeb0
2 changed files with 28 additions and 11 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue