[PATCH v3] PCI: dwc: Add suspend_poweroff flag for platforms with RC power loss
From: hongxing . zhu
Date: Thu Sep 17 2026 - 02:29:31 EST
From: Richard Zhu <hongxing.zhu@xxxxxxx>
Some platforms like i.MX power off their PCIe RC controllers during
system suspend, requiring full re-initialization on resume. These
platforms need to enter L2 state to properly notify endpoints before
power loss.
According to PCIe base spec r7.0, sec 5.2, the system software should
transition the device into D3Hot before broadcasting the PME_Turn_Off
message to initiate L2 entry. In practice, endpoint drivers properly
put their devices into D3hot during the suspend flow. However, some
wakeup-capable endpoint devices that don't support PME from D3cold
fail the D3cold capability check in pci_host_common_d3cold_possible(),
which currently prevents L2 entry even though the devices are already
in the required D3hot state.
For platforms where the RC loses power during suspend, L2 entry is
essential regardless of D3cold support, as the link will be lost anyway
when the RC powers off. Without L2 entry, endpoints are not properly
notified before power loss, which can cause issues during resume and
reinitialization.
Add a suspend_poweroff flag to allow L2 entry for devices that are in
D3hot but fail the D3cold capability check, and enable it for i.MX PCIe
controllers that lose power during suspend.
Signed-off-by: Richard Zhu <hongxing.zhu@xxxxxxx>
---
Changes in v3:
- Refine commit message and comments to clarify that devices are in D3hot
but fail D3cold capability check, blocking L2 entry
- Remove misleading spec violation statements
Changes in v2:
- Rename the new added flag.
- Update commit description and comments.
---
drivers/pci/controller/dwc/pci-imx6.c | 8 ++++++++
drivers/pci/controller/dwc/pcie-designware-host.c | 10 ++++++++--
drivers/pci/controller/dwc/pcie-designware.h | 8 ++++++++
3 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 3f55c91d0921a..c9e630b2c4fdd 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -2016,6 +2016,14 @@ static int imx_pcie_probe(struct platform_device *pdev)
goto err_pwrctrl_destroy;
}
+ /*
+ * i.MX RC is powered off during suspend, requiring L2 entry
+ * to notify endpoints before power loss. Some endpoints fail
+ * the D3cold capability check, which would normally prevent
+ * L2 entry. Force L2 entry anyway since the link will be lost
+ * when RC powers off.
+ */
+ pci->pp.suspend_poweroff = true;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY))
pci->pp.skip_l23_ready = true;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_KEEP_MSI_CAP))
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index f5a38e6fd8d79..133050c12628a 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -1224,14 +1224,20 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
{
- bool pme_capable = false;
+ bool d3cold, pme_capable = false;
int ret = 0;
u32 val;
if (!dw_pcie_link_up(pci))
goto stop_link;
- if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable))
+ d3cold = pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable);
+ /*
+ * Enter L2 if D3cold is possible, or if the platform loses RC power
+ * during suspend (in which case L2 entry is required to notify
+ * endpoints before power loss, regardless of D3cold support).
+ */
+ if (!d3cold && !pci->pp.suspend_poweroff)
return 0;
if (pci->pp.ops->pme_turn_off) {
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 0735ae9409240..e66b3f259fc82 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -471,6 +471,14 @@ struct dw_pcie_rp {
bool native_ecam;
bool skip_l23_ready;
bool skip_pwrctrl_off;
+ /*
+ * suspend_poweroff: Allow L2 entry during suspend even when D3cold
+ * capability check fails. Used on platforms where RC loses power
+ * during suspend. Some wakeup-capable endpoints in D3hot fail the
+ * D3cold capability check, but L2 entry is still required to
+ * properly notify endpoints before RC power loss.
+ */
+ bool suspend_poweroff;
};
struct dw_pcie_ep_ops {
base-commit: 1fefdcbe1697cf25a7f7b307078742d9757fff46
--
2.34.1