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