|
@@ -135,6 +135,78 @@ static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */
|
|
|
36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * struct iwl_txpwr_section: eeprom section information
|
|
|
+ * @offset: indirect address into eeprom image
|
|
|
+ * @count: number of "struct iwl_eeprom_enhanced_txpwr" in this section
|
|
|
+ * @band: band type for the section
|
|
|
+ * @is_common - true: common section, false: channel section
|
|
|
+ * @is_cck - true: cck section, false: not cck section
|
|
|
+ * @is_ht_40 - true: all channel in the section are HT40 channel,
|
|
|
+ * false: legacy or HT 20 MHz
|
|
|
+ * ignore if it is common section
|
|
|
+ * @iwl_eeprom_section_channel: channel array in the section,
|
|
|
+ * ignore if common section
|
|
|
+ */
|
|
|
+struct iwl_txpwr_section {
|
|
|
+ u32 offset;
|
|
|
+ u8 count;
|
|
|
+ enum ieee80211_band band;
|
|
|
+ bool is_common;
|
|
|
+ bool is_cck;
|
|
|
+ bool is_ht40;
|
|
|
+ u8 iwl_eeprom_section_channel[EEPROM_MAX_TXPOWER_SECTION_ELEMENTS];
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * section 1 - 3 are regulatory tx power apply to all channels based on
|
|
|
+ * modulation: CCK, OFDM
|
|
|
+ * Band: 2.4GHz, 5.2GHz
|
|
|
+ * section 4 - 10 are regulatory tx power apply to specified channels
|
|
|
+ * For example:
|
|
|
+ * 1L - Channel 1 Legacy
|
|
|
+ * 1HT - Channel 1 HT
|
|
|
+ * (1,+1) - Channel 1 HT40 "_above_"
|
|
|
+ *
|
|
|
+ * Section 1: all CCK channels
|
|
|
+ * Section 2: all 2.4 GHz OFDM (Legacy, HT and HT40) channels
|
|
|
+ * Section 3: all 5.2 GHz OFDM (Legacy, HT and HT40) channels
|
|
|
+ * Section 4: 2.4 GHz 20MHz channels: 1L, 1HT, 2L, 2HT, 10L, 10HT, 11L, 11HT
|
|
|
+ * Section 5: 2.4 GHz 40MHz channels: (1,+1) (2,+1) (6,+1) (7,+1) (9,+1)
|
|
|
+ * Section 6: 5.2 GHz 20MHz channels: 36L, 64L, 100L, 36HT, 64HT, 100HT
|
|
|
+ * Section 7: 5.2 GHz 40MHz channels: (36,+1) (60,+1) (100,+1)
|
|
|
+ * Section 8: 2.4 GHz channel: 13L, 13HT
|
|
|
+ * Section 9: 2.4 GHz channel: 140L, 140HT
|
|
|
+ * Section 10: 2.4 GHz 40MHz channels: (132,+1) (44,+1)
|
|
|
+ *
|
|
|
+ */
|
|
|
+static const struct iwl_txpwr_section enhinfo[] = {
|
|
|
+ { EEPROM_LB_CCK_20_COMMON, 1, IEEE80211_BAND_2GHZ, true, true, false },
|
|
|
+ { EEPROM_LB_OFDM_COMMON, 3, IEEE80211_BAND_2GHZ, true, false, false },
|
|
|
+ { EEPROM_HB_OFDM_COMMON, 3, IEEE80211_BAND_5GHZ, true, false, false },
|
|
|
+ { EEPROM_LB_OFDM_20_BAND, 8, IEEE80211_BAND_2GHZ,
|
|
|
+ false, false, false,
|
|
|
+ {1, 1, 2, 2, 10, 10, 11, 11 } },
|
|
|
+ { EEPROM_LB_OFDM_HT40_BAND, 5, IEEE80211_BAND_2GHZ,
|
|
|
+ false, false, true,
|
|
|
+ { 1, 2, 6, 7, 9 } },
|
|
|
+ { EEPROM_HB_OFDM_20_BAND, 6, IEEE80211_BAND_5GHZ,
|
|
|
+ false, false, false,
|
|
|
+ { 36, 64, 100, 36, 64, 100 } },
|
|
|
+ { EEPROM_HB_OFDM_HT40_BAND, 3, IEEE80211_BAND_5GHZ,
|
|
|
+ false, false, true,
|
|
|
+ { 36, 60, 100 } },
|
|
|
+ { EEPROM_LB_OFDM_20_CHANNEL_13, 2, IEEE80211_BAND_2GHZ,
|
|
|
+ false, false, false,
|
|
|
+ { 13, 13 } },
|
|
|
+ { EEPROM_HB_OFDM_20_CHANNEL_140, 2, IEEE80211_BAND_5GHZ,
|
|
|
+ false, false, false,
|
|
|
+ { 140, 140 } },
|
|
|
+ { EEPROM_HB_OFDM_HT40_BAND_1, 2, IEEE80211_BAND_5GHZ,
|
|
|
+ false, false, true,
|
|
|
+ { 132, 44 } },
|
|
|
+};
|
|
|
+
|
|
|
/******************************************************************************
|
|
|
*
|
|
|
* EEPROM related functions
|
|
@@ -643,6 +715,178 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * iwl_get_max_txpower_avg - get the highest tx power from all chains.
|
|
|
+ * find the highest tx power from all chains for the channel
|
|
|
+ */
|
|
|
+static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
|
|
|
+ struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, int element)
|
|
|
+{
|
|
|
+ s8 max_txpower_avg = 0; /* (dBm) */
|
|
|
+
|
|
|
+ IWL_DEBUG_INFO(priv, "%d - "
|
|
|
+ "chain_a: %d dB chain_b: %d dB "
|
|
|
+ "chain_c: %d dB mimo2: %d dB mimo3: %d dB\n",
|
|
|
+ element,
|
|
|
+ enhanced_txpower[element].chain_a_max >> 1,
|
|
|
+ enhanced_txpower[element].chain_b_max >> 1,
|
|
|
+ enhanced_txpower[element].chain_c_max >> 1,
|
|
|
+ enhanced_txpower[element].mimo2_max >> 1,
|
|
|
+ enhanced_txpower[element].mimo3_max >> 1);
|
|
|
+ /* Take the highest tx power from any valid chains */
|
|
|
+ if ((priv->cfg->valid_tx_ant & ANT_A) &&
|
|
|
+ (enhanced_txpower[element].chain_a_max > max_txpower_avg))
|
|
|
+ max_txpower_avg = enhanced_txpower[element].chain_a_max;
|
|
|
+ if ((priv->cfg->valid_tx_ant & ANT_B) &&
|
|
|
+ (enhanced_txpower[element].chain_b_max > max_txpower_avg))
|
|
|
+ max_txpower_avg = enhanced_txpower[element].chain_b_max;
|
|
|
+ if ((priv->cfg->valid_tx_ant & ANT_C) &&
|
|
|
+ (enhanced_txpower[element].chain_c_max > max_txpower_avg))
|
|
|
+ max_txpower_avg = enhanced_txpower[element].chain_c_max;
|
|
|
+ if (((priv->cfg->valid_tx_ant == ANT_AB) |
|
|
|
+ (priv->cfg->valid_tx_ant == ANT_BC) |
|
|
|
+ (priv->cfg->valid_tx_ant == ANT_AC)) &&
|
|
|
+ (enhanced_txpower[element].mimo2_max > max_txpower_avg))
|
|
|
+ max_txpower_avg = enhanced_txpower[element].mimo2_max;
|
|
|
+ if ((priv->cfg->valid_tx_ant == ANT_ABC) &&
|
|
|
+ (enhanced_txpower[element].mimo3_max > max_txpower_avg))
|
|
|
+ max_txpower_avg = enhanced_txpower[element].mimo3_max;
|
|
|
+
|
|
|
+ /* max. tx power in EEPROM is in 1/2 dBm format
|
|
|
+ * convert from 1/2 dBm to dBm
|
|
|
+ */
|
|
|
+ return max_txpower_avg >> 1;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * iwl_update_common_txpower: update channel tx power
|
|
|
+ * update tx power per band based on EEPROM enhanced tx power info.
|
|
|
+ */
|
|
|
+static s8 iwl_update_common_txpower(struct iwl_priv *priv,
|
|
|
+ struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
|
|
|
+ int section, int element)
|
|
|
+{
|
|
|
+ struct iwl_channel_info *ch_info;
|
|
|
+ int ch;
|
|
|
+ bool is_ht40 = false;
|
|
|
+ s8 max_txpower_avg; /* (dBm) */
|
|
|
+
|
|
|
+ /* it is common section, contain all type (Legacy, HT and HT40)
|
|
|
+ * based on the element in the section to determine
|
|
|
+ * is it HT 40 or not
|
|
|
+ */
|
|
|
+ if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX)
|
|
|
+ is_ht40 = true;
|
|
|
+ max_txpower_avg =
|
|
|
+ iwl_get_max_txpower_avg(priv, enhanced_txpower, element);
|
|
|
+ ch_info = priv->channel_info;
|
|
|
+
|
|
|
+ for (ch = 0; ch < priv->channel_count; ch++) {
|
|
|
+ /* find matching band and update tx power if needed */
|
|
|
+ if ((ch_info->band == enhinfo[section].band) &&
|
|
|
+ (ch_info->max_power_avg < max_txpower_avg) && (!is_ht40)) {
|
|
|
+ /* Update regulatory-based run-time data */
|
|
|
+ ch_info->max_power_avg = ch_info->curr_txpow =
|
|
|
+ max_txpower_avg;
|
|
|
+ ch_info->scan_power = max_txpower_avg;
|
|
|
+ }
|
|
|
+ if ((ch_info->band == enhinfo[section].band) && is_ht40 &&
|
|
|
+ ch_info->ht40_max_power_avg &&
|
|
|
+ (ch_info->ht40_max_power_avg < max_txpower_avg)) {
|
|
|
+ /* Update regulatory-based run-time data */
|
|
|
+ ch_info->ht40_max_power_avg = max_txpower_avg;
|
|
|
+ ch_info->ht40_curr_txpow = max_txpower_avg;
|
|
|
+ ch_info->ht40_scan_power = max_txpower_avg;
|
|
|
+ }
|
|
|
+ ch_info++;
|
|
|
+ }
|
|
|
+ return max_txpower_avg;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * iwl_update_channel_txpower: update channel tx power
|
|
|
+ * update channel tx power based on EEPROM enhanced tx power info.
|
|
|
+ */
|
|
|
+static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
|
|
|
+ struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
|
|
|
+ int section, int element)
|
|
|
+{
|
|
|
+ struct iwl_channel_info *ch_info;
|
|
|
+ int ch;
|
|
|
+ u8 channel;
|
|
|
+ s8 max_txpower_avg; /* (dBm) */
|
|
|
+
|
|
|
+ channel = enhinfo[section].iwl_eeprom_section_channel[element];
|
|
|
+ max_txpower_avg =
|
|
|
+ iwl_get_max_txpower_avg(priv, enhanced_txpower, element);
|
|
|
+
|
|
|
+ ch_info = priv->channel_info;
|
|
|
+ for (ch = 0; ch < priv->channel_count; ch++) {
|
|
|
+ /* find matching channel and update tx power if needed */
|
|
|
+ if (ch_info->channel == channel) {
|
|
|
+ if ((ch_info->max_power_avg < max_txpower_avg) &&
|
|
|
+ (!enhinfo[section].is_ht40)) {
|
|
|
+ /* Update regulatory-based run-time data */
|
|
|
+ ch_info->max_power_avg = max_txpower_avg;
|
|
|
+ ch_info->curr_txpow = max_txpower_avg;
|
|
|
+ ch_info->scan_power = max_txpower_avg;
|
|
|
+ }
|
|
|
+ if ((enhinfo[section].is_ht40) &&
|
|
|
+ (ch_info->ht40_max_power_avg) &&
|
|
|
+ (ch_info->ht40_max_power_avg < max_txpower_avg)) {
|
|
|
+ /* Update regulatory-based run-time data */
|
|
|
+ ch_info->ht40_max_power_avg = max_txpower_avg;
|
|
|
+ ch_info->ht40_curr_txpow = max_txpower_avg;
|
|
|
+ ch_info->ht40_scan_power = max_txpower_avg;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ch_info++;
|
|
|
+ }
|
|
|
+ return max_txpower_avg;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * iwlcore_eeprom_enhanced_txpower: process enhanced tx power info
|
|
|
+ */
|
|
|
+void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
|
|
|
+{
|
|
|
+ int eeprom_section_count = 0;
|
|
|
+ int section, element;
|
|
|
+ struct iwl_eeprom_enhanced_txpwr *enhanced_txpower;
|
|
|
+ u32 offset;
|
|
|
+ s8 max_txpower_avg; /* (dBm) */
|
|
|
+
|
|
|
+ /* Loop through all the sections
|
|
|
+ * adjust bands and channel's max tx power
|
|
|
+ * Set the tx_power_user_lmt to the highest power
|
|
|
+ * supported by any channels and chains
|
|
|
+ */
|
|
|
+ for (section = 0; section < ARRAY_SIZE(enhinfo); section++) {
|
|
|
+ eeprom_section_count = enhinfo[section].count;
|
|
|
+ offset = enhinfo[section].offset;
|
|
|
+ enhanced_txpower = (struct iwl_eeprom_enhanced_txpwr *)
|
|
|
+ iwl_eeprom_query_addr(priv, offset);
|
|
|
+
|
|
|
+ for (element = 0; element < eeprom_section_count; element++) {
|
|
|
+ if (enhinfo[section].is_common)
|
|
|
+ max_txpower_avg =
|
|
|
+ iwl_update_common_txpower(priv,
|
|
|
+ enhanced_txpower, section, element);
|
|
|
+ else
|
|
|
+ max_txpower_avg =
|
|
|
+ iwl_update_channel_txpower(priv,
|
|
|
+ enhanced_txpower, section, element);
|
|
|
+
|
|
|
+ /* Update the tx_power_user_lmt to the highest power
|
|
|
+ * supported by any channel */
|
|
|
+ if (max_txpower_avg > priv->tx_power_user_lmt)
|
|
|
+ priv->tx_power_user_lmt = max_txpower_avg;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(iwlcore_eeprom_enhanced_txpower);
|
|
|
+
|
|
|
#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \
|
|
|
? # x " " : "")
|
|
|
|
|
@@ -790,6 +1034,14 @@ int iwl_init_channel_map(struct iwl_priv *priv)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* for newer device (6000 series and up)
|
|
|
+ * EEPROM contain enhanced tx power information
|
|
|
+ * driver need to process addition information
|
|
|
+ * to determine the max channel tx power limits
|
|
|
+ */
|
|
|
+ if (priv->cfg->ops->lib->eeprom_ops.update_enhanced_txpower)
|
|
|
+ priv->cfg->ops->lib->eeprom_ops.update_enhanced_txpower(priv);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL(iwl_init_channel_map);
|