[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20260128-d3cold-v1-3-dd8f3f0ce824@oss.qualcomm.com>
Date: Wed, 28 Jan 2026 17:10:43 +0530
From: Krishna Chaitanya Chundru <krishna.chundru@....qualcomm.com>
To: Jingoo Han <jingoohan1@...il.com>, Manivannan Sadhasivam <mani@...nel.org>,
Lorenzo Pieralisi <lpieralisi@...nel.org>,
Krzysztof WilczyĆski <kwilczynski@...nel.org>,
Rob Herring <robh@...nel.org>, Bjorn Helgaas <bhelgaas@...gle.com>,
Will Deacon <will@...nel.org>
Cc: linux-pci@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-arm-msm@...r.kernel.org, linux-arm-kernel@...ts.infradead.org,
jonathanh@...dia.com,
Krishna Chaitanya Chundru <krishna.chundru@....qualcomm.com>
Subject: [PATCH 3/3] 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.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@....qualcomm.com>
---
drivers/pci/controller/dwc/pcie-qcom.c | 114 ++++++++++++++++++++-------------
1 file changed, 68 insertions(+), 46 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 7b92e7a1c0d9364a9cefe1450818f9cbfc7fd3ac..bb4e5a29c452a6c0d4b31b2a9ff3aa62b984fb64 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -150,6 +150,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
@@ -283,7 +284,6 @@ struct qcom_pcie {
const struct qcom_pcie_cfg *cfg;
struct dentry *debugfs;
struct list_head ports;
- bool suspended;
bool use_pm_opp;
};
@@ -1075,6 +1075,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);
@@ -1355,10 +1361,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 */
@@ -2016,53 +2030,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;
}
@@ -2076,20 +2088,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);
+ ret = icc_enable(pcie->icc_mem);
+ if (ret) {
+ dev_err(dev, "Failed to enable PCIe-MEM interconnect path: %d\n", ret);
+ return ret;
+ }
+ ret = dw_pcie_resume_noirq(pcie->pci);
if (ret)
return ret;
-
- pcie->suspended = false;
+ } 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);
--
2.34.1
Powered by blists - more mailing lists