iwlwifi: eliminate the possible 1/2 dBm tx power loss in 6x00 & 6x50 series
In both 6x00 and 6x50 series, the enhanced/extended tx power table in EEPROM is used to set the max. tx power limit. This new tx power table is in 1/2 dBm format, which creates an issue of possibility of 1/2 dBm loss when driver set the tx power limit; because of driver keep track and report the tx power in dBm format. In order to prevent the 1/2 dBm loss, keep track of the true max tx power in 1/2 dBm format in driver; do the comparison and adjust the tx power if needed when send tx power command to uCode. Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
4d6ccbf57f
commit
ae16fc3c31
|
@ -1250,6 +1250,22 @@ int iwl5000_send_tx_power(struct iwl_priv *priv)
|
||||||
|
|
||||||
/* half dBm need to multiply */
|
/* half dBm need to multiply */
|
||||||
tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt);
|
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.flags = IWL50_TX_POWER_NO_CLOSED;
|
||||||
tx_power_cmd.srv_chan_lmt = IWL50_TX_POWER_AUTO;
|
tx_power_cmd.srv_chan_lmt = IWL50_TX_POWER_AUTO;
|
||||||
|
|
||||||
|
|
|
@ -1247,6 +1247,7 @@ struct iwl_priv {
|
||||||
/* TX Power */
|
/* TX Power */
|
||||||
s8 tx_power_user_lmt;
|
s8 tx_power_user_lmt;
|
||||||
s8 tx_power_device_lmt;
|
s8 tx_power_device_lmt;
|
||||||
|
s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
#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
|
* find the highest tx power from all chains for the channel
|
||||||
*/
|
*/
|
||||||
static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
|
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) */
|
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))
|
(enhanced_txpower[element].mimo3_max > max_txpower_avg))
|
||||||
max_txpower_avg = enhanced_txpower[element].mimo3_max;
|
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,
|
static s8 iwl_update_common_txpower(struct iwl_priv *priv,
|
||||||
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
|
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;
|
struct iwl_channel_info *ch_info;
|
||||||
int ch;
|
int ch;
|
||||||
|
@ -820,20 +825,22 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv,
|
||||||
if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX)
|
if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX)
|
||||||
is_ht40 = true;
|
is_ht40 = true;
|
||||||
max_txpower_avg =
|
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;
|
ch_info = priv->channel_info;
|
||||||
|
|
||||||
for (ch = 0; ch < priv->channel_count; ch++) {
|
for (ch = 0; ch < priv->channel_count; ch++) {
|
||||||
/* find matching band and update tx power if needed */
|
/* find matching band and update tx power if needed */
|
||||||
if ((ch_info->band == enhinfo[section].band) &&
|
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 */
|
/* Update regulatory-based run-time data */
|
||||||
ch_info->max_power_avg = ch_info->curr_txpow =
|
ch_info->max_power_avg = ch_info->curr_txpow =
|
||||||
max_txpower_avg;
|
max_txpower_avg;
|
||||||
ch_info->scan_power = max_txpower_avg;
|
ch_info->scan_power = max_txpower_avg;
|
||||||
}
|
}
|
||||||
if ((ch_info->band == enhinfo[section].band) && is_ht40 &&
|
if ((ch_info->band == enhinfo[section].band) && is_ht40 &&
|
||||||
ch_info->ht40_max_power_avg &&
|
|
||||||
(ch_info->ht40_max_power_avg < max_txpower_avg)) {
|
(ch_info->ht40_max_power_avg < max_txpower_avg)) {
|
||||||
/* Update regulatory-based run-time data */
|
/* Update regulatory-based run-time data */
|
||||||
ch_info->ht40_max_power_avg = max_txpower_avg;
|
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,
|
static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
|
||||||
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
|
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;
|
struct iwl_channel_info *ch_info;
|
||||||
int ch;
|
int ch;
|
||||||
|
@ -858,7 +865,8 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
|
||||||
|
|
||||||
channel = enhinfo[section].iwl_eeprom_section_channel[element];
|
channel = enhinfo[section].iwl_eeprom_section_channel[element];
|
||||||
max_txpower_avg =
|
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;
|
ch_info = priv->channel_info;
|
||||||
for (ch = 0; ch < priv->channel_count; ch++) {
|
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;
|
ch_info->scan_power = max_txpower_avg;
|
||||||
}
|
}
|
||||||
if ((enhinfo[section].is_ht40) &&
|
if ((enhinfo[section].is_ht40) &&
|
||||||
(ch_info->ht40_max_power_avg) &&
|
|
||||||
(ch_info->ht40_max_power_avg < max_txpower_avg)) {
|
(ch_info->ht40_max_power_avg < max_txpower_avg)) {
|
||||||
/* Update regulatory-based run-time data */
|
/* Update regulatory-based run-time data */
|
||||||
ch_info->ht40_max_power_avg = max_txpower_avg;
|
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;
|
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
s8 max_txpower_avg; /* (dBm) */
|
s8 max_txpower_avg; /* (dBm) */
|
||||||
|
s8 max_txpower_in_half_dbm; /* (half-dBm) */
|
||||||
|
|
||||||
/* Loop through all the sections
|
/* Loop through all the sections
|
||||||
* adjust bands and channel's max tx power
|
* 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)
|
if (enhinfo[section].is_common)
|
||||||
max_txpower_avg =
|
max_txpower_avg =
|
||||||
iwl_update_common_txpower(priv,
|
iwl_update_common_txpower(priv,
|
||||||
enhanced_txpower, section, element);
|
enhanced_txpower, section,
|
||||||
|
element,
|
||||||
|
&max_txpower_in_half_dbm);
|
||||||
else
|
else
|
||||||
max_txpower_avg =
|
max_txpower_avg =
|
||||||
iwl_update_channel_txpower(priv,
|
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
|
/* Update the tx_power_user_lmt to the highest power
|
||||||
* supported by any channel */
|
* supported by any channel */
|
||||||
if (max_txpower_avg > priv->tx_power_user_lmt)
|
if (max_txpower_avg > priv->tx_power_user_lmt)
|
||||||
priv->tx_power_user_lmt = max_txpower_avg;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue