@@ -1251,6 +1251,22 @@ int iwl5000_send_tx_power(struct iwl_priv *priv)
/* half dBm need to multiply */
tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt);
+
+ if (priv->tx_power_lmt_in_half_dbm &&
+ priv->tx_power_lmt_in_half_dbm < tx_power_cmd.global_lmt) {
+ /*
+ * For the newer devices which using enhanced/extend tx power
+ * table in EEPROM, the format is in half dBm. driver need to
+ * convert to dBm format before report to mac80211.
+ * By doing so, there is a possibility of 1/2 dBm resolution
+ * lost. driver will perform "round-up" operation before
+ * reporting, but it will cause 1/2 dBm tx power over the
+ * regulatory limit. Perform the checking here, if the
+ * "tx_power_user_lmt" is higher than EEPROM value (in
+ * half-dBm format), lower the tx power based on EEPROM
+ */
+ tx_power_cmd.global_lmt = priv->tx_power_lmt_in_half_dbm;
+ }
tx_power_cmd.flags = IWL50_TX_POWER_NO_CLOSED;
tx_power_cmd.srv_chan_lmt = IWL50_TX_POWER_AUTO;
@@ -1247,6 +1247,7 @@ struct iwl_priv {
/* TX Power */
s8 tx_power_user_lmt;
s8 tx_power_device_lmt;
+ s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */
#ifdef CONFIG_IWLWIFI_DEBUG
@@ -762,7 +762,8 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv,
* 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)
+ struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
+ int element, s8 *max_txpower_in_half_dbm)
{
s8 max_txpower_avg = 0; /* (dBm) */
@@ -794,10 +795,14 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
(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
+ /*
+ * max. tx power in EEPROM is in 1/2 dBm format
+ * convert from 1/2 dBm to dBm (round-up convert)
+ * but we also do not want to loss 1/2 dBm resolution which
+ * will impact performance
*/
- return max_txpower_avg >> 1;
+ *max_txpower_in_half_dbm = max_txpower_avg;
+ return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1);
}
/**
@@ -806,7 +811,7 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
*/
static s8 iwl_update_common_txpower(struct iwl_priv *priv,
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
- int section, int element)
+ int section, int element, s8 *max_txpower_in_half_dbm)
{
struct iwl_channel_info *ch_info;
int ch;
@@ -820,20 +825,22 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv,
if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX)
is_ht40 = true;
max_txpower_avg =
- iwl_get_max_txpower_avg(priv, enhanced_txpower, element);
+ iwl_get_max_txpower_avg(priv, enhanced_txpower,
+ element, max_txpower_in_half_dbm);
+
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)) {
+ (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;
+ 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;
@@ -849,7 +856,7 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv,
*/
static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
- int section, int element)
+ int section, int element, s8 *max_txpower_in_half_dbm)
{
struct iwl_channel_info *ch_info;
int ch;
@@ -858,7 +865,8 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
channel = enhinfo[section].iwl_eeprom_section_channel[element];
max_txpower_avg =
- iwl_get_max_txpower_avg(priv, enhanced_txpower, element);
+ iwl_get_max_txpower_avg(priv, enhanced_txpower,
+ element, max_txpower_in_half_dbm);
ch_info = priv->channel_info;
for (ch = 0; ch < priv->channel_count; ch++) {
@@ -872,7 +880,6 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
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;
@@ -894,6 +901,7 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower;
u32 offset;
s8 max_txpower_avg; /* (dBm) */
+ s8 max_txpower_in_half_dbm; /* (half-dBm) */
/* Loop through all the sections
* adjust bands and channel's max tx power
@@ -920,16 +928,29 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
if (enhinfo[section].is_common)
max_txpower_avg =
iwl_update_common_txpower(priv,
- enhanced_txpower, section, element);
+ enhanced_txpower, section,
+ element,
+ &max_txpower_in_half_dbm);
else
max_txpower_avg =
iwl_update_channel_txpower(priv,
- enhanced_txpower, section, element);
+ enhanced_txpower, section,
+ element,
+ &max_txpower_in_half_dbm);
/* 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;
+
+ /*
+ * Update the tx_power_lmt_in_half_dbm to
+ * the highest power supported by any channel
+ */
+ if (max_txpower_in_half_dbm >
+ priv->tx_power_lmt_in_half_dbm)
+ priv->tx_power_lmt_in_half_dbm =
+ max_txpower_in_half_dbm;
}
}
}