Commit | Line | Data |
---|---|---|
0975cc8f WYG |
1 | /****************************************************************************** |
2 | * | |
51368bf7 | 3 | * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. |
0975cc8f WYG |
4 | * |
5 | * Portions of this file are derived from the ipw3945 project, as well | |
6 | * as portions of the ieee80211 subsystem header files. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of version 2 of the GNU General Public License as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along with | |
18 | * this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
20 | * | |
21 | * The full GNU General Public License is included in this distribution in the | |
22 | * file called LICENSE. | |
23 | * | |
24 | * Contact Information: | |
25 | * Intel Linux Wireless <ilw@linux.intel.com> | |
26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
27 | *****************************************************************************/ | |
28 | ||
29 | ||
30 | #include <linux/kernel.h> | |
31 | #include <linux/module.h> | |
32 | #include <linux/slab.h> | |
33 | #include <linux/init.h> | |
0975cc8f | 34 | #include <net/mac80211.h> |
0975cc8f | 35 | #include "iwl-io.h" |
65de7e84 | 36 | #include "iwl-modparams.h" |
1023fdc4 JB |
37 | #include "iwl-debug.h" |
38 | #include "agn.h" | |
1023fdc4 JB |
39 | #include "dev.h" |
40 | #include "commands.h" | |
41 | #include "tt.h" | |
0975cc8f WYG |
42 | |
43 | /* default Thermal Throttling transaction table | |
44 | * Current state | Throttling Down | Throttling Up | |
45 | *============================================================================= | |
46 | * Condition Nxt State Condition Nxt State Condition Nxt State | |
47 | *----------------------------------------------------------------------------- | |
48 | * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A | |
49 | * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 | |
50 | * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 | |
51 | * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 | |
52 | *============================================================================= | |
53 | */ | |
54 | static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { | |
55 | {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, | |
56 | {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1}, | |
57 | {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} | |
58 | }; | |
59 | static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { | |
60 | {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, | |
61 | {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1}, | |
62 | {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} | |
63 | }; | |
64 | static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { | |
65 | {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, | |
66 | {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}, | |
67 | {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} | |
68 | }; | |
69 | static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { | |
70 | {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, | |
71 | {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, | |
72 | {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} | |
73 | }; | |
74 | ||
75 | /* Advance Thermal Throttling default restriction table */ | |
76 | static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { | |
77 | {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, | |
78 | {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, | |
79 | {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, | |
80 | {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } | |
81 | }; | |
82 | ||
83 | bool iwl_tt_is_low_power_state(struct iwl_priv *priv) | |
84 | { | |
85 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
86 | ||
87 | if (tt->state >= IWL_TI_1) | |
88 | return true; | |
89 | return false; | |
90 | } | |
91 | ||
92 | u8 iwl_tt_current_power_mode(struct iwl_priv *priv) | |
93 | { | |
94 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
95 | ||
96 | return tt->tt_power_mode; | |
97 | } | |
98 | ||
99 | bool iwl_ht_enabled(struct iwl_priv *priv) | |
100 | { | |
101 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
102 | struct iwl_tt_restriction *restriction; | |
103 | ||
104 | if (!priv->thermal_throttle.advanced_tt) | |
105 | return true; | |
106 | restriction = tt->restriction + tt->state; | |
107 | return restriction->is_ht; | |
108 | } | |
109 | ||
110 | static bool iwl_within_ct_kill_margin(struct iwl_priv *priv) | |
111 | { | |
112 | s32 temp = priv->temperature; /* degrees CELSIUS except specified */ | |
113 | bool within_margin = false; | |
114 | ||
0975cc8f WYG |
115 | if (!priv->thermal_throttle.advanced_tt) |
116 | within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= | |
117 | CT_KILL_THRESHOLD_LEGACY) ? true : false; | |
118 | else | |
119 | within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= | |
120 | CT_KILL_THRESHOLD) ? true : false; | |
121 | return within_margin; | |
122 | } | |
123 | ||
124 | bool iwl_check_for_ct_kill(struct iwl_priv *priv) | |
125 | { | |
126 | bool is_ct_kill = false; | |
127 | ||
128 | if (iwl_within_ct_kill_margin(priv)) { | |
129 | iwl_tt_enter_ct_kill(priv); | |
130 | is_ct_kill = true; | |
131 | } | |
132 | return is_ct_kill; | |
133 | } | |
134 | ||
135 | enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) | |
136 | { | |
137 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
138 | struct iwl_tt_restriction *restriction; | |
139 | ||
140 | if (!priv->thermal_throttle.advanced_tt) | |
141 | return IWL_ANT_OK_MULTI; | |
142 | restriction = tt->restriction + tt->state; | |
143 | return restriction->tx_stream; | |
144 | } | |
145 | ||
146 | enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) | |
147 | { | |
148 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
149 | struct iwl_tt_restriction *restriction; | |
150 | ||
151 | if (!priv->thermal_throttle.advanced_tt) | |
152 | return IWL_ANT_OK_MULTI; | |
153 | restriction = tt->restriction + tt->state; | |
154 | return restriction->rx_stream; | |
155 | } | |
156 | ||
157 | #define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ | |
158 | #define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ | |
159 | ||
160 | /* | |
161 | * toggle the bit to wake up uCode and check the temperature | |
162 | * if the temperature is below CT, uCode will stay awake and send card | |
163 | * state notification with CT_KILL bit clear to inform Thermal Throttling | |
164 | * Management to change state. Otherwise, uCode will go back to sleep | |
165 | * without doing anything, driver should continue the 5 seconds timer | |
166 | * to wake up uCode for temperature check until temperature drop below CT | |
167 | */ | |
168 | static void iwl_tt_check_exit_ct_kill(unsigned long data) | |
169 | { | |
170 | struct iwl_priv *priv = (struct iwl_priv *)data; | |
171 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
172 | unsigned long flags; | |
173 | ||
83626404 | 174 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
0975cc8f WYG |
175 | return; |
176 | ||
177 | if (tt->state == IWL_TI_CT_KILL) { | |
178 | if (priv->thermal_throttle.ct_kill_toggle) { | |
68e8dfda | 179 | iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, |
0975cc8f WYG |
180 | CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); |
181 | priv->thermal_throttle.ct_kill_toggle = false; | |
182 | } else { | |
68e8dfda | 183 | iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, |
0975cc8f WYG |
184 | CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); |
185 | priv->thermal_throttle.ct_kill_toggle = true; | |
186 | } | |
68e8dfda | 187 | iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); |
e56b04ef LE |
188 | if (iwl_trans_grab_nic_access(priv->trans, false, &flags)) |
189 | iwl_trans_release_nic_access(priv->trans, &flags); | |
0975cc8f WYG |
190 | |
191 | /* Reschedule the ct_kill timer to occur in | |
192 | * CT_KILL_EXIT_DURATION seconds to ensure we get a | |
193 | * thermal update */ | |
f38f884b | 194 | IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n"); |
0975cc8f WYG |
195 | mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, |
196 | jiffies + CT_KILL_EXIT_DURATION * HZ); | |
197 | } | |
198 | } | |
199 | ||
200 | static void iwl_perform_ct_kill_task(struct iwl_priv *priv, | |
201 | bool stop) | |
202 | { | |
203 | if (stop) { | |
f38f884b | 204 | IWL_DEBUG_TEMP(priv, "Stop all queues\n"); |
859cfb0a | 205 | if (priv->mac80211_registered) |
0975cc8f | 206 | ieee80211_stop_queues(priv->hw); |
f38f884b | 207 | IWL_DEBUG_TEMP(priv, |
0975cc8f WYG |
208 | "Schedule 5 seconds CT_KILL Timer\n"); |
209 | mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, | |
210 | jiffies + CT_KILL_EXIT_DURATION * HZ); | |
211 | } else { | |
f38f884b | 212 | IWL_DEBUG_TEMP(priv, "Wake all queues\n"); |
859cfb0a | 213 | if (priv->mac80211_registered) |
0975cc8f WYG |
214 | ieee80211_wake_queues(priv->hw); |
215 | } | |
216 | } | |
217 | ||
218 | static void iwl_tt_ready_for_ct_kill(unsigned long data) | |
219 | { | |
220 | struct iwl_priv *priv = (struct iwl_priv *)data; | |
221 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
222 | ||
83626404 | 223 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
0975cc8f WYG |
224 | return; |
225 | ||
226 | /* temperature timer expired, ready to go into CT_KILL state */ | |
227 | if (tt->state != IWL_TI_CT_KILL) { | |
f38f884b | 228 | IWL_DEBUG_TEMP(priv, "entering CT_KILL state when " |
0975cc8f WYG |
229 | "temperature timer expired\n"); |
230 | tt->state = IWL_TI_CT_KILL; | |
9a716863 | 231 | set_bit(STATUS_CT_KILL, &priv->status); |
0975cc8f WYG |
232 | iwl_perform_ct_kill_task(priv, true); |
233 | } | |
234 | } | |
235 | ||
236 | static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) | |
237 | { | |
f38f884b | 238 | IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); |
0975cc8f WYG |
239 | /* make request to retrieve statistics information */ |
240 | iwl_send_statistics_request(priv, CMD_SYNC, false); | |
241 | /* Reschedule the ct_kill wait timer */ | |
242 | mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, | |
243 | jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); | |
244 | } | |
245 | ||
246 | #define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) | |
247 | #define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) | |
248 | #define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) | |
249 | ||
250 | /* | |
251 | * Legacy thermal throttling | |
252 | * 1) Avoid NIC destruction due to high temperatures | |
253 | * Chip will identify dangerously high temperatures that can | |
254 | * harm the device and will power down | |
255 | * 2) Avoid the NIC power down due to high temperature | |
256 | * Throttle early enough to lower the power consumption before | |
257 | * drastic steps are needed | |
258 | */ | |
259 | static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) | |
260 | { | |
261 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
262 | enum iwl_tt_state old_state; | |
263 | ||
264 | #ifdef CONFIG_IWLWIFI_DEBUG | |
265 | if ((tt->tt_previous_temp) && | |
266 | (temp > tt->tt_previous_temp) && | |
267 | ((temp - tt->tt_previous_temp) > | |
268 | IWL_TT_INCREASE_MARGIN)) { | |
f38f884b | 269 | IWL_DEBUG_TEMP(priv, |
0975cc8f WYG |
270 | "Temperature increase %d degree Celsius\n", |
271 | (temp - tt->tt_previous_temp)); | |
272 | } | |
273 | #endif | |
274 | old_state = tt->state; | |
275 | /* in Celsius */ | |
276 | if (temp >= IWL_MINIMAL_POWER_THRESHOLD) | |
277 | tt->state = IWL_TI_CT_KILL; | |
278 | else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) | |
279 | tt->state = IWL_TI_2; | |
280 | else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) | |
281 | tt->state = IWL_TI_1; | |
282 | else | |
283 | tt->state = IWL_TI_0; | |
284 | ||
285 | #ifdef CONFIG_IWLWIFI_DEBUG | |
286 | tt->tt_previous_temp = temp; | |
287 | #endif | |
288 | /* stop ct_kill_waiting_tm timer */ | |
289 | del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); | |
290 | if (tt->state != old_state) { | |
291 | switch (tt->state) { | |
292 | case IWL_TI_0: | |
293 | /* | |
294 | * When the system is ready to go back to IWL_TI_0 | |
295 | * we only have to call iwl_power_update_mode() to | |
296 | * do so. | |
297 | */ | |
298 | break; | |
299 | case IWL_TI_1: | |
300 | tt->tt_power_mode = IWL_POWER_INDEX_3; | |
301 | break; | |
302 | case IWL_TI_2: | |
303 | tt->tt_power_mode = IWL_POWER_INDEX_4; | |
304 | break; | |
305 | default: | |
306 | tt->tt_power_mode = IWL_POWER_INDEX_5; | |
307 | break; | |
308 | } | |
b1eea297 | 309 | mutex_lock(&priv->mutex); |
0975cc8f | 310 | if (old_state == IWL_TI_CT_KILL) |
9a716863 | 311 | clear_bit(STATUS_CT_KILL, &priv->status); |
0975cc8f WYG |
312 | if (tt->state != IWL_TI_CT_KILL && |
313 | iwl_power_update_mode(priv, true)) { | |
314 | /* TT state not updated | |
315 | * try again during next temperature read | |
316 | */ | |
317 | if (old_state == IWL_TI_CT_KILL) | |
9a716863 | 318 | set_bit(STATUS_CT_KILL, &priv->status); |
0975cc8f WYG |
319 | tt->state = old_state; |
320 | IWL_ERR(priv, "Cannot update power mode, " | |
321 | "TT state not updated\n"); | |
322 | } else { | |
323 | if (tt->state == IWL_TI_CT_KILL) { | |
324 | if (force) { | |
9a716863 | 325 | set_bit(STATUS_CT_KILL, &priv->status); |
0975cc8f WYG |
326 | iwl_perform_ct_kill_task(priv, true); |
327 | } else { | |
328 | iwl_prepare_ct_kill_task(priv); | |
329 | tt->state = old_state; | |
330 | } | |
331 | } else if (old_state == IWL_TI_CT_KILL && | |
332 | tt->state != IWL_TI_CT_KILL) | |
333 | iwl_perform_ct_kill_task(priv, false); | |
f38f884b | 334 | IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n", |
0975cc8f | 335 | tt->state); |
f38f884b | 336 | IWL_DEBUG_TEMP(priv, "Power Index change to %u\n", |
0975cc8f WYG |
337 | tt->tt_power_mode); |
338 | } | |
b1eea297 | 339 | mutex_unlock(&priv->mutex); |
0975cc8f WYG |
340 | } |
341 | } | |
342 | ||
343 | /* | |
344 | * Advance thermal throttling | |
345 | * 1) Avoid NIC destruction due to high temperatures | |
346 | * Chip will identify dangerously high temperatures that can | |
347 | * harm the device and will power down | |
348 | * 2) Avoid the NIC power down due to high temperature | |
349 | * Throttle early enough to lower the power consumption before | |
350 | * drastic steps are needed | |
351 | * Actions include relaxing the power down sleep thresholds and | |
352 | * decreasing the number of TX streams | |
353 | * 3) Avoid throughput performance impact as much as possible | |
354 | * | |
355 | *============================================================================= | |
356 | * Condition Nxt State Condition Nxt State Condition Nxt State | |
357 | *----------------------------------------------------------------------------- | |
358 | * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A | |
359 | * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 | |
360 | * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 | |
361 | * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 | |
362 | *============================================================================= | |
363 | */ | |
364 | static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) | |
365 | { | |
366 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
367 | int i; | |
368 | bool changed = false; | |
369 | enum iwl_tt_state old_state; | |
370 | struct iwl_tt_trans *transaction; | |
371 | ||
372 | old_state = tt->state; | |
373 | for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { | |
374 | /* based on the current TT state, | |
375 | * find the curresponding transaction table | |
376 | * each table has (IWL_TI_STATE_MAX - 1) entries | |
377 | * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) | |
378 | * will advance to the correct table. | |
379 | * then based on the current temperature | |
380 | * find the next state need to transaction to | |
381 | * go through all the possible (IWL_TI_STATE_MAX - 1) entries | |
382 | * in the current table to see if transaction is needed | |
383 | */ | |
384 | transaction = tt->transaction + | |
385 | ((old_state * (IWL_TI_STATE_MAX - 1)) + i); | |
386 | if (temp >= transaction->tt_low && | |
387 | temp <= transaction->tt_high) { | |
388 | #ifdef CONFIG_IWLWIFI_DEBUG | |
389 | if ((tt->tt_previous_temp) && | |
390 | (temp > tt->tt_previous_temp) && | |
391 | ((temp - tt->tt_previous_temp) > | |
392 | IWL_TT_INCREASE_MARGIN)) { | |
f38f884b | 393 | IWL_DEBUG_TEMP(priv, |
0975cc8f WYG |
394 | "Temperature increase %d " |
395 | "degree Celsius\n", | |
396 | (temp - tt->tt_previous_temp)); | |
397 | } | |
398 | tt->tt_previous_temp = temp; | |
399 | #endif | |
400 | if (old_state != | |
401 | transaction->next_state) { | |
402 | changed = true; | |
403 | tt->state = | |
404 | transaction->next_state; | |
405 | } | |
406 | break; | |
407 | } | |
408 | } | |
409 | /* stop ct_kill_waiting_tm timer */ | |
410 | del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); | |
411 | if (changed) { | |
0975cc8f WYG |
412 | if (tt->state >= IWL_TI_1) { |
413 | /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ | |
414 | tt->tt_power_mode = IWL_POWER_INDEX_5; | |
246ed355 JB |
415 | |
416 | if (!iwl_ht_enabled(priv)) { | |
417 | struct iwl_rxon_context *ctx; | |
418 | ||
419 | for_each_context(priv, ctx) { | |
420 | struct iwl_rxon_cmd *rxon; | |
421 | ||
422 | rxon = &ctx->staging; | |
423 | ||
424 | /* disable HT */ | |
425 | rxon->flags &= ~( | |
426 | RXON_FLG_CHANNEL_MODE_MSK | | |
427 | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | | |
428 | RXON_FLG_HT40_PROT_MSK | | |
429 | RXON_FLG_HT_PROT_MSK); | |
430 | } | |
431 | } else { | |
0975cc8f WYG |
432 | /* check HT capability and set |
433 | * according to the system HT capability | |
434 | * in case get disabled before */ | |
435 | iwl_set_rxon_ht(priv, &priv->current_ht_config); | |
436 | } | |
437 | ||
438 | } else { | |
439 | /* | |
440 | * restore system power setting -- it will be | |
441 | * recalculated automatically. | |
442 | */ | |
443 | ||
444 | /* check HT capability and set | |
445 | * according to the system HT capability | |
446 | * in case get disabled before */ | |
447 | iwl_set_rxon_ht(priv, &priv->current_ht_config); | |
448 | } | |
b1eea297 | 449 | mutex_lock(&priv->mutex); |
0975cc8f | 450 | if (old_state == IWL_TI_CT_KILL) |
9a716863 | 451 | clear_bit(STATUS_CT_KILL, &priv->status); |
0975cc8f WYG |
452 | if (tt->state != IWL_TI_CT_KILL && |
453 | iwl_power_update_mode(priv, true)) { | |
454 | /* TT state not updated | |
455 | * try again during next temperature read | |
456 | */ | |
457 | IWL_ERR(priv, "Cannot update power mode, " | |
458 | "TT state not updated\n"); | |
459 | if (old_state == IWL_TI_CT_KILL) | |
9a716863 | 460 | set_bit(STATUS_CT_KILL, &priv->status); |
0975cc8f WYG |
461 | tt->state = old_state; |
462 | } else { | |
f38f884b | 463 | IWL_DEBUG_TEMP(priv, |
0975cc8f WYG |
464 | "Thermal Throttling to new state: %u\n", |
465 | tt->state); | |
466 | if (old_state != IWL_TI_CT_KILL && | |
467 | tt->state == IWL_TI_CT_KILL) { | |
468 | if (force) { | |
f38f884b | 469 | IWL_DEBUG_TEMP(priv, |
0975cc8f | 470 | "Enter IWL_TI_CT_KILL\n"); |
9a716863 | 471 | set_bit(STATUS_CT_KILL, &priv->status); |
0975cc8f WYG |
472 | iwl_perform_ct_kill_task(priv, true); |
473 | } else { | |
0975cc8f | 474 | tt->state = old_state; |
3104ba15 | 475 | iwl_prepare_ct_kill_task(priv); |
0975cc8f WYG |
476 | } |
477 | } else if (old_state == IWL_TI_CT_KILL && | |
478 | tt->state != IWL_TI_CT_KILL) { | |
f38f884b | 479 | IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n"); |
0975cc8f WYG |
480 | iwl_perform_ct_kill_task(priv, false); |
481 | } | |
482 | } | |
b1eea297 | 483 | mutex_unlock(&priv->mutex); |
0975cc8f WYG |
484 | } |
485 | } | |
486 | ||
487 | /* Card State Notification indicated reach critical temperature | |
488 | * if PSP not enable, no Thermal Throttling function will be performed | |
489 | * just set the GP1 bit to acknowledge the event | |
490 | * otherwise, go into IWL_TI_CT_KILL state | |
491 | * since Card State Notification will not provide any temperature reading | |
492 | * for Legacy mode | |
493 | * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() | |
494 | * for advance mode | |
495 | * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state | |
496 | */ | |
497 | static void iwl_bg_ct_enter(struct work_struct *work) | |
498 | { | |
499 | struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); | |
500 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
501 | ||
83626404 | 502 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
0975cc8f WYG |
503 | return; |
504 | ||
83626404 | 505 | if (!iwl_is_ready(priv)) |
0975cc8f WYG |
506 | return; |
507 | ||
508 | if (tt->state != IWL_TI_CT_KILL) { | |
509 | IWL_ERR(priv, "Device reached critical temperature " | |
510 | "- ucode going to sleep!\n"); | |
511 | if (!priv->thermal_throttle.advanced_tt) | |
512 | iwl_legacy_tt_handler(priv, | |
513 | IWL_MINIMAL_POWER_THRESHOLD, | |
514 | true); | |
515 | else | |
516 | iwl_advance_tt_handler(priv, | |
517 | CT_KILL_THRESHOLD + 1, true); | |
518 | } | |
519 | } | |
520 | ||
521 | /* Card State Notification indicated out of critical temperature | |
522 | * since Card State Notification will not provide any temperature reading | |
523 | * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature | |
524 | * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state | |
525 | */ | |
526 | static void iwl_bg_ct_exit(struct work_struct *work) | |
527 | { | |
528 | struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); | |
529 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
530 | ||
83626404 | 531 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
0975cc8f WYG |
532 | return; |
533 | ||
83626404 | 534 | if (!iwl_is_ready(priv)) |
0975cc8f WYG |
535 | return; |
536 | ||
537 | /* stop ct_kill_exit_tm timer */ | |
538 | del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); | |
539 | ||
540 | if (tt->state == IWL_TI_CT_KILL) { | |
541 | IWL_ERR(priv, | |
542 | "Device temperature below critical" | |
543 | "- ucode awake!\n"); | |
544 | /* | |
545 | * exit from CT_KILL state | |
546 | * reset the current temperature reading | |
547 | */ | |
548 | priv->temperature = 0; | |
549 | if (!priv->thermal_throttle.advanced_tt) | |
550 | iwl_legacy_tt_handler(priv, | |
551 | IWL_REDUCED_PERFORMANCE_THRESHOLD_2, | |
552 | true); | |
553 | else | |
554 | iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD, | |
555 | true); | |
556 | } | |
557 | } | |
558 | ||
559 | void iwl_tt_enter_ct_kill(struct iwl_priv *priv) | |
560 | { | |
83626404 | 561 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
0975cc8f WYG |
562 | return; |
563 | ||
f38f884b | 564 | IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n"); |
1ee158d8 | 565 | queue_work(priv->workqueue, &priv->ct_enter); |
0975cc8f | 566 | } |
0975cc8f WYG |
567 | |
568 | void iwl_tt_exit_ct_kill(struct iwl_priv *priv) | |
569 | { | |
83626404 | 570 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
0975cc8f WYG |
571 | return; |
572 | ||
f38f884b | 573 | IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n"); |
1ee158d8 | 574 | queue_work(priv->workqueue, &priv->ct_exit); |
0975cc8f | 575 | } |
0975cc8f WYG |
576 | |
577 | static void iwl_bg_tt_work(struct work_struct *work) | |
578 | { | |
579 | struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); | |
580 | s32 temp = priv->temperature; /* degrees CELSIUS except specified */ | |
581 | ||
83626404 | 582 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
0975cc8f WYG |
583 | return; |
584 | ||
0975cc8f WYG |
585 | if (!priv->thermal_throttle.advanced_tt) |
586 | iwl_legacy_tt_handler(priv, temp, false); | |
587 | else | |
588 | iwl_advance_tt_handler(priv, temp, false); | |
589 | } | |
590 | ||
591 | void iwl_tt_handler(struct iwl_priv *priv) | |
592 | { | |
83626404 | 593 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
0975cc8f WYG |
594 | return; |
595 | ||
f38f884b | 596 | IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n"); |
1ee158d8 | 597 | queue_work(priv->workqueue, &priv->tt_work); |
0975cc8f | 598 | } |
0975cc8f WYG |
599 | |
600 | /* Thermal throttling initialization | |
601 | * For advance thermal throttling: | |
602 | * Initialize Thermal Index and temperature threshold table | |
603 | * Initialize thermal throttling restriction table | |
604 | */ | |
605 | void iwl_tt_initialize(struct iwl_priv *priv) | |
606 | { | |
607 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
608 | int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); | |
609 | struct iwl_tt_trans *transaction; | |
610 | ||
f38f884b | 611 | IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n"); |
0975cc8f WYG |
612 | |
613 | memset(tt, 0, sizeof(struct iwl_tt_mgmt)); | |
614 | ||
615 | tt->state = IWL_TI_0; | |
616 | init_timer(&priv->thermal_throttle.ct_kill_exit_tm); | |
617 | priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; | |
618 | priv->thermal_throttle.ct_kill_exit_tm.function = | |
619 | iwl_tt_check_exit_ct_kill; | |
620 | init_timer(&priv->thermal_throttle.ct_kill_waiting_tm); | |
621 | priv->thermal_throttle.ct_kill_waiting_tm.data = | |
622 | (unsigned long)priv; | |
623 | priv->thermal_throttle.ct_kill_waiting_tm.function = | |
624 | iwl_tt_ready_for_ct_kill; | |
625 | /* setup deferred ct kill work */ | |
626 | INIT_WORK(&priv->tt_work, iwl_bg_tt_work); | |
627 | INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); | |
628 | INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); | |
629 | ||
0d8877a1 | 630 | if (priv->lib->adv_thermal_throttle) { |
f38f884b | 631 | IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); |
7f90dce1 EG |
632 | tt->restriction = kcalloc(IWL_TI_STATE_MAX, |
633 | sizeof(struct iwl_tt_restriction), | |
634 | GFP_KERNEL); | |
635 | tt->transaction = kcalloc(IWL_TI_STATE_MAX * | |
636 | (IWL_TI_STATE_MAX - 1), | |
637 | sizeof(struct iwl_tt_trans), | |
638 | GFP_KERNEL); | |
0975cc8f WYG |
639 | if (!tt->restriction || !tt->transaction) { |
640 | IWL_ERR(priv, "Fallback to Legacy Throttling\n"); | |
641 | priv->thermal_throttle.advanced_tt = false; | |
642 | kfree(tt->restriction); | |
643 | tt->restriction = NULL; | |
644 | kfree(tt->transaction); | |
645 | tt->transaction = NULL; | |
646 | } else { | |
647 | transaction = tt->transaction + | |
648 | (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); | |
649 | memcpy(transaction, &tt_range_0[0], size); | |
650 | transaction = tt->transaction + | |
651 | (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); | |
652 | memcpy(transaction, &tt_range_1[0], size); | |
653 | transaction = tt->transaction + | |
654 | (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); | |
655 | memcpy(transaction, &tt_range_2[0], size); | |
656 | transaction = tt->transaction + | |
657 | (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); | |
658 | memcpy(transaction, &tt_range_3[0], size); | |
659 | size = sizeof(struct iwl_tt_restriction) * | |
660 | IWL_TI_STATE_MAX; | |
661 | memcpy(tt->restriction, | |
662 | &restriction_range[0], size); | |
663 | priv->thermal_throttle.advanced_tt = true; | |
664 | } | |
665 | } else { | |
f38f884b | 666 | IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n"); |
0975cc8f WYG |
667 | priv->thermal_throttle.advanced_tt = false; |
668 | } | |
669 | } | |
0975cc8f WYG |
670 | |
671 | /* cleanup thermal throttling management related memory and timer */ | |
672 | void iwl_tt_exit(struct iwl_priv *priv) | |
673 | { | |
674 | struct iwl_tt_mgmt *tt = &priv->thermal_throttle; | |
675 | ||
676 | /* stop ct_kill_exit_tm timer if activated */ | |
677 | del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); | |
678 | /* stop ct_kill_waiting_tm timer if activated */ | |
679 | del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); | |
680 | cancel_work_sync(&priv->tt_work); | |
681 | cancel_work_sync(&priv->ct_enter); | |
682 | cancel_work_sync(&priv->ct_exit); | |
683 | ||
684 | if (priv->thermal_throttle.advanced_tt) { | |
685 | /* free advance thermal throttling memory */ | |
686 | kfree(tt->restriction); | |
687 | tt->restriction = NULL; | |
688 | kfree(tt->transaction); | |
689 | tt->transaction = NULL; | |
690 | } | |
691 | } |