qcacld-3.0: Acquire wakelock before sending VDEV_STOP

The host driver should not attempt to power collapse while pending
VDEV_STOP commands are in flight. Acquire a wakelock before sending a
VDEV_STOP request to firmware, and release it upon receipt of VDEV_STOP
response.

Change-Id: Iac90fc249e9571090df6948ed931cc07b67e938d
CRs-Fixed: 2014496
diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h
index 4692319..3835f95 100644
--- a/core/wma/inc/wma_internal.h
+++ b/core/wma/inc/wma_internal.h
@@ -1239,4 +1239,36 @@
 };
 #endif /* FEATURE_WLAN_DIAG_SUPPORT */
 
+/**
+ * wma_acquire_wmi_resp_wakelock() - acquire the WMI response wakelock
+ * @wma: the WMA handle containing the wakelock to acquire
+ * @msec: the wakelock duration in milliseconds
+ *
+ * Return: void
+ */
+void wma_acquire_wmi_resp_wakelock(t_wma_handle *wma, uint32_t msec);
+
+/**
+ * wma_release_wmi_resp_wakelock() - release the WMI response wakelock
+ * @wma: the WMA handle containing the wakelock to release
+ *
+ * Return: void
+ */
+void wma_release_wmi_resp_wakelock(t_wma_handle *wma);
+
+/**
+ * wma_send_vdev_stop_to_fw() - send the vdev stop command to firmware
+ * @wma: the WMA handle containing a reference to the wmi_handle to use
+ * @vdev_id: the VDEV Id of the VDEV to stop
+ *
+ * This is a helper function that acquires the WMI response wakelock before
+ * sending down the VDEV_STOP command to firmware. This wakelock is
+ * automatically released on failure. Consumers should call
+ * wma_release_wmi_resp_wakelock() upon receipt of the VDEV_STOP response from
+ * firmware, to avoid power penalties.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wma_send_vdev_stop_to_fw(t_wma_handle *wma, uint8_t vdev_id);
+
 #endif
diff --git a/core/wma/src/wma_data.c b/core/wma/src/wma_data.c
index 84da522..2bbc736 100644
--- a/core/wma/src/wma_data.c
+++ b/core/wma/src/wma_data.c
@@ -1223,7 +1223,7 @@
 			wma->interfaces[vdev_id].handle,
 			OL_TXQ_PAUSE_REASON_VDEV_STOP);
 		wma->interfaces[vdev_id].pause_bitmap |= (1 << PAUSE_TYPE_HOST);
-		if (wmi_unified_vdev_stop_send(wma->wmi_handle, vdev_id)) {
+		if (wma_send_vdev_stop_to_fw(wma, vdev_id)) {
 			WMA_LOGP("%s: %d Failed to send vdev stop",
 				 __func__, __LINE__);
 		}
diff --git a/core/wma/src/wma_dev_if.c b/core/wma/src/wma_dev_if.c
index a8196af..5c87025 100644
--- a/core/wma/src/wma_dev_if.c
+++ b/core/wma/src/wma_dev_if.c
@@ -1375,6 +1375,9 @@
 	struct wma_txrx_node *iface;
 	int32_t status = 0;
 	void *soc = cds_get_context(QDF_MODULE_ID_SOC);
+
+	wma_release_wmi_resp_wakelock(wma);
+
 #ifdef FEATURE_AP_MCC_CH_AVOIDANCE
 	tpAniSirGlobal mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
 	if (NULL == mac_ctx) {
@@ -1435,6 +1438,7 @@
 		tpDeleteBssParams params =
 			(tpDeleteBssParams) req_msg->user_data;
 		struct beacon_info *bcn;
+
 		if (resp_event->vdev_id > wma->max_bssid) {
 			WMA_LOGE("%s: Invalid vdev_id %d", __func__,
 				 resp_event->vdev_id);
@@ -4578,7 +4582,7 @@
 		OL_TXQ_PAUSE_REASON_VDEV_STOP);
 	iface->pause_bitmap |= (1 << PAUSE_TYPE_HOST);
 
-	if (wmi_unified_vdev_stop_send(wma->wmi_handle, params->smesessionId)) {
+	if (wma_send_vdev_stop_to_fw(wma, params->smesessionId)) {
 		WMA_LOGP("%s: %d Failed to send vdev stop", __func__, __LINE__);
 		wma_remove_vdev_req(wma, params->smesessionId,
 				WMA_TARGET_REQ_TYPE_VDEV_STOP);
diff --git a/core/wma/src/wma_mgmt.c b/core/wma/src/wma_mgmt.c
index 72387eb..1166d98 100644
--- a/core/wma/src/wma_mgmt.c
+++ b/core/wma/src/wma_mgmt.c
@@ -2933,7 +2933,7 @@
 		OL_TXQ_PAUSE_REASON_VDEV_STOP);
 	wma_handle->interfaces[pReq->sessionId].pause_bitmap |=
 							(1 << PAUSE_TYPE_HOST);
-	if (wmi_unified_vdev_stop_send(wma_handle->wmi_handle, pReq->sessionId)) {
+	if (wma_send_vdev_stop_to_fw(wma_handle, pReq->sessionId)) {
 		WMA_LOGE("%s: %d Failed to send vdev stop", __func__, __LINE__);
 		qdf_atomic_set(&intr[pReq->sessionId].vdev_restart_params.
 			       hidden_ssid_restart_in_progress, 0);
diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c
index de7b851..0e3e400 100644
--- a/core/wma/src/wma_utils.c
+++ b/core/wma/src/wma_utils.c
@@ -3960,3 +3960,30 @@
 	return (state == WLAN_VDEV_S_RUN) ? true : false;
 }
 
+void wma_acquire_wmi_resp_wakelock(t_wma_handle *wma, uint32_t msec)
+{
+	cds_host_diag_log_work(&wma->wmi_cmd_rsp_wake_lock,
+			       msec,
+			       WIFI_POWER_EVENT_WAKELOCK_WMI_CMD_RSP);
+	qdf_wake_lock_timeout_acquire(&wma->wmi_cmd_rsp_wake_lock, msec);
+	qdf_runtime_pm_prevent_suspend(wma->wmi_cmd_rsp_runtime_lock);
+}
+
+void wma_release_wmi_resp_wakelock(t_wma_handle *wma)
+{
+	qdf_wake_lock_release(&wma->wmi_cmd_rsp_wake_lock,
+			      WIFI_POWER_EVENT_WAKELOCK_WMI_CMD_RSP);
+	qdf_runtime_pm_allow_suspend(wma->wmi_cmd_rsp_runtime_lock);
+}
+
+QDF_STATUS wma_send_vdev_stop_to_fw(t_wma_handle *wma, uint8_t vdev_id)
+{
+	QDF_STATUS status;
+
+	wma_acquire_wmi_resp_wakelock(wma, WMA_VDEV_STOP_REQUEST_TIMEOUT);
+	status = wmi_unified_vdev_stop_send(wma->wmi_handle, vdev_id);
+	if (QDF_IS_STATUS_ERROR(status))
+		wma_release_wmi_resp_wakelock(wma);
+
+	return status;
+}