iwlwifi: Thermal Throttling Management - Part 1

Part 1 of Thermal Throttling Management -

Thermal Throttling feature is used to put NIC into low power state when
driver detect the Radio temperature reach pre-defined threshold

Two Thermal Throttling Management Methods; this patch introduce the
Legacy Thermal Management:
   IWL_TI_0: normal temperature, system power state
   IWL_TI_1: high temperature detect, low power state
   IWL_TI_2: higher temperature detected, lower power state
   IWL_TI_CT_KILL: critical temperature detected, lowest power state

Once get into CT_KILL state, uCode go into sleep, driver will stop all
the active queues, then move to IWL_TI_CT_KILL state; also set up 5
seconds timer to toggle CSR flag, uCode wake up upon CSR flag change,
then measure the temperature.
If temperature is above CT_KILL exit threshold, uCode go backto sleep;
if temperature is below CT_KILL exit threshold, uCode send Card State
Notification response with appropriate CT_KILL status flag, and uCode
remain awake, Driver receive Card State Notification Response and update
the card temperature to the CT_KILL exit threshold.

Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Wey-Yi Guy
2009-07-24 11:13:02 -07:00
committed by John W. Linville
父節點 672639de13
當前提交 39b73fb15e
共有 6 個文件被更改,包括 301 次插入12 次删除

查看文件

@@ -36,6 +36,7 @@
#include "iwl-eeprom.h"
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
#include "iwl-commands.h"
#include "iwl-debug.h"
#include "iwl-power.h"
@@ -211,6 +212,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
{
struct iwl_power_mgr *setting = &(priv->power_data);
int ret = 0;
struct iwl_tt_mgmt *tt = &priv->power_data.tt;
u16 uninitialized_var(final_mode);
bool update_chains;
@@ -223,6 +225,10 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
if (setting->power_disabled)
final_mode = IWL_POWER_MODE_CAM;
if (tt->state >= IWL_TI_1) {
/* TT power setting overwrite user & system power setting */
final_mode = tt->tt_power_mode;
}
if (iwl_is_ready_rf(priv) &&
((setting->power_mode != final_mode) || force)) {
struct iwl_powertable_cmd cmd;
@@ -267,6 +273,249 @@ int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode)
}
EXPORT_SYMBOL(iwl_power_set_user_mode);
#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */
/*
* toggle the bit to wake up uCode and check the temperature
* if the temperature is below CT, uCode will stay awake and send card
* state notification with CT_KILL bit clear to inform Thermal Throttling
* Management to change state. Otherwise, uCode will go back to sleep
* without doing anything, driver should continue the 5 seconds timer
* to wake up uCode for temperature check until temperature drop below CT
*/
static void iwl_tt_check_exit_ct_kill(unsigned long data)
{
struct iwl_priv *priv = (struct iwl_priv *)data;
struct iwl_tt_mgmt *tt = &priv->power_data.tt;
unsigned long flags;
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
if (tt->state == IWL_TI_CT_KILL) {
if (priv->power_data.ct_kill_toggle) {
iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
priv->power_data.ct_kill_toggle = false;
} else {
iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
priv->power_data.ct_kill_toggle = true;
}
iwl_read32(priv, CSR_UCODE_DRV_GP1);
spin_lock_irqsave(&priv->reg_lock, flags);
if (!iwl_grab_nic_access(priv))
iwl_release_nic_access(priv);
spin_unlock_irqrestore(&priv->reg_lock, flags);
/* Reschedule the ct_kill timer to occur in
* CT_KILL_EXIT_DURATION seconds to ensure we get a
* thermal update */
mod_timer(&priv->power_data.ct_kill_exit_tm, jiffies +
CT_KILL_EXIT_DURATION * HZ);
}
}
static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
bool stop)
{
if (stop) {
IWL_DEBUG_POWER(priv, "Stop all queues\n");
if (priv->mac80211_registered)
ieee80211_stop_queues(priv->hw);
IWL_DEBUG_POWER(priv,
"Schedule 5 seconds CT_KILL Timer\n");
mod_timer(&priv->power_data.ct_kill_exit_tm, jiffies +
CT_KILL_EXIT_DURATION * HZ);
} else {
IWL_DEBUG_POWER(priv, "Wake all queues\n");
if (priv->mac80211_registered)
ieee80211_wake_queues(priv->hw);
}
}
#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY)
#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100)
#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90)
/*
* Legacy thermal throttling
* 1) Avoid NIC destruction due to high temperatures
* Chip will identify dangerously high temperatures that can
* harm the device and will power down
* 2) Avoid the NIC power down due to high temperature
* Throttle early enough to lower the power consumption before
* drastic steps are needed
*/
static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp)
{
struct iwl_tt_mgmt *tt = &priv->power_data.tt;
enum iwl_tt_state new_state;
struct iwl_power_mgr *setting = &priv->power_data;
#ifdef CONFIG_IWLWIFI_DEBUG
if ((tt->tt_previous_temp) &&
(temp > tt->tt_previous_temp) &&
((temp - tt->tt_previous_temp) >
IWL_TT_INCREASE_MARGIN)) {
IWL_DEBUG_POWER(priv,
"Temperature increase %d degree Celsius\n",
(temp - tt->tt_previous_temp));
}
#endif
/* in Celsius */
if (temp >= IWL_MINIMAL_POWER_THRESHOLD)
new_state = IWL_TI_CT_KILL;
else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2)
new_state = IWL_TI_2;
else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1)
new_state = IWL_TI_1;
else
new_state = IWL_TI_0;
#ifdef CONFIG_IWLWIFI_DEBUG
tt->tt_previous_temp = temp;
#endif
if (tt->state != new_state) {
if (tt->state == IWL_TI_0) {
tt->sys_power_mode = setting->power_mode;
IWL_DEBUG_POWER(priv, "current power mode: %u\n",
setting->power_mode);
}
switch (new_state) {
case IWL_TI_0:
/* when system ready to go back to IWL_TI_0 state
* using system power mode instead of TT power mode
* revert back to the orginal power mode which was saved
* before enter Thermal Throttling state
* update priv->power_data.user_power_setting to the
* required power mode to make sure
* iwl_power_update_mode() will update power correctly.
*/
priv->power_data.user_power_setting =
tt->sys_power_mode;
tt->tt_power_mode = tt->sys_power_mode;
break;
case IWL_TI_1:
tt->tt_power_mode = IWL_POWER_INDEX_3;
break;
case IWL_TI_2:
tt->tt_power_mode = IWL_POWER_INDEX_4;
break;
default:
tt->tt_power_mode = IWL_POWER_INDEX_5;
break;
}
if (iwl_power_update_mode(priv, true)) {
/* TT state not updated
* try again during next temperature read
*/
IWL_ERR(priv, "Cannot update power mode, "
"TT state not updated\n");
} else {
if (new_state == IWL_TI_CT_KILL)
iwl_perform_ct_kill_task(priv, true);
else if (tt->state == IWL_TI_CT_KILL &&
new_state != IWL_TI_CT_KILL)
iwl_perform_ct_kill_task(priv, false);
tt->state = new_state;
IWL_DEBUG_POWER(priv, "Temperature state changed %u\n",
tt->state);
IWL_DEBUG_POWER(priv, "Power Index change to %u\n",
tt->tt_power_mode);
}
}
}
/* Card State Notification indicated reach critical temperature
* if PSP not enable, no Thermal Throttling function will be performed
* just set the GP1 bit to acknowledge the event
* otherwise, go into IWL_TI_CT_KILL state
* since Card State Notification will not provide any temperature reading
* so just pass the CT_KILL temperature to iwl_legacy_tt_handler()
*/
void iwl_tt_enter_ct_kill(struct iwl_priv *priv)
{
struct iwl_tt_mgmt *tt = &priv->power_data.tt;
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
if (tt->state != IWL_TI_CT_KILL) {
IWL_ERR(priv, "Device reached critical temperature "
"- ucode going to sleep!\n");
iwl_legacy_tt_handler(priv, IWL_MINIMAL_POWER_THRESHOLD);
}
}
EXPORT_SYMBOL(iwl_tt_enter_ct_kill);
/* Card State Notification indicated out of critical temperature
* since Card State Notification will not provide any temperature reading
* so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature
* to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state
*/
void iwl_tt_exit_ct_kill(struct iwl_priv *priv)
{
struct iwl_tt_mgmt *tt = &priv->power_data.tt;
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
/* stop ct_kill_exit_tm timer */
del_timer_sync(&priv->power_data.ct_kill_exit_tm);
if (tt->state == IWL_TI_CT_KILL) {
IWL_ERR(priv,
"Device temperature below critical"
"- ucode awake!\n");
iwl_legacy_tt_handler(priv,
IWL_REDUCED_PERFORMANCE_THRESHOLD_2);
}
}
EXPORT_SYMBOL(iwl_tt_exit_ct_kill);
void iwl_tt_handler(struct iwl_priv *priv)
{
s32 temp = priv->temperature; /* degrees CELSIUS except 4965 */
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965)
temp = KELVIN_TO_CELSIUS(priv->temperature);
iwl_legacy_tt_handler(priv, temp);
}
EXPORT_SYMBOL(iwl_tt_handler);
/* Thermal throttling initialization
*/
void iwl_tt_initialize(struct iwl_priv *priv)
{
struct iwl_tt_mgmt *tt = &priv->power_data.tt;
struct iwl_power_mgr *setting = &priv->power_data;
IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling \n");
memset(tt, 0, sizeof(struct iwl_tt_mgmt));
tt->state = IWL_TI_0;
tt->sys_power_mode = setting->power_mode;
tt->tt_power_mode = tt->sys_power_mode;
init_timer(&priv->power_data.ct_kill_exit_tm);
priv->power_data.ct_kill_exit_tm.data = (unsigned long)priv;
priv->power_data.ct_kill_exit_tm.function = iwl_tt_check_exit_ct_kill;
}
EXPORT_SYMBOL(iwl_tt_initialize);
/* cleanup thermal throttling management related memory and timer */
void iwl_tt_exit(struct iwl_priv *priv)
{
/* stop ct_kill_exit_tm timer if activated */
del_timer_sync(&priv->power_data.ct_kill_exit_tm);
}
EXPORT_SYMBOL(iwl_tt_exit);
/* initialize to default */
void iwl_power_initialize(struct iwl_priv *priv)
{