From fc4e84ceb77c9584de762ef7eeee8becbd9fa7c3 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Mon, 24 Nov 2025 02:24:34 -0800 Subject: [PATCH 1/7] FROMLIST: dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Add Kaanapali compatible Document compatible for the QMP PCIe PHY on Kaanapali platform. Link: https://lore.kernel.org/all/20251124-kaanapali-pcie-phy-v4-1-d04ee9cca83b@oss.qualcomm.com/ Signed-off-by: Jingyi Wang Reviewed-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Signed-off-by: Qiang Yu --- .../devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 3a35120a77ec0..83df98c352702 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -183,6 +183,7 @@ allOf: enum: - qcom,glymur-qmp-gen4x2-pcie-phy - qcom,glymur-qmp-gen5x4-pcie-phy + - qcom,kaanapali-qmp-gen3x2-pcie-phy - qcom,qcs8300-qmp-gen4x2-pcie-phy - qcom,sa8775p-qmp-gen4x2-pcie-phy - qcom,sa8775p-qmp-gen4x4-pcie-phy From 11bb1bc83bd2624b99548070fce4e6988f14de66 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 28 Jan 2026 17:10:42 +0530 Subject: [PATCH 2/7] FROMLIST: PCI: dwc: Use common D3cold eligibility helper in suspend path Previously, the driver skipped putting the link into L2/device state in D3cold whenever L1 ASPM was enabled, since some devices (e.g. NVMe) expect low resume latency and may not tolerate deeper power states. However, such devices typically remain in D0 and are already covered by the new helper's requirement that all endpoints be in D3hot before the host bridge may enter D3cold. So, replace the local L1/L1SS-based check in dw_pcie_suspend_noirq() with the shared pci_host_common_can_enter_d3cold() helper to decide whether the DesignWare host bridge can safely transition to D3cold. Link: https://lore.kernel.org/r/20260128-d3cold-v1-2-dd8f3f0ce824@oss.qualcomm.com Signed-off-by: Krishna Chaitanya Chundru --- drivers/pci/controller/dwc/pcie-designware-host.c | 7 +------ drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 6ae6189e9b8a9..fafcef9aa33f8 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1218,18 +1218,13 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) int dw_pcie_suspend_noirq(struct dw_pcie *pci) { - u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); int ret = 0; u32 val; if (!dw_pcie_link_up(pci)) goto stop_link; - /* - * If L1SS is supported, then do not put the link into L2 as some - * devices such as NVMe expect low resume latency. - */ - if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1) + if (!pci_host_common_can_enter_d3cold(pci->pp.bridge)) 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 ae6389dd9caa5..3f06b1af63c5f 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -26,6 +26,7 @@ #include #include +#include "../pci-host-common.h" #include "../../pci.h" /* DWC PCIe IP-core versions (native support since v4.70a) */ From b5a78e601aab82d30c53916ef378d7eeca515ac6 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 28 Jan 2026 17:10:41 +0530 Subject: [PATCH 3/7] FROMLIST: PCI: host-common: Add shared D3cold eligibility helper for host bridges Add a common helper, pci_host_common_can_enter_d3cold(), to determine whether a PCI host bridge can safely transition to D3cold. The helper walks all devices on the bridge's bus and only allows the host bridge to enter D3cold if all PCIe endpoints are already in PCI_D3hot. For devices that may wake the system, it additionally requires that the device supports PME wakeup from D3cold(with WAKE#). Devices without wakeup enabled are not restricted by this check and can be allowed to keep device in D3cold. Link: https://lore.kernel.org/r/20260128-d3cold-v1-1-dd8f3f0ce824@oss.qualcomm.com Signed-off-by: Krishna Chaitanya Chundru --- drivers/pci/controller/pci-host-common.c | 29 ++++++++++++++++++++++++ drivers/pci/controller/pci-host-common.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index d6258c1cffe5e..1e53819f5e720 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -106,5 +106,34 @@ void pci_host_common_remove(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(pci_host_common_remove); +static int pci_host_common_check_d3cold(struct pci_dev *pdev, void *userdata) +{ + bool *d3cold_allow = userdata; + + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT) + return 0; + + if (pdev->current_state != PCI_D3hot) + goto exit; + + if (device_may_wakeup(&pdev->dev) && !pci_pme_capable(pdev, PCI_D3cold)) + goto exit; + + return 0; +exit: + *d3cold_allow = false; + return -EBUSY; +} + +bool pci_host_common_can_enter_d3cold(struct pci_host_bridge *bridge) +{ + bool d3cold_allow = true; + + pci_walk_bus(bridge->bus, pci_host_common_check_d3cold, &d3cold_allow); + + return d3cold_allow; +} +EXPORT_SYMBOL_GPL(pci_host_common_can_enter_d3cold); + MODULE_DESCRIPTION("Common library for PCI host controller drivers"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h index b5075d4bd7eb3..18a731bca0588 100644 --- a/drivers/pci/controller/pci-host-common.h +++ b/drivers/pci/controller/pci-host-common.h @@ -20,4 +20,6 @@ void pci_host_common_remove(struct platform_device *pdev); struct pci_config_window *pci_host_common_ecam_create(struct device *dev, struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops); + +bool pci_host_common_can_enter_d3cold(struct pci_host_bridge *bridge); #endif From fb8bbdccef33e5c20e0648234a9736919843caf7 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 28 Jan 2026 17:10:43 +0530 Subject: [PATCH 4/7] FROMLIST: PCI: qcom: Add D3cold support Add pme_turn_off() support and use DWC common suspend resume methods for device D3cold entry & exit. If the device is not kept in D3cold use existing methods like keeping icc votes, opp votes etc.. intact. In qcom_pcie_deinit_2_7_0(), explicitly disable PCIe clocks and resets in the controller. Remove suspended flag from qcom_pcie structure as it is no longer needed. Link: https://lore.kernel.org/r/20260128-d3cold-v1-3-dd8f3f0ce824@oss.qualcomm.com Signed-off-by: Krishna Chaitanya Chundru --- drivers/pci/controller/dwc/pcie-qcom.c | 116 +++++++++++++++---------- 1 file changed, 69 insertions(+), 47 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 67a16af69ddc7..7793d92f0418f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -144,6 +144,7 @@ /* ELBI_SYS_CTRL register fields */ #define ELBI_SYS_CTRL_LT_ENABLE BIT(0) +#define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4) /* AXI_MSTR_RESP_COMP_CTRL0 register fields */ #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 @@ -282,7 +283,6 @@ struct qcom_pcie { const struct qcom_pcie_cfg *cfg; struct dentry *debugfs; struct list_head ports; - bool suspended; bool use_pm_opp; }; @@ -1064,6 +1064,12 @@ static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + u32 val; + + /* Disable PCIe clocks and resets */ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); @@ -1367,10 +1373,18 @@ static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp) pcie->cfg->ops->host_post_init(pcie); } +static void qcom_pcie_host_pme_turn_off(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + writel(ELBI_SYS_CTRL_PME_TURNOFF_MSG, pci->elbi_base + ELBI_SYS_CTRL); +} + static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { .init = qcom_pcie_host_init, .deinit = qcom_pcie_host_deinit, .post_init = qcom_pcie_host_post_init, + .pme_turn_off = qcom_pcie_host_pme_turn_off, }; /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ @@ -2034,53 +2048,51 @@ static int qcom_pcie_suspend_noirq(struct device *dev) if (!pcie) return 0; - /* - * Set minimum bandwidth required to keep data path functional during - * suspend. - */ - if (pcie->icc_mem) { - ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1)); - if (ret) { - dev_err(dev, - "Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", - ret); - return ret; - } - } + ret = dw_pcie_suspend_noirq(pcie->pci); + if (ret) + return ret; - /* - * Turn OFF the resources only for controllers without active PCIe - * devices. For controllers with active devices, the resources are kept - * ON and the link is expected to be in L0/L1 (sub)states. - * - * Turning OFF the resources for controllers with active PCIe devices - * will trigger access violation during the end of the suspend cycle, - * as kernel tries to access the PCIe devices config space for masking - * MSIs. - * - * Also, it is not desirable to put the link into L2/L3 state as that - * implies VDD supply will be removed and the devices may go into - * powerdown state. This will affect the lifetime of the storage devices - * like NVMe. - */ - if (!dw_pcie_link_up(pcie->pci)) { - qcom_pcie_host_deinit(&pcie->pci->pp); - pcie->suspended = true; - } + if (pcie->pci->suspended) { + ret = icc_disable(pcie->icc_mem); + if (ret) + dev_err(dev, "Failed to disable PCIe-MEM interconnect path: %d\n", ret); - /* - * Only disable CPU-PCIe interconnect path if the suspend is non-S2RAM. - * Because on some platforms, DBI access can happen very late during the - * S2RAM and a non-active CPU-PCIe interconnect path may lead to NoC - * error. - */ - if (pm_suspend_target_state != PM_SUSPEND_MEM) { ret = icc_disable(pcie->icc_cpu); if (ret) dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", ret); if (pcie->use_pm_opp) dev_pm_opp_set_opp(pcie->pci->dev, NULL); + } else { + /* + * Set minimum bandwidth required to keep data path functional during + * suspend. + */ + if (pcie->icc_mem) { + ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1)); + if (ret) { + dev_err(dev, + "Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", + ret); + return ret; + } + } + + /* + * Only disable CPU-PCIe interconnect path if the suspend is non-S2RAM. + * Because on some platforms, DBI access can happen very late during the + * S2RAM and a non-active CPU-PCIe interconnect path may lead to NoC + * error. + */ + if (pm_suspend_target_state != PM_SUSPEND_MEM) { + ret = icc_disable(pcie->icc_cpu); + if (ret) + dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", + ret); + + if (pcie->use_pm_opp) + dev_pm_opp_set_opp(pcie->pci->dev, NULL); + } } return ret; } @@ -2094,20 +2106,30 @@ static int qcom_pcie_resume_noirq(struct device *dev) if (!pcie) return 0; - if (pm_suspend_target_state != PM_SUSPEND_MEM) { + if (pcie->pci->suspended) { ret = icc_enable(pcie->icc_cpu); if (ret) { dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret); return ret; } - } - if (pcie->suspended) { - ret = qcom_pcie_host_init(&pcie->pci->pp); - if (ret) + ret = icc_enable(pcie->icc_mem); + if (ret) { + dev_err(dev, "Failed to enable PCIe-MEM interconnect path: %d\n", ret); return ret; - - pcie->suspended = false; + } + ret = dw_pcie_resume_noirq(pcie->pci); + if (ret && (ret != -ETIMEDOUT)) + return ret; + } else { + if (pm_suspend_target_state != PM_SUSPEND_MEM) { + ret = icc_enable(pcie->icc_cpu); + if (ret) { + dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", + ret); + return ret; + } + } } qcom_pcie_icc_opp_update(pcie); From ece953449797a67a6bca59f211113f3576ecd857 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 28 Jan 2026 17:52:42 +0530 Subject: [PATCH 5/7] FROMLIST: PCI: qcom: Prevent GDSC power down on suspend Currently, the driver expects the devices to remain in D0 across system suspend, but the genpd framework may still power down the associated GDSC during suspend. When that happens, the PCIe link goes down and cannot be recovered on resume. Prevent genpd from turning off the PCIe GDSC by using dev_pm_genpd_rpm_always_on() so that the power domain stays on while the controller is suspended. This preserves the link state across suspend/resume and avoids unrecoverable link failures. Fixes: 82a823833f4e ("PCI: qcom: Add Qualcomm PCIe controller driver") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260128-genpd_fix-v1-1-cd45a249d12f@oss.qualcomm.com Signed-off-by: Krishna Chaitanya Chundru --- drivers/pci/controller/dwc/pcie-qcom.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 7793d92f0418f..9aef8ee344ae7 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -2052,6 +2053,11 @@ static int qcom_pcie_suspend_noirq(struct device *dev) if (ret) return ret; + if (pcie->pci->suspended) + dev_pm_genpd_rpm_always_on(dev, false); + else + dev_pm_genpd_rpm_always_on(dev, true); + if (pcie->pci->suspended) { ret = icc_disable(pcie->icc_mem); if (ret) From 6a697f861422a7e8a1e0e189455baffdad08a9c1 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Tue, 17 Feb 2026 16:49:08 +0530 Subject: [PATCH 6/7] FROMLIST: PCI: qcom: Add .get_ltssm() helper For older targets like sc7280, we see reading DBI after sending PME turn off message is causing NOC error. To avoid unsafe DBI accesses, introduce qcom_pcie_get_ltssm(), which retrieves the LTSSM state from the PARF_LTSSM register instead. Link: https://lore.kernel.org/r/20260217-d3cold-v2-3-89b322864043@oss.qualcomm.com Signed-off-by: Krishna Chaitanya Chundru --- drivers/pci/controller/dwc/pcie-qcom.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 9aef8ee344ae7..7827161bc6f8a 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -132,6 +132,7 @@ /* PARF_LTSSM register fields */ #define LTSSM_EN BIT(8) +#define PARF_LTSSM_STATE_MASK GENMASK(5, 0) /* PARF_NO_SNOOP_OVERRIDE register fields */ #define WR_NO_SNOOP_OVERRIDE_EN BIT(1) @@ -1262,6 +1263,15 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci) return val & PCI_EXP_LNKSTA_DLLLA; } +static enum dw_pcie_ltssm qcom_pcie_get_ltssm(struct dw_pcie *pci) +{ + struct qcom_pcie *pcie = to_qcom_pcie(pci); + u32 val; + + val = readl(pcie->parf + PARF_LTSSM); + return (enum dw_pcie_ltssm)FIELD_GET(PARF_LTSSM_STATE_MASK, val); +} + static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie) { struct qcom_pcie_port *port; @@ -1522,6 +1532,7 @@ static const struct qcom_pcie_cfg cfg_fw_managed = { static const struct dw_pcie_ops dw_pcie_ops = { .link_up = qcom_pcie_link_up, .start_link = qcom_pcie_start_link, + .get_ltssm = qcom_pcie_get_ltssm, }; static int qcom_pcie_icc_init(struct qcom_pcie *pcie) From 8a0f8a3415f3e7948c97edda01f00db0692520b9 Mon Sep 17 00:00:00 2001 From: Sushrut Shree Trivedi Date: Thu, 30 Apr 2026 10:12:18 +0530 Subject: [PATCH 7/7] FROMLIST: pci: quirks: Advertise D3cold capability for UPD720201 PCIe-to-USB bridge UPD720201 does not advertise D3cold support until firmware is loaded post pci enumeration. This results in upd blocking D3cold entry during system suspend and causing overall failure to enter XO shutdown. Hence, add a quirk to advertise D3cold PME capability since the HW actually supports and advertises it post firmware loading. Link: https://lore.kernel.org/all/20260430-d3cold_support-v1-1-6734f280c481@oss.qualcomm.com/ Signed-off-by: Sushrut Shree Trivedi --- drivers/pci/quirks.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 48946cca4be72..d2897192e49c4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6380,3 +6380,13 @@ static void pci_mask_replay_timer_timeout(struct pci_dev *pdev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9750, pci_mask_replay_timer_timeout); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9755, pci_mask_replay_timer_timeout); #endif + +/* + * Renesas PCIe-to-USB bridge UPD720201 does not advertise D3cold + * capability by default until firmware is loaded post-enumeration. + */ +static void quirk_enable_d3cold(struct pci_dev *dev) +{ + dev->pme_support = dev->pme_support | (1 << PCI_D3cold); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_RENESAS, 0x0014, quirk_enable_d3cold);