qcacld-2.0: Schedule of thermal suspend and resume

If the temperature reported from fw is higher than the suspend threshold,
then trigger the suspend action, and if it is lower than the resume
threshold, the resume action is triggered.

Change-Id: I23ba5676967c4f89ec3ea697ec561f174038d21e
CRs-Fixed: 2080395
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index a986653..2f9e90e 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1807,6 +1807,8 @@
    bool system_suspended;
    volatile int thermal_suspend_state;
    spinlock_t thermal_suspend_lock;
+   struct workqueue_struct *thermal_suspend_wq;
+   struct delayed_work thermal_suspend_work;
 #endif
 
    /**Track whether driver has been suspended.*/
@@ -2244,6 +2246,17 @@
 VOS_STATUS hdd_enable_bmps_imps(hdd_context_t *pHddCtx);
 VOS_STATUS hdd_disable_bmps_imps(hdd_context_t *pHddCtx, tANI_U8 session_type);
 
+/**
+ * hdd_thermal_suspend_queue_work() - Queue a thermal suspend work
+ * @hdd_ctx:     Pointer to hdd_context_t
+ * @ms: Delay time in milliseconds to execute the work
+ *
+ * Queue thermal suspend work on the workqueue after delay
+ *
+ * Return: false if work was already on a queue, true otherwise.
+ */
+bool hdd_thermal_suspend_queue_work(hdd_context_t *hdd_ctx, unsigned long ms);
+
 void wlan_hdd_cfg80211_update_wiphy_caps(struct wiphy *wiphy);
 VOS_STATUS hdd_setIbssPowerSaveParams(hdd_adapter_t *pAdapter);
 VOS_STATUS wlan_hdd_restart_driver(hdd_context_t *pHddCtx);
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 91ed5e3..efe1c1f 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -1770,6 +1770,18 @@
 #endif
 
 #ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+static bool
+hdd_system_suspend_state(hdd_context_t *hdd_ctx)
+{
+	unsigned long flags;
+	bool s;
+
+	spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags);
+	s = hdd_ctx->system_suspended;
+	spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags);
+	return s;
+}
+
 bool
 hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state)
 {
@@ -1798,6 +1810,160 @@
 	return s;
 }
 
+static bool
+hdd_thermal_suspend_transit(hdd_context_t *hdd_ctx, int target, int *old)
+{
+	unsigned long flags;
+	int s;
+	bool ret = false;
+
+	spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags);
+
+	s = hdd_ctx->thermal_suspend_state;
+	if (old)
+		*old = s;
+
+	switch (target) {
+	case HDD_WLAN_THERMAL_ACTIVE:
+		if (s == HDD_WLAN_THERMAL_RESUMING ||
+			s == HDD_WLAN_THERMAL_SUSPENDING)
+			ret = true;
+		break;
+	case HDD_WLAN_THERMAL_SUSPENDING:
+		if (s == HDD_WLAN_THERMAL_ACTIVE)
+			ret = true;
+		break;
+	case HDD_WLAN_THERMAL_SUSPENDED:
+		if (s == HDD_WLAN_THERMAL_SUSPENDING ||
+			s == HDD_WLAN_THERMAL_RESUMING)
+			ret = true;
+		break;
+	case HDD_WLAN_THERMAL_RESUMING:
+		if (s == HDD_WLAN_THERMAL_SUSPENDED)
+			ret = true;
+		break;
+	case HDD_WLAN_THERMAL_DEINIT:
+		if (s != HDD_WLAN_THERMAL_DEINIT)
+			ret = true;
+		break;
+	}
+
+	if (ret)
+		hdd_ctx->thermal_suspend_state = target;
+
+	spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags);
+	return ret;
+}
+
+static void
+hdd_thermal_suspend_cleanup(hdd_context_t *hdd_ctx)
+{
+	int state;
+	bool okay;
+
+	if (!hdd_ctx->thermal_suspend_wq)
+		return;
+
+	cancel_delayed_work_sync(&hdd_ctx->thermal_suspend_work);
+	okay = hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_DEINIT,
+					   &state);
+	destroy_workqueue(hdd_ctx->thermal_suspend_wq);
+	hdd_ctx->thermal_suspend_wq = NULL;
+
+	if (!okay || state == HDD_WLAN_THERMAL_ACTIVE)
+		return;
+
+	if (state == HDD_WLAN_THERMAL_SUSPENDED) {
+		hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
+		__wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy, true);
+		hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
+	}
+}
+
+static void
+hdd_thermal_suspend_work(struct work_struct *work)
+{
+	hdd_context_t *hdd_ctx =
+		container_of(work, hdd_context_t, thermal_suspend_work.work);
+	struct wiphy *wiphy = hdd_ctx->wiphy;
+	int ret;
+
+	while (hdd_system_suspend_state(hdd_ctx)) {
+		hddLog(LOG1, FL("Waiting for system resume complete"));
+		schedule_timeout_interruptible(100 * HZ / 1000);
+	}
+
+	if (hdd_thermal_suspend_transit(hdd_ctx,
+		HDD_WLAN_THERMAL_SUSPENDING, NULL)) {
+		hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
+		ret = __wlan_hdd_cfg80211_suspend_wlan(wiphy, NULL, true);
+		hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
+		if (ret) {
+			hdd_thermal_suspend_transit(hdd_ctx,
+				HDD_WLAN_THERMAL_ACTIVE, NULL);
+			hddLog(LOGE, FL("Thermal suspend failed: %d"), ret);
+			return;
+		}
+		hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_SUSPENDED,
+						NULL);
+	} else if (hdd_thermal_suspend_transit(hdd_ctx,
+		   HDD_WLAN_THERMAL_RESUMING, NULL)) {
+		hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
+		ret = __wlan_hdd_cfg80211_resume_wlan(wiphy, true);
+		hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
+		if (ret) {
+			hdd_thermal_suspend_transit(hdd_ctx,
+				HDD_WLAN_THERMAL_SUSPENDED, NULL);
+			hddLog(LOGE, FL("Thermal resume failed: %d"), ret);
+			return;
+		}
+		hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_ACTIVE,
+						NULL);
+	} else {
+		hddLog(LOGE, FL("Should not reach here"));
+	}
+}
+
+/**
+ * hdd_thermal_suspend_queue_work() - Queue a thermal suspend work
+ * @hdd_ctx: Pointer to hdd_context_t
+ * @ms: Delay time in milliseconds to execute the work
+ *
+ * Queue thermal suspend work on the workqueue after delay
+ *
+ * Return: false if work was already on a queue, true otherwise.
+ */
+bool
+hdd_thermal_suspend_queue_work(hdd_context_t *hdd_ctx, unsigned long ms)
+{
+	hddLog(LOG1, FL("Queue a thermal suspend work, delay %ld ms"), ms);
+	return queue_delayed_work(hdd_ctx->thermal_suspend_wq,
+		&hdd_ctx->thermal_suspend_work, (ms * HZ) / 1000);
+}
+
+static void
+hdd_thermal_temp_ind_event_cb(hdd_context_t *hdd_ctx, uint32_t degreeC)
+{
+	if (!hdd_ctx->cfg_ini->thermal_shutdown_auto_enabled) {
+		return;
+	}
+
+	/*
+	 * Here, we only do thermal suspend.
+	 *
+	 * We can only resume FW elsewhere in two ways:
+	 * 1. triggered by wakeup interrupt from FW when it detects T < Tresume
+	 * 2. user space app launch thermal resume after suspend as app wants
+	 *
+	 * Key factor: FW cannot provide temperature when it suspended.
+	 */
+	if ((hdd_thermal_suspend_state(hdd_ctx) == HDD_WLAN_THERMAL_ACTIVE &&
+		degreeC >= hdd_ctx->cfg_ini->thermal_suspend_threshold) ||
+		(hdd_thermal_suspend_state(hdd_ctx) == HDD_WLAN_THERMAL_SUSPENDED &&
+		degreeC < hdd_ctx->cfg_ini->thermal_resume_threshold)) {
+		hdd_thermal_suspend_queue_work(hdd_ctx, 0);
+	}
+}
 #else
 bool
 hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state)
@@ -1805,7 +1971,20 @@
 	return TRUE;
 }
 
+static inline void
+hdd_thermal_suspend_cleanup(hdd_context_t *hdd_ctx)
+{
+	return;
+}
+
+static inline void
+hdd_thermal_temp_ind_event_cb(hdd_context_t *hdd_ctx, uint32_t degreeC)
+{
+	return;
+}
+
 #endif
+
 /**---------------------------------------------------------------------------
 
   \brief hdd_setIbssPowerSaveParams - update IBSS Power Save params to WMA.
@@ -15993,6 +16172,10 @@
 	pHddCtx->system_suspended = false;
 	pHddCtx->thermal_suspend_state = HDD_WLAN_THERMAL_ACTIVE;
 	spin_lock_init(&pHddCtx->thermal_suspend_lock);
+	INIT_DELAYED_WORK(&pHddCtx->thermal_suspend_work, hdd_thermal_suspend_work);
+	pHddCtx->thermal_suspend_wq = create_singlethread_workqueue("thermal_wq");
+	if (!pHddCtx->thermal_suspend_wq)
+		return VOS_STATUS_E_INVAL;
 	return VOS_STATUS_SUCCESS;
 }
 
@@ -17014,6 +17197,9 @@
    sme_add_set_thermal_level_callback(pHddCtx->hHal,
                      (tSmeSetThermalLevelCallback)hdd_set_thermal_level_cb);
 
+   sme_add_thermal_temperature_ind_callback(pHddCtx->hHal,
+                    (tSmeThermalTempIndCb)hdd_thermal_temp_ind_event_cb);
+
    /* Bad peer tx flow control */
    wlan_hdd_bad_peer_txctl(pHddCtx);
 
@@ -17677,6 +17863,8 @@
    }
    else
    {
+       hdd_thermal_suspend_cleanup(pHddCtx);
+
       /*
        * Check IPA HW pipe shutdown properly or not
        * If not, force shut down HW pipe
diff --git a/CORE/MAC/inc/wniApi.h b/CORE/MAC/inc/wniApi.h
index 70d10e9..e4379ed 100644
--- a/CORE/MAC/inc/wniApi.h
+++ b/CORE/MAC/inc/wniApi.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.
  *
@@ -386,7 +386,9 @@
 #endif
     eWNI_SME_FW_STATUS_IND,
     eWNI_SME_SET_THERMAL_LEVEL_IND,
-
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+    eWNI_SME_THERMAL_TEMPERATURE_IND,
+#endif
     eWNI_SME_OCB_SET_CONFIG_RSP,
     eWNI_SME_OCB_GET_TSF_TIMER_RSP,
     eWNI_SME_DCC_GET_STATS_RSP,
diff --git a/CORE/SERVICES/WMA/wma.c b/CORE/SERVICES/WMA/wma.c
index 4880c07..7c532d8 100644
--- a/CORE/SERVICES/WMA/wma.c
+++ b/CORE/SERVICES/WMA/wma.c
@@ -28958,9 +28958,53 @@
 
 	return eHAL_STATUS_SUCCESS;
 }
+
+static void wma_thermal_temperature_ind(int32_t degree_c)
+{
+	VOS_STATUS vos_status = VOS_STATUS_SUCCESS;
+	vos_msg_t sme_msg = {0};
+
+	sme_msg.type = eWNI_SME_THERMAL_TEMPERATURE_IND;
+	sme_msg.bodyptr = NULL;
+	sme_msg.bodyval = (u_int32_t)degree_c;
+
+	vos_status = vos_mq_post_message(VOS_MODULE_ID_SME, &sme_msg);
+	if (!VOS_IS_STATUS_SUCCESS(vos_status))
+		WMA_LOGE(FL("Fail to post temperature ind msg"));
+}
+
+#define CELSIUS_MIN_DEGREE (-273)
+static bool need_thermal_temperature_ind(tp_thermal_mgmt info, int32_t degree_c)
+{
+	static int32_t t_last = CELSIUS_MIN_DEGREE;
+	int ret = false;
+	int32_t t_curr = degree_c;
+	int32_t t_r = (int32_t)info->thermal_resume_threshold;
+	int32_t t_w = (int32_t)info->thermal_warning_threshold;
+	int32_t t_s = (int32_t)info->thermal_suspend_threshold;
+
+	if ((t_last >= t_r && t_curr < t_r) ||
+	    (t_last < t_w && t_curr >= t_w) ||
+	    (t_last < t_s && t_curr >= t_s) ||
+	    (t_last == CELSIUS_MIN_DEGREE))
+		ret = true;
+
+	t_last = t_curr;
+	return ret;
+}
 #endif /* FEATURE_WLAN_THERMAL_SHUTDOWN */
 
 #ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+static void
+wma_thermal_shutdown_evt_handler(tp_thermal_mgmt info, int32_t degree_c)
+{
+    if (!info->thermal_shutdown_enabled)
+        return;
+
+    if (need_thermal_temperature_ind(info, degree_c))
+        wma_thermal_temperature_ind(degree_c);
+}
+
 static void wma_fetch_set_thermal_params(tp_wma_handle wma,
 					t_thermal_mgmt *pThermalParams)
 {
@@ -28986,6 +29030,12 @@
 {
 	return;
 }
+
+static inline void
+wma_thermal_shutdown_evt_handler(tp_thermal_mgmt info, int32_t degree_c)
+{
+	return;
+}
 #endif
 /* function   : wma_process_init_thermal_info
  * Description : This function initializes the thermal management table in WMA,
@@ -35427,6 +35477,61 @@
 	return level;
 }
 
+static int wma_thermal_throttle_handler(void *handle, u_int32_t degree_c)
+{
+	tp_wma_handle wma = (tp_wma_handle)handle;
+	u_int8_t thermal_level;
+	t_thermal_cmd_params thermal_params;
+	ol_txrx_pdev_handle curr_pdev;
+	tp_thermal_mgmt info;
+
+	/* Check if thermal mitigation is enabled */
+	if (!wma->thermal_mgmt_info.thermalMgmtEnabled){
+		WMA_LOGD("Thermal mgmt is not enabled, ignoring event");
+		return 0;
+	}
+
+	curr_pdev = vos_get_context(VOS_MODULE_ID_TXRX, wma->vos_context);
+	if (NULL == curr_pdev) {
+		WMA_LOGE("%s: Failed to get pdev", __func__);
+		return -EINVAL;
+	}
+
+	/* Get the thermal mitigation level for the reported temperature*/
+	thermal_level = wma_thermal_mgmt_get_level(handle, degree_c);
+	WMA_LOGD("Thermal mgmt level  %d", thermal_level);
+
+	info = &wma->thermal_mgmt_info;
+	if (thermal_level == info->thermalCurrLevel) {
+		WMA_LOGD("Current level %d is same as the set level, ignoring",
+			 info->thermalCurrLevel);
+		return 0;
+	}
+
+	info->thermalCurrLevel = thermal_level;
+
+	/* Inform txrx */
+	ol_tx_throttle_set_level(curr_pdev, thermal_level);
+
+	/* Send SME SET_THERMAL_LEVEL_IND message */
+	wma_set_thermal_level_ind(thermal_level);
+
+	/* Get the temperature thresholds to set in firmware */
+	thermal_params.minTemp =
+		info->thermalLevels[thermal_level].minTempThreshold;
+	thermal_params.maxTemp =
+		info->thermalLevels[thermal_level].maxTempThreshold;
+	thermal_params.thermalEnable = info->thermalMgmtEnabled;
+
+	if (VOS_STATUS_SUCCESS != wma_set_thermal_mgmt(wma, thermal_params)) {
+		WMA_LOGE("Could not send thermal mgmt cmd to the firmware!");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
 /* function   : wma_thermal_mgmt_evt_handler
  * Description : This function handles the thermal mgmt event from the firmware
  * Args       :
@@ -35441,73 +35546,32 @@
 {
 	tp_wma_handle wma;
 	wmi_thermal_mgmt_event_fixed_param *tm_event;
-	u_int8_t thermal_level;
-	t_thermal_cmd_params thermal_params;
 	WMI_THERMAL_MGMT_EVENTID_param_tlvs *param_buf;
-	ol_txrx_pdev_handle curr_pdev;
+	int ret;
 
 	if (NULL == event || NULL == handle) {
-                WMA_LOGE("Invalid thermal mitigation event buffer");
-                return -EINVAL;
-        }
+			WMA_LOGE("Invalid thermal mitigation event buffer");
+			return -EINVAL;
+	}
 
 	wma = (tp_wma_handle) handle;
-
 	if (NULL == wma) {
 		WMA_LOGE("%s: Failed to get wma handle", __func__);
 		return -EINVAL;
 	}
 
 	param_buf = (WMI_THERMAL_MGMT_EVENTID_param_tlvs *) event;
-
-	curr_pdev = vos_get_context(VOS_MODULE_ID_TXRX, wma->vos_context);
-	if (NULL == curr_pdev) {
-		WMA_LOGE("%s: Failed to get pdev", __func__);
-		return -EINVAL;
-	}
-
-	/* Check if thermal mitigation is enabled */
-	if (!wma->thermal_mgmt_info.thermalMgmtEnabled){
-		WMA_LOGE("Thermal mgmt is not enabled, ignoring event");
-		return -EINVAL;
-	}
-
 	tm_event = param_buf->fixed_param;
 	WMA_LOGD("Thermal mgmt event received with temperature %d",
 		 tm_event->temperature_degreeC);
 
-	/* Get the thermal mitigation level for the reported temperature*/
-	thermal_level = wma_thermal_mgmt_get_level(handle, tm_event->temperature_degreeC);
-	WMA_LOGD("Thermal mgmt level  %d", thermal_level);
+	ret = wma_thermal_throttle_handler(handle,
+		tm_event->temperature_degreeC);
 
-	if (thermal_level == wma->thermal_mgmt_info.thermalCurrLevel) {
-		WMA_LOGD("Current level %d is same as the set level, ignoring",
-				  wma->thermal_mgmt_info.thermalCurrLevel);
-		return 0;
-	}
+	wma_thermal_shutdown_evt_handler(&wma->thermal_mgmt_info,
+		tm_event->temperature_degreeC);
 
-	wma->thermal_mgmt_info.thermalCurrLevel = thermal_level;
-
-	/* Inform txrx */
-	ol_tx_throttle_set_level(curr_pdev, thermal_level);
-
-	/* Send SME SET_THERMAL_LEVEL_IND message */
-	wma_set_thermal_level_ind(thermal_level);
-
-	/* Get the temperature thresholds to set in firmware */
-	thermal_params.minTemp =
-		 wma->thermal_mgmt_info.thermalLevels[thermal_level].minTempThreshold;
-	thermal_params.maxTemp =
-		 wma->thermal_mgmt_info.thermalLevels[thermal_level].maxTempThreshold;
-	thermal_params.thermalEnable =
-		 wma->thermal_mgmt_info.thermalMgmtEnabled;
-
-	if (VOS_STATUS_SUCCESS != wma_set_thermal_mgmt(wma, thermal_params)) {
-		WMA_LOGE("Could not send thermal mgmt command to the firmware!");
-		return -EINVAL;
-	}
-
-	return 0;
+	return ret;
 }
 
 #ifdef FEATURE_WLAN_CH_AVOID
diff --git a/CORE/SME/inc/smeInternal.h b/CORE/SME/inc/smeInternal.h
index 44f8a24..d63c9a9 100644
--- a/CORE/SME/inc/smeInternal.h
+++ b/CORE/SME/inc/smeInternal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -252,7 +252,9 @@
     void *radio_chan_stats_context;
     ocb_callback radio_chan_stats_callback;
     void (*set_thermal_level_cb)(void *hdd_context, uint8_t level);
-
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+    void (*thermal_temp_ind_cb)(void *pContext, uint32_t degree_c);
+#endif
     void (*rssi_threshold_breached_cb)(void *, struct rssi_breach_event *);
     void (*lost_link_info_cb)(void *context,
 			      struct sir_lost_link_info *lost_link_info);
diff --git a/CORE/SME/inc/sme_Api.h b/CORE/SME/inc/sme_Api.h
index 820323b..7ed05ff 100644
--- a/CORE/SME/inc/sme_Api.h
+++ b/CORE/SME/inc/sme_Api.h
@@ -4614,6 +4614,17 @@
 typedef void ( *tSmeSetThermalLevelCallback)(void *pContext, u_int8_t level);
 void sme_add_set_thermal_level_callback(tHalHandle hHal,
                    tSmeSetThermalLevelCallback callback);
+typedef void (*tSmeThermalTempIndCb)(void *pContext, u_int32_t degree_c);
+/**
+ * sme_add_thermal_temperature_ind_callback() - Set callback fn for thermal
+ * temperature indication
+ * hHal: Handler to HAL
+ * callback: The callback function
+ *
+ * Return: void
+ */
+void sme_add_thermal_temperature_ind_callback(tHalHandle hHal,
+                   tSmeThermalTempIndCb callback);
 
 eHalStatus sme_handle_set_fcc_channel(tHalHandle hHal,
 		bool fcc_constraint,
diff --git a/CORE/SME/src/sme_common/sme_Api.c b/CORE/SME/src/sme_common/sme_Api.c
index f6df3c2..1206f1b 100644
--- a/CORE/SME/src/sme_common/sme_Api.c
+++ b/CORE/SME/src/sme_common/sme_Api.c
@@ -3450,6 +3450,15 @@
                    pMac->sme.set_thermal_level_cb(pMac->hHdd, pMsg->bodyval);
                }
                break;
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+          case eWNI_SME_THERMAL_TEMPERATURE_IND:
+               if (pMac->sme.thermal_temp_ind_cb)
+               {
+                   pMac->sme.thermal_temp_ind_cb(pMac->hHdd, pMsg->bodyval);
+               }
+               break;
+#endif
+
           case eWNI_SME_LOST_LINK_INFO_IND:
                if (pMac->sme.lost_link_info_cb) {
                    pMac->sme.lost_link_info_cb(pMac->hHdd,
@@ -15391,6 +15400,29 @@
 	return eHAL_STATUS_FAILURE;
 }
 
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+/**
+ * sme_add_thermal_temperature_ind_callback() - Set callback fn for thermal
+ * temperature indication
+ * hHal: Handler to HAL
+ * callback: The callback function
+ *
+ * Return: void
+ */
+void sme_add_thermal_temperature_ind_callback(tHalHandle hHal,
+				   tSmeThermalTempIndCb callback)
+{
+	tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
+
+	pMac->sme.thermal_temp_ind_cb = callback;
+}
+#else
+inline void sme_add_thermal_temperature_ind_callback(tHalHandle hHal,
+				   tSmeThermalTempIndCb callback)
+{
+	return;
+}
+#endif
 
 /* ---------------------------------------------------------------------------
    \fn sme_TxpowerLimit
diff --git a/CORE/SYS/legacy/src/utils/src/macTrace.c b/CORE/SYS/legacy/src/utils/src/macTrace.c
index 18f9b55..213c3da 100644
--- a/CORE/SYS/legacy/src/utils/src/macTrace.c
+++ b/CORE/SYS/legacy/src/utils/src/macTrace.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -565,6 +565,9 @@
 	CASE_RETURN_STRING(eWNI_SME_EPNO_NETWORK_FOUND_IND);
 #endif
 	CASE_RETURN_STRING(eWNI_SME_SET_THERMAL_LEVEL_IND);
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+	CASE_RETURN_STRING(eWNI_SME_THERMAL_TEMPERATURE_IND);
+#endif
 	CASE_RETURN_STRING(eWNI_SME_OCB_SET_CONFIG_RSP);
 	CASE_RETURN_STRING(eWNI_SME_OCB_GET_TSF_TIMER_RSP);
 	CASE_RETURN_STRING(eWNI_SME_DCC_GET_STATS_RSP);
diff --git a/CORE/VOSS/inc/vos_diag_core_event.h b/CORE/VOSS/inc/vos_diag_core_event.h
index c62f925..0c176c3 100644
--- a/CORE/VOSS/inc/vos_diag_core_event.h
+++ b/CORE/VOSS/inc/vos_diag_core_event.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -518,6 +518,9 @@
 	WIFI_POWER_EVENT_WAKELOCK_PNO,
 	WIFI_POWER_EVENT_WAKELOCK_DEL_STA,
 	WIFI_POWER_EVENT_WAKELOCK_DFS,
+#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
+	WIFI_POWER_EVENT_WAKELOCK_THERMAL,
+#endif
 	WIFI_POWER_EVENT_WAKELOCK_MISC,
 };