From 4e9f69d08253471d92bcc46b363fbada06bd59c4 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 24 Sep 2026 12:06:33 +0800 Subject: [PATCH] PCI: dw-rockchip: Move INTx IRQ setup to probe Since commit b376b3ff9cb0 ("PCI: dw-rockchip: Implement .reset_root_port() and use for link down"), .reset_root_port() re-runs the host ops .init() callback to reprogram the Root Complex after a controller reset. That works for the register programming, but .init() is not re-entrant: it also creates the INTx IRQ domain and installs the chained INTx handler. Every Root Port reset therefore ends up with a second IRQ domain registered for the same fwnode: the previous one is leaked, as it is never removed, and worse, 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. After a link down recovery, INTx interrupts are silently lost. Fix it by moving the of_irq_get_byname() lookup, the INTx IRQ domain creation and the chained handler installation out of .init() and into rockchip_pcie_configure_rc(), just before dw_pcie_host_init(). The lookup has to happen before the host is initialized, because dw_pcie_host_init() enumerates the bus and probes the downstream devices, and pci_assign_irq() maps their INTx interrupts at that point: if the domain does not exist yet, the mapping fails and the devices end up without a usable INTx. This also mirrors how the qcom driver requests its global IRQ, and leaves .init() with nothing but idempotent register programming, so both .reset_root_port() and dw_pcie_resume_noirq() can safely re-run it. Re-running of_irq_get_byname() on every resume is also gone. Fixes: b376b3ff9cb0 ("PCI: dw-rockchip: Implement .reset_root_port() and use for link down") Suggested-by: Niklas Cassel Signed-off-by: Shawn Lin Signed-off-by: Bjorn Helgaas Tested-by: Diederik de Haas # NanoPC-T6 Plus Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/1790222793-77182-1-git-send-email-shawn.lin@rock-chips.com --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index af26a07c0c9e..1c8fdbd4cd17 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -421,23 +421,9 @@ static void rockchip_pcie_stop_link(struct dw_pcie *pci) static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); - struct device *dev = rockchip->pci.dev; - int irq, ret; - - irq = of_irq_get_byname(dev->of_node, "legacy"); - 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"); - - 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; @@ -730,6 +716,19 @@ static int rockchip_pcie_configure_rc(struct platform_device *pdev, PCIE_CLIENT_SET_MODE(PCIE_CLIENT_MODE_RC), PCIE_CLIENT_GENERAL_CON); + irq = of_irq_get_byname(dev->of_node, "legacy"); + if (irq < 0) + return irq; + + ret = rockchip_pcie_init_irq_domain(rockchip); + if (ret < 0) { + dev_err(dev, "failed to init irq domain\n"); + return ret; + } + + irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler, + rockchip); + pp = &rockchip->pci.pp; pp->ops = &rockchip_pcie_host_ops;