diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h index 00f5e42ebb24..40202988c989 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -94,6 +94,8 @@ #define PCIE20_SUBSYSTEM 0x2c #define PCIE20_CAP_ID_NXT_PTR 0x40 #define PCIE20_CON_STATUS 0x44 +#define PCIE20_MASK_PME_EN BIT(8) +#define PCIE20_MASK_PME_STATUS BIT(15) #define PCIE20_MSI_CAP_ID_NEXT_CTRL 0x50 #define PCIE20_MSI_LOWER 0x54 #define PCIE20_MSI_UPPER 0x58 @@ -360,6 +362,7 @@ struct ep_pcie_dev_t { bool mhi_soc_reset_en; bool aoss_rst_clear; bool avoid_reboot_in_d3hot; + bool pme_in_wake_from_d3cold; u32 dbi_base_reg; u32 slv_space_reg; u32 phy_status_reg; @@ -411,6 +414,8 @@ struct ep_pcie_dev_t { int perst_irq; atomic_t host_wake_pending; bool conf_ipa_msi_iatu; + bool wake_from_d3cold; + bool pme_en_d3; struct ep_pcie_register_event *event_reg; struct work_struct handle_bme_work; diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index 671063b7dd63..2c45fc9d7ab3 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "ep_pcie_com.h" #include @@ -46,6 +47,9 @@ #define ICC_AVG_BW 500 #define ICC_PEAK_BW 800 #define PERST_RAW_RESET_STATUS BIT(11) +#define PCIE_PME_COUNT 2 +#define PCIE_PME_SLEEP_DELAY_US 10000 +#define PCIE_PME_RESEND_DELAY_US 100000 /* debug mask sys interface */ static int ep_pcie_debug_mask; @@ -120,6 +124,7 @@ static const struct ep_pcie_irq_info_t ep_pcie_irq_info[EP_PCIE_MAX_IRQ] = { }; static int ep_pcie_core_wakeup_host_internal(enum ep_pcie_event event); +static void ep_pcie_core_issue_inband_pme(void); int ep_pcie_get_debug_mask(void) { @@ -790,6 +795,17 @@ static void ep_pcie_core_init(struct ep_pcie_dev_t *dev, bool configured) ep_pcie_write_mask(dev->dm_core + PCIE20_CAP_ID_NXT_PTR, 0, BIT(31)|BIT(30)|BIT(27)); + if (dev->pme_in_wake_from_d3cold && dev->pme_en_d3) { + /* + * PME_ENABLE is cleared after d3cold state if + * retention flops are not present in the target. + * Set PME_ENABLE, if it is seen as set during + * d3hot to send PME message + */ + ep_pcie_write_mask(dev->dm_core + PCIE20_CON_STATUS, 0, PCIE20_MASK_PME_EN); + dev->pme_en_d3 = false; + } + /* Set the Endpoint L0s Acceptable Latency to 1us (max) */ ep_pcie_write_reg_field(dev->dm_core, PCIE20_DEVICE_CAPABILITIES, @@ -1675,6 +1691,46 @@ static void ep_pcie_core_toggle_wake_gpio(bool is_on) } +static void ep_pcie_core_issue_pme_for_wake_from_d3cold(void) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + u32 pme_count = 0, pme_reg; + bool pme_en, pme_status; + + pme_reg = readl_relaxed(dev->dm_core + PCIE20_CON_STATUS); + pme_en = pme_reg & PCIE20_MASK_PME_EN; + pme_status = pme_reg & PCIE20_MASK_PME_STATUS; + + EP_PCIE_INFO(dev, "PCIe V%d: pme_reg: 0x%x, pme_en: %d, pme_status: %d\n", + dev->rev, pme_reg, pme_en, pme_status); + + if (pme_en) { + do { + /* Sending PME for device initiated d3_cold exit */ + ep_pcie_core_issue_inband_pme(); + /*Polling for pme_status to be 0 for 100ms*/ + readx_poll_timeout(readl_relaxed, dev->dm_core + PCIE20_CON_STATUS, pme_reg, + !(pme_reg & PCIE20_MASK_PME_STATUS), PCIE_PME_SLEEP_DELAY_US, + PCIE_PME_RESEND_DELAY_US); + + pme_status = pme_reg & PCIE20_MASK_PME_STATUS; + EP_PCIE_DBG(dev, "PCIe V%d: pme_status:%d\n", dev->rev, pme_status); + + pme_count++; + + if (!pme_status) + break; + + } while (pme_count < PCIE_PME_COUNT); + + if (pme_status && pme_count == PCIE_PME_COUNT) { + EP_PCIE_INFO(dev, + "PCIe V%d: Host failed to acknowledge PME for device-initiated wake\n", + dev->rev); + } + } +} + int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt) { int ret = 0; @@ -1951,6 +2007,16 @@ int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt) dev->rev); } + if (dev->pme_in_wake_from_d3cold && dev->wake_from_d3cold) { + /* + * Sending PME after link training for device + * initiated D3cold to D0 transition, based on + * PCIe specification + */ + ep_pcie_core_issue_pme_for_wake_from_d3cold(); + dev->wake_from_d3cold = false; + } + checkbme: /* Clear AOSS_CC_RESET_STATUS::PERST_RAW_RESET_STATUS when linking up */ if (dev->aoss_rst_clear) @@ -2277,6 +2343,9 @@ static irqreturn_t ep_pcie_handle_dstate_change_irq(int irq, void *data) dev->rev, dev->d3_counter); ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(1)); + dev->pme_en_d3 = readl_relaxed(dev->dm_core + + PCIE20_CON_STATUS) & PCIE20_MASK_PME_EN; + if (dev->enumerated) ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_HOT); else @@ -3147,6 +3216,13 @@ static int ep_pcie_core_wakeup_host_internal(enum ep_pcie_event event) if (!atomic_read(&dev->perst_deast)) { /*D3 cold handling*/ ep_pcie_core_toggle_wake_gpio(true); + if (dev->pme_in_wake_from_d3cold) { + /* + * Set below flag for device initiated + * d3cold wake to send PME message + */ + dev->wake_from_d3cold = true; + } } else if (dev->l23_ready) { EP_PCIE_ERR(dev, "PCIe V%d: request to assert WAKE# when in D3hot\n", @@ -3423,6 +3499,13 @@ static int ep_pcie_probe(struct platform_device *pdev) "PCIe V%d: PME during reboot/panic (in D3hot) is %s needed\n", ep_pcie_dev.rev, ep_pcie_dev.avoid_reboot_in_d3hot ? "" : "not"); + ep_pcie_dev.pme_in_wake_from_d3cold = + of_property_read_bool((&pdev->dev)->of_node, + "qcom,pme-in-wake-from-d3cold"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: PME is%s sent during wake from d3cold\n", + ep_pcie_dev.rev, ep_pcie_dev.pme_in_wake_from_d3cold ? "" : " not"); + ret = of_property_read_u32((&pdev->dev)->of_node, "qcom,mhi-soc-reset-offset", &ep_pcie_dev.mhi_soc_reset_offset);