Skip to content

Commit 1103f9c

Browse files
konradybcioshashim-quic
authored andcommitted
FROMLIST: PCI: qcom: properly implement RC shutdown/power up
Currently, we've only been minimizing the power draw while keeping the RC up at all times. This is suboptimal, as it draws a whole lot of power and prevents the SoC from power collapsing. Implement full shutdown and re-initialization to allow for powering off the controller. This is mainly intended for SC8280XP with a broken power rail setup, which requires a full RC shutdown/reinit in order to reach SoC-wide power collapse, but sleeping is generally better than not sleeping and less destructive suspend can be implemented later for platforms that support it. Link: https://lore.kernel.org/linux-pci/20240210-topic-8280_pcie-v2-0-1cef4b606883@linaro.org/ Co-developed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: Konrad Dybcio <konrad.dybcio@linaro.org> Signed-off-by: Krishna Chaitanya Chundru <krichai@qti.qualcomm.com>
1 parent 4c69f68 commit 1103f9c

File tree

2 files changed

+135
-55
lines changed

2 files changed

+135
-55
lines changed

drivers/pci/controller/dwc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ config PCIE_QCOM_COMMON
292292
config PCIE_QCOM
293293
bool "Qualcomm PCIe controller (host mode)"
294294
depends on OF && (ARCH_QCOM || COMPILE_TEST)
295+
depends on QCOM_COMMAND_DB || QCOM_COMMAND_DB=n
295296
depends on PCI_MSI
296297
select PCIE_DW_HOST
297298
select CRC8

drivers/pci/controller/dwc/pcie-qcom.c

Lines changed: 134 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,19 @@
3232
#include <linux/slab.h>
3333
#include <linux/types.h>
3434
#include <linux/units.h>
35+
#include <soc/qcom/cmd-db.h>
3536

3637
#include "../../pci.h"
3738
#include "pcie-designware.h"
3839
#include "pcie-qcom-common.h"
3940

41+
#include <dt-bindings/interconnect/qcom,icc.h>
42+
#include <dt-bindings/interconnect/qcom,rpm-icc.h>
43+
4044
/* PARF registers */
4145
#define PARF_SYS_CTRL 0x00
4246
#define PARF_PM_CTRL 0x20
47+
#define PARF_PM_STTS 0x24
4348
#define PARF_PCS_DEEMPH 0x34
4449
#define PARF_PCS_SWING 0x38
4550
#define PARF_PHY_CTRL 0x40
@@ -70,6 +75,7 @@
7075

7176
/* ELBI registers */
7277
#define ELBI_SYS_CTRL 0x04
78+
#define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4)
7379

7480
/* DBI registers */
7581
#define AXI_MSTR_RESP_COMP_CTRL0 0x818
@@ -94,7 +100,10 @@
94100
#define L1_CLK_RMV_DIS BIT(1)
95101

96102
/* PARF_PM_CTRL register fields */
97-
#define REQ_NOT_ENTR_L1 BIT(5)
103+
#define REQ_NOT_ENTR_L1 BIT(5) /* "Prevent L0->L1" */
104+
105+
/* PARF_PM_STTS register fields */
106+
#define PM_ENTER_L23 BIT(5)
98107

99108
/* PARF_PCS_DEEMPH register fields */
100109
#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) FIELD_PREP(GENMASK(21, 16), x)
@@ -147,6 +156,7 @@
147156

148157
/* ELBI_SYS_CTRL register fields */
149158
#define ELBI_SYS_CTRL_LT_ENABLE BIT(0)
159+
#define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4)
150160

151161
/* AXI_MSTR_RESP_COMP_CTRL0 register fields */
152162
#define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4
@@ -276,6 +286,7 @@ struct qcom_pcie {
276286
struct dentry *debugfs;
277287
bool suspended;
278288
bool use_pm_opp;
289+
bool soc_is_rpmh;
279290
};
280291

281292
#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
@@ -310,6 +321,24 @@ static int qcom_pcie_start_link(struct dw_pcie *pci)
310321
return 0;
311322
}
312323

324+
static int qcom_pcie_stop_link(struct dw_pcie *pci)
325+
{
326+
struct qcom_pcie *pcie = to_qcom_pcie(pci);
327+
u32 ret_l23, val;
328+
329+
writel(ELBI_SYS_CTRL_PME_TURNOFF_MSG, pcie->elbi + ELBI_SYS_CTRL);
330+
readl(pcie->elbi + ELBI_SYS_CTRL);
331+
332+
ret_l23 = readl_poll_timeout(pcie->parf + PARF_PM_STTS, val,
333+
val & PM_ENTER_L23, 10000, 100000);
334+
if (ret_l23) {
335+
dev_err(pci->dev, "Failed to enter L2/L3\n");
336+
return -ETIMEDOUT;
337+
}
338+
339+
return 0;
340+
}
341+
313342
static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci)
314343
{
315344
struct qcom_pcie *pcie = to_qcom_pcie(pci);
@@ -1036,9 +1065,19 @@ static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie)
10361065
static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
10371066
{
10381067
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
1068+
u32 val;
1069+
1070+
/* Disable PCIe clocks and resets */
1071+
val = readl(pcie->parf + PARF_PHY_CTRL);
1072+
val |= PHY_TEST_PWR_DOWN;
1073+
writel(val, pcie->parf + PARF_PHY_CTRL);
1074+
readl(pcie->parf + PARF_PHY_CTRL);
10391075

10401076
clk_bulk_disable_unprepare(res->num_clks, res->clks);
10411077

1078+
reset_control_assert(res->rst);
1079+
usleep_range(2000, 2500);
1080+
10421081
regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
10431082
}
10441083

@@ -1734,6 +1773,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)
17341773
pcie->parf + PARF_INT_ALL_MASK);
17351774
}
17361775

1776+
/* If the soc features RPMh, cmd_db must have been prepared by now */
1777+
pcie->soc_is_rpmh = !cmd_db_ready();
1778+
17371779
qcom_pcie_icc_opp_update(pcie);
17381780

17391781
if (pcie->mhi)
@@ -1752,86 +1794,123 @@ static int qcom_pcie_probe(struct platform_device *pdev)
17521794
return ret;
17531795
}
17541796

1755-
static int qcom_pcie_suspend_noirq(struct device *dev)
1797+
static int qcom_pcie_resume_noirq(struct device *dev)
17561798
{
17571799
struct qcom_pcie *pcie = dev_get_drvdata(dev);
1758-
int ret = 0;
1800+
int ret;
17591801

1760-
/*
1761-
* Set minimum bandwidth required to keep data path functional during
1762-
* suspend.
1763-
*/
1764-
if (pcie->icc_mem) {
1765-
ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1));
1802+
if (pcie->soc_is_rpmh) {
1803+
/*
1804+
* Undo the tag change from qcom_pcie_suspend_noirq first in
1805+
* case RPMh spontaneously decides to power collapse the
1806+
* platform based on other inputs.
1807+
*/
1808+
icc_set_tag(pcie->icc_mem, QCOM_ICC_TAG_ALWAYS);
1809+
1810+
/* Flush the tag change */
1811+
ret = icc_enable(pcie->icc_mem);
17661812
if (ret) {
1767-
dev_err(dev,
1768-
"Failed to set bandwidth for PCIe-MEM interconnect path: %d\n",
1769-
ret);
1813+
dev_err(pcie->pci->dev, "failed to icc_enable: %d", ret);
17701814
return ret;
17711815
}
17721816
}
17731817

1774-
/*
1775-
* Turn OFF the resources only for controllers without active PCIe
1776-
* devices. For controllers with active devices, the resources are kept
1777-
* ON and the link is expected to be in L0/L1 (sub)states.
1778-
*
1779-
* Turning OFF the resources for controllers with active PCIe devices
1780-
* will trigger access violation during the end of the suspend cycle,
1781-
* as kernel tries to access the PCIe devices config space for masking
1782-
* MSIs.
1783-
*
1784-
* Also, it is not desirable to put the link into L2/L3 state as that
1785-
* implies VDD supply will be removed and the devices may go into
1786-
* powerdown state. This will affect the lifetime of the storage devices
1787-
* like NVMe.
1788-
*/
1789-
if (!dw_pcie_link_up(pcie->pci)) {
1790-
qcom_pcie_host_deinit(&pcie->pci->pp);
1791-
pcie->suspended = true;
1818+
/* Only check this now to make sure the icc tag has been set. */
1819+
if (!pcie->suspended)
1820+
return 0;
1821+
1822+
ret = icc_enable(pcie->icc_cpu);
1823+
if (ret) {
1824+
dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret);
1825+
goto revert_icc_tag;
17921826
}
17931827

1794-
/*
1795-
* Only disable CPU-PCIe interconnect path if the suspend is non-S2RAM.
1796-
* Because on some platforms, DBI access can happen very late during the
1797-
* S2RAM and a non-active CPU-PCIe interconnect path may lead to NoC
1798-
* error.
1799-
*/
1800-
if (pm_suspend_target_state != PM_SUSPEND_MEM) {
1801-
ret = icc_disable(pcie->icc_cpu);
1802-
if (ret)
1803-
dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", ret);
1828+
ret = qcom_pcie_host_init(&pcie->pci->pp);
1829+
if (ret)
1830+
goto revert_icc_tag;
1831+
1832+
dw_pcie_setup_rc(&pcie->pci->pp);
1833+
1834+
ret = qcom_pcie_start_link(pcie->pci);
1835+
if (ret)
1836+
goto deinit_host;
1837+
1838+
/* Ignore the retval, the devices may come up later. */
1839+
dw_pcie_wait_for_link(pcie->pci);
1840+
1841+
qcom_pcie_icc_opp_update(pcie);
1842+
1843+
pcie->suspended = false;
1844+
1845+
return 0;
18041846

1805-
if (pcie->use_pm_opp)
1806-
dev_pm_opp_set_opp(pcie->pci->dev, NULL);
1847+
deinit_host:
1848+
qcom_pcie_host_deinit(&pcie->pci->pp);
1849+
revert_icc_tag:
1850+
if (pcie->soc_is_rpmh) {
1851+
icc_set_tag(pcie->icc_mem, QCOM_ICC_TAG_WAKE);
1852+
1853+
/* Ignore the retval, failing here would be tragic anyway.. */
1854+
icc_enable(pcie->icc_mem);
18071855
}
1856+
18081857
return ret;
18091858
}
18101859

1811-
static int qcom_pcie_resume_noirq(struct device *dev)
1860+
static int qcom_pcie_suspend_noirq(struct device *dev)
18121861
{
18131862
struct qcom_pcie *pcie = dev_get_drvdata(dev);
1814-
int ret;
1863+
int ret = 0;
1864+
1865+
if (pcie->suspended)
1866+
return 0;
1867+
1868+
if (dw_pcie_link_up(pcie->pci)) {
1869+
ret = qcom_pcie_stop_link(pcie->pci);
1870+
if (ret)
1871+
return ret;
1872+
}
1873+
1874+
qcom_pcie_host_deinit(&pcie->pci->pp);
1875+
1876+
if (pcie->soc_is_rpmh) {
1877+
/*
1878+
* The PCIe RC may be covertly accessed by the secure firmware
1879+
* on sleep exit. Use the WAKE bucket to let RPMh pull the plug
1880+
* on PCIe in sleep, but guarantee it comes back up for resume.
1881+
*/
1882+
icc_set_tag(pcie->icc_mem, QCOM_ICC_TAG_WAKE);
1883+
1884+
/* Flush the tag change */
1885+
ret = icc_enable(pcie->icc_mem);
1886+
if (ret) {
1887+
dev_err(pcie->pci->dev, "failed to icc_enable %d\n", ret);
18151888

1816-
if (pm_suspend_target_state != PM_SUSPEND_MEM) {
1817-
ret = icc_enable(pcie->icc_cpu);
1889+
/* Revert everything and pray icc calls succeed */
1890+
return qcom_pcie_resume_noirq(dev);
1891+
}
1892+
} else {
1893+
/*
1894+
* Set minimum bandwidth required to keep data path functional
1895+
* during suspend.
1896+
*/
1897+
ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1));
18181898
if (ret) {
1819-
dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret);
1899+
dev_err(dev, "Failed to set interconnect bandwidth: %d\n", ret);
18201900
return ret;
18211901
}
18221902
}
18231903

1824-
if (pcie->suspended) {
1825-
ret = qcom_pcie_host_init(&pcie->pci->pp);
1826-
if (ret)
1827-
return ret;
1904+
pcie->suspended = true;
18281905

1829-
pcie->suspended = false;
1830-
}
1906+
ret = icc_disable(pcie->icc_cpu);
1907+
if (ret)
1908+
dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", ret);
18311909

1832-
qcom_pcie_icc_opp_update(pcie);
1910+
if (pcie->use_pm_opp)
1911+
dev_pm_opp_set_opp(pcie->pci->dev, NULL);
18331912

1834-
return 0;
1913+
return ret;
18351914
}
18361915

18371916
static const struct of_device_id qcom_pcie_match[] = {

0 commit comments

Comments
 (0)