]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
iwlwifi: add workaround to disable wide channels in 5GHz
authorLuca Coelho <luciano.coelho@intel.com>
Tue, 15 Aug 2017 17:48:41 +0000 (20:48 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Fri, 18 Aug 2017 14:36:41 +0000 (17:36 +0300)
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: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/fw/nvm.c
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h

index ae03d0f5564fca609c3c239add8387c38aa1ab71..e81f6dd3744e9e36f2a85bde27955ee02de30922 100644 (file)
@@ -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;
index 1172e4572a82042fe27ffc231e87d4a67cf6fa85..ea165b3e6dd3ec461fbad12af4633315b5454365 100644 (file)
@@ -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;
index 3fd6506a02ab816a6ca85213ad37b51a0f6b9e96..50d9b3eaa4f88825f56731267814446fffedbcef 100644 (file)
@@ -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