summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRajeev Kumar <rajekuma@codeaurora.org>2016-08-29 13:53:52 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-09-02 18:23:58 -0700
commitfa4d902036972e677c4dbe2bc1efd5f1e94713af (patch)
tree17cd9aac2f24da71692e02c51d32bc1d53439ca8
parentda6a63e2a98556a32574a2084963d47979c75d3c (diff)
qcacld-3.0: Fix race condition between PM suspend and fw irq
PM suspend and FW interrupt handler is running at same time which is leading to multiple race conditions and host is missing FW wake up interrupt which is causing APPS not waking up reliably. Define .suspend_noirq and .resume_noirq callbacks and make sure there is no pending FW interrupt before allowing PM suspend to complete. Kernel PM suspend framework gives guarantee that suspend_noirq and device interrupt handler can't run simultaneously. This helps WLAN driver to reliably detect FW wake up interrupt during PM suspend and fail PM suspend gracefully if FW has requested for initial wake up. Change-Id: I9e55f58aee29017fe09efeb5231b8be23053f7b7 CRs-Fixed: 1060748
-rw-r--r--core/hdd/src/wlan_hdd_driver_ops.c176
-rw-r--r--core/wma/inc/wma_api.h1
-rw-r--r--core/wma/src/wma_features.c25
3 files changed, 193 insertions, 9 deletions
diff --git a/core/hdd/src/wlan_hdd_driver_ops.c b/core/hdd/src/wlan_hdd_driver_ops.c
index e335d2b76ba2..acff948192da 100644
--- a/core/hdd/src/wlan_hdd_driver_ops.c
+++ b/core/hdd/src/wlan_hdd_driver_ops.c
@@ -554,16 +554,9 @@ static int __wlan_hdd_bus_suspend(pm_message_t state)
if (err)
goto resume_wma;
- err = wma_is_target_wake_up_received();
- if (err)
- goto resume_hif;
-
hdd_err("suspend done, status = %d", err);
return err;
-resume_hif:
- status = hif_bus_resume(hif_ctx);
- QDF_BUG(!status);
resume_wma:
status = wma_bus_resume();
QDF_BUG(!status);
@@ -597,6 +590,77 @@ int wlan_hdd_bus_suspend(pm_message_t state)
}
/**
+ * __wlan_hdd_bus_suspend_noirq() - handle .suspend_noirq callback
+ *
+ * This function is called by the platform driver to complete the
+ * bus suspend callback when device interrupts are disabled by kernel.
+ * Call HIF and WMA suspend_noirq callbacks to make sure there is no
+ * wake up pending from FW before allowing suspend.
+ *
+ * Return: 0 for success and -EBUSY if FW is requesting wake up
+ */
+int __wlan_hdd_bus_suspend_noirq(void)
+{
+ hdd_context_t *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
+ void *hif_ctx;
+ int err = wlan_hdd_validate_context(hdd_ctx);
+ int status;
+
+ if (err)
+ goto done;
+
+ hif_ctx = cds_get_context(QDF_MODULE_ID_HIF);
+ if (NULL == hif_ctx) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) {
+ hdd_info("Driver Module closed return success");
+ return 0;
+ }
+
+ err = hif_bus_suspend_noirq(hif_ctx);
+ if (err)
+ goto done;
+
+ err = wma_is_target_wake_up_received();
+ if (err)
+ goto resume_hif_noirq;
+
+ hdd_err("suspend_noirq done, status = %d", err);
+ return err;
+
+resume_hif_noirq:
+ status = hif_bus_resume_noirq(hif_ctx);
+ QDF_BUG(!status);
+done:
+ hdd_err("suspend_noirq done, status = %d", err);
+ return err;
+}
+
+/**
+ * wlan_hdd_bus_suspend_noirq() - handle .suspend_noirq callback
+ *
+ * This function is called by the platform driver to complete the
+ * bus suspend callback when device interrupts are disabled by kernel.
+ * Call HIF and WMA suspend_noirq callbacks to make sure there is no
+ * wake up pending from FW before allowing suspend.
+ *
+ * Return: 0 for success and -EBUSY if FW is requesting wake up
+ */
+int wlan_hdd_bus_suspend_noirq(void)
+{
+ int ret;
+
+ cds_ssr_protect(__func__);
+ ret = __wlan_hdd_bus_suspend_noirq();
+ cds_ssr_unprotect(__func__);
+
+ return ret;
+}
+
+/**
* __wlan_hdd_bus_resume() - handles platform resume
*
* Does precondtion validation. Ensures that a subsystem restart isn't in
@@ -660,6 +724,63 @@ int wlan_hdd_bus_resume(void)
}
/**
+ * __wlan_hdd_bus_resume_noirq(): handle bus resume no irq
+ *
+ * This function is called by the platform driver to do bus
+ * resume no IRQ before calling resume callback. Call WMA and HIF
+ * layers to complete the resume_noirq.
+ *
+ * Return: 0 for success and negative error code for failure
+ */
+int __wlan_hdd_bus_resume_noirq(void)
+{
+ hdd_context_t *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
+ void *hif_ctx;
+ int status = wlan_hdd_validate_context(hdd_ctx);
+
+ if (status)
+ return status;
+
+ if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) {
+ hdd_info("Driver Module closed return success");
+ return 0;
+ }
+
+ hif_ctx = cds_get_context(QDF_MODULE_ID_HIF);
+ if (NULL == hif_ctx)
+ return -EINVAL;
+
+ status = wma_clear_target_wake_up();
+ QDF_BUG(!status);
+
+ status = hif_bus_resume_noirq(hif_ctx);
+ QDF_BUG(!status);
+
+ hdd_err("resume_noirq done");
+ return status;
+}
+
+/**
+ * wlan_hdd_bus_resume_noirq(): handle bus resume no irq
+ *
+ * This function is called by the platform driver to do bus
+ * resume no IRQ before calling resume callback. Call WMA and HIF
+ * layers to complete the resume_noirq.
+ *
+ * Return: 0 for success and negative error code for failure
+ */
+int wlan_hdd_bus_resume_noirq(void)
+{
+ int ret;
+
+ cds_ssr_protect(__func__);
+ ret = __wlan_hdd_bus_resume_noirq();
+ cds_ssr_unprotect(__func__);
+
+ return ret;
+}
+
+/**
* wlan_hdd_bus_reset_resume() - resume wlan bus after reset
*
* This function is called to tell the driver that the device has been resumed
@@ -921,6 +1042,45 @@ static int wlan_hdd_pld_resume(struct device *dev,
return wlan_hdd_bus_resume();
}
+
+/**
+ * wlan_hdd_pld_suspend_noirq() - handle suspend no irq
+ * @dev: device
+ * @pld_bus_type: PLD bus type
+ *
+ * Complete the actions started by suspend(). Carry out any
+ * additional operations required for suspending the device that might be
+ * racing with its driver's interrupt handler, which is guaranteed not to
+ * run while suspend_noirq() is being executed. Make sure to resume device
+ * if FW has sent initial wake up message and expecting APPS to wake up.
+ *
+ * Return: 0 on success
+ */
+static int wlan_hdd_pld_suspend_noirq(struct device *dev,
+ enum pld_bus_type bus_type)
+{
+ return wlan_hdd_bus_suspend_noirq();
+}
+
+/**
+ * wlan_hdd_pld_resume_noirq() - handle resume no irq
+ * @dev: device
+ * @pld_bus_type: PLD bus type
+ *
+ * Prepare for the execution of resume() by carrying out any
+ * operations required for resuming the device that might be racing with
+ * its driver's interrupt handler, which is guaranteed not to run while
+ * resume_noirq() is being executed. Make sure to clear target initial
+ * wake up request such that next suspend can happen cleanly.
+ *
+ * Return: 0 on success
+ */
+static int wlan_hdd_pld_resume_noirq(struct device *dev,
+ enum pld_bus_type bus_type)
+{
+ return wlan_hdd_bus_resume_noirq();
+}
+
/**
* wlan_hdd_pld_reset_resume() - reset resume function registered to PLD
* @dev: device
@@ -985,6 +1145,8 @@ struct pld_driver_ops wlan_drv_ops = {
.crash_shutdown = wlan_hdd_pld_crash_shutdown,
.suspend = wlan_hdd_pld_suspend,
.resume = wlan_hdd_pld_resume,
+ .suspend_noirq = wlan_hdd_pld_suspend_noirq,
+ .resume_noirq = wlan_hdd_pld_resume_noirq,
.reset_resume = wlan_hdd_pld_reset_resume,
.modem_status = wlan_hdd_pld_notify_handler,
#ifdef FEATURE_RUNTIME_PM
diff --git a/core/wma/inc/wma_api.h b/core/wma/inc/wma_api.h
index 1bc33afb7379..a63db28d3d8e 100644
--- a/core/wma/inc/wma_api.h
+++ b/core/wma/inc/wma_api.h
@@ -146,6 +146,7 @@ int wma_runtime_suspend(void);
int wma_runtime_resume(void);
int wma_bus_suspend(void);
int wma_is_target_wake_up_received(void);
+int wma_clear_target_wake_up(void);
QDF_STATUS wma_suspend_target(WMA_HANDLE handle, int disable_target_intr);
void wma_target_suspend_acknowledge(void *context, bool wow_nack);
void wma_handle_initial_wake_up(void);
diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c
index 41f3d173ea3f..81726963d750 100644
--- a/core/wma/src/wma_features.c
+++ b/core/wma/src/wma_features.c
@@ -6625,9 +6625,30 @@ int wma_is_target_wake_up_received(void)
if (wma->wow_initial_wake_up) {
WMA_LOGE("Target initial wake up received try again");
return -EAGAIN;
- } else {
- return 0;
}
+
+ return 0;
+}
+
+/**
+ * wma_clear_target_wake_up() - clear initial wake up
+ *
+ * Clear target initial wake up reason
+ *
+ * Return: 0 for success and negative error code for failure
+ */
+int wma_clear_target_wake_up(void)
+{
+ tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA);
+
+ if (NULL == wma) {
+ WMA_LOGE("%s: wma is NULL", __func__);
+ return -EFAULT;
+ }
+
+ wma->wow_initial_wake_up = false;
+
+ return 0;
}
/**