[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