|
@@ -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;
|