[PATCH 2/2] PCI: dw-rockchip: Do not recreate the INTx irq domain on root port reset
Shawn Lin
shawn.lin at rock-chips.com
Wed Sep 2 19:44:37 PDT 2026
.reset_root_port() re-runs the host ops .init() callback to reprogram
the Root Complex after the controller reset. However, .init() also
creates a new INTx irq domain on every root port reset, so that:
- the previous irq domain is leaked, as it is never removed, and two
irq domains end up registered for the same fwnode;
- the INTx virqs of the downstream PCI devices were allocated in the
previous irq domain and are never re-mapped, while the chained
handler now looks up virqs in the new, empty domain. Hence, after a
link down recovery, INTx interrupts are silently lost.
Split the (re)programming of the Root Complex registers out of .init()
into rockchip_pcie_host_hw_init() and call that from .reset_root_port()
instead. The INTx irq domain and the chained handler are now only set up
once, at probe time, which keeps the already mapped virqs valid across
root port resets.
Fixes: b376b3ff9cb0 ("PCI: dw-rockchip: Implement .reset_root_port() and use for link down")
Cc: Niklas Cassel <cassel at kernel.org>
Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 37 +++++++++++++++++----------
1 file changed, 23 insertions(+), 14 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 50824c8..ff92394 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -418,6 +418,27 @@ static void rockchip_pcie_stop_link(struct dw_pcie *pci)
rockchip_pcie_ltssm_trace(rockchip, false);
}
+/*
+ * (Re)program the Root Complex registers that are cleared by the controller
+ * reset. Called from .init() at probe time and from .reset_root_port().
+ * The INTx irq domain must not be touched here, as downstream devices hold
+ * virqs mapped in it.
+ */
+static void rockchip_pcie_host_hw_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+
+ pci->dbi_base2 = pci->dbi_base + PCIE_TYPE0_HDR_DBI2_OFFSET;
+
+ rockchip_pcie_configure_l1ss(pci);
+ rockchip_pcie_enable_l0s(pci);
+ pp->bridge->reset_root_port = rockchip_pcie_rc_reset_root_port;
+
+ /* Disable Root Ports BAR0 and BAR1 as they report bogus size */
+ dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0);
+ dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_1, 0x0);
+}
+
static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -429,8 +450,6 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
if (irq < 0)
return irq;
- pci->dbi_base2 = pci->dbi_base + PCIE_TYPE0_HDR_DBI2_OFFSET;
-
ret = rockchip_pcie_init_irq_domain(rockchip);
if (ret < 0) {
dev_err(dev, "failed to init irq domain\n");
@@ -440,13 +459,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler,
rockchip);
- rockchip_pcie_configure_l1ss(pci);
- rockchip_pcie_enable_l0s(pci);
- pp->bridge->reset_root_port = rockchip_pcie_rc_reset_root_port;
-
- /* Disable Root Ports BAR0 and BAR1 as they report bogus size */
- dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0);
- dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_1, 0x0);
+ rockchip_pcie_host_hw_init(pp);
return 0;
}
@@ -920,11 +933,7 @@ static int rockchip_pcie_rc_reset_root_port(struct pci_host_bridge *bridge,
if (ret)
goto deinit_phy;
- ret = pp->ops->init(pp);
- if (ret) {
- dev_err(dev, "Host init failed: %d\n", ret);
- goto deinit_clk;
- }
+ rockchip_pcie_host_hw_init(pp);
/* LTSSM enable control mode */
val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1);
--
2.7.4
More information about the Linux-rockchip
mailing list