|
@@ -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);
|
|
|
|