aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-power.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-power.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-power.c249
1 files changed, 249 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c
index f2ea3f05f6e1..d7fdb5825450 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.c
+++ b/drivers/net/wireless/iwlwifi/iwl-power.c
@@ -36,6 +36,7 @@
36#include "iwl-eeprom.h" 36#include "iwl-eeprom.h"
37#include "iwl-dev.h" 37#include "iwl-dev.h"
38#include "iwl-core.h" 38#include "iwl-core.h"
39#include "iwl-io.h"
39#include "iwl-commands.h" 40#include "iwl-commands.h"
40#include "iwl-debug.h" 41#include "iwl-debug.h"
41#include "iwl-power.h" 42#include "iwl-power.h"
@@ -211,6 +212,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
211{ 212{
212 struct iwl_power_mgr *setting = &(priv->power_data); 213 struct iwl_power_mgr *setting = &(priv->power_data);
213 int ret = 0; 214 int ret = 0;
215 struct iwl_tt_mgmt *tt = &priv->power_data.tt;
214 u16 uninitialized_var(final_mode); 216 u16 uninitialized_var(final_mode);
215 bool update_chains; 217 bool update_chains;
216 218
@@ -223,6 +225,10 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
223 if (setting->power_disabled) 225 if (setting->power_disabled)
224 final_mode = IWL_POWER_MODE_CAM; 226 final_mode = IWL_POWER_MODE_CAM;
225 227
228 if (tt->state >= IWL_TI_1) {
229 /* TT power setting overwrite user & system power setting */
230 final_mode = tt->tt_power_mode;
231 }
226 if (iwl_is_ready_rf(priv) && 232 if (iwl_is_ready_rf(priv) &&
227 ((setting->power_mode != final_mode) || force)) { 233 ((setting->power_mode != final_mode) || force)) {
228 struct iwl_powertable_cmd cmd; 234 struct iwl_powertable_cmd cmd;
@@ -267,6 +273,249 @@ int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode)
267} 273}
268EXPORT_SYMBOL(iwl_power_set_user_mode); 274EXPORT_SYMBOL(iwl_power_set_user_mode);
269 275
276#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */
277
278/*
279 * toggle the bit to wake up uCode and check the temperature
280 * if the temperature is below CT, uCode will stay awake and send card
281 * state notification with CT_KILL bit clear to inform Thermal Throttling
282 * Management to change state. Otherwise, uCode will go back to sleep
283 * without doing anything, driver should continue the 5 seconds timer
284 * to wake up uCode for temperature check until temperature drop below CT
285 */
286static void iwl_tt_check_exit_ct_kill(unsigned long data)
287{
288 struct iwl_priv *priv = (struct iwl_priv *)data;
289 struct iwl_tt_mgmt *tt = &priv->power_data.tt;
290 unsigned long flags;
291
292 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
293 return;
294
295 if (tt->state == IWL_TI_CT_KILL) {
296 if (priv->power_data.ct_kill_toggle) {
297 iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
298 CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
299 priv->power_data.ct_kill_toggle = false;
300 } else {
301 iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
302 CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
303 priv->power_data.ct_kill_toggle = true;
304 }
305 iwl_read32(priv, CSR_UCODE_DRV_GP1);
306 spin_lock_irqsave(&priv->reg_lock, flags);
307 if (!iwl_grab_nic_access(priv))
308 iwl_release_nic_access(priv);
309 spin_unlock_irqrestore(&priv->reg_lock, flags);
310
311 /* Reschedule the ct_kill timer to occur in
312 * CT_KILL_EXIT_DURATION seconds to ensure we get a
313 * thermal update */
314 mod_timer(&priv->power_data.ct_kill_exit_tm, jiffies +
315 CT_KILL_EXIT_DURATION * HZ);
316 }
317}
318
319static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
320 bool stop)
321{
322 if (stop) {
323 IWL_DEBUG_POWER(priv, "Stop all queues\n");
324 if (priv->mac80211_registered)
325 ieee80211_stop_queues(priv->hw);
326 IWL_DEBUG_POWER(priv,
327 "Schedule 5 seconds CT_KILL Timer\n");
328 mod_timer(&priv->power_data.ct_kill_exit_tm, jiffies +
329 CT_KILL_EXIT_DURATION * HZ);
330 } else {
331 IWL_DEBUG_POWER(priv, "Wake all queues\n");
332 if (priv->mac80211_registered)
333 ieee80211_wake_queues(priv->hw);
334 }
335}
336
337#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY)
338#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100)
339#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90)
340
341/*
342 * Legacy thermal throttling
343 * 1) Avoid NIC destruction due to high temperatures
344 * Chip will identify dangerously high temperatures that can
345 * harm the device and will power down
346 * 2) Avoid the NIC power down due to high temperature
347 * Throttle early enough to lower the power consumption before
348 * drastic steps are needed
349 */
350static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp)
351{
352 struct iwl_tt_mgmt *tt = &priv->power_data.tt;
353 enum iwl_tt_state new_state;
354 struct iwl_power_mgr *setting = &priv->power_data;
355
356#ifdef CONFIG_IWLWIFI_DEBUG
357 if ((tt->tt_previous_temp) &&
358 (temp > tt->tt_previous_temp) &&
359 ((temp - tt->tt_previous_temp) >
360 IWL_TT_INCREASE_MARGIN)) {
361 IWL_DEBUG_POWER(priv,
362 "Temperature increase %d degree Celsius\n",
363 (temp - tt->tt_previous_temp));
364 }
365#endif
366 /* in Celsius */
367 if (temp >= IWL_MINIMAL_POWER_THRESHOLD)
368 new_state = IWL_TI_CT_KILL;
369 else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2)
370 new_state = IWL_TI_2;
371 else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1)
372 new_state = IWL_TI_1;
373 else
374 new_state = IWL_TI_0;
375
376#ifdef CONFIG_IWLWIFI_DEBUG
377 tt->tt_previous_temp = temp;
378#endif
379 if (tt->state != new_state) {
380 if (tt->state == IWL_TI_0) {
381 tt->sys_power_mode = setting->power_mode;
382 IWL_DEBUG_POWER(priv, "current power mode: %u\n",
383 setting->power_mode);
384 }
385 switch (new_state) {
386 case IWL_TI_0:
387 /* when system ready to go back to IWL_TI_0 state
388 * using system power mode instead of TT power mode
389 * revert back to the orginal power mode which was saved
390 * before enter Thermal Throttling state
391 * update priv->power_data.user_power_setting to the
392 * required power mode to make sure
393 * iwl_power_update_mode() will update power correctly.
394 */
395 priv->power_data.user_power_setting =
396 tt->sys_power_mode;
397 tt->tt_power_mode = tt->sys_power_mode;
398 break;
399 case IWL_TI_1:
400 tt->tt_power_mode = IWL_POWER_INDEX_3;
401 break;
402 case IWL_TI_2:
403 tt->tt_power_mode = IWL_POWER_INDEX_4;
404 break;
405 default:
406 tt->tt_power_mode = IWL_POWER_INDEX_5;
407 break;
408 }
409 if (iwl_power_update_mode(priv, true)) {
410 /* TT state not updated
411 * try again during next temperature read
412 */
413 IWL_ERR(priv, "Cannot update power mode, "
414 "TT state not updated\n");
415 } else {
416 if (new_state == IWL_TI_CT_KILL)
417 iwl_perform_ct_kill_task(priv, true);
418 else if (tt->state == IWL_TI_CT_KILL &&
419 new_state != IWL_TI_CT_KILL)
420 iwl_perform_ct_kill_task(priv, false);
421 tt->state = new_state;
422 IWL_DEBUG_POWER(priv, "Temperature state changed %u\n",
423 tt->state);
424 IWL_DEBUG_POWER(priv, "Power Index change to %u\n",
425 tt->tt_power_mode);
426 }
427 }
428}
429
430/* Card State Notification indicated reach critical temperature
431 * if PSP not enable, no Thermal Throttling function will be performed
432 * just set the GP1 bit to acknowledge the event
433 * otherwise, go into IWL_TI_CT_KILL state
434 * since Card State Notification will not provide any temperature reading
435 * so just pass the CT_KILL temperature to iwl_legacy_tt_handler()
436 */
437void iwl_tt_enter_ct_kill(struct iwl_priv *priv)
438{
439 struct iwl_tt_mgmt *tt = &priv->power_data.tt;
440
441 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
442 return;
443
444 if (tt->state != IWL_TI_CT_KILL) {
445 IWL_ERR(priv, "Device reached critical temperature "
446 "- ucode going to sleep!\n");
447 iwl_legacy_tt_handler(priv, IWL_MINIMAL_POWER_THRESHOLD);
448 }
449}
450EXPORT_SYMBOL(iwl_tt_enter_ct_kill);
451
452/* Card State Notification indicated out of critical temperature
453 * since Card State Notification will not provide any temperature reading
454 * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature
455 * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state
456 */
457void iwl_tt_exit_ct_kill(struct iwl_priv *priv)
458{
459 struct iwl_tt_mgmt *tt = &priv->power_data.tt;
460
461 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
462 return;
463
464 /* stop ct_kill_exit_tm timer */
465 del_timer_sync(&priv->power_data.ct_kill_exit_tm);
466
467 if (tt->state == IWL_TI_CT_KILL) {
468 IWL_ERR(priv,
469 "Device temperature below critical"
470 "- ucode awake!\n");
471 iwl_legacy_tt_handler(priv,
472 IWL_REDUCED_PERFORMANCE_THRESHOLD_2);
473 }
474}
475EXPORT_SYMBOL(iwl_tt_exit_ct_kill);
476
477void iwl_tt_handler(struct iwl_priv *priv)
478{
479 s32 temp = priv->temperature; /* degrees CELSIUS except 4965 */
480
481 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
482 return;
483
484 if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965)
485 temp = KELVIN_TO_CELSIUS(priv->temperature);
486
487 iwl_legacy_tt_handler(priv, temp);
488}
489EXPORT_SYMBOL(iwl_tt_handler);
490
491/* Thermal throttling initialization
492 */
493void iwl_tt_initialize(struct iwl_priv *priv)
494{
495 struct iwl_tt_mgmt *tt = &priv->power_data.tt;
496 struct iwl_power_mgr *setting = &priv->power_data;
497
498 IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling \n");
499
500 memset(tt, 0, sizeof(struct iwl_tt_mgmt));
501
502 tt->state = IWL_TI_0;
503 tt->sys_power_mode = setting->power_mode;
504 tt->tt_power_mode = tt->sys_power_mode;
505 init_timer(&priv->power_data.ct_kill_exit_tm);
506 priv->power_data.ct_kill_exit_tm.data = (unsigned long)priv;
507 priv->power_data.ct_kill_exit_tm.function = iwl_tt_check_exit_ct_kill;
508}
509EXPORT_SYMBOL(iwl_tt_initialize);
510
511/* cleanup thermal throttling management related memory and timer */
512void iwl_tt_exit(struct iwl_priv *priv)
513{
514 /* stop ct_kill_exit_tm timer if activated */
515 del_timer_sync(&priv->power_data.ct_kill_exit_tm);
516}
517EXPORT_SYMBOL(iwl_tt_exit);
518
270/* initialize to default */ 519/* initialize to default */
271void iwl_power_initialize(struct iwl_priv *priv) 520void iwl_power_initialize(struct iwl_priv *priv)
272{ 521{