/* * 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 >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 * IWL_TI_2 T >= 114 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, bool force) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 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; } } /* stop ct_kill_waiting_tm timer */ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); if (changed) { struct iwl_rxon_cmd *rxon = &priv->staging_rxon; if (tt->state >= IWL_TI_1) { /* 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_HT40_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 -- it will be * recalculated automatically. */ /* 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); } mutex_lock(&priv->mutex); if (old_state == IWL_TI_CT_KILL) clear_bit(STATUS_CT_KILL, &priv->status); if (tt->state != IWL_TI_CT_KILL && 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"); if (old_state == IWL_TI_CT_KILL) set_bit(STATUS_CT_KILL, &priv->status); 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) { if (force) { IWL_DEBUG_POWER(priv, "Enter IWL_TI_CT_KILL\n"); set_bit(STATUS_CT_KILL, &priv->status); iwl_perform_ct_kill_task(priv, true); } else { iwl_prepare_ct_kill_task(priv); tt->state = old_state; } } 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); } } mutex_unlock(&priv->mutex); } }
int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) { struct iwl_priv *priv = hw->priv; struct iwl_rxon_context *ctx; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = conf->channel; const struct iwl_channel_info *ch_info; int ret = 0; IWL_DEBUG_MAC80211(priv, "changed %#x", changed); mutex_lock(&priv->mutex); if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); goto out; } if (!iwl_is_ready(priv)) { IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); goto out; } if (changed & (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { /* mac80211 uses static for non-HT which is what we want */ priv->current_ht_config.smps = conf->smps_mode; /* * Recalculate chain counts. * * If monitor mode is enabled then mac80211 will * set up the SM PS mode to OFF if an HT channel is * configured. */ if (priv->cfg->ops->hcmd->set_rxon_chain) for_each_context(priv, ctx) priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { unsigned long flags; ch_info = iwl_get_channel_info(priv, channel->band, channel->hw_value); if (!is_channel_valid(ch_info)) { IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n"); ret = -EINVAL; goto out; } spin_lock_irqsave(&priv->lock, flags); for_each_context(priv, ctx) { /* Configure HT40 channels */ if (ctx->ht.enabled != conf_is_ht(conf)) ctx->ht.enabled = conf_is_ht(conf); if (ctx->ht.enabled) { if (conf_is_ht40_minus(conf)) { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; ctx->ht.is_40mhz = true; } else if (conf_is_ht40_plus(conf)) { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; ctx->ht.is_40mhz = true; } else { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; ctx->ht.is_40mhz = false; } } else ctx->ht.is_40mhz = false; /* * Default to no protection. Protection mode will * later be set from BSS config in iwl_ht_conf */ ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; /* if we are switching from ht to 2.4 clear flags * from any ht related info since 2.4 does not * support ht */ if (le16_to_cpu(ctx->staging.channel) != channel->hw_value) ctx->staging.flags = 0; iwl_set_rxon_channel(priv, channel, ctx); iwl_set_rxon_ht(priv, &priv->current_ht_config); iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); } spin_unlock_irqrestore(&priv->lock, flags); iwl_update_bcast_stations(priv); /* * The list of supported rates and rate mask can be different * for each band; since the band may have changed, reset * the rate mask to what mac80211 lists. */ iwl_set_rate(priv); }
/* * 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 >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 * IWL_TI_2 T >= 114 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, bool force) { struct iwl_tt_mgmt *tt = &priv->thermal_throttle; 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_TEMP(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; } } /* stop ct_kill_waiting_tm timer */ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); if (changed) { if (tt->state >= IWL_TI_1) { /* 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)) { struct iwl_rxon_context *ctx; for_each_context(priv, ctx) { struct iwl_rxon_cmd *rxon; rxon = &ctx->staging; /* disable HT */ rxon->flags &= ~( RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_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 {
/** * iwl_legacy_mac_config - mac80211 config callback */ int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed) { struct iwl_priv *priv = hw->priv; const struct iwl_channel_info *ch_info; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = conf->channel; struct iwl_ht_config *ht_conf = &priv->current_ht_config; struct iwl_rxon_context *ctx; unsigned long flags = 0; int ret = 0; u16 ch; int scan_active = 0; bool ht_changed[NUM_IWL_RXON_CTX] = {}; if (WARN_ON(!priv->cfg->ops->legacy)) return -EOPNOTSUPP; mutex_lock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n", channel->hw_value, changed); if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { scan_active = 1; IWL_DEBUG_MAC80211(priv, "scan active\n"); } if (changed & (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { /* mac80211 uses static for non-HT which is what we want */ priv->current_ht_config.smps = conf->smps_mode; /* * Recalculate chain counts. * * If monitor mode is enabled then mac80211 will * set up the SM PS mode to OFF if an HT channel is * configured. */ if (priv->cfg->ops->hcmd->set_rxon_chain) for_each_context(priv, ctx) priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); } /* during scanning mac80211 will delay channel setting until * scan finish with changed = 0 */ if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { if (scan_active) goto set_ch_out; ch = channel->hw_value; ch_info = iwl_get_channel_info(priv, channel->band, ch); if (!is_channel_valid(ch_info)) { IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n"); ret = -EINVAL; goto set_ch_out; } spin_lock_irqsave(&priv->lock, flags); for_each_context(priv, ctx) { /* Configure HT40 channels */ if (ctx->ht.enabled != conf_is_ht(conf)) { ctx->ht.enabled = conf_is_ht(conf); ht_changed[ctx->ctxid] = true; } if (ctx->ht.enabled) { if (conf_is_ht40_minus(conf)) { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; ctx->ht.is_40mhz = true; } else if (conf_is_ht40_plus(conf)) { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; ctx->ht.is_40mhz = true; } else { ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; ctx->ht.is_40mhz = false; } } else ctx->ht.is_40mhz = false; /* * Default to no protection. Protection mode will * later be set from BSS config in iwl_ht_conf */ ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; /* if we are switching from ht to 2.4 clear flags * from any ht related info since 2.4 does not * support ht */ if ((le16_to_cpu(ctx->staging.channel) != ch)) ctx->staging.flags = 0; iwl_set_rxon_channel(priv, channel, ctx); iwl_set_rxon_ht(priv, ht_conf); iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); } spin_unlock_irqrestore(&priv->lock, flags); if (priv->cfg->ops->legacy->update_bcast_stations) ret = priv->cfg->ops->legacy->update_bcast_stations(priv); set_ch_out: /* The list of supported rates and rate mask can be different * for each band; since the band may have changed, reset * the rate mask to what mac80211 lists */ iwl_set_rate(priv); } if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { ret = iwl_power_update_mode(priv, false); if (ret) IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); } if (changed & IEEE80211_CONF_CHANGE_POWER) { IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", priv->tx_power_user_lmt, conf->power_level); iwl_set_tx_power(priv, conf->power_level, false); } if (!iwl_is_ready(priv)) { IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); goto out; } if (scan_active) goto out; for_each_context(priv, ctx) { if (memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging))) iwlcore_commit_rxon(priv, ctx); else IWL_DEBUG_INFO(priv, "Not re-sending same RXON configuration.\n"); if (ht_changed[ctx->ctxid]) iwl_update_qos(priv, ctx); } out: IWL_DEBUG_MAC80211(priv, "leave\n"); mutex_unlock(&priv->mutex); return ret; }