diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index df98fee69892b..0d1340c9b3642 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -443,65 +443,13 @@ static ssize_t counter_value_read(struct file *file, char __user *buf, return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); } -static const char *ltssm_status_string(enum dw_pcie_ltssm ltssm) -{ - const char *str; - - switch (ltssm) { -#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_1); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_2); - default: - str = "DW_PCIE_LTSSM_UNKNOWN"; - break; - } - - return str + strlen("DW_PCIE_LTSSM_"); -} - static int ltssm_status_show(struct seq_file *s, void *v) { struct dw_pcie *pci = s->private; enum dw_pcie_ltssm val; val = dw_pcie_get_ltssm(pci); - seq_printf(s, "%s (0x%02x)\n", ltssm_status_string(val), val); + seq_printf(s, "%s (0x%02x)\n", dw_pcie_ltssm_status_string(val), val); return 0; } diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 57293c180ad89..50e8e637df8e5 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -16,9 +16,11 @@ #include #include #include +#include #include #include +#include "../pci-host-common.h" #include "../../pci.h" #include "pcie-designware.h" @@ -664,8 +666,13 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) goto err_remove_edma; } - /* Ignore errors, the link may come up later */ - dw_pcie_wait_for_link(pci); + /* + * Only fail on timeout error. Other errors indicate the device may + * become available later, so continue without failing. + */ + ret = dw_pcie_wait_for_link(pci); + if (ret == -ETIMEDOUT) + goto err_stop_link; ret = pci_host_probe(bridge); if (ret) @@ -1158,13 +1165,14 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) int dw_pcie_suspend_noirq(struct dw_pcie *pci) { + bool pme_capable = false; int ret = 0; u32 val; if (!dw_pcie_link_up(pci)) goto stop_link; - if (!pci_host_common_can_enter_d3cold(pci->pp.bridge)) + if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable)) return 0; pci->pp.skip_pwrctrl_off = true; @@ -1206,6 +1214,7 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) udelay(1); stop_link: + pci->pp.skip_pwrctrl_off = pme_capable; dw_pcie_stop_link(pci); if (pci->pp.ops->deinit) pci->pp.ops->deinit(&pci->pp); @@ -1223,9 +1232,6 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) if (!pci->suspended) return 0; - pci->suspended = false; - pci->pp.skip_pwrctrl_off = false; - if (pci->pp.ops->init) { ret = pci->pp.ops->init(&pci->pp); if (ret) { @@ -1241,8 +1247,21 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) return ret; ret = dw_pcie_wait_for_link(pci); - if (ret) - return ret; + if (ret == -ETIMEDOUT) + goto err_stop_link; + + if (pci->pp.ops->post_init) + pci->pp.ops->post_init(&pci->pp); + + pci->suspended = false; + + return 0; + +err_stop_link: + dw_pcie_stop_link(pci); + + if (pci->pp.ops->deinit) + pci->pp.ops->deinit(&pci->pp); return ret; } diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 345365ea97c74..2fa9f6ee149ed 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -692,9 +692,69 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index) dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0); } +const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm) +{ + const char *str; + + switch (ltssm) { +#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_1); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_2); + default: + str = "DW_PCIE_LTSSM_UNKNOWN"; + break; + } + + return str + strlen("DW_PCIE_LTSSM_"); +} + +/** + * dw_pcie_wait_for_link - Wait for the PCIe link to be up + * @pci: DWC instance + * + * Returns: 0 if link is up, -ENODEV if device is not found, -EIO if the device + * is found but not active and -ETIMEDOUT if the link fails to come up for other + * reasons. + */ int dw_pcie_wait_for_link(struct dw_pcie *pci) { - u32 offset, val; + u32 offset, val, ltssm; int retries; /* Check if the link is up or not */ @@ -706,7 +766,29 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) } if (retries >= PCIE_LINK_WAIT_MAX_RETRIES) { - dev_info(pci->dev, "Phy link never came up\n"); + /* + * If the link is in Detect.Quiet or Detect.Active state, it + * indicates that no device is detected. + */ + ltssm = dw_pcie_get_ltssm(pci); + if (ltssm == DW_PCIE_LTSSM_DETECT_QUIET || + ltssm == DW_PCIE_LTSSM_DETECT_ACT) { + dev_info(pci->dev, "Device not found\n"); + return -ENODEV; + + /* + * If the link is in POLL.{Active/Compliance} state, then the + * device is found to be connected to the bus, but it is not + * active i.e., the device firmware might not yet initialized. + */ + } else if (ltssm == DW_PCIE_LTSSM_POLL_ACTIVE || + ltssm == DW_PCIE_LTSSM_POLL_COMPLIANCE) { + dev_info(pci->dev, "Device found, but not active\n"); + return -EIO; + } + + dev_err(pci->dev, "Link failed to come up. LTSSM: %s\n", + dw_pcie_ltssm_status_string(ltssm)); return -ETIMEDOUT; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index bc34858c0851f..376d710c5039d 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -809,6 +809,8 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci) return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val); } +const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm); + #ifdef CONFIG_PCIE_DW_HOST int dw_pcie_suspend_noirq(struct dw_pcie *pci); int dw_pcie_resume_noirq(struct dw_pcie *pci); diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 4deed3ed5089a..5d812003da4f7 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -72,6 +72,7 @@ /* ELBI registers */ #define ELBI_SYS_CTRL 0x04 +#define ELBI_SYS_STTS 0x08 /* DBI registers */ #define AXI_MSTR_RESP_COMP_CTRL0 0x818 @@ -148,6 +149,9 @@ #define ELBI_SYS_CTRL_LT_ENABLE BIT(0) #define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4) +/* ELBI_SYS_STTS register fields */ +#define ELBI_SYS_STTS_LTSSM_STATE_MASK GENMASK(17, 12) + /* AXI_MSTR_RESP_COMP_CTRL0 register fields */ #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_4K 0x5 @@ -248,6 +252,7 @@ struct qcom_pcie_ops { void (*deinit)(struct qcom_pcie *pcie); void (*ltssm_enable)(struct qcom_pcie *pcie); int (*config_sid)(struct qcom_pcie *pcie); + enum dw_pcie_ltssm (*get_ltssm)(struct qcom_pcie *pcie); }; /** @@ -417,6 +422,15 @@ static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) writel(val, pci->elbi_base + ELBI_SYS_CTRL); } +static enum dw_pcie_ltssm qcom_pcie_2_1_0_get_ltssm(struct qcom_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + u32 val; + + val = readl(pci->elbi_base + ELBI_SYS_STTS); + return (enum dw_pcie_ltssm)FIELD_GET(ELBI_SYS_STTS_LTSSM_STATE_MASK, val); +} + static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; @@ -506,7 +520,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) u32 val; int ret; - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -673,6 +687,12 @@ static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; + u32 val; + + /* Force PHY to lowest power state*/ + 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); regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); @@ -705,7 +725,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) { u32 val; - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -769,6 +789,12 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_4_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; + u32 val; + + /* Force PHY to lowest power state*/ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); reset_control_bulk_assert(res->num_resets, res->resets); clk_bulk_disable_unprepare(res->num_clks, res->clks); @@ -837,6 +863,12 @@ static int qcom_pcie_get_resources_2_3_3(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_3_3(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; + u32 val; + + /* Force PHY to lowest power state */ + 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); } @@ -892,6 +924,7 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); u32 val; + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -987,7 +1020,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie) /* configure PCIe to RC mode */ writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE); - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -1060,7 +1093,7 @@ 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 */ + /* Force PHY to lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val |= PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -1168,6 +1201,12 @@ static int qcom_pcie_get_resources_2_9_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_9_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; + u32 val; + + /* Force PHY to lowest power state */ + 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); } @@ -1208,6 +1247,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) u32 val; int i; + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -1260,7 +1300,11 @@ static enum dw_pcie_ltssm qcom_pcie_get_ltssm(struct dw_pcie *pci) struct qcom_pcie *pcie = to_qcom_pcie(pci); u32 val; + if (pcie->cfg->ops->get_ltssm) + return pcie->cfg->ops->get_ltssm(pcie); + val = readl(pcie->parf + PARF_LTSSM); + return (enum dw_pcie_ltssm)FIELD_GET(PARF_LTSSM_STATE_MASK, val); } @@ -1341,9 +1385,10 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) err_assert_reset: qcom_pcie_perst_assert(pcie); err_pwrctrl_power_off: - pci_pwrctrl_power_off_devices(pci->dev); + if (!pp->skip_pwrctrl_off) + pci_pwrctrl_power_off_devices(pci->dev); err_pwrctrl_destroy: - if (ret != -EPROBE_DEFER) + if (ret != -EPROBE_DEFER && !pci->suspended) pci_pwrctrl_destroy_devices(pci->dev); err_disable_phy: qcom_pcie_phy_power_off(pcie); @@ -1402,6 +1447,7 @@ static const struct qcom_pcie_ops ops_2_1_0 = { .post_init = qcom_pcie_post_init_2_1_0, .deinit = qcom_pcie_deinit_2_1_0, .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, + .get_ltssm = qcom_pcie_2_1_0_get_ltssm, }; /* Qcom IP rev.: 1.0.0 Synopsys IP rev.: 4.11a */ @@ -1411,6 +1457,7 @@ static const struct qcom_pcie_ops ops_1_0_0 = { .post_init = qcom_pcie_post_init_1_0_0, .deinit = qcom_pcie_deinit_1_0_0, .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, + .get_ltssm = qcom_pcie_2_1_0_get_ltssm, }; /* Qcom IP rev.: 2.3.2 Synopsys IP rev.: 4.21a */ @@ -1989,11 +2036,6 @@ 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) diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index aed69fc95241f..bf0bb91e982e6 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -17,6 +17,9 @@ #include "pci-host-common.h" +#define PCI_HOST_D3COLD_ALLOWED BIT(0) +#define PCI_HOST_PME_D3COLD_CAPABLE BIT(1) + static void gen_pci_unmap_cfg(void *ptr) { pci_ecam_free((struct pci_config_window *)ptr); @@ -105,34 +108,73 @@ 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) +static int __pci_host_common_d3cold_possible(struct pci_dev *pdev, void *userdata) { - bool *d3cold_allow = userdata; + u32 *flags = userdata; + int type; + + /* Ignore conventional PCI devices */ + if (!pci_is_pcie(pdev)) + return 0; + + type = pci_pcie_type(pdev); + if (type != PCI_EXP_TYPE_ENDPOINT && + type != PCI_EXP_TYPE_LEG_END && + type != PCI_EXP_TYPE_RC_END) + return 0; - if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT) + if (!pdev->dev.driver && !pci_is_enabled(pdev)) return 0; if (pdev->current_state != PCI_D3hot) goto exit; - if (device_may_wakeup(&pdev->dev) && !pci_pme_capable(pdev, PCI_D3cold)) - goto exit; + if (device_may_wakeup(&pdev->dev)) { + if (!pci_pme_capable(pdev, PCI_D3cold)) + goto exit; + else + *flags |= PCI_HOST_PME_D3COLD_CAPABLE; + } return 0; + exit: - *d3cold_allow = false; - return -EBUSY; + *flags &= ~PCI_HOST_D3COLD_ALLOWED; + + return -EOPNOTSUPP; } -bool pci_host_common_can_enter_d3cold(struct pci_host_bridge *bridge) +/** + * pci_host_common_d3cold_possible - Determine whether the host bridge can transition the + * devices into D3Cold. + * + * @bridge: PCI host bridge to check + * @pme_capable: Pointer to update if there is any device which is capable of generating + * PME from D3cold. + * + * Walk downstream PCIe endpoint devices and determine whether the host bridge + * is permitted to transition the devices into D3cold. + * + * Devices under host bridge can enter D3cold only if all active PCIe endpoints are in + * PCI_D3hot and any wakeup-enabled endpoint is capable of generating PME from D3cold. + * Inactive endpoints are ignored. + * + * The @pme_capable output allows PCIe controller drivers to apply + * platform-specific handling to preserve wakeup functionality. + * + * Return: %true if the host bridge may enter D3cold, otherwise %false. + */ +bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge, bool *pme_capable) { - bool d3cold_allow = true; + u32 flags = PCI_HOST_D3COLD_ALLOWED; + + pci_walk_bus(bridge->bus, __pci_host_common_d3cold_possible, &flags); - pci_walk_bus(bridge->bus, pci_host_common_check_d3cold, &d3cold_allow); + *pme_capable = !!(flags & PCI_HOST_PME_D3COLD_CAPABLE); - return d3cold_allow; + return !!(flags & PCI_HOST_D3COLD_ALLOWED); } -EXPORT_SYMBOL_GPL(pci_host_common_can_enter_d3cold); +EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible); 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 5ec7e6929f4e0..87f4c1cac26ff 100644 --- a/drivers/pci/controller/pci-host-common.h +++ b/drivers/pci/controller/pci-host-common.h @@ -20,5 +20,5 @@ 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); +bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge, bool *pme_capable); #endif