18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Portions of this file are derived from the ipw3945 project, as well 88c2ecf20Sopenharmony_ci * as portions of the ieee80211 subsystem header files. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Contact Information: 118c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 128c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 138c2ecf20Sopenharmony_ci *****************************************************************************/ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <net/mac80211.h> 208c2ecf20Sopenharmony_ci#include "iwl-io.h" 218c2ecf20Sopenharmony_ci#include "iwl-modparams.h" 228c2ecf20Sopenharmony_ci#include "iwl-debug.h" 238c2ecf20Sopenharmony_ci#include "agn.h" 248c2ecf20Sopenharmony_ci#include "dev.h" 258c2ecf20Sopenharmony_ci#include "commands.h" 268c2ecf20Sopenharmony_ci#include "tt.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* default Thermal Throttling transaction table 298c2ecf20Sopenharmony_ci * Current state | Throttling Down | Throttling Up 308c2ecf20Sopenharmony_ci *============================================================================= 318c2ecf20Sopenharmony_ci * Condition Nxt State Condition Nxt State Condition Nxt State 328c2ecf20Sopenharmony_ci *----------------------------------------------------------------------------- 338c2ecf20Sopenharmony_ci * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A 348c2ecf20Sopenharmony_ci * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 358c2ecf20Sopenharmony_ci * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 368c2ecf20Sopenharmony_ci * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 378c2ecf20Sopenharmony_ci *============================================================================= 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { 408c2ecf20Sopenharmony_ci {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, 418c2ecf20Sopenharmony_ci {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1}, 428c2ecf20Sopenharmony_ci {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_cistatic const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { 458c2ecf20Sopenharmony_ci {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, 468c2ecf20Sopenharmony_ci {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1}, 478c2ecf20Sopenharmony_ci {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_cistatic const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { 508c2ecf20Sopenharmony_ci {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, 518c2ecf20Sopenharmony_ci {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}, 528c2ecf20Sopenharmony_ci {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_cistatic const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { 558c2ecf20Sopenharmony_ci {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, 568c2ecf20Sopenharmony_ci {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, 578c2ecf20Sopenharmony_ci {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Advance Thermal Throttling default restriction table */ 618c2ecf20Sopenharmony_cistatic const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { 628c2ecf20Sopenharmony_ci {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, 638c2ecf20Sopenharmony_ci {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, 648c2ecf20Sopenharmony_ci {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, 658c2ecf20Sopenharmony_ci {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cibool iwl_tt_is_low_power_state(struct iwl_priv *priv) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (tt->state >= IWL_TI_1) 738c2ecf20Sopenharmony_ci return true; 748c2ecf20Sopenharmony_ci return false; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciu8 iwl_tt_current_power_mode(struct iwl_priv *priv) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return tt->tt_power_mode; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cibool iwl_ht_enabled(struct iwl_priv *priv) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 878c2ecf20Sopenharmony_ci struct iwl_tt_restriction *restriction; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!priv->thermal_throttle.advanced_tt) 908c2ecf20Sopenharmony_ci return true; 918c2ecf20Sopenharmony_ci restriction = tt->restriction + tt->state; 928c2ecf20Sopenharmony_ci return restriction->is_ht; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic bool iwl_within_ct_kill_margin(struct iwl_priv *priv) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci s32 temp = priv->temperature; /* degrees CELSIUS except specified */ 988c2ecf20Sopenharmony_ci bool within_margin = false; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!priv->thermal_throttle.advanced_tt) 1018c2ecf20Sopenharmony_ci within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= 1028c2ecf20Sopenharmony_ci CT_KILL_THRESHOLD_LEGACY) ? true : false; 1038c2ecf20Sopenharmony_ci else 1048c2ecf20Sopenharmony_ci within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= 1058c2ecf20Sopenharmony_ci CT_KILL_THRESHOLD) ? true : false; 1068c2ecf20Sopenharmony_ci return within_margin; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cibool iwl_check_for_ct_kill(struct iwl_priv *priv) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci bool is_ct_kill = false; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (iwl_within_ct_kill_margin(priv)) { 1148c2ecf20Sopenharmony_ci iwl_tt_enter_ct_kill(priv); 1158c2ecf20Sopenharmony_ci is_ct_kill = true; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci return is_ct_kill; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cienum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 1238c2ecf20Sopenharmony_ci struct iwl_tt_restriction *restriction; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!priv->thermal_throttle.advanced_tt) 1268c2ecf20Sopenharmony_ci return IWL_ANT_OK_MULTI; 1278c2ecf20Sopenharmony_ci restriction = tt->restriction + tt->state; 1288c2ecf20Sopenharmony_ci return restriction->tx_stream; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cienum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 1348c2ecf20Sopenharmony_ci struct iwl_tt_restriction *restriction; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!priv->thermal_throttle.advanced_tt) 1378c2ecf20Sopenharmony_ci return IWL_ANT_OK_MULTI; 1388c2ecf20Sopenharmony_ci restriction = tt->restriction + tt->state; 1398c2ecf20Sopenharmony_ci return restriction->rx_stream; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ 1438c2ecf20Sopenharmony_ci#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * toggle the bit to wake up uCode and check the temperature 1478c2ecf20Sopenharmony_ci * if the temperature is below CT, uCode will stay awake and send card 1488c2ecf20Sopenharmony_ci * state notification with CT_KILL bit clear to inform Thermal Throttling 1498c2ecf20Sopenharmony_ci * Management to change state. Otherwise, uCode will go back to sleep 1508c2ecf20Sopenharmony_ci * without doing anything, driver should continue the 5 seconds timer 1518c2ecf20Sopenharmony_ci * to wake up uCode for temperature check until temperature drop below CT 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic void iwl_tt_check_exit_ct_kill(struct timer_list *t) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct iwl_priv *priv = from_timer(priv, t, 1568c2ecf20Sopenharmony_ci thermal_throttle.ct_kill_exit_tm); 1578c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 1588c2ecf20Sopenharmony_ci unsigned long flags; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 1618c2ecf20Sopenharmony_ci return; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (tt->state == IWL_TI_CT_KILL) { 1648c2ecf20Sopenharmony_ci if (priv->thermal_throttle.ct_kill_toggle) { 1658c2ecf20Sopenharmony_ci iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, 1668c2ecf20Sopenharmony_ci CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); 1678c2ecf20Sopenharmony_ci priv->thermal_throttle.ct_kill_toggle = false; 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, 1708c2ecf20Sopenharmony_ci CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); 1718c2ecf20Sopenharmony_ci priv->thermal_throttle.ct_kill_toggle = true; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); 1748c2ecf20Sopenharmony_ci if (iwl_trans_grab_nic_access(priv->trans, &flags)) 1758c2ecf20Sopenharmony_ci iwl_trans_release_nic_access(priv->trans, &flags); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Reschedule the ct_kill timer to occur in 1788c2ecf20Sopenharmony_ci * CT_KILL_EXIT_DURATION seconds to ensure we get a 1798c2ecf20Sopenharmony_ci * thermal update */ 1808c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n"); 1818c2ecf20Sopenharmony_ci mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, 1828c2ecf20Sopenharmony_ci jiffies + CT_KILL_EXIT_DURATION * HZ); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void iwl_perform_ct_kill_task(struct iwl_priv *priv, 1878c2ecf20Sopenharmony_ci bool stop) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci if (stop) { 1908c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Stop all queues\n"); 1918c2ecf20Sopenharmony_ci if (priv->mac80211_registered) 1928c2ecf20Sopenharmony_ci ieee80211_stop_queues(priv->hw); 1938c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, 1948c2ecf20Sopenharmony_ci "Schedule 5 seconds CT_KILL Timer\n"); 1958c2ecf20Sopenharmony_ci mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, 1968c2ecf20Sopenharmony_ci jiffies + CT_KILL_EXIT_DURATION * HZ); 1978c2ecf20Sopenharmony_ci } else { 1988c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Wake all queues\n"); 1998c2ecf20Sopenharmony_ci if (priv->mac80211_registered) 2008c2ecf20Sopenharmony_ci ieee80211_wake_queues(priv->hw); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void iwl_tt_ready_for_ct_kill(struct timer_list *t) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct iwl_priv *priv = from_timer(priv, t, 2078c2ecf20Sopenharmony_ci thermal_throttle.ct_kill_waiting_tm); 2088c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 2118c2ecf20Sopenharmony_ci return; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* temperature timer expired, ready to go into CT_KILL state */ 2148c2ecf20Sopenharmony_ci if (tt->state != IWL_TI_CT_KILL) { 2158c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "entering CT_KILL state when " 2168c2ecf20Sopenharmony_ci "temperature timer expired\n"); 2178c2ecf20Sopenharmony_ci tt->state = IWL_TI_CT_KILL; 2188c2ecf20Sopenharmony_ci set_bit(STATUS_CT_KILL, &priv->status); 2198c2ecf20Sopenharmony_ci iwl_perform_ct_kill_task(priv, true); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void iwl_prepare_ct_kill_task(struct iwl_priv *priv) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); 2268c2ecf20Sopenharmony_ci /* make request to retrieve statistics information */ 2278c2ecf20Sopenharmony_ci iwl_send_statistics_request(priv, 0, false); 2288c2ecf20Sopenharmony_ci /* Reschedule the ct_kill wait timer */ 2298c2ecf20Sopenharmony_ci mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, 2308c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) 2348c2ecf20Sopenharmony_ci#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) 2358c2ecf20Sopenharmony_ci#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* 2388c2ecf20Sopenharmony_ci * Legacy thermal throttling 2398c2ecf20Sopenharmony_ci * 1) Avoid NIC destruction due to high temperatures 2408c2ecf20Sopenharmony_ci * Chip will identify dangerously high temperatures that can 2418c2ecf20Sopenharmony_ci * harm the device and will power down 2428c2ecf20Sopenharmony_ci * 2) Avoid the NIC power down due to high temperature 2438c2ecf20Sopenharmony_ci * Throttle early enough to lower the power consumption before 2448c2ecf20Sopenharmony_ci * drastic steps are needed 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 2498c2ecf20Sopenharmony_ci enum iwl_tt_state old_state; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 2528c2ecf20Sopenharmony_ci if ((tt->tt_previous_temp) && 2538c2ecf20Sopenharmony_ci (temp > tt->tt_previous_temp) && 2548c2ecf20Sopenharmony_ci ((temp - tt->tt_previous_temp) > 2558c2ecf20Sopenharmony_ci IWL_TT_INCREASE_MARGIN)) { 2568c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, 2578c2ecf20Sopenharmony_ci "Temperature increase %d degree Celsius\n", 2588c2ecf20Sopenharmony_ci (temp - tt->tt_previous_temp)); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci#endif 2618c2ecf20Sopenharmony_ci old_state = tt->state; 2628c2ecf20Sopenharmony_ci /* in Celsius */ 2638c2ecf20Sopenharmony_ci if (temp >= IWL_MINIMAL_POWER_THRESHOLD) 2648c2ecf20Sopenharmony_ci tt->state = IWL_TI_CT_KILL; 2658c2ecf20Sopenharmony_ci else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) 2668c2ecf20Sopenharmony_ci tt->state = IWL_TI_2; 2678c2ecf20Sopenharmony_ci else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) 2688c2ecf20Sopenharmony_ci tt->state = IWL_TI_1; 2698c2ecf20Sopenharmony_ci else 2708c2ecf20Sopenharmony_ci tt->state = IWL_TI_0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 2738c2ecf20Sopenharmony_ci tt->tt_previous_temp = temp; 2748c2ecf20Sopenharmony_ci#endif 2758c2ecf20Sopenharmony_ci /* stop ct_kill_waiting_tm timer */ 2768c2ecf20Sopenharmony_ci del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); 2778c2ecf20Sopenharmony_ci if (tt->state != old_state) { 2788c2ecf20Sopenharmony_ci switch (tt->state) { 2798c2ecf20Sopenharmony_ci case IWL_TI_0: 2808c2ecf20Sopenharmony_ci /* 2818c2ecf20Sopenharmony_ci * When the system is ready to go back to IWL_TI_0 2828c2ecf20Sopenharmony_ci * we only have to call iwl_power_update_mode() to 2838c2ecf20Sopenharmony_ci * do so. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci case IWL_TI_1: 2878c2ecf20Sopenharmony_ci tt->tt_power_mode = IWL_POWER_INDEX_3; 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case IWL_TI_2: 2908c2ecf20Sopenharmony_ci tt->tt_power_mode = IWL_POWER_INDEX_4; 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci default: 2938c2ecf20Sopenharmony_ci tt->tt_power_mode = IWL_POWER_INDEX_5; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci mutex_lock(&priv->mutex); 2978c2ecf20Sopenharmony_ci if (old_state == IWL_TI_CT_KILL) 2988c2ecf20Sopenharmony_ci clear_bit(STATUS_CT_KILL, &priv->status); 2998c2ecf20Sopenharmony_ci if (tt->state != IWL_TI_CT_KILL && 3008c2ecf20Sopenharmony_ci iwl_power_update_mode(priv, true)) { 3018c2ecf20Sopenharmony_ci /* TT state not updated 3028c2ecf20Sopenharmony_ci * try again during next temperature read 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci if (old_state == IWL_TI_CT_KILL) 3058c2ecf20Sopenharmony_ci set_bit(STATUS_CT_KILL, &priv->status); 3068c2ecf20Sopenharmony_ci tt->state = old_state; 3078c2ecf20Sopenharmony_ci IWL_ERR(priv, "Cannot update power mode, " 3088c2ecf20Sopenharmony_ci "TT state not updated\n"); 3098c2ecf20Sopenharmony_ci } else { 3108c2ecf20Sopenharmony_ci if (tt->state == IWL_TI_CT_KILL) { 3118c2ecf20Sopenharmony_ci if (force) { 3128c2ecf20Sopenharmony_ci set_bit(STATUS_CT_KILL, &priv->status); 3138c2ecf20Sopenharmony_ci iwl_perform_ct_kill_task(priv, true); 3148c2ecf20Sopenharmony_ci } else { 3158c2ecf20Sopenharmony_ci iwl_prepare_ct_kill_task(priv); 3168c2ecf20Sopenharmony_ci tt->state = old_state; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci } else if (old_state == IWL_TI_CT_KILL) { 3198c2ecf20Sopenharmony_ci iwl_perform_ct_kill_task(priv, false); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n", 3228c2ecf20Sopenharmony_ci tt->state); 3238c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Power Index change to %u\n", 3248c2ecf20Sopenharmony_ci tt->tt_power_mode); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci mutex_unlock(&priv->mutex); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* 3318c2ecf20Sopenharmony_ci * Advance thermal throttling 3328c2ecf20Sopenharmony_ci * 1) Avoid NIC destruction due to high temperatures 3338c2ecf20Sopenharmony_ci * Chip will identify dangerously high temperatures that can 3348c2ecf20Sopenharmony_ci * harm the device and will power down 3358c2ecf20Sopenharmony_ci * 2) Avoid the NIC power down due to high temperature 3368c2ecf20Sopenharmony_ci * Throttle early enough to lower the power consumption before 3378c2ecf20Sopenharmony_ci * drastic steps are needed 3388c2ecf20Sopenharmony_ci * Actions include relaxing the power down sleep thresholds and 3398c2ecf20Sopenharmony_ci * decreasing the number of TX streams 3408c2ecf20Sopenharmony_ci * 3) Avoid throughput performance impact as much as possible 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci *============================================================================= 3438c2ecf20Sopenharmony_ci * Condition Nxt State Condition Nxt State Condition Nxt State 3448c2ecf20Sopenharmony_ci *----------------------------------------------------------------------------- 3458c2ecf20Sopenharmony_ci * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A 3468c2ecf20Sopenharmony_ci * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 3478c2ecf20Sopenharmony_ci * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 3488c2ecf20Sopenharmony_ci * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 3498c2ecf20Sopenharmony_ci *============================================================================= 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 3548c2ecf20Sopenharmony_ci int i; 3558c2ecf20Sopenharmony_ci bool changed = false; 3568c2ecf20Sopenharmony_ci enum iwl_tt_state old_state; 3578c2ecf20Sopenharmony_ci struct iwl_tt_trans *transaction; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci old_state = tt->state; 3608c2ecf20Sopenharmony_ci for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { 3618c2ecf20Sopenharmony_ci /* based on the current TT state, 3628c2ecf20Sopenharmony_ci * find the curresponding transaction table 3638c2ecf20Sopenharmony_ci * each table has (IWL_TI_STATE_MAX - 1) entries 3648c2ecf20Sopenharmony_ci * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) 3658c2ecf20Sopenharmony_ci * will advance to the correct table. 3668c2ecf20Sopenharmony_ci * then based on the current temperature 3678c2ecf20Sopenharmony_ci * find the next state need to transaction to 3688c2ecf20Sopenharmony_ci * go through all the possible (IWL_TI_STATE_MAX - 1) entries 3698c2ecf20Sopenharmony_ci * in the current table to see if transaction is needed 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci transaction = tt->transaction + 3728c2ecf20Sopenharmony_ci ((old_state * (IWL_TI_STATE_MAX - 1)) + i); 3738c2ecf20Sopenharmony_ci if (temp >= transaction->tt_low && 3748c2ecf20Sopenharmony_ci temp <= transaction->tt_high) { 3758c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 3768c2ecf20Sopenharmony_ci if ((tt->tt_previous_temp) && 3778c2ecf20Sopenharmony_ci (temp > tt->tt_previous_temp) && 3788c2ecf20Sopenharmony_ci ((temp - tt->tt_previous_temp) > 3798c2ecf20Sopenharmony_ci IWL_TT_INCREASE_MARGIN)) { 3808c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, 3818c2ecf20Sopenharmony_ci "Temperature increase %d " 3828c2ecf20Sopenharmony_ci "degree Celsius\n", 3838c2ecf20Sopenharmony_ci (temp - tt->tt_previous_temp)); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci tt->tt_previous_temp = temp; 3868c2ecf20Sopenharmony_ci#endif 3878c2ecf20Sopenharmony_ci if (old_state != 3888c2ecf20Sopenharmony_ci transaction->next_state) { 3898c2ecf20Sopenharmony_ci changed = true; 3908c2ecf20Sopenharmony_ci tt->state = 3918c2ecf20Sopenharmony_ci transaction->next_state; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci /* stop ct_kill_waiting_tm timer */ 3978c2ecf20Sopenharmony_ci del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); 3988c2ecf20Sopenharmony_ci if (changed) { 3998c2ecf20Sopenharmony_ci if (tt->state >= IWL_TI_1) { 4008c2ecf20Sopenharmony_ci /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ 4018c2ecf20Sopenharmony_ci tt->tt_power_mode = IWL_POWER_INDEX_5; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (!iwl_ht_enabled(priv)) { 4048c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci for_each_context(priv, ctx) { 4078c2ecf20Sopenharmony_ci struct iwl_rxon_cmd *rxon; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci rxon = &ctx->staging; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* disable HT */ 4128c2ecf20Sopenharmony_ci rxon->flags &= ~( 4138c2ecf20Sopenharmony_ci RXON_FLG_CHANNEL_MODE_MSK | 4148c2ecf20Sopenharmony_ci RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | 4158c2ecf20Sopenharmony_ci RXON_FLG_HT40_PROT_MSK | 4168c2ecf20Sopenharmony_ci RXON_FLG_HT_PROT_MSK); 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci /* check HT capability and set 4208c2ecf20Sopenharmony_ci * according to the system HT capability 4218c2ecf20Sopenharmony_ci * in case get disabled before */ 4228c2ecf20Sopenharmony_ci iwl_set_rxon_ht(priv, &priv->current_ht_config); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci } else { 4268c2ecf20Sopenharmony_ci /* 4278c2ecf20Sopenharmony_ci * restore system power setting -- it will be 4288c2ecf20Sopenharmony_ci * recalculated automatically. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* check HT capability and set 4328c2ecf20Sopenharmony_ci * according to the system HT capability 4338c2ecf20Sopenharmony_ci * in case get disabled before */ 4348c2ecf20Sopenharmony_ci iwl_set_rxon_ht(priv, &priv->current_ht_config); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci mutex_lock(&priv->mutex); 4378c2ecf20Sopenharmony_ci if (old_state == IWL_TI_CT_KILL) 4388c2ecf20Sopenharmony_ci clear_bit(STATUS_CT_KILL, &priv->status); 4398c2ecf20Sopenharmony_ci if (tt->state != IWL_TI_CT_KILL && 4408c2ecf20Sopenharmony_ci iwl_power_update_mode(priv, true)) { 4418c2ecf20Sopenharmony_ci /* TT state not updated 4428c2ecf20Sopenharmony_ci * try again during next temperature read 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ci IWL_ERR(priv, "Cannot update power mode, " 4458c2ecf20Sopenharmony_ci "TT state not updated\n"); 4468c2ecf20Sopenharmony_ci if (old_state == IWL_TI_CT_KILL) 4478c2ecf20Sopenharmony_ci set_bit(STATUS_CT_KILL, &priv->status); 4488c2ecf20Sopenharmony_ci tt->state = old_state; 4498c2ecf20Sopenharmony_ci } else { 4508c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, 4518c2ecf20Sopenharmony_ci "Thermal Throttling to new state: %u\n", 4528c2ecf20Sopenharmony_ci tt->state); 4538c2ecf20Sopenharmony_ci if (old_state != IWL_TI_CT_KILL && 4548c2ecf20Sopenharmony_ci tt->state == IWL_TI_CT_KILL) { 4558c2ecf20Sopenharmony_ci if (force) { 4568c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, 4578c2ecf20Sopenharmony_ci "Enter IWL_TI_CT_KILL\n"); 4588c2ecf20Sopenharmony_ci set_bit(STATUS_CT_KILL, &priv->status); 4598c2ecf20Sopenharmony_ci iwl_perform_ct_kill_task(priv, true); 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci tt->state = old_state; 4628c2ecf20Sopenharmony_ci iwl_prepare_ct_kill_task(priv); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci } else if (old_state == IWL_TI_CT_KILL && 4658c2ecf20Sopenharmony_ci tt->state != IWL_TI_CT_KILL) { 4668c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n"); 4678c2ecf20Sopenharmony_ci iwl_perform_ct_kill_task(priv, false); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci mutex_unlock(&priv->mutex); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/* Card State Notification indicated reach critical temperature 4758c2ecf20Sopenharmony_ci * if PSP not enable, no Thermal Throttling function will be performed 4768c2ecf20Sopenharmony_ci * just set the GP1 bit to acknowledge the event 4778c2ecf20Sopenharmony_ci * otherwise, go into IWL_TI_CT_KILL state 4788c2ecf20Sopenharmony_ci * since Card State Notification will not provide any temperature reading 4798c2ecf20Sopenharmony_ci * for Legacy mode 4808c2ecf20Sopenharmony_ci * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() 4818c2ecf20Sopenharmony_ci * for advance mode 4828c2ecf20Sopenharmony_ci * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_cistatic void iwl_bg_ct_enter(struct work_struct *work) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); 4878c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 4908c2ecf20Sopenharmony_ci return; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (!iwl_is_ready(priv)) 4938c2ecf20Sopenharmony_ci return; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (tt->state != IWL_TI_CT_KILL) { 4968c2ecf20Sopenharmony_ci IWL_ERR(priv, "Device reached critical temperature " 4978c2ecf20Sopenharmony_ci "- ucode going to sleep!\n"); 4988c2ecf20Sopenharmony_ci if (!priv->thermal_throttle.advanced_tt) 4998c2ecf20Sopenharmony_ci iwl_legacy_tt_handler(priv, 5008c2ecf20Sopenharmony_ci IWL_MINIMAL_POWER_THRESHOLD, 5018c2ecf20Sopenharmony_ci true); 5028c2ecf20Sopenharmony_ci else 5038c2ecf20Sopenharmony_ci iwl_advance_tt_handler(priv, 5048c2ecf20Sopenharmony_ci CT_KILL_THRESHOLD + 1, true); 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/* Card State Notification indicated out of critical temperature 5098c2ecf20Sopenharmony_ci * since Card State Notification will not provide any temperature reading 5108c2ecf20Sopenharmony_ci * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature 5118c2ecf20Sopenharmony_ci * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_cistatic void iwl_bg_ct_exit(struct work_struct *work) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); 5168c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (!iwl_is_ready(priv)) 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* stop ct_kill_exit_tm timer */ 5258c2ecf20Sopenharmony_ci del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (tt->state == IWL_TI_CT_KILL) { 5288c2ecf20Sopenharmony_ci IWL_ERR(priv, 5298c2ecf20Sopenharmony_ci "Device temperature below critical" 5308c2ecf20Sopenharmony_ci "- ucode awake!\n"); 5318c2ecf20Sopenharmony_ci /* 5328c2ecf20Sopenharmony_ci * exit from CT_KILL state 5338c2ecf20Sopenharmony_ci * reset the current temperature reading 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci priv->temperature = 0; 5368c2ecf20Sopenharmony_ci if (!priv->thermal_throttle.advanced_tt) 5378c2ecf20Sopenharmony_ci iwl_legacy_tt_handler(priv, 5388c2ecf20Sopenharmony_ci IWL_REDUCED_PERFORMANCE_THRESHOLD_2, 5398c2ecf20Sopenharmony_ci true); 5408c2ecf20Sopenharmony_ci else 5418c2ecf20Sopenharmony_ci iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD, 5428c2ecf20Sopenharmony_ci true); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_civoid iwl_tt_enter_ct_kill(struct iwl_priv *priv) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 5498c2ecf20Sopenharmony_ci return; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n"); 5528c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->ct_enter); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_civoid iwl_tt_exit_ct_kill(struct iwl_priv *priv) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 5588c2ecf20Sopenharmony_ci return; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n"); 5618c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->ct_exit); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic void iwl_bg_tt_work(struct work_struct *work) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); 5678c2ecf20Sopenharmony_ci s32 temp = priv->temperature; /* degrees CELSIUS except specified */ 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 5708c2ecf20Sopenharmony_ci return; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (!priv->thermal_throttle.advanced_tt) 5738c2ecf20Sopenharmony_ci iwl_legacy_tt_handler(priv, temp, false); 5748c2ecf20Sopenharmony_ci else 5758c2ecf20Sopenharmony_ci iwl_advance_tt_handler(priv, temp, false); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_civoid iwl_tt_handler(struct iwl_priv *priv) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 5818c2ecf20Sopenharmony_ci return; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n"); 5848c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->tt_work); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/* Thermal throttling initialization 5888c2ecf20Sopenharmony_ci * For advance thermal throttling: 5898c2ecf20Sopenharmony_ci * Initialize Thermal Index and temperature threshold table 5908c2ecf20Sopenharmony_ci * Initialize thermal throttling restriction table 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_civoid iwl_tt_initialize(struct iwl_priv *priv) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 5958c2ecf20Sopenharmony_ci int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); 5968c2ecf20Sopenharmony_ci struct iwl_tt_trans *transaction; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n"); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci memset(tt, 0, sizeof(struct iwl_tt_mgmt)); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci tt->state = IWL_TI_0; 6038c2ecf20Sopenharmony_ci timer_setup(&priv->thermal_throttle.ct_kill_exit_tm, 6048c2ecf20Sopenharmony_ci iwl_tt_check_exit_ct_kill, 0); 6058c2ecf20Sopenharmony_ci timer_setup(&priv->thermal_throttle.ct_kill_waiting_tm, 6068c2ecf20Sopenharmony_ci iwl_tt_ready_for_ct_kill, 0); 6078c2ecf20Sopenharmony_ci /* setup deferred ct kill work */ 6088c2ecf20Sopenharmony_ci INIT_WORK(&priv->tt_work, iwl_bg_tt_work); 6098c2ecf20Sopenharmony_ci INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); 6108c2ecf20Sopenharmony_ci INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (priv->lib->adv_thermal_throttle) { 6138c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); 6148c2ecf20Sopenharmony_ci tt->restriction = kcalloc(IWL_TI_STATE_MAX, 6158c2ecf20Sopenharmony_ci sizeof(struct iwl_tt_restriction), 6168c2ecf20Sopenharmony_ci GFP_KERNEL); 6178c2ecf20Sopenharmony_ci tt->transaction = kcalloc(IWL_TI_STATE_MAX * 6188c2ecf20Sopenharmony_ci (IWL_TI_STATE_MAX - 1), 6198c2ecf20Sopenharmony_ci sizeof(struct iwl_tt_trans), 6208c2ecf20Sopenharmony_ci GFP_KERNEL); 6218c2ecf20Sopenharmony_ci if (!tt->restriction || !tt->transaction) { 6228c2ecf20Sopenharmony_ci IWL_ERR(priv, "Fallback to Legacy Throttling\n"); 6238c2ecf20Sopenharmony_ci priv->thermal_throttle.advanced_tt = false; 6248c2ecf20Sopenharmony_ci kfree(tt->restriction); 6258c2ecf20Sopenharmony_ci tt->restriction = NULL; 6268c2ecf20Sopenharmony_ci kfree(tt->transaction); 6278c2ecf20Sopenharmony_ci tt->transaction = NULL; 6288c2ecf20Sopenharmony_ci } else { 6298c2ecf20Sopenharmony_ci transaction = tt->transaction + 6308c2ecf20Sopenharmony_ci (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); 6318c2ecf20Sopenharmony_ci memcpy(transaction, &tt_range_0[0], size); 6328c2ecf20Sopenharmony_ci transaction = tt->transaction + 6338c2ecf20Sopenharmony_ci (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); 6348c2ecf20Sopenharmony_ci memcpy(transaction, &tt_range_1[0], size); 6358c2ecf20Sopenharmony_ci transaction = tt->transaction + 6368c2ecf20Sopenharmony_ci (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); 6378c2ecf20Sopenharmony_ci memcpy(transaction, &tt_range_2[0], size); 6388c2ecf20Sopenharmony_ci transaction = tt->transaction + 6398c2ecf20Sopenharmony_ci (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); 6408c2ecf20Sopenharmony_ci memcpy(transaction, &tt_range_3[0], size); 6418c2ecf20Sopenharmony_ci size = sizeof(struct iwl_tt_restriction) * 6428c2ecf20Sopenharmony_ci IWL_TI_STATE_MAX; 6438c2ecf20Sopenharmony_ci memcpy(tt->restriction, 6448c2ecf20Sopenharmony_ci &restriction_range[0], size); 6458c2ecf20Sopenharmony_ci priv->thermal_throttle.advanced_tt = true; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci } else { 6488c2ecf20Sopenharmony_ci IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n"); 6498c2ecf20Sopenharmony_ci priv->thermal_throttle.advanced_tt = false; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci/* cleanup thermal throttling management related memory and timer */ 6548c2ecf20Sopenharmony_civoid iwl_tt_exit(struct iwl_priv *priv) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* stop ct_kill_exit_tm timer if activated */ 6598c2ecf20Sopenharmony_ci del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); 6608c2ecf20Sopenharmony_ci /* stop ct_kill_waiting_tm timer if activated */ 6618c2ecf20Sopenharmony_ci del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); 6628c2ecf20Sopenharmony_ci cancel_work_sync(&priv->tt_work); 6638c2ecf20Sopenharmony_ci cancel_work_sync(&priv->ct_enter); 6648c2ecf20Sopenharmony_ci cancel_work_sync(&priv->ct_exit); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (priv->thermal_throttle.advanced_tt) { 6678c2ecf20Sopenharmony_ci /* free advance thermal throttling memory */ 6688c2ecf20Sopenharmony_ci kfree(tt->restriction); 6698c2ecf20Sopenharmony_ci tt->restriction = NULL; 6708c2ecf20Sopenharmony_ci kfree(tt->transaction); 6718c2ecf20Sopenharmony_ci tt->transaction = NULL; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci} 674