[PATCH v4 3/3] PCI: imx6: Add root port reset to support link recovery
Bough Chen
haibo.chen at nxp.com
Tue May 12 20:32:26 PDT 2026
> -----Original Message-----
> From: Richard Zhu <hongxing.zhu at nxp.com>
> Sent: 2026年5月13日 10:51
> To: robh at kernel.org; krzk+dt at kernel.org; conor+dt at kernel.org;
> bhelgaas at google.com; Frank Li <frank.li at nxp.com>; l.stach at pengutronix.de;
> lpieralisi at kernel.org; kwilczynski at kernel.org; mani at kernel.org;
> s.hauer at pengutronix.de; kernel at pengutronix.de; festevam at gmail.com
> Cc: linux-pci at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> devicetree at vger.kernel.org; imx at lists.linux.dev; linux-kernel at vger.kernel.org;
> Hongxing Zhu <hongxing.zhu at nxp.com>
> Subject: [PATCH v4 3/3] PCI: imx6: Add root port reset to support link recovery
>
> The PCIe link can go down due to various unexpected circumstances. Add root
> port reset support to enable link recovery for the i.MX PCIe controller when the
> optional "intr" interrupt is present.
>
> Reset root port to uninitialize, initialize the PCIe controller, and restart the PCIe
> link at end when a link down event happens.
>
> On i.MX95 platforms, link events and PME share the same interrupt line.
> The link event interrupt cannot use a threaded-only IRQ handler because the
> PME driver uses request_irq() with only the IRQF_SHARED flag set, which
> requires a primary handler.
>
> To handle this shared interrupt scenario, register a primary interrupt handler
> with IRQF_SHARED for link events and manipulate the link event enable bits to
> ensure the shared interrupt source triggers only one handler at a time.
>
> Signed-off-by: Richard Zhu <hongxing.zhu at nxp.com>
> ---
> drivers/pci/controller/dwc/pci-imx6.c | 123 ++++++++++++++++++++++++++
> 1 file changed, 123 insertions(+)
>
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> b/drivers/pci/controller/dwc/pci-imx6.c
> index 1034ac5c5f5c..79c92c77b85b 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -34,6 +34,7 @@
> #include <linux/pm_runtime.h>
>
> #include "../../pci.h"
> +#include "../pci-host-common.h"
> #include "pcie-designware.h"
>
> #define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9)
> @@ -78,6 +79,10 @@
> #define IMX95_SID_MASK GENMASK(5, 0)
> #define IMX95_MAX_LUT 32
>
> +#define IMX95_LINK_INT_CTRL_STS 0x1040
> +#define IMX95_LINK_DOWN_INT_STS BIT(11)
> +#define IMX95_LINK_DOWN_INT_EN BIT(10)
> +
> #define IMX95_PCIE_RST_CTRL 0x3010
> #define IMX95_PCIE_COLD_RST BIT(0)
>
> @@ -125,6 +130,8 @@ enum imx_pcie_variants {
> #define IMX_PCIE_MAX_INSTANCES 2
>
> struct imx_pcie;
> +static int imx_pcie_reset_root_port(struct pci_host_bridge *bridge,
> + struct pci_dev *pdev);
>
> struct imx_pcie_drvdata {
> enum imx_pcie_variants variant;
> @@ -158,6 +165,7 @@ struct imx_pcie {
> bool supports_clkreq;
> bool enable_ext_refclk;
> struct regmap *iomuxc_gpr;
> + u32 lnk_intr;
> u16 msi_ctrl;
> u32 controller_id;
> struct reset_control *pciephy_reset;
> @@ -1301,6 +1309,13 @@ static int imx_pcie_host_init(struct dw_pcie_rp
> *pp)
>
> imx_setup_phy_mpll(imx_pcie);
>
> + /*
> + * Callback invoked by PCI core when link down is detected and
> + * recovery is needed.
> + */
> + if (pp->bridge)
> + pp->bridge->reset_root_port = imx_pcie_reset_root_port;
> +
> return 0;
>
> err_phy_off:
> @@ -1568,6 +1583,9 @@ static int imx_pcie_suspend_noirq(struct device
> *dev)
> if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_SUPPORTS_SUSPEND))
> return 0;
>
> + if (imx_pcie->lnk_intr)
> + regmap_clear_bits(imx_pcie->iomuxc_gpr,
> IMX95_LINK_INT_CTRL_STS,
> + IMX95_LINK_DOWN_INT_EN);
> imx_pcie_msi_save_restore(imx_pcie, true);
> if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT))
> imx_pcie_lut_save(imx_pcie);
> @@ -1618,6 +1636,9 @@ static int imx_pcie_resume_noirq(struct device *dev)
> if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT))
> imx_pcie_lut_restore(imx_pcie);
> imx_pcie_msi_save_restore(imx_pcie, false);
> + if (imx_pcie->lnk_intr)
> + regmap_set_bits(imx_pcie->iomuxc_gpr, IMX95_LINK_INT_CTRL_STS,
> + IMX95_LINK_DOWN_INT_EN);
>
> return 0;
> }
> @@ -1627,6 +1648,84 @@ static const struct dev_pm_ops imx_pcie_pm_ops =
> {
> imx_pcie_resume_noirq)
> };
>
> +static irqreturn_t imx_pcie_lnk_irq_isr(int irq, void *priv) {
> + struct imx_pcie *imx_pcie = priv;
> + struct dw_pcie *pci = imx_pcie->pci;
> + struct device *dev = pci->dev;
> + u32 val;
> +
> + regmap_read(imx_pcie->iomuxc_gpr, IMX95_LINK_INT_CTRL_STS, &val);
> + if (val & IMX95_LINK_DOWN_INT_STS) {
> + dev_dbg(dev, "PCIe link down detected, initiating recovery\n");
> + regmap_clear_bits(imx_pcie->iomuxc_gpr,
> IMX95_LINK_INT_CTRL_STS,
> + IMX95_LINK_DOWN_INT_EN);
> + regmap_set_bits(imx_pcie->iomuxc_gpr, IMX95_LINK_INT_CTRL_STS,
> + IMX95_LINK_DOWN_INT_STS);
Hi Richard
Better to add comment here to point out that write the IMX95_LINK_DOWN_INT_STS means clear this bit, or mention this bit is W1C.
Regards
Haibo Chen
More information about the linux-arm-kernel
mailing list