[PATCH v9] PCI: dwc: Don't poll L2 if skip_l23_wait is true

Manivannan Sadhasivam mani at kernel.org
Tue Jan 20 23:53:57 PST 2026


On Wed, Jan 14, 2026 at 04:33:00PM +0800, Richard Zhu wrote:
> Refer to PCIe r6.0, sec 5.2, fig 5-1 Link Power Management State Flow
> Diagram. Both L0 and L2/L3 Ready can be transferred to LDn directly.
> 
> It's harmless to let dw_pcie_suspend_noirq() proceed suspend after the
> PME_Turn_Off is sent out, whatever the LTSSM state is in L2 or L3 after
> a recommended 10ms max wait refer to PCIe r6.0, sec 5.3.3.2.1 PME
> Synchronization.
> 
> The LTSSM states are inaccessible on i.MX6QP and i.MX7D after the
> PME_Turn_Off is sent out.
> 
> To support this case, don't poll L2 state and apply a simple delay of
> PCIE_PME_TO_L2_TIMEOUT_US(10ms) if the skip_l23_wait flag is true in
> suspend.
> 

I think this patch should simply say:

"In i.MX6QP and i.MX7D SoCs, LTSSM registers are not accessible once
PME_Turn_Off is broadcasted to the link. So there is no way to verify whether
the link has entered L2/L3 state or not.

Hence, add a new flag 'dw_pcie_rp::skip_l23_wait' and set it for the above
mentioned SoCs. This flag when set, will allow the DWC core to skip the L23 poll
and just wait for 10ms as per the delay mentioned in PCIe spec r6.0 sec
5.3.3.2.1."

Does it look good?

- Mani

> Cc: stable at vger.kernel.org
> Fixes: 4774faf854f5 ("PCI: dwc: Implement generic suspend/resume functionality")
> Fixes: a528d1a72597 ("PCI: imx6: Use DWC common suspend resume method")
> Signed-off-by: Richard Zhu <hongxing.zhu at nxp.com>
> Reviewed-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/pci/controller/dwc/pci-imx6.c             |  5 +++++
>  drivers/pci/controller/dwc/pcie-designware-host.c | 15 +++++++++++++++
>  drivers/pci/controller/dwc/pcie-designware.h      |  1 +
>  3 files changed, 21 insertions(+)
> 
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> index 4668fc9648bf..cbe98824427b 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -114,6 +114,7 @@ enum imx_pcie_variants {
>  #define IMX_PCIE_FLAG_BROKEN_SUSPEND		BIT(9)
>  #define IMX_PCIE_FLAG_HAS_LUT			BIT(10)
>  #define IMX_PCIE_FLAG_8GT_ECN_ERR051586		BIT(11)
> +#define IMX_PCIE_FLAG_SKIP_L23_WAIT		BIT(12)
>  
>  #define imx_check_flag(pci, val)	(pci->drvdata->flags & val)
>  
> @@ -1777,6 +1778,8 @@ static int imx_pcie_probe(struct platform_device *pdev)
>  		 */
>  		imx_pcie_add_lut_by_rid(imx_pcie, 0);
>  	} else {
> +		if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_WAIT))
> +			pci->pp.skip_l23_wait = true;
>  		pci->pp.use_atu_msg = true;
>  		ret = dw_pcie_host_init(&pci->pp);
>  		if (ret < 0)
> @@ -1838,6 +1841,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
>  		.variant = IMX6QP,
>  		.flags = IMX_PCIE_FLAG_IMX_PHY |
>  			 IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
> +			 IMX_PCIE_FLAG_SKIP_L23_WAIT |
>  			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
>  		.dbi_length = 0x200,
>  		.gpr = "fsl,imx6q-iomuxc-gpr",
> @@ -1854,6 +1858,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
>  		.variant = IMX7D,
>  		.flags = IMX_PCIE_FLAG_SUPPORTS_SUSPEND |
>  			 IMX_PCIE_FLAG_HAS_APP_RESET |
> +			 IMX_PCIE_FLAG_SKIP_L23_WAIT |
>  			 IMX_PCIE_FLAG_HAS_PHY_RESET,
>  		.gpr = "fsl,imx7d-iomuxc-gpr",
>  		.mode_off[0] = IOMUXC_GPR12,
> diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> index fad0cbedefbc..5aa7f23bb58e 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> @@ -1194,6 +1194,21 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
>  			return ret;
>  	}
>  
> +	/*
> +	 * Skip L23 poll and wait to avoid the read hang, when LTSSM is
> +	 * not powered in L2/L3/LDn properly.
> +	 *
> +	 * Refer to PCIe r6.0, sec 5.2, fig 5-1 Link Power Management
> +	 * State Flow Diagram. Both L0 and L2/L3 Ready can be
> +	 * transferred to LDn directly. On the LTSSM states poll broken
> +	 * platforms, add a max 10ms delay refer to PCIe r6.0,
> +	 * sec 5.3.3.2.1 PME Synchronization.
> +	 */
> +	if (pci->pp.skip_l23_wait) {
> +		mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000);
> +		goto stop_link;
> +	}
> +
>  	ret = read_poll_timeout(dw_pcie_get_ltssm, val,
>  				val == DW_PCIE_LTSSM_L2_IDLE ||
>  				val <= DW_PCIE_LTSSM_DETECT_WAIT,
> diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> index f87c67a7a482..b31f8061f23a 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.h
> +++ b/drivers/pci/controller/dwc/pcie-designware.h
> @@ -442,6 +442,7 @@ struct dw_pcie_rp {
>  	struct pci_config_window *cfg;
>  	bool			ecam_enabled;
>  	bool			native_ecam;
> +	bool                    skip_l23_wait;
>  };
>  
>  struct dw_pcie_ep_ops {
> -- 
> 2.37.1
> 

-- 
மணிவண்ணன் சதாசிவம்



More information about the linux-arm-kernel mailing list