Commit 01a9c948 authored by Luca Coelho's avatar Luca Coelho

iwlwifi: add workaround to disable wide channels in 5GHz

The OTP in some SKUs have erroneously allowed 40MHz and 80MHz channels
in the 5.2GHz band.  The firmware has been modified to not allow this
in those SKUs, so the driver needs to do the same otherwise the
firmware will assert when we try to use it.

Cc: stable@vger.kernel.org
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 482e4844
......@@ -148,7 +148,8 @@ struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt)
rsp->regulatory.channel_profile,
nvm->valid_tx_ant & fwrt->fw->valid_tx_ant,
nvm->valid_rx_ant & fwrt->fw->valid_rx_ant,
rsp->regulatory.lar_enabled && lar_fw_supported);
rsp->regulatory.lar_enabled && lar_fw_supported,
false);
iwl_free_resp(&hcmd);
return nvm;
......
......@@ -79,6 +79,7 @@
/* NVM offsets (in words) definitions */
enum wkp_nvm_offsets {
/* NVM HW-Section offset (in words) definitions */
SUBSYSTEM_ID = 0x0A,
HW_ADDR = 0x15,
/* NVM SW-Section offset (in words) definitions */
......@@ -258,13 +259,12 @@ static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz,
static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 * const nvm_ch_flags,
bool lar_supported)
bool lar_supported, bool no_wide_in_5ghz)
{
int ch_idx;
int n_channels = 0;
struct ieee80211_channel *channel;
u16 ch_flags;
bool is_5ghz;
int num_of_ch, num_2ghz_channels;
const u8 *nvm_chan;
......@@ -279,12 +279,20 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
}
for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
bool is_5ghz = (ch_idx >= num_2ghz_channels);
ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
if (ch_idx >= num_2ghz_channels &&
!data->sku_cap_band_52GHz_enable)
if (is_5ghz && !data->sku_cap_band_52GHz_enable)
continue;
/* workaround to disable wide channels in 5GHz */
if (no_wide_in_5ghz && is_5ghz) {
ch_flags &= ~(NVM_CHANNEL_40MHZ |
NVM_CHANNEL_80MHZ |
NVM_CHANNEL_160MHZ);
}
if (ch_flags & NVM_CHANNEL_160MHZ)
data->vht160_supported = true;
......@@ -307,8 +315,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
n_channels++;
channel->hw_value = nvm_chan[ch_idx];
channel->band = (ch_idx < num_2ghz_channels) ?
NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
channel->band = is_5ghz ?
NL80211_BAND_5GHZ : NL80211_BAND_2GHZ;
channel->center_freq =
ieee80211_channel_to_frequency(
channel->hw_value, channel->band);
......@@ -320,7 +328,6 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
* is not used in mvm, and is used for backwards compatibility
*/
channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
is_5ghz = channel->band == NL80211_BAND_5GHZ;
/* don't put limitations in case we're using LAR */
if (!lar_supported)
......@@ -438,14 +445,15 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
struct iwl_nvm_data *data, const __le16 *nvm_ch_flags,
u8 tx_chains, u8 rx_chains, bool lar_supported)
u8 tx_chains, u8 rx_chains, bool lar_supported,
bool no_wide_in_5ghz)
{
int n_channels;
int n_used = 0;
struct ieee80211_supported_band *sband;
n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags,
lar_supported);
lar_supported, no_wide_in_5ghz);
sband = &data->bands[NL80211_BAND_2GHZ];
sband->band = NL80211_BAND_2GHZ;
sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
......@@ -651,6 +659,39 @@ static int iwl_set_hw_address(struct iwl_trans *trans,
return 0;
}
static bool
iwl_nvm_no_wide_in_5ghz(struct device *dev, const struct iwl_cfg *cfg,
const __le16 *nvm_hw)
{
/*
* Workaround a bug in Indonesia SKUs where the regulatory in
* some 7000-family OTPs erroneously allow wide channels in
* 5GHz. To check for Indonesia, we take the SKU value from
* bits 1-4 in the subsystem ID and check if it is either 5 or
* 9. In those cases, we need to force-disable wide channels
* in 5GHz otherwise the FW will throw a sysassert when we try
* to use them.
*/
if (cfg->device_family == IWL_DEVICE_FAMILY_7000) {
/*
* Unlike the other sections in the NVM, the hw
* section uses big-endian.
*/
u16 subsystem_id = be16_to_cpup((const __be16 *)nvm_hw
+ SUBSYSTEM_ID);
u8 sku = (subsystem_id & 0x1e) >> 1;
if (sku == 5 || sku == 9) {
IWL_DEBUG_EEPROM(dev,
"disabling wide channels in 5GHz (0x%0x %d)\n",
subsystem_id, sku);
return true;
}
}
return false;
}
struct iwl_nvm_data *
iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
......@@ -661,6 +702,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
struct device *dev = trans->dev;
struct iwl_nvm_data *data;
bool lar_enabled;
bool no_wide_in_5ghz = iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw);
u32 sku, radio_cfg;
u16 lar_config;
const __le16 *ch_section;
......@@ -731,7 +773,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
}
iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains,
lar_fw_supported && lar_enabled);
lar_fw_supported && lar_enabled, no_wide_in_5ghz);
data->calib_version = 255;
return data;
......
......@@ -93,7 +93,8 @@ void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
*/
void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
struct iwl_nvm_data *data, const __le16 *nvm_ch_flags,
u8 tx_chains, u8 rx_chains, bool lar_supported);
u8 tx_chains, u8 rx_chains, bool lar_supported,
bool no_wide_in_5ghz);
/**
* iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment