diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-power.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-power.c | 285 |
1 files changed, 281 insertions, 4 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index d7fdb5825450..00937b3ee8ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -98,6 +98,45 @@ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} }; +/* default Thermal Throttling transaction table + * Current state | Throttling Down | Throttling Up + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, + {IWL_TI_1, 105, CT_KILL_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, + {IWL_TI_2, 110, CT_KILL_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; + +/* Advance Thermal Throttling default restriction table */ +static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { + {IWL_TX_MULTI, true, IWL_RX_MULTI}, + {IWL_TX_SINGLE, true, IWL_RX_MULTI}, + {IWL_TX_SINGLE, false, IWL_RX_SINGLE}, + {IWL_TX_NONE, false, IWL_RX_NONE} +}; /* set card power command */ static int iwl_set_power(struct iwl_priv *priv, void *cmd) @@ -273,6 +312,42 @@ int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode) } EXPORT_SYMBOL(iwl_power_set_user_mode); +bool iwl_ht_enabled(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->power_data.tt; + struct iwl_tt_restriction *restriction; + + if (!priv->power_data.adv_tt) + return true; + restriction = tt->restriction + tt->state; + return restriction->is_ht; +} +EXPORT_SYMBOL(iwl_ht_enabled); + +u8 iwl_tx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->power_data.tt; + struct iwl_tt_restriction *restriction; + + if (!priv->power_data.adv_tt) + return IWL_TX_MULTI; + restriction = tt->restriction + tt->state; + return restriction->tx_stream; +} +EXPORT_SYMBOL(iwl_tx_ant_restriction); + +u8 iwl_rx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->power_data.tt; + struct iwl_tt_restriction *restriction; + + if (!priv->power_data.adv_tt) + return IWL_RX_MULTI; + restriction = tt->restriction + tt->state; + return restriction->rx_stream; +} +EXPORT_SYMBOL(iwl_rx_ant_restriction); + #define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ /* @@ -427,12 +502,147 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp) } } +/* + * Advance 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 + * Actions include relaxing the power down sleep thresholds and + * decreasing the number of TX streams + * 3) Avoid throughput performance impact as much as possible + * + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp) +{ + struct iwl_tt_mgmt *tt = &priv->power_data.tt; + int i; + bool changed = false; + enum iwl_tt_state old_state; + struct iwl_tt_trans *transaction; + + old_state = tt->state; + for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { + /* based on the current TT state, + * find the curresponding transaction table + * each table has (IWL_TI_STATE_MAX - 1) entries + * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) + * will advance to the correct table. + * then based on the current temperature + * find the next state need to transaction to + * go through all the possible (IWL_TI_STATE_MAX - 1) entries + * in the current table to see if transaction is needed + */ + transaction = tt->transaction + + ((old_state * (IWL_TI_STATE_MAX - 1)) + i); + if (temp >= transaction->tt_low && + temp <= transaction->tt_high) { +#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)); + } + tt->tt_previous_temp = temp; +#endif + if (old_state != + transaction->next_state) { + changed = true; + tt->state = + transaction->next_state; + } + break; + } + } + if (changed) { + struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + struct iwl_power_mgr *setting = &priv->power_data; + + if (tt->state >= IWL_TI_1) { + /* if switching from IWL_TI_0 to other TT state + * save previous power setting in tt->sys_power_mode */ + if (old_state == IWL_TI_0) + tt->sys_power_mode = setting->power_mode; + /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ + tt->tt_power_mode = IWL_POWER_INDEX_5; + if (!iwl_ht_enabled(priv)) + /* disable HT */ + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | + RXON_FLG_FAT_PROT_MSK | + RXON_FLG_HT_PROT_MSK); + else { + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + + } else { + /* restore system power setting */ + /* the previous power mode was saved in + * tt->sys_power_mode when system move into + * Thermal Throttling state + * set power_data.user_power_setting to the previous + * system power mode to make sure power will get + * updated correctly + */ + priv->power_data.user_power_setting = + tt->sys_power_mode; + tt->tt_power_mode = tt->sys_power_mode; + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + 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"); + tt->state = old_state; + } else { + IWL_DEBUG_POWER(priv, + "Thermal Throttling to new state: %u\n", + tt->state); + if (old_state != IWL_TI_CT_KILL && + tt->state == IWL_TI_CT_KILL) { + IWL_DEBUG_POWER(priv, "Enter IWL_TI_CT_KILL\n"); + iwl_perform_ct_kill_task(priv, true); + + } else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) { + IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n"); + iwl_perform_ct_kill_task(priv, false); + } + } + } +} + /* 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 + * for Legacy mode * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() + * for advance mode + * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state */ void iwl_tt_enter_ct_kill(struct iwl_priv *priv) { @@ -444,7 +654,12 @@ void iwl_tt_enter_ct_kill(struct iwl_priv *priv) 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); + if (!priv->power_data.adv_tt) + iwl_legacy_tt_handler(priv, + IWL_MINIMAL_POWER_THRESHOLD); + else + iwl_advance_tt_handler(priv, + CT_KILL_THRESHOLD + 1); } } EXPORT_SYMBOL(iwl_tt_enter_ct_kill); @@ -468,8 +683,11 @@ void iwl_tt_exit_ct_kill(struct iwl_priv *priv) IWL_ERR(priv, "Device temperature below critical" "- ucode awake!\n"); - iwl_legacy_tt_handler(priv, - IWL_REDUCED_PERFORMANCE_THRESHOLD_2); + if (!priv->power_data.adv_tt) + iwl_legacy_tt_handler(priv, + IWL_REDUCED_PERFORMANCE_THRESHOLD_2); + else + iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD); } } EXPORT_SYMBOL(iwl_tt_exit_ct_kill); @@ -484,16 +702,24 @@ void iwl_tt_handler(struct iwl_priv *priv) 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); + if (!priv->power_data.adv_tt) + iwl_legacy_tt_handler(priv, temp); + else + iwl_advance_tt_handler(priv, temp); } EXPORT_SYMBOL(iwl_tt_handler); /* Thermal throttling initialization + * For advance thermal throttling: + * Initialize Thermal Index and temperature threshold table + * Initialize thermal throttling restriction table */ 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; + int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); + struct iwl_tt_trans *transaction; IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling \n"); @@ -505,14 +731,65 @@ void iwl_tt_initialize(struct iwl_priv *priv) 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; + switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_6x00: + case CSR_HW_REV_TYPE_6x50: + IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n"); + tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) * + IWL_TI_STATE_MAX, GFP_KERNEL); + tt->transaction = kzalloc(sizeof(struct iwl_tt_trans) * + IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1), + GFP_KERNEL); + if (!tt->restriction || !tt->transaction) { + IWL_ERR(priv, "Fallback to Legacy Throttling\n"); + priv->power_data.adv_tt = false; + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } else { + transaction = tt->transaction + + (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_0[0], size); + transaction = tt->transaction + + (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_1[0], size); + transaction = tt->transaction + + (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_2[0], size); + transaction = tt->transaction + + (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_3[0], size); + size = sizeof(struct iwl_tt_restriction) * + IWL_TI_STATE_MAX; + memcpy(tt->restriction, + &restriction_range[0], size); + priv->power_data.adv_tt = true; + } + break; + default: + IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n"); + priv->power_data.adv_tt = false; + break; + } } EXPORT_SYMBOL(iwl_tt_initialize); /* cleanup thermal throttling management related memory and timer */ void iwl_tt_exit(struct iwl_priv *priv) { + struct iwl_tt_mgmt *tt = &priv->power_data.tt; + /* stop ct_kill_exit_tm timer if activated */ del_timer_sync(&priv->power_data.ct_kill_exit_tm); + + if (priv->power_data.adv_tt) { + /* free advance thermal throttling memory */ + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } } EXPORT_SYMBOL(iwl_tt_exit); |