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:
parent
672639de13
commit
39b73fb15e
6 changed files with 301 additions and 12 deletions
|
@ -1797,6 +1797,7 @@ static void iwl4965_temperature_calib(struct iwl_priv *priv)
|
|||
}
|
||||
|
||||
priv->temperature = temp;
|
||||
iwl_tt_handler(priv);
|
||||
set_bit(STATUS_TEMPERATURE, &priv->status);
|
||||
|
||||
if (!priv->disable_tx_power_cal &&
|
||||
|
|
|
@ -1405,6 +1405,7 @@ void iwl5000_temperature(struct iwl_priv *priv)
|
|||
{
|
||||
/* store temperature from statistics (in Celsius) */
|
||||
priv->temperature = le32_to_cpu(priv->statistics.general.temperature);
|
||||
iwl_tt_handler(priv);
|
||||
}
|
||||
|
||||
static void iwl5150_temperature(struct iwl_priv *priv)
|
||||
|
|
|
@ -637,7 +637,6 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
|
|||
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
|
||||
u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
|
||||
unsigned long status = priv->status;
|
||||
unsigned long reg_flags;
|
||||
|
||||
IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n",
|
||||
(flags & HW_CARD_DISABLED) ? "Kill" : "On",
|
||||
|
@ -657,19 +656,12 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
|
|||
CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
|
||||
iwl_write_direct32(priv, HBUS_TARG_MBX_C,
|
||||
HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
|
||||
|
||||
}
|
||||
|
||||
if (flags & RF_CARD_DISABLED) {
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
if (!iwl_grab_nic_access(priv))
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
if (flags & RF_CARD_DISABLED)
|
||||
iwl_tt_enter_ct_kill(priv);
|
||||
}
|
||||
if (!(flags & RF_CARD_DISABLED))
|
||||
iwl_tt_exit_ct_kill(priv);
|
||||
|
||||
if (flags & HW_CARD_DISABLED)
|
||||
set_bit(STATUS_RF_KILL_HW, &priv->status);
|
||||
|
@ -3015,6 +3007,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
test_bit(STATUS_RF_KILL_HW, &priv->status));
|
||||
|
||||
iwl_power_initialize(priv);
|
||||
iwl_tt_initialize(priv);
|
||||
return 0;
|
||||
|
||||
out_remove_sysfs:
|
||||
|
@ -3067,6 +3060,8 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
|
|||
iwl_down(priv);
|
||||
}
|
||||
|
||||
iwl_tt_exit(priv);
|
||||
|
||||
/* make sure we flush any pending irq or
|
||||
* tasklet for the driver
|
||||
*/
|
||||
|
|
|
@ -2232,6 +2232,7 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv)
|
|||
iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
priv->power_data.ct_kill_toggle = false;
|
||||
|
||||
switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
|
||||
case CSR_HW_REV_TYPE_1000:
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -33,6 +33,38 @@
|
|||
|
||||
struct iwl_priv;
|
||||
|
||||
#define IWL_TT_INCREASE_MARGIN 5
|
||||
|
||||
/* Thermal Throttling State Machine states */
|
||||
enum iwl_tt_state {
|
||||
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 */
|
||||
IWL_TI_STATE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tt_mgnt - Thermal Throttling Management structure
|
||||
* @state: current Thermal Throttling state
|
||||
* @tt_power_mode: Thermal Throttling power mode index
|
||||
* being used to set power level when
|
||||
* when thermal throttling state != IWL_TI_0
|
||||
* the tt_power_mode should set to different
|
||||
* power mode based on the current tt state
|
||||
* @sys_power_mode: previous system power mode
|
||||
* before transition into TT state
|
||||
* @tt_previous_temperature: last measured temperature
|
||||
*/
|
||||
struct iwl_tt_mgmt {
|
||||
enum iwl_tt_state state;
|
||||
u8 tt_power_mode;
|
||||
u8 sys_power_mode;
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
s32 tt_previous_temp;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum {
|
||||
IWL_POWER_MODE_CAM, /* Continuously Aware Mode, always on */
|
||||
IWL_POWER_INDEX_1,
|
||||
|
@ -59,10 +91,20 @@ struct iwl_power_mgr {
|
|||
u8 power_mode;
|
||||
u8 user_power_setting; /* set by user through sysfs */
|
||||
u8 power_disabled; /* set by mac80211's CONF_PS */
|
||||
struct iwl_tt_mgmt tt; /* Thermal Throttling Management */
|
||||
bool ct_kill_toggle; /* use to toggle the CSR bit when
|
||||
* checking uCode temperature
|
||||
*/
|
||||
struct timer_list ct_kill_exit_tm;
|
||||
};
|
||||
|
||||
int iwl_power_update_mode(struct iwl_priv *priv, bool force);
|
||||
int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode);
|
||||
void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
|
||||
void iwl_tt_exit_ct_kill(struct iwl_priv *priv);
|
||||
void iwl_tt_handler(struct iwl_priv *priv);
|
||||
void iwl_tt_initialize(struct iwl_priv *priv);
|
||||
void iwl_tt_exit(struct iwl_priv *priv);
|
||||
void iwl_power_initialize(struct iwl_priv *priv);
|
||||
|
||||
#endif /* __iwl_power_setting_h__ */
|
||||
|
|
Loading…
Reference in a new issue