From 782a07e2bc550c4fa8761a76d2c5602db7c749a4 Mon Sep 17 00:00:00 2001 From: Dustin Brown Date: Wed, 7 Dec 2016 14:14:24 -0800 Subject: [PATCH] qcacmn: Port 3-stage suspend/resume to PCI SNOC bus suspend/resume uses a 3-stage process. Port the same process to PCI code paths by adding a hif_pci_bus_suspend_noirq callback for the suspend case and a dummy callback for the resume case. Change-Id: I83d8c5e10aecde6812aa8164f93c8f1628a33101 CRs-Fixed: 1098552 --- hif/src/dispatcher/multibus_pci.c | 2 + hif/src/dispatcher/pci_api.h | 2 + hif/src/pcie/if_pci.c | 265 ++++++++++++++++-------------- 3 files changed, 149 insertions(+), 120 deletions(-) diff --git a/hif/src/dispatcher/multibus_pci.c b/hif/src/dispatcher/multibus_pci.c index 44f2953d0a02..ad1ffdf7c8d1 100644 --- a/hif/src/dispatcher/multibus_pci.c +++ b/hif/src/dispatcher/multibus_pci.c @@ -49,6 +49,8 @@ QDF_STATUS hif_initialize_pci_ops(struct hif_softc *hif_sc) bus_ops->hif_reset_soc = &hif_pci_reset_soc; bus_ops->hif_bus_suspend = &hif_pci_bus_suspend; bus_ops->hif_bus_resume = &hif_pci_bus_resume; + bus_ops->hif_bus_suspend_noirq = &hif_pci_bus_suspend_noirq; + bus_ops->hif_bus_resume_noirq = &hif_pci_bus_resume_noirq; /* do not put the target to sleep for epping or maxperf mode */ if (CONFIG_ATH_PCIE_MAX_PERF == 0 && diff --git a/hif/src/dispatcher/pci_api.h b/hif/src/dispatcher/pci_api.h index 6395cf484c12..88c52f9054f6 100644 --- a/hif/src/dispatcher/pci_api.h +++ b/hif/src/dispatcher/pci_api.h @@ -32,7 +32,9 @@ void hif_pci_close(struct hif_softc *hif_ctx); void hif_pci_prevent_linkdown(struct hif_softc *scn, bool flag); void hif_pci_reset_soc(struct hif_softc *ol_sc); int hif_pci_bus_suspend(struct hif_softc *scn); +int hif_pci_bus_suspend_noirq(struct hif_softc *scn); int hif_pci_bus_resume(struct hif_softc *scn); +int hif_pci_bus_resume_noirq(struct hif_softc *scn); int hif_pci_target_sleep_state_adjust(struct hif_softc *scn, bool sleep_ok, bool wait_for_it); diff --git a/hif/src/pcie/if_pci.c b/hif/src/pcie/if_pci.c index b714454c1f65..b7471a9283c2 100644 --- a/hif/src/pcie/if_pci.c +++ b/hif/src/pcie/if_pci.c @@ -2727,143 +2727,48 @@ void hif_pci_prevent_linkdown(struct hif_softc *scn, bool flag) #endif /** - * hif_bus_suspend_link_up() - suspend the bus + * hif_pci_bus_enable_wake_irq() - enable pci bus wake irq * * Configures the pci irq line as a wakeup source. * * Return: 0 for success and non-zero for failure */ -static int hif_bus_suspend_link_up(struct hif_softc *scn) +static int hif_pci_bus_enable_wake_irq(struct hif_softc *scn) { - struct pci_dev *pdev; - int status; struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); - if (!sc) + if (!sc) { + HIF_ERROR("%s: sc is null", __func__); return -EFAULT; + } - pdev = sc->pdev; + if (!sc->pdev) { + HIF_ERROR("%s: pdev is null", __func__); + return -EFAULT; + } - status = hif_drain_tasklets(scn); - if (status != 0) - return status; - - if (unlikely(enable_irq_wake(pdev->irq))) { - HIF_ERROR("%s: Fail to enable wake IRQ!", __func__); + if (unlikely(enable_irq_wake(sc->pdev->irq))) { + HIF_ERROR("%s: Failed to enable wake IRQ", __func__); return -EINVAL; } - hif_cancel_deferred_target_sleep(scn); - return 0; } /** - * hif_bus_resume_link_up() - hif bus resume API + * hif_pci_bus_suspend(): prepare hif for suspend * - * This function disables the wakeup source. - * - * Return: 0 for success and non-zero for failure - */ -static int hif_bus_resume_link_up(struct hif_softc *scn) -{ - struct pci_dev *pdev; - struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); - - if (!sc) - return -EFAULT; - - pdev = sc->pdev; - - if (!pdev) { - HIF_ERROR("%s: pci_dev is null", __func__); - return -EFAULT; - } - - if (unlikely(disable_irq_wake(pdev->irq))) { - HIF_ERROR("%s: Fail to disable wake IRQ!", __func__); - return -EFAULT; - } - - return 0; -} - -/** - * hif_bus_suspend_link_down() - suspend the bus - * - * Suspends the hif layer taking care of draining recieve queues and - * shutting down copy engines if needed. Ensures opy engine interrupts - * are disabled when it returns. Prevents register access after it - * returns. - * - * Return: 0 for success and non-zero for failure - */ -static int hif_bus_suspend_link_down(struct hif_softc *scn) -{ - struct pci_dev *pdev; - struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); - int status = 0; - - pdev = sc->pdev; - - disable_irq(pdev->irq); - - status = hif_drain_tasklets(scn); - if (status != 0) { - enable_irq(pdev->irq); - return status; - } - - /* Stop the HIF Sleep Timer */ - hif_cancel_deferred_target_sleep(scn); - - qdf_atomic_set(&scn->link_suspended, 1); - - return 0; -} - -/** - * hif_bus_resume_link_down() - hif bus resume API - * - * This function resumes the bus reenabling interupts. - * - * Return: 0 for success and non-zero for failure - */ -static int hif_bus_resume_link_down(struct hif_softc *scn) -{ - struct pci_dev *pdev; - struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); - - if (!sc) - return -EFAULT; - - pdev = sc->pdev; - - if (!pdev) { - HIF_ERROR("%s: pci_dev is null", __func__); - return -EFAULT; - } - - qdf_atomic_set(&scn->link_suspended, 0); - - enable_irq(pdev->irq); - - return 0; -} - -/** - * hif_pci_suspend(): prepare hif for suspend - * - * chose suspend type based on link suspend voting. + * Enables pci bus wake irq based on link suspend voting. * * Return: 0 for success and non-zero error code for failure */ int hif_pci_bus_suspend(struct hif_softc *scn) { if (hif_can_suspend_link(GET_HIF_OPAQUE_HDL(scn))) - return hif_bus_suspend_link_down(scn); - else - return hif_bus_suspend_link_up(scn); + return 0; + + /* pci link is staying up; enable wake irq */ + return hif_pci_bus_enable_wake_irq(scn); } /** @@ -2906,9 +2811,38 @@ static int __hif_check_link_status(struct hif_softc *scn) } /** - * hif_bus_resume(): prepare hif for resume + * hif_pci_bus_disable_wake_irq() - disable pci bus wake irq * - * chose suspend type based on link suspend voting. + * Deconfigures the pci irq line as a wakeup source. + * + * Return: 0 for success and non-zero for failure + */ +static int hif_pci_bus_disable_wake_irq(struct hif_softc *scn) +{ + struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn); + + if (!sc) { + HIF_ERROR("%s: sc is null", __func__); + return -EFAULT; + } + + if (!sc->pdev) { + HIF_ERROR("%s: pdev is null", __func__); + return -EFAULT; + } + + if (unlikely(disable_irq_wake(sc->pdev->irq))) { + HIF_ERROR("%s: Failed to disable wake IRQ", __func__); + return -EFAULT; + } + + return 0; +} + +/** + * hif_pci_bus_resume(): prepare hif for resume + * + * Disables pci bus wake irq based on link suspend voting. * * Return: 0 for success and non-zero error code for failure */ @@ -2921,9 +2855,50 @@ int hif_pci_bus_resume(struct hif_softc *scn) return ret; if (hif_can_suspend_link(GET_HIF_OPAQUE_HDL(scn))) - return hif_bus_resume_link_down(scn); - else - return hif_bus_resume_link_up(scn); + return 0; + + /* pci link is up; disable wake irq */ + return hif_pci_bus_disable_wake_irq(scn); +} + +/** + * hif_pci_bus_suspend_noirq() - ensure there are no pending transactions + * @scn: hif context + * + * Ensure that if we recieved the wakeup message before the irq + * was disabled that the message is pocessed before suspending. + * + * Return: -EBUSY if we fail to flush the tasklets. + */ +int hif_pci_bus_suspend_noirq(struct hif_softc *scn) +{ + if (hif_drain_tasklets(scn) != 0) + return -EBUSY; + + /* Stop the HIF Sleep Timer */ + hif_cancel_deferred_target_sleep(scn); + + if (hif_can_suspend_link(GET_HIF_OPAQUE_HDL(scn))) + qdf_atomic_set(&scn->link_suspended, 1); + + return 0; +} + +/** + * hif_pci_bus_resume_noirq() - ensure there are no pending transactions + * @scn: hif context + * + * Ensure that if we recieved the wakeup message before the irq + * was disabled that the message is pocessed before suspending. + * + * Return: -EBUSY if we fail to flush the tasklets. + */ +int hif_pci_bus_resume_noirq(struct hif_softc *scn) +{ + if (hif_can_suspend_link(GET_HIF_OPAQUE_HDL(scn))) + qdf_atomic_set(&scn->link_suspended, 0); + + return 0; } #ifdef FEATURE_RUNTIME_PM @@ -3112,7 +3087,33 @@ void hif_process_runtime_resume_success(struct hif_opaque_softc *hif_ctx) */ int hif_runtime_suspend(struct hif_opaque_softc *hif_ctx) { - return hif_pci_bus_suspend(HIF_GET_SOFTC(hif_ctx)); + struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); + struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn) + int err; + + err = hif_pci_bus_suspend(scn); + if (err) + goto exit_with_error; + + /* + * normal 3-stage suspend from CNSS disables irqs before calling + * noirq stage + */ + disable_irq(sc->pdev->irq); + + err = hif_pci_bus_suspend_noirq(scn); + if (err) + goto bus_resume; + + return 0; + +bus_resume: + enable_irq(sc->pdev->irq); + err = hif_pci_bus_resume(scn); + QDF_BUG(err == 0); + +exit_with_error: + return err; } #ifdef WLAN_FEATURE_FASTPATH @@ -3157,11 +3158,35 @@ static void hif_fastpath_resume(struct hif_opaque_softc *hif_ctx) {} */ int hif_runtime_resume(struct hif_opaque_softc *hif_ctx) { - int status = hif_pci_bus_resume(HIF_GET_SOFTC(hif_ctx)); + struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); + struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn) + int err; + + err = hif_pci_bus_resume_noirq(scn); + if (err) + goto exit_with_error; + + /* + * normal 3-stage resume from CNSS enables irqs after calling + * noirq stage + */ + enable_irq(sc->pdev->irq); + + err = hif_pci_bus_resume(scn); + if (err) + goto bus_suspend_noirq; hif_fastpath_resume(hif_ctx); - return status; + return 0; + +bus_suspend_noirq: + disable_irq(sc->pdev->irq); + err = hif_pci_bus_suspend_noirq(scn); + QDF_BUG(err == 0); + +exit_with_error: + return err; } #endif /* #ifdef FEATURE_RUNTIME_PM */