From 4966230ae8ebdcdce177c1e45fee63fa62078d4c Mon Sep 17 00:00:00 2001 From: Gauri Joshi Date: Fri, 14 Aug 2020 12:31:57 +0530 Subject: [PATCH 1/6] msm: ep_pcie: Acquire wakelock as part of D0 event During bootup scenario, there will not be any PERST de-assert interrupt. So if host send mhi suspend event followed by D3 hot and D0 event, then the PCIE link will be in an enabled state but there will not be any wake lock to prevent system suspend scenario. To prevent the above scenario we will acquire wakelock as part of D0 during bootup scenario and from PERST-deassert for subsequent cases. The change also involves to prevent system suspend if there is PERST de-assertion during system suspend scenario. Registering for suspend_noirq(), as this guarantees that there can be no wakeup interrupts(PERST assertion) during this call. Change-Id: I19051df46cc8353492e34175a47665ab89dc86af Signed-off-by: Subramanian Ananthanarayanan Signed-off-by: Gauri Joshi --- drivers/platform/msm/ep_pcie/ep_pcie_core.c | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index 7e4f8bcecf4f..be8d1f5d644f 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -2208,6 +2208,16 @@ static irqreturn_t ep_pcie_handle_dstate_change_irq(int irq, void *data) EP_PCIE_DBG(dev, "PCIe V%d: No. %ld change to D0 state\n", dev->rev, dev->d0_counter); + /* + * During device bootup, there will not be any PERT-deassert, + * so aquire wakelock from D0 event + */ + if (!atomic_read(&dev->ep_pcie_dev_wake)) { + pm_stay_awake(&dev->pdev->dev); + atomic_set(&dev->ep_pcie_dev_wake, 1); + EP_PCIE_DBG(dev, "PCIe V%d: Acquired wakelock in D0\n", + dev->rev); + } ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D0); } else { EP_PCIE_ERR(dev, @@ -3334,11 +3344,44 @@ static const struct of_device_id ep_pcie_match[] = { {} }; +static int ep_pcie_suspend_noirq(struct device *pdev) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + + /* Allow suspend if autonomous M2 is enabled */ + if (dev->m2_autonomous) { + EP_PCIE_DBG(dev, + "PCIe V%d: Autonomous M2 is enabled, allow suspend\n", + dev->rev); + return 0; + } + + /* Allow suspend only after D3 cold is received */ + if (atomic_read(&dev->perst_deast)) { + EP_PCIE_DBG(dev, + "PCIe V%d: Perst not asserted, fail suspend\n", + dev->rev); + return -EBUSY; + } + + EP_PCIE_DBG(dev, + "PCIe V%d: Perst asserted, allow suspend\n", + dev->rev); + + return 0; +} + +static const struct dev_pm_ops ep_pcie_pm_ops = { + .suspend_noirq = ep_pcie_suspend_noirq, +}; + static struct platform_driver ep_pcie_driver = { .probe = ep_pcie_probe, .remove = ep_pcie_remove, .driver = { .name = "pcie-ep", + .pm = &ep_pcie_pm_ops, .of_match_table = ep_pcie_match, }, }; From 49401bbfc292adb0cb203db8f9b0e16af9eafd74 Mon Sep 17 00:00:00 2001 From: Gauri Joshi Date: Mon, 9 Nov 2020 13:11:40 +0530 Subject: [PATCH 2/6] msm: ep_pcie: Changes to acquire wakelock as part of PERST IRQ Changes to acquire wakelock as part of PERST deassert in non enumerated scenario. Change-Id: Ica992e12fe827e29416fbe56469d7be8b92ff5c5 Signed-off-by: Subramanian Ananthanarayanan Signed-off-by: Gauri Joshi --- drivers/platform/msm/ep_pcie/ep_pcie_core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index be8d1f5d644f..658a8eda9ff5 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -2330,6 +2330,18 @@ static irqreturn_t ep_pcie_handle_perst_irq(int irq, void *data) "PCIe V%d: PCIe is not enumerated yet; PERST is %sasserted\n", dev->rev, perst ? "de" : ""); if (perst) { + /* + * Hold a wakelock to avoid delay during + * link enablement in PCIE layer in non + * enumerated scenario. + */ + if (!atomic_read(&dev->ep_pcie_dev_wake)) { + pm_stay_awake(&dev->pdev->dev); + atomic_set(&dev->ep_pcie_dev_wake, 1); + EP_PCIE_DBG(dev, + "PCIe V%d: Acquired wakelock\n", + dev->rev); + } /* start work for link enumeration with the host side */ schedule_work(&dev->handle_perst_work); } else { From fcdc5358066e9db785c0c3f03598f8db18070503 Mon Sep 17 00:00:00 2001 From: Gauri Joshi Date: Fri, 28 Aug 2020 12:31:40 +0530 Subject: [PATCH 3/6] msm: ep_pcie: Update PERST status when BME is not set During customer testing, when host is in BIOS stage during critical battery level device boots up till AMSS. We get a BME IRQ fired, but BME is no set as host has bailed out enumeration process due to critical battery level. This is followed by a PERST assert, in disable_endpoint we bail out as the PERST is in de-asserted state according to the status variable, though the actual line status is assert. The host then continuosly tries to do PERST assert/de-assert but we never end up doing link training as from device perspective the link is already initialized state. Adding change to update PERST status in case of BME not getting set. This helps in disable_endpoint to happen followed by successful link training. Change-Id: Idd576f4cb0ab649b5aa86e6b1d2cdea204218878 Signed-off-by: Subramanian Ananthanarayanan Signed-off-by: Gauri Joshi --- drivers/platform/msm/ep_pcie/ep_pcie_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index 658a8eda9ff5..0a8d82ef822e 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -2329,6 +2329,7 @@ static irqreturn_t ep_pcie_handle_perst_irq(int irq, void *data) EP_PCIE_DBG(dev, "PCIe V%d: PCIe is not enumerated yet; PERST is %sasserted\n", dev->rev, perst ? "de" : ""); + atomic_set(&dev->perst_deast, perst ? 1 : 0); if (perst) { /* * Hold a wakelock to avoid delay during From 9186f10287512fc1ccd84338af4d22c53f1dbe9a Mon Sep 17 00:00:00 2001 From: Gauri Joshi Date: Thu, 22 Oct 2020 12:40:38 +0530 Subject: [PATCH 4/6] msm: ep_pcie: Avoid check for l23_ready in d3hot,d0 scenario ep_pcie_core_wakeup_host() checks for l23_ready, this results in WAKE pin getting asserted while PERST is already de-asserted. Becausee enable_end_point already completed, WAKE is never toggled back to the original state and remains asserted. Removing the check for l23_ready and thus preventing WAKE assert when PERST is in de-asserted state. Change-Id: I1389ad0f24af7d0d1877563327190737f9872611 Signed-off-by: Subramanian Ananthanarayanan Signed-off-by: Gauri Joshi --- drivers/platform/msm/ep_pcie/ep_pcie_com.h | 1 + drivers/platform/msm/ep_pcie/ep_pcie_core.c | 133 +++++++++++++------- 2 files changed, 87 insertions(+), 47 deletions(-) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h index 8e8a14a71fce..b9731458eaa7 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -401,6 +401,7 @@ struct ep_pcie_dev_t { bool client_ready; atomic_t ep_pcie_dev_wake; atomic_t perst_deast; + atomic_t host_wake_pending; struct ep_pcie_register_event *event_reg; struct work_struct handle_perst_work; diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index 0a8d82ef822e..e142e30f8dec 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -113,6 +113,8 @@ static const struct ep_pcie_irq_info_t ep_pcie_irq_info[EP_PCIE_MAX_IRQ] = { {"int_global", 0}, }; +static int ep_pcie_core_wakeup_host_internal(enum ep_pcie_event event); + int ep_pcie_get_debug_mask(void) { return ep_pcie_debug_mask; @@ -1615,6 +1617,35 @@ disable_clkreq: } EXPORT_SYMBOL(ep_pcie_core_l1ss_sleep_config_enable); +static void ep_pcie_core_toggle_wake_gpio(bool is_on) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + u32 val = dev->gpio[EP_PCIE_GPIO_WAKE].on; + + if (!is_on) { + val = !dev->gpio[EP_PCIE_GPIO_WAKE].on; + EP_PCIE_DBG(dev, + "PCIe V%d: deassert PCIe WAKE# after PERST# is deasserted\n", + dev->rev); + } else { + dev->wake_counter++; + } + + /* + * Toggle WAKE# GPIO until to prosed state + */ + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, val); + + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld to %sassert PCIe WAKE#; perst is %sasserted; D3hot is %s received, WAKE GPIO state:%d\n", + dev->rev, dev->wake_counter, + is_on ? "":"de-", + atomic_read(&dev->perst_deast) ? "de-" : "", + dev->l23_ready ? "" : "not", + gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num)); + +} + int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt) { int ret = 0; @@ -1773,18 +1804,8 @@ int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt) EP_PCIE_DBG(dev, "PCIe V%d: WAKE GPIO initial:%d\n", dev->rev, gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num)); - gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, - 1 - dev->gpio[EP_PCIE_GPIO_WAKE].on); - EP_PCIE_DBG(dev, - "PCIe V%d: WAKE GPIO after deassertion:%d\n", - dev->rev, - gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num)); - gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, - dev->gpio[EP_PCIE_GPIO_WAKE].on); - EP_PCIE_DBG(dev, - "PCIe V%d: WAKE GPIO after assertion:%d\n", - dev->rev, - gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num)); + ep_pcie_core_toggle_wake_gpio(false); + ep_pcie_core_toggle_wake_gpio(true); } /* wait for host side to deassert PERST */ @@ -1805,16 +1826,11 @@ int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt) dev->rev); ret = EP_PCIE_ERROR; goto link_fail; - } else { - atomic_set(&dev->perst_deast, 1); - if (opt & EP_PCIE_OPT_AST_WAKE) { - /* deassert PCIe WAKE# */ - EP_PCIE_DBG(dev, - "PCIe V%d: deassert PCIe WAKE# after PERST# is deasserted\n", - dev->rev); - gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, - 1 - dev->gpio[EP_PCIE_GPIO_WAKE].on); - } + } + atomic_set(&dev->perst_deast, 1); + if (opt & EP_PCIE_OPT_AST_WAKE) { + /* deassert PCIe WAKE# */ + ep_pcie_core_toggle_wake_gpio(false); } /* init PCIe PHY */ @@ -1907,8 +1923,7 @@ checkbme: * is triggered to send data from device to host at which point * it will assert WAKE#. */ - gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, - 1 - dev->gpio[EP_PCIE_GPIO_WAKE].on); + ep_pcie_core_toggle_wake_gpio(false); if (dev->active_config) ep_pcie_write_reg(dev->dm_core, PCIE20_AUX_CLK_FREQ_REG, 0x14); @@ -2019,6 +2034,18 @@ int ep_pcie_core_disable_endpoint(void) dev->rev, atomic_read(&dev->perst_deast), atomic_read(&dev->ep_pcie_dev_wake)); } + + /* + * In some caes though device requested to do an inband PME + * the host might still proceed with PERST assertion, below + * code is to toggle WAKE in such sceanrios. + */ + if (atomic_read(&dev->host_wake_pending)) { + EP_PCIE_DBG(dev, "PCIe V%d: %s: wake pending, init wakeup\n", + dev->rev); + ep_pcie_core_wakeup_host_internal(EP_PCIE_EVENT_PM_D3_COLD); + } + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); out: mutex_unlock(&dev->setup_mtx); @@ -2202,12 +2229,18 @@ static irqreturn_t ep_pcie_handle_dstate_change_irq(int irq, void *data) EP_PCIE_DBG(dev, "PCIe V%d: do not notify client about this D3 hot event since enumeration by HLOS is not done yet\n", dev->rev); + if (atomic_read(&dev->host_wake_pending)) + ep_pcie_core_wakeup_host_internal( + EP_PCIE_EVENT_PM_D3_HOT); + } else if (dstate == 0) { dev->l23_ready = false; dev->d0_counter++; + atomic_set(&dev->host_wake_pending, 0); EP_PCIE_DBG(dev, - "PCIe V%d: No. %ld change to D0 state\n", - dev->rev, dev->d0_counter); + "PCIe V%d: No. %ld change to D0 state, clearing wake pending:%d\n", + dev->rev, dev->d0_counter, + atomic_read(&dev->host_wake_pending)); /* * During device bootup, there will not be any PERT-deassert, * so aquire wakelock from D0 event @@ -2997,11 +3030,8 @@ int ep_pcie_core_trigger_msi(u32 idx) static void ep_pcie_core_issue_inband_pme(void) { struct ep_pcie_dev_t *dev = &ep_pcie_dev; - unsigned long irqsave_flags; u32 pm_ctrl = 0; - spin_lock_irqsave(&dev->isr_lock, irqsave_flags); - EP_PCIE_DBG(dev, "PCIe V%d: request to assert inband wake\n", dev->rev); @@ -3014,35 +3044,44 @@ static void ep_pcie_core_issue_inband_pme(void) EP_PCIE_DBG(dev, "PCIe V%d: completed assert for inband wake\n", dev->rev); - - spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); } - -static int ep_pcie_core_wakeup_host(enum ep_pcie_event event) +static int ep_pcie_core_wakeup_host_internal(enum ep_pcie_event event) { struct ep_pcie_dev_t *dev = &ep_pcie_dev; - if (event == EP_PCIE_EVENT_PM_D3_HOT) - ep_pcie_core_issue_inband_pme(); - - if (atomic_read(&dev->perst_deast) && !dev->l23_ready) { + if (!atomic_read(&dev->perst_deast)) { + /*D3 cold handling*/ + ep_pcie_core_toggle_wake_gpio(true); + } else if (dev->l23_ready) { EP_PCIE_ERR(dev, - "PCIe V%d: request to assert WAKE# when PERST is de-asserted and D3hot is not received\n", + "PCIe V%d: request to assert WAKE# when in D3hot\n", + dev->rev); + /*D3 hot handling*/ + ep_pcie_core_issue_inband_pme(); + } else { + /*D0 handling*/ + EP_PCIE_ERR(dev, + "PCIe V%d: request to assert WAKE# when in D0\n", dev->rev); - return EP_PCIE_ERROR; } - dev->wake_counter++; + atomic_set(&dev->host_wake_pending, 1); EP_PCIE_DBG(dev, - "PCIe V%d: No. %ld to assert PCIe WAKE#; perst is %s de-asserted; D3hot is %s received\n", - dev->rev, dev->wake_counter, + "PCIe V%d: Set wake pending : %d and return ; perst is %s de-asserted; D3hot is %s set\n", + dev->rev, atomic_read(&dev->host_wake_pending), atomic_read(&dev->perst_deast) ? "" : "not", dev->l23_ready ? "" : "not"); - /* - * Assert WAKE# GPIO until link is back to L0. - */ - gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, - dev->gpio[EP_PCIE_GPIO_WAKE].on); + return 0; + +} +static int ep_pcie_core_wakeup_host(enum ep_pcie_event event) +{ + unsigned long irqsave_flags; + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + ep_pcie_core_wakeup_host_internal(event); + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); return 0; } From dd572f235194188eae38e1d5de36eb2ed79ca121 Mon Sep 17 00:00:00 2001 From: Gauri Joshi Date: Wed, 21 Oct 2020 15:37:49 +0530 Subject: [PATCH 5/6] msm: ep_pcie: Add IPA MSI ATU config All transactions originating from IPA have the Relaxed Ordering bit set by default. Setup an ATU region to clear the bit for MSIs triggered via IPA DMA as MSI writes shouldn't have it set. Change-Id: Ib092834ca2d1a3e01aa3e63e1b35f7ab6766d9a0 Signed-off-by: Siva Kumar Akkireddi Signed-off-by: Gauri Joshi --- drivers/platform/msm/ep_pcie/ep_pcie_com.h | 2 ++ drivers/platform/msm/ep_pcie/ep_pcie_core.c | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h index b9731458eaa7..11ae50e0aa77 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -187,6 +187,7 @@ #define EP_PCIE_OATU_INDEX_MSI 1 #define EP_PCIE_OATU_INDEX_CTRL 2 #define EP_PCIE_OATU_INDEX_DATA 3 +#define EP_PCIE_OATU_INDEX_IPA_MSI 4 #define EP_PCIE_OATU_UPPER 0x100 @@ -402,6 +403,7 @@ struct ep_pcie_dev_t { atomic_t ep_pcie_dev_wake; atomic_t perst_deast; atomic_t host_wake_pending; + bool conf_ipa_msi_iatu; struct ep_pcie_register_event *event_reg; struct work_struct handle_perst_work; diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index e142e30f8dec..2270d346823b 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -2015,6 +2015,7 @@ int ep_pcie_core_disable_endpoint(void) EP_PCIE_DBG(dev, "PCIe V%d: shut down the link\n", dev->rev); } + dev->conf_ipa_msi_iatu = false; val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS); EP_PCIE_DBG(dev, "PCIe V%d: LTSSM_STATE during disable:0x%x\n", @@ -2947,6 +2948,24 @@ int ep_pcie_core_get_msi_config(struct ep_pcie_msi_config *cfg) ep_pcie_dev.msi_cfg.upper = upper; ep_pcie_dev.msi_cfg.data = data; ep_pcie_dev.msi_cfg.msg_num = cfg->msg_num; + ep_pcie_dev.conf_ipa_msi_iatu = false; + } + /* + * All transactions originating from IPA have the RO + * bit set by default. Setup another ATU region to clear + * the RO bit for MSIs triggered via IPA DMA. + */ + if (ep_pcie_dev.active_config && + !ep_pcie_dev.conf_ipa_msi_iatu) { + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_IPA_MSI, + lower, 0, + (lower + resource_size(msi) - 1), + lower, upper); + ep_pcie_dev.conf_ipa_msi_iatu = true; + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: Conf iATU for IPA MSI info: lower:0x%x; upper:0x%x\n", + ep_pcie_dev.rev, lower, upper); } return 0; } From 43c5563a578951457fc496a97a0f0ee04d5444cd Mon Sep 17 00:00:00 2001 From: Gauri Joshi Date: Mon, 9 Nov 2020 13:03:00 +0530 Subject: [PATCH 6/6] msm: ep_pcie: Changes to avoid handle_perst_wq schedule out Queue handle_perst_wq as system_highpri_wq to avoid WQ getting scheduled out. Change-Id: I28fdc6e3d51b98b132c04bf552c40c9dad3cbd0f Signed-off-by: Subramanian Ananthanarayanan Signed-off-by: Gauri Joshi --- drivers/platform/msm/ep_pcie/ep_pcie_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index 2270d346823b..ea548c8c4180 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -2378,7 +2378,7 @@ static irqreturn_t ep_pcie_handle_perst_irq(int irq, void *data) dev->rev); } /* start work for link enumeration with the host side */ - schedule_work(&dev->handle_perst_work); + queue_work(system_highpri_wq, &dev->handle_perst_work); } else { dev->no_notify = true; /* shutdown the link if the link is already on */