Browse Source

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
Dustin Brown 8 years ago
parent
commit
782a07e2bc
3 changed files with 147 additions and 118 deletions
  1. 2 0
      hif/src/dispatcher/multibus_pci.c
  2. 2 0
      hif/src/dispatcher/pci_api.h
  3. 143 118
      hif/src/pcie/if_pci.c

+ 2 - 0
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 &&

+ 2 - 0
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);
 

+ 143 - 118
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)
-		return -EFAULT;
-
-	pdev = sc->pdev;
-
-	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__);
-		return -EINVAL;
-	}
-
-	hif_cancel_deferred_target_sleep(scn);
-
-	return 0;
-}
-
-/**
- * hif_bus_resume_link_up() - hif bus resume API
- *
- * 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__);
+	if (!sc) {
+		HIF_ERROR("%s: sc is null", __func__);
 		return -EFAULT;
 	}
 
-	if (unlikely(disable_irq_wake(pdev->irq))) {
-		HIF_ERROR("%s: Fail to disable wake IRQ!", __func__);
+	if (!sc->pdev) {
+		HIF_ERROR("%s: pdev is null", __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;
+	if (unlikely(enable_irq_wake(sc->pdev->irq))) {
+		HIF_ERROR("%s: Failed to enable wake IRQ", __func__);
+		return -EINVAL;
 	}
 
-	qdf_atomic_set(&scn->link_suspended, 0);
-
-	enable_irq(pdev->irq);
-
 	return 0;
 }
 
 /**
- * hif_pci_suspend(): prepare hif for suspend
+ * hif_pci_bus_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 */