From a13ebeb960e4f80a2f322bfecf4ac7a97cba9a69 Mon Sep 17 00:00:00 2001 From: Sai Chaitanya Kaveti Date: Wed, 28 Sep 2022 20:59:38 +0530 Subject: [PATCH] msm: ep_pcie: Sending PME for device initiated D3cold exit According to the PCIe specification, PME message is to be sent for device initiated D3cold to D0 transition. To support this sending PME immediately after link training while transitioning from D3cold to D0. As retention flops are not available for sdxlemur, sticky bits in the registers are not retained during low power state. PME enable bit is being cleared because of this. To handle this added a flag to check the status of pme_en after D3 event and set pme_en during link training if the flag is set. Change-Id: I474a675a35566c2ff051374c9d212c47d08d622d Signed-off-by: Sai Chaitanya Kaveti --- drivers/platform/msm/ep_pcie/ep_pcie_com.h | 5 ++ drivers/platform/msm/ep_pcie/ep_pcie_core.c | 83 +++++++++++++++++++++ 2 files changed, 88 insertions(+) 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);