qcacld-2.0: Actions of suspend and resume in thermal shutdown

Set auto shutdown timer value to 0 to indicate suspend and 1 to indicate
 resume

Change-Id: I52a16050ce7f24e5aef1141764b4c65b6f471317
CRs-Fixed: 2080395
diff --git a/CORE/HDD/inc/wlan_hdd_cfg80211.h b/CORE/HDD/inc/wlan_hdd_cfg80211.h
index 18c0076..821ae60 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg80211.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg80211.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -2772,8 +2772,8 @@
 #endif
 
 void hdd_suspend_wlan(void (*callback)(void *callbackContext, boolean suspended),
-                      void *callbackContext);
-void hdd_resume_wlan(void);
+                      void *callbackContext, bool thermal);
+void hdd_resume_wlan(bool thermal);
 
 #if defined(FEATURE_WLAN_CH_AVOID) || defined(FEATURE_WLAN_FORCE_SAP_SCC)
 int wlan_hdd_send_avoid_freq_event(hdd_context_t *pHddCtx,
@@ -2793,9 +2793,17 @@
 
 struct cfg80211_bss* wlan_hdd_cfg80211_update_bss_list(
    hdd_adapter_t *pAdapter, tSirMacAddr bssid);
+int __wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy,
+                                   struct cfg80211_wowlan *wow, bool thermal);
 
 int wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy,
                                    struct cfg80211_wowlan *wow);
+int __wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy, bool thermal);
+int wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy);
+
+bool hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state);
+int hdd_thermal_suspend_state(hdd_context_t *hdd_ctx);
+
 void wlan_hdd_cfg80211_acs_ch_select_evt(hdd_adapter_t *adapter);
 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
 int wlan_hdd_send_roam_auth_event(hdd_context_t *hdd_ctx_ptr, uint8_t *bssid,
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index bad420e..a986653 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1237,6 +1237,9 @@
    sHddMib_t  hdd_mib;
 
    tANI_U8 sessionId;
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+   bool netif_carrier_on;
+#endif
 
    /* Completion variable for session close */
    struct completion session_close_comp_var;
@@ -1800,6 +1803,11 @@
    v_BOOL_t isLoadInProgress;
 
    v_BOOL_t isUnloadInProgress;
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+   bool system_suspended;
+   volatile int thermal_suspend_state;
+   spinlock_t thermal_suspend_lock;
+#endif
 
    /**Track whether driver has been suspended.*/
    hdd_ps_state_t hdd_ps_state;
diff --git a/CORE/HDD/inc/wlan_hdd_power.h b/CORE/HDD/inc/wlan_hdd_power.h
index c1f4bf6..2daea99 100644
--- a/CORE/HDD/inc/wlan_hdd_power.h
+++ b/CORE/HDD/inc/wlan_hdd_power.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012, 2014, 2016-2018 The Linux Foundation.  All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -88,6 +88,16 @@
 	 HDD_WLAN_RESUME
 };
 
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+enum thermal_suspend_state {
+	HDD_WLAN_THERMAL_ACTIVE,
+	HDD_WLAN_THERMAL_SUSPENDING,
+	HDD_WLAN_THERMAL_SUSPENDED,
+	HDD_WLAN_THERMAL_RESUMING,
+	HDD_WLAN_THERMAL_DEINIT
+};
+#endif
+
 /*-------------------------------------------------------------------------
  * Function declarations and documentation
  * ------------------------------------------------------------------------*/
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 8ed1096..1ca8662 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -29822,12 +29822,49 @@
 }
 #endif
 
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+static void wlan_hdd_thermal_suspend(hdd_context_t *pHddCtx)
+{
+	/* send auto shutdown timer val 0 to fw as suspend */
+	sme_set_auto_shutdown_timer(pHddCtx->hHal, 0);
+}
+
+static int wlan_hdd_thermal_resume(hdd_context_t *pHddCtx, bool thermal)
+{
+	hddLog(LOG1, "state=%d, thermal=%d\n",
+			   hdd_thermal_suspend_state(pHddCtx), thermal);
+
+	/* If system suspend after thermal suspend, assure system resume first. */
+	if (hdd_thermal_suspend_state(pHddCtx) == HDD_WLAN_THERMAL_SUSPENDED &&
+		!thermal) {
+		hddLog(LOG1, FL("System resume when thermal suspended"));
+		return -EINVAL;
+	}
+
+	/* send auto shutdown timer val 1 to fw as resume */
+	sme_set_auto_shutdown_timer(pHddCtx->hHal, 1);
+
+	return 0;
+}
+#else
+static int wlan_hdd_thermal_resume(hdd_context_t *pHddCtx, bool thermal)
+{
+	return 0;
+}
+
+static void wlan_hdd_thermal_suspend(hdd_context_t *pHddCtx)
+{
+	return;
+}
+
+#endif
+
 /*
  * FUNCTION: __wlan_hdd_cfg80211_resume_wlan
  * this is called when cfg80211 driver resume
  * driver updates  latest sched_scan scan result(if any) to cfg80211 database
  */
-int __wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy)
+int __wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy, bool thermal)
 {
     hdd_context_t *pHddCtx = wiphy_priv(wiphy);
     hdd_adapter_t *pAdapter;
@@ -29854,9 +29891,13 @@
         return 0;
     }
 
-    if (hif_is_80211_fw_wow_required()) {
-       result = wma_resume_fw();
-       if (result) {
+    result = wlan_hdd_thermal_resume(pHddCtx, thermal);
+    if (0 != result)
+        return result;
+
+    if (!thermal && hif_is_80211_fw_wow_required()) {
+        result = wma_resume_fw();
+        if (result) {
           /* SSR happened while we were waiting for this */
           if (result == VOS_STATUS_E_ALREADY)
               return 0;
@@ -29868,9 +29909,8 @@
           if (!vos_is_logp_in_progress(VOS_MODULE_ID_HDD, NULL))
               VOS_BUG(0);
           return -EBUSY;
-       }
+        }
     }
-
     dev = pHddCtx->parent_dev;
     vos_request_bus_bandwidth(dev, CNSS_BUS_WIDTH_MEDIUM);
 
@@ -29889,7 +29929,7 @@
 
 #endif
 
-    hdd_resume_wlan();
+    hdd_resume_wlan(thermal);
 
     MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_CFG80211_RESUME_WLAN,
                           NO_SESSION, pHddCtx->isWiphySuspended));
@@ -29945,21 +29985,69 @@
 
 int wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy)
 {
+    hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
     int ret;
 
     vos_ssr_protect(__func__);
-    ret = __wlan_hdd_cfg80211_resume_wlan(wiphy);
+    ret = __wlan_hdd_cfg80211_resume_wlan(wiphy, false);
+    hdd_system_suspend_state_set(hdd_ctx, false);
     vos_ssr_unprotect(__func__);
 
     return ret;
 }
 
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+static bool wlan_hdd_is_thermal_suspended(hdd_context_t *pHddCtx)
+{
+	if (hdd_thermal_suspend_state(pHddCtx) == HDD_WLAN_THERMAL_SUSPENDED) {
+		hddLog(LOG1, FL("System suspend when thermal suspended"));
+		return true;
+	}
+	return false;
+}
+
+static VOS_STATUS wlan_hdd_suspend_mc_thread(pVosSchedContext vosSchedCtx,
+					hdd_context_t *pHddCtx)
+{
+	return VOS_STATUS_SUCCESS;
+}
+#else
+static bool wlan_hdd_is_thermal_suspended(hdd_context_t *pHddCtx)
+{
+	return false;
+}
+
+static VOS_STATUS wlan_hdd_suspend_mc_thread(pVosSchedContext vosSchedCtx,
+					hdd_context_t *pHddCtx)
+{
+	int rc;
+
+	/* Suspend MC thread */
+	set_bit(MC_SUSPEND_EVENT, &vosSchedCtx->mcEventFlag);
+	wake_up_interruptible(&vosSchedCtx->mcWaitQueue);
+
+	/* Wait for suspend confirmation from MC thread */
+	rc = wait_for_completion_timeout(&pHddCtx->mc_sus_event_var,
+			msecs_to_jiffies(WLAN_WAIT_TIME_MCTHREAD_SUSPEND));
+	if (!rc)
+	{
+		clear_bit(MC_SUSPEND_EVENT, &vosSchedCtx->mcEventFlag);
+		VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+				   "%s: Failed to stop mc thread", __func__);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	pHddCtx->isMcThreadSuspended = TRUE;
+	return VOS_STATUS_SUCCESS;
+}
+
+#endif
 /*
  * FUNCTION: __wlan_hdd_cfg80211_suspend_wlan
  * this is called when cfg80211 driver suspends
  */
 int __wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy,
-                                   struct cfg80211_wowlan *wow)
+                                   struct cfg80211_wowlan *wow, bool thermal)
 {
 #ifdef QCA_CONFIG_SMP
 #define RX_TLSHIM_SUSPEND_TIMEOUT 200 /* msecs */
@@ -29990,6 +30078,10 @@
         return -EINVAL;
     }
 
+    if (wlan_hdd_is_thermal_suspended(pHddCtx)) {
+        return 0;
+    }
+
     /* If RADAR detection is in progress (HDD), prevent suspend. The flag
      * "dfs_cac_block_tx" is set to TRUE when RADAR is found and stay TRUE until
      * CAC is done for a SoftAP which is in started state.
@@ -30076,7 +30168,7 @@
     /* Wait for the target to be ready for suspend */
     INIT_COMPLETION(pHddCtx->ready_to_suspend);
 
-    hdd_suspend_wlan(&wlan_hdd_cfg80211_ready_to_suspend, pHddCtx);
+    hdd_suspend_wlan(&wlan_hdd_cfg80211_ready_to_suspend, pHddCtx, thermal);
 
     rc = wait_for_completion_timeout(&pHddCtx->ready_to_suspend,
                              msecs_to_jiffies(WLAN_WAIT_TIME_READY_TO_SUSPEND));
@@ -30095,21 +30187,9 @@
     }
 
     /* Suspend MC thread */
-    set_bit(MC_SUSPEND_EVENT, &vosSchedContext->mcEventFlag);
-    wake_up_interruptible(&vosSchedContext->mcWaitQueue);
-
-    /* Wait for suspend confirmation from MC thread */
-    rc = wait_for_completion_timeout(&pHddCtx->mc_sus_event_var,
-                                 msecs_to_jiffies(WLAN_WAIT_TIME_MCTHREAD_SUSPEND));
-    if (!rc)
-    {
-        clear_bit(MC_SUSPEND_EVENT, &vosSchedContext->mcEventFlag);
-        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
-                   "%s: Failed to stop mc thread", __func__);
+    status = wlan_hdd_suspend_mc_thread(vosSchedContext, pHddCtx);
+    if (VOS_STATUS_SUCCESS != status)
         goto resume_tx;
-    }
-
-    pHddCtx->isMcThreadSuspended = TRUE;
 
 #ifdef QCA_CONFIG_SMP
     /* Suspend tlshim rx thread */
@@ -30133,17 +30213,20 @@
 
     vos_request_bus_bandwidth(dev, CNSS_BUS_WIDTH_NONE);
 
-    if (hif_is_80211_fw_wow_required()) {
-       rc = wma_suspend_fw();
-       if (rc) {
-          hddLog(LOGE, FL("Failed to suspend FW err:%d"), rc);
-          goto fail_suspend;
-       }
+    if (thermal) {
+        wlan_hdd_thermal_suspend(pHddCtx);
+    } else {
+        if (hif_is_80211_fw_wow_required()) {
+           rc = wma_suspend_fw();
+           if (rc) {
+              hddLog(LOGE, FL("Failed to suspend FW err:%d"), rc);
+              goto fail_suspend;
+           }
+        }
     }
 
     EXIT();
     return 0;
-
 fail_suspend:
     vos_request_bus_bandwidth(dev, CNSS_BUS_WIDTH_MEDIUM);
     pHddCtx->isWiphySuspended = FALSE;
@@ -30162,7 +30245,7 @@
 
 resume_tx:
 
-    hdd_resume_wlan();
+    hdd_resume_wlan(thermal);
 
     return -ETIME;
 }
@@ -30170,15 +30253,18 @@
 int wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy,
                                    struct cfg80211_wowlan *wow)
 {
+    hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
     int ret;
 
     vos_ssr_protect(__func__);
-    ret = __wlan_hdd_cfg80211_suspend_wlan(wiphy, wow);
+    ret = __wlan_hdd_cfg80211_suspend_wlan(wiphy, wow, false);
+    hdd_system_suspend_state_set(hdd_ctx, true);
     vos_ssr_unprotect(__func__);
 
     return ret;
 }
 
+
 #ifdef QCA_HT_2040_COEX
 /**
  * __wlan_hdd_cfg80211_set_ap_channel_width() - set ap channel bandwidth
diff --git a/CORE/HDD/src/wlan_hdd_early_suspend.c b/CORE/HDD/src/wlan_hdd_early_suspend.c
index 36a5e8f..62d842d 100644
--- a/CORE/HDD/src/wlan_hdd_early_suspend.c
+++ b/CORE/HDD/src/wlan_hdd_early_suspend.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1551,9 +1551,40 @@
                   pHddCtx->configuredMcastBcastFilter);
 }
 
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+static void hdd_thermal_off_carrier(hdd_adapter_t *pAdapter)
+{
+	if (netif_carrier_ok(pAdapter->dev)) {
+		pAdapter->netif_carrier_on = TRUE;
+		wlan_hdd_netif_queue_control(pAdapter,
+		  WLAN_NETIF_CARRIER_OFF, WLAN_CONTROL_PATH);
+	} else {
+		pAdapter->netif_carrier_on = FALSE;
+	}
+}
+
+static void hdd_thermal_on_carrier(hdd_adapter_t *pAdapter)
+{
+	if (pAdapter->netif_carrier_on) {
+	/* Thermal shutdown is an urgent accident visible to user space. */
+		wlan_hdd_netif_queue_control(pAdapter,
+			WLAN_NETIF_CARRIER_ON, WLAN_CONTROL_PATH);
+	}
+}
+#else
+static inline void hdd_thermal_off_carrier(hdd_adapter_t *pAdapter)
+{
+	return;
+}
+
+static inline void hdd_thermal_on_carrier(hdd_adapter_t *pAdapter)
+{
+	return;
+}
+#endif
 //Suspend routine registered with Android OS
 void hdd_suspend_wlan(void (*callback)(void *callbackContext, boolean suspended),
-                      void *callbackContext)
+                      void *callbackContext, bool thermal)
 {
    hdd_context_t *pHddCtx = NULL;
    v_CONTEXT_t pVosContext = NULL;
@@ -1634,8 +1665,16 @@
 send_suspend_ind:
        //stop all TX queues before suspend
        hddLog(LOG1, FL("Disabling queues"));
+
+      /* Thermal shutdown is an urgent accident visible to user space. */
+        if (thermal) {
+            hdd_thermal_off_carrier(pAdapter);
+        }
+
        wlan_hdd_netif_queue_control(pAdapter, WLAN_NETIF_TX_DISABLE,
-                  WLAN_CONTROL_PATH);
+                      WLAN_CONTROL_PATH);
+
+
        WLANTL_PauseUnPauseQs(pVosContext, true);
 
       /* Keep this suspend indication at the end (before processing next adaptor)
@@ -1788,7 +1827,7 @@
    }
 }
 
-void hdd_resume_wlan(void)
+void hdd_resume_wlan(bool thermal)
 {
    hdd_context_t *pHddCtx = NULL;
    hdd_adapter_t *pAdapter = NULL;
@@ -1882,11 +1921,16 @@
 send_resume_ind:
       //wake the tx queues
       hddLog(LOG1, FL("Enabling queues"));
+
       WLANTL_PauseUnPauseQs(pVosContext, false);
 
       wlan_hdd_netif_queue_control(pAdapter,
-            WLAN_WAKE_ALL_NETIF_QUEUE,
-            WLAN_CONTROL_PATH);
+                WLAN_WAKE_ALL_NETIF_QUEUE,
+                WLAN_CONTROL_PATH);
+
+      if (thermal) {
+        hdd_thermal_on_carrier(pAdapter);
+      }
 
       hdd_conf_resume_ind(pAdapter);
 
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 4ed51ed..91ed5e3 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -1769,6 +1769,43 @@
 }
 #endif
 
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+bool
+hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state)
+{
+	unsigned long flags;
+	bool old;
+
+	spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags);
+	old = hdd_ctx->system_suspended;
+	hdd_ctx->system_suspended = state;
+	spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags);
+
+	return old;
+}
+
+
+int
+hdd_thermal_suspend_state(hdd_context_t *hdd_ctx)
+{
+	unsigned long flags;
+	int s;
+
+	spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags);
+	s = hdd_ctx->thermal_suspend_state;
+	spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags);
+
+	return s;
+}
+
+#else
+bool
+hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state)
+{
+	return TRUE;
+}
+
+#endif
 /**---------------------------------------------------------------------------
 
   \brief hdd_setIbssPowerSaveParams - update IBSS Power Save params to WMA.
@@ -15951,6 +15988,14 @@
 }
 
 #ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+static VOS_STATUS hdd_init_thermal_ctx(hdd_context_t *pHddCtx)
+{
+	pHddCtx->system_suspended = false;
+	pHddCtx->thermal_suspend_state = HDD_WLAN_THERMAL_ACTIVE;
+	spin_lock_init(&pHddCtx->thermal_suspend_lock);
+	return VOS_STATUS_SUCCESS;
+}
+
 static void hdd_get_thermal_shutdown_ini_param(tSmeThermalParams   *pthermalParam,
 						hdd_context_t    *pHddCtx)
 {
@@ -15964,9 +16009,15 @@
 	  pHddCtx->cfg_ini->thermal_warning_threshold;
 	pthermalParam->thermal_suspend_threshold =
 	  pHddCtx->cfg_ini->thermal_suspend_threshold;
-	pthermalParam->thermal_sample_rate = pHddCtx->cfg_ini->thermal_sample_rate;
+	pthermalParam->thermal_sample_rate =
+		pHddCtx->cfg_ini->thermal_sample_rate;
 }
 #else
+static inline VOS_STATUS hdd_init_thermal_ctx(hdd_context_t *pHddCtx)
+{
+	return VOS_STATUS_SUCCESS;
+}
+
 static inline void hdd_get_thermal_shutdown_ini_param(tSmeThermalParams   *pthermalParam,
 						hdd_context_t    *pHddCtx)
 {
@@ -16043,6 +16094,11 @@
 
    pHddCtx->wiphy = wiphy;
    pHddCtx->isLoadInProgress = TRUE;
+
+   status = hdd_init_thermal_ctx(pHddCtx);
+   if (VOS_STATUS_SUCCESS != status)
+        goto err_free_hdd_context;
+
    pHddCtx->ioctl_scan_mode = eSIR_ACTIVE_SCAN;
    vos_set_wakelock_logging(false);