diff options
author | Eran Harary <eran.harary@intel.com> | 2014-02-04 07:21:38 -0500 |
---|---|---|
committer | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2014-02-13 06:49:37 -0500 |
commit | 77db0a3c27dc0f027e5f3956f4ba77246c89a548 (patch) | |
tree | 343938182601b80be4b451661e4efaa19fc35b6a /drivers/net/wireless | |
parent | 7303dd7f312f0d07a4bf45c62608d5233b5e8062 (diff) |
iwlwifi: mvm: new NVM format in family 8000
Support the changes below:
- Fields and sections structure were changed.
- the NVM file built from DWord instead of Words.
- sections header format was changed.
Signed-off-by: Eran Harary <eran.harary@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-drv.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-drv.h | 14 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-fw.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 239 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-nvm-parse.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/nvm.c | 63 |
8 files changed, 271 insertions, 71 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index b3bc30b4292b..662d9936485c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c | |||
@@ -728,6 +728,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, | |||
728 | if (tlv_len != sizeof(u32)) | 728 | if (tlv_len != sizeof(u32)) |
729 | goto invalid_tlv_len; | 729 | goto invalid_tlv_len; |
730 | drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data); | 730 | drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data); |
731 | drv->fw.valid_tx_ant = (drv->fw.phy_config & | ||
732 | FW_PHY_CFG_TX_CHAIN) >> | ||
733 | FW_PHY_CFG_TX_CHAIN_POS; | ||
734 | drv->fw.valid_rx_ant = (drv->fw.phy_config & | ||
735 | FW_PHY_CFG_RX_CHAIN) >> | ||
736 | FW_PHY_CFG_RX_CHAIN_POS; | ||
731 | break; | 737 | break; |
732 | case IWL_UCODE_TLV_SECURE_SEC_RT: | 738 | case IWL_UCODE_TLV_SECURE_SEC_RT: |
733 | iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, | 739 | iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, |
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index 592c01e11013..3c72cb710b0c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h | |||
@@ -70,6 +70,20 @@ | |||
70 | #define DRV_COPYRIGHT "Copyright(c) 2003- 2014 Intel Corporation" | 70 | #define DRV_COPYRIGHT "Copyright(c) 2003- 2014 Intel Corporation" |
71 | #define DRV_AUTHOR "<ilw@linux.intel.com>" | 71 | #define DRV_AUTHOR "<ilw@linux.intel.com>" |
72 | 72 | ||
73 | /* radio config bits (actual values from NVM definition) */ | ||
74 | #define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ | ||
75 | #define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ | ||
76 | #define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ | ||
77 | #define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ | ||
78 | #define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ | ||
79 | #define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ | ||
80 | |||
81 | #define NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(x) (x & 0xF) | ||
82 | #define NVM_RF_CFG_DASH_MSK_FAMILY_8000(x) ((x >> 4) & 0xF) | ||
83 | #define NVM_RF_CFG_STEP_MSK_FAMILY_8000(x) ((x >> 8) & 0xF) | ||
84 | #define NVM_RF_CFG_TYPE_MSK_FAMILY_8000(x) ((x >> 12) & 0xFFF) | ||
85 | #define NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(x) ((x >> 24) & 0xF) | ||
86 | #define NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(x) ((x >> 28) & 0xF) | ||
73 | 87 | ||
74 | /** | 88 | /** |
75 | * DOC: Driver system flows - drv component | 89 | * DOC: Driver system flows - drv component |
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h index e3c7deafabe6..f0548b8a64b0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h | |||
@@ -81,16 +81,17 @@ struct iwl_nvm_data { | |||
81 | bool sku_cap_band_24GHz_enable; | 81 | bool sku_cap_band_24GHz_enable; |
82 | bool sku_cap_band_52GHz_enable; | 82 | bool sku_cap_band_52GHz_enable; |
83 | bool sku_cap_11n_enable; | 83 | bool sku_cap_11n_enable; |
84 | bool sku_cap_11ac_enable; | ||
84 | bool sku_cap_amt_enable; | 85 | bool sku_cap_amt_enable; |
85 | bool sku_cap_ipan_enable; | 86 | bool sku_cap_ipan_enable; |
86 | 87 | ||
87 | u8 radio_cfg_type; | 88 | u16 radio_cfg_type; |
88 | u8 radio_cfg_step; | 89 | u8 radio_cfg_step; |
89 | u8 radio_cfg_dash; | 90 | u8 radio_cfg_dash; |
90 | u8 radio_cfg_pnum; | 91 | u8 radio_cfg_pnum; |
91 | u8 valid_tx_ant, valid_rx_ant; | 92 | u8 valid_tx_ant, valid_rx_ant; |
92 | 93 | ||
93 | u16 nvm_version; | 94 | u32 nvm_version; |
94 | s8 max_tx_pwr_half_dbm; | 95 | s8 max_tx_pwr_half_dbm; |
95 | 96 | ||
96 | struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; | 97 | struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index b0d09987540d..73671072d0f6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h | |||
@@ -288,6 +288,8 @@ struct iwl_fw { | |||
288 | 288 | ||
289 | struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX]; | 289 | struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX]; |
290 | u32 phy_config; | 290 | u32 phy_config; |
291 | u8 valid_tx_ant; | ||
292 | u8 valid_rx_ant; | ||
291 | 293 | ||
292 | bool mvm_fw; | 294 | bool mvm_fw; |
293 | 295 | ||
@@ -296,14 +298,12 @@ struct iwl_fw { | |||
296 | 298 | ||
297 | static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw) | 299 | static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw) |
298 | { | 300 | { |
299 | return (fw->phy_config & FW_PHY_CFG_TX_CHAIN) >> | 301 | return fw->valid_tx_ant; |
300 | FW_PHY_CFG_TX_CHAIN_POS; | ||
301 | } | 302 | } |
302 | 303 | ||
303 | static inline u8 iwl_fw_valid_rx_ant(const struct iwl_fw *fw) | 304 | static inline u8 iwl_fw_valid_rx_ant(const struct iwl_fw *fw) |
304 | { | 305 | { |
305 | return (fw->phy_config & FW_PHY_CFG_RX_CHAIN) >> | 306 | return fw->valid_rx_ant; |
306 | FW_PHY_CFG_RX_CHAIN_POS; | ||
307 | } | 307 | } |
308 | 308 | ||
309 | #endif /* __iwl_fw_h__ */ | 309 | #endif /* __iwl_fw_h__ */ |
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 42780971aa04..df3ea60c87d9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | |||
@@ -71,7 +71,7 @@ enum wkp_nvm_offsets { | |||
71 | /* NVM HW-Section offset (in words) definitions */ | 71 | /* NVM HW-Section offset (in words) definitions */ |
72 | HW_ADDR = 0x15, | 72 | HW_ADDR = 0x15, |
73 | 73 | ||
74 | /* NVM SW-Section offset (in words) definitions */ | 74 | /* NVM SW-Section offset (in words) definitions */ |
75 | NVM_SW_SECTION = 0x1C0, | 75 | NVM_SW_SECTION = 0x1C0, |
76 | NVM_VERSION = 0, | 76 | NVM_VERSION = 0, |
77 | RADIO_CFG = 1, | 77 | RADIO_CFG = 1, |
@@ -79,11 +79,32 @@ enum wkp_nvm_offsets { | |||
79 | N_HW_ADDRS = 3, | 79 | N_HW_ADDRS = 3, |
80 | NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION, | 80 | NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION, |
81 | 81 | ||
82 | /* NVM calibration section offset (in words) definitions */ | 82 | /* NVM calibration section offset (in words) definitions */ |
83 | NVM_CALIB_SECTION = 0x2B8, | 83 | NVM_CALIB_SECTION = 0x2B8, |
84 | XTAL_CALIB = 0x316 - NVM_CALIB_SECTION | 84 | XTAL_CALIB = 0x316 - NVM_CALIB_SECTION |
85 | }; | 85 | }; |
86 | 86 | ||
87 | enum family_8000_nvm_offsets { | ||
88 | /* NVM HW-Section offset (in words) definitions */ | ||
89 | HW_ADDR0_FAMILY_8000 = 0x12, | ||
90 | HW_ADDR1_FAMILY_8000 = 0x16, | ||
91 | MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1, | ||
92 | |||
93 | /* NVM SW-Section offset (in words) definitions */ | ||
94 | NVM_SW_SECTION_FAMILY_8000 = 0x1C0, | ||
95 | NVM_VERSION_FAMILY_8000 = 0, | ||
96 | RADIO_CFG_FAMILY_8000 = 2, | ||
97 | SKU_FAMILY_8000 = 4, | ||
98 | N_HW_ADDRS_FAMILY_8000 = 5, | ||
99 | |||
100 | /* NVM REGULATORY -Section offset (in words) definitions */ | ||
101 | NVM_CHANNELS_FAMILY_8000 = 0, | ||
102 | |||
103 | /* NVM calibration section offset (in words) definitions */ | ||
104 | NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8, | ||
105 | XTAL_CALIB_FAMILY_8000 = 0x316 - NVM_CALIB_SECTION_FAMILY_8000 | ||
106 | }; | ||
107 | |||
87 | /* SKU Capabilities (actual values from NVM definition) */ | 108 | /* SKU Capabilities (actual values from NVM definition) */ |
88 | enum nvm_sku_bits { | 109 | enum nvm_sku_bits { |
89 | NVM_SKU_CAP_BAND_24GHZ = BIT(0), | 110 | NVM_SKU_CAP_BAND_24GHZ = BIT(0), |
@@ -92,14 +113,6 @@ enum nvm_sku_bits { | |||
92 | NVM_SKU_CAP_11AC_ENABLE = BIT(3), | 113 | NVM_SKU_CAP_11AC_ENABLE = BIT(3), |
93 | }; | 114 | }; |
94 | 115 | ||
95 | /* radio config bits (actual values from NVM definition) */ | ||
96 | #define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ | ||
97 | #define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ | ||
98 | #define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ | ||
99 | #define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ | ||
100 | #define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ | ||
101 | #define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ | ||
102 | |||
103 | /* | 116 | /* |
104 | * These are the channel numbers in the order that they are stored in the NVM | 117 | * These are the channel numbers in the order that they are stored in the NVM |
105 | */ | 118 | */ |
@@ -112,7 +125,17 @@ static const u8 iwl_nvm_channels[] = { | |||
112 | 149, 153, 157, 161, 165 | 125 | 149, 153, 157, 161, 165 |
113 | }; | 126 | }; |
114 | 127 | ||
128 | static const u8 iwl_nvm_channels_family_8000[] = { | ||
129 | /* 2.4 GHz */ | ||
130 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, | ||
131 | /* 5 GHz */ | ||
132 | 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, | ||
133 | 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, | ||
134 | 149, 153, 157, 161, 165, 169, 173, 177, 181 | ||
135 | }; | ||
136 | |||
115 | #define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) | 137 | #define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) |
138 | #define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000) | ||
116 | #define NUM_2GHZ_CHANNELS 14 | 139 | #define NUM_2GHZ_CHANNELS 14 |
117 | #define FIRST_2GHZ_HT_MINUS 5 | 140 | #define FIRST_2GHZ_HT_MINUS 5 |
118 | #define LAST_2GHZ_HT_PLUS 9 | 141 | #define LAST_2GHZ_HT_PLUS 9 |
@@ -179,13 +202,23 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
179 | struct ieee80211_channel *channel; | 202 | struct ieee80211_channel *channel; |
180 | u16 ch_flags; | 203 | u16 ch_flags; |
181 | bool is_5ghz; | 204 | bool is_5ghz; |
205 | int num_of_ch; | ||
206 | const u8 *nvm_chan; | ||
207 | |||
208 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { | ||
209 | num_of_ch = IWL_NUM_CHANNELS; | ||
210 | nvm_chan = &iwl_nvm_channels[0]; | ||
211 | } else { | ||
212 | num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000; | ||
213 | nvm_chan = &iwl_nvm_channels_family_8000[0]; | ||
214 | } | ||
182 | 215 | ||
183 | for (ch_idx = 0; ch_idx < IWL_NUM_CHANNELS; ch_idx++) { | 216 | for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { |
184 | ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); | 217 | ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); |
185 | if (!(ch_flags & NVM_CHANNEL_VALID)) { | 218 | if (!(ch_flags & NVM_CHANNEL_VALID)) { |
186 | IWL_DEBUG_EEPROM(dev, | 219 | IWL_DEBUG_EEPROM(dev, |
187 | "Ch. %d Flags %x [%sGHz] - No traffic\n", | 220 | "Ch. %d Flags %x [%sGHz] - No traffic\n", |
188 | iwl_nvm_channels[ch_idx], | 221 | nvm_chan[ch_idx], |
189 | ch_flags, | 222 | ch_flags, |
190 | (ch_idx >= NUM_2GHZ_CHANNELS) ? | 223 | (ch_idx >= NUM_2GHZ_CHANNELS) ? |
191 | "5.2" : "2.4"); | 224 | "5.2" : "2.4"); |
@@ -195,7 +228,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
195 | channel = &data->channels[n_channels]; | 228 | channel = &data->channels[n_channels]; |
196 | n_channels++; | 229 | n_channels++; |
197 | 230 | ||
198 | channel->hw_value = iwl_nvm_channels[ch_idx]; | 231 | channel->hw_value = nvm_chan[ch_idx]; |
199 | channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ? | 232 | channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ? |
200 | IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; | 233 | IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; |
201 | channel->center_freq = | 234 | channel->center_freq = |
@@ -206,11 +239,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
206 | channel->flags = IEEE80211_CHAN_NO_HT40; | 239 | channel->flags = IEEE80211_CHAN_NO_HT40; |
207 | if (ch_idx < NUM_2GHZ_CHANNELS && | 240 | if (ch_idx < NUM_2GHZ_CHANNELS && |
208 | (ch_flags & NVM_CHANNEL_40MHZ)) { | 241 | (ch_flags & NVM_CHANNEL_40MHZ)) { |
209 | if (iwl_nvm_channels[ch_idx] <= LAST_2GHZ_HT_PLUS) | 242 | if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) |
210 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | 243 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; |
211 | if (iwl_nvm_channels[ch_idx] >= FIRST_2GHZ_HT_MINUS) | 244 | if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) |
212 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | 245 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; |
213 | } else if (iwl_nvm_channels[ch_idx] <= LAST_5GHZ_HT && | 246 | } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT && |
214 | (ch_flags & NVM_CHANNEL_40MHZ)) { | 247 | (ch_flags & NVM_CHANNEL_40MHZ)) { |
215 | if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) | 248 | if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) |
216 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | 249 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; |
@@ -302,14 +335,23 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, | |||
302 | } | 335 | } |
303 | 336 | ||
304 | static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | 337 | static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, |
305 | struct iwl_nvm_data *data, const __le16 *nvm_sw, | 338 | struct iwl_nvm_data *data, |
306 | bool enable_vht, u8 tx_chains, u8 rx_chains) | 339 | const __le16 *ch_section, bool enable_vht, |
340 | u8 tx_chains, u8 rx_chains) | ||
307 | { | 341 | { |
308 | int n_channels = iwl_init_channel_map(dev, cfg, data, | 342 | int n_channels; |
309 | &nvm_sw[NVM_CHANNELS]); | ||
310 | int n_used = 0; | 343 | int n_used = 0; |
311 | struct ieee80211_supported_band *sband; | 344 | struct ieee80211_supported_band *sband; |
312 | 345 | ||
346 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | ||
347 | n_channels = iwl_init_channel_map( | ||
348 | dev, cfg, data, | ||
349 | &ch_section[NVM_CHANNELS]); | ||
350 | else | ||
351 | n_channels = iwl_init_channel_map( | ||
352 | dev, cfg, data, | ||
353 | &ch_section[NVM_CHANNELS_FAMILY_8000]); | ||
354 | |||
313 | sband = &data->bands[IEEE80211_BAND_2GHZ]; | 355 | sband = &data->bands[IEEE80211_BAND_2GHZ]; |
314 | sband->band = IEEE80211_BAND_2GHZ; | 356 | sband->band = IEEE80211_BAND_2GHZ; |
315 | sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; | 357 | sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; |
@@ -335,35 +377,122 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | |||
335 | n_used, n_channels); | 377 | n_used, n_channels); |
336 | } | 378 | } |
337 | 379 | ||
380 | static int iwl_get_sku(const struct iwl_cfg *cfg, | ||
381 | const __le16 *nvm_sw) | ||
382 | { | ||
383 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | ||
384 | return le16_to_cpup(nvm_sw + SKU); | ||
385 | else | ||
386 | return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000)); | ||
387 | } | ||
388 | |||
389 | static int iwl_get_nvm_version(const struct iwl_cfg *cfg, | ||
390 | const __le16 *nvm_sw) | ||
391 | { | ||
392 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | ||
393 | return le16_to_cpup(nvm_sw + NVM_VERSION); | ||
394 | else | ||
395 | return le32_to_cpup((__le32 *)(nvm_sw + | ||
396 | NVM_VERSION_FAMILY_8000)); | ||
397 | } | ||
398 | |||
399 | static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, | ||
400 | const __le16 *nvm_sw) | ||
401 | { | ||
402 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | ||
403 | return le16_to_cpup(nvm_sw + RADIO_CFG); | ||
404 | else | ||
405 | return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000)); | ||
406 | } | ||
407 | |||
408 | #define N_HW_ADDRS_MASK_FAMILY_8000 0xF | ||
409 | static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, | ||
410 | const __le16 *nvm_sw) | ||
411 | { | ||
412 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | ||
413 | return le16_to_cpup(nvm_sw + N_HW_ADDRS); | ||
414 | else | ||
415 | return le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)) | ||
416 | & N_HW_ADDRS_MASK_FAMILY_8000; | ||
417 | } | ||
418 | |||
419 | static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, | ||
420 | struct iwl_nvm_data *data, | ||
421 | u32 radio_cfg) | ||
422 | { | ||
423 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { | ||
424 | data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg); | ||
425 | data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg); | ||
426 | data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg); | ||
427 | data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg); | ||
428 | data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK(radio_cfg); | ||
429 | data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK(radio_cfg); | ||
430 | return; | ||
431 | } | ||
432 | |||
433 | /* set the radio configuration for family 8000 */ | ||
434 | data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK_FAMILY_8000(radio_cfg); | ||
435 | data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg); | ||
436 | data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg); | ||
437 | data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg); | ||
438 | data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(radio_cfg); | ||
439 | data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg); | ||
440 | } | ||
441 | |||
442 | static void iwl_set_hw_address(const struct iwl_cfg *cfg, | ||
443 | struct iwl_nvm_data *data, | ||
444 | const __le16 *nvm_sec) | ||
445 | { | ||
446 | u8 hw_addr[ETH_ALEN]; | ||
447 | |||
448 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | ||
449 | memcpy(hw_addr, nvm_sec + HW_ADDR, ETH_ALEN); | ||
450 | else | ||
451 | memcpy(hw_addr, nvm_sec + MAC_ADDRESS_OVERRIDE_FAMILY_8000, | ||
452 | ETH_ALEN); | ||
453 | |||
454 | /* The byte order is little endian 16 bit, meaning 214365 */ | ||
455 | data->hw_addr[0] = hw_addr[1]; | ||
456 | data->hw_addr[1] = hw_addr[0]; | ||
457 | data->hw_addr[2] = hw_addr[3]; | ||
458 | data->hw_addr[3] = hw_addr[2]; | ||
459 | data->hw_addr[4] = hw_addr[5]; | ||
460 | data->hw_addr[5] = hw_addr[4]; | ||
461 | } | ||
462 | |||
338 | struct iwl_nvm_data * | 463 | struct iwl_nvm_data * |
339 | iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, | 464 | iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, |
340 | const __le16 *nvm_hw, const __le16 *nvm_sw, | 465 | const __le16 *nvm_hw, const __le16 *nvm_sw, |
341 | const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains) | 466 | const __le16 *nvm_calib, const __le16 *regulatory, |
467 | const __le16 *mac_override, u8 tx_chains, u8 rx_chains) | ||
342 | { | 468 | { |
343 | struct iwl_nvm_data *data; | 469 | struct iwl_nvm_data *data; |
344 | u8 hw_addr[ETH_ALEN]; | 470 | u32 sku; |
345 | u16 radio_cfg, sku; | 471 | u32 radio_cfg; |
346 | 472 | ||
347 | data = kzalloc(sizeof(*data) + | 473 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) |
348 | sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, | 474 | data = kzalloc(sizeof(*data) + |
349 | GFP_KERNEL); | 475 | sizeof(struct ieee80211_channel) * |
476 | IWL_NUM_CHANNELS, | ||
477 | GFP_KERNEL); | ||
478 | else | ||
479 | data = kzalloc(sizeof(*data) + | ||
480 | sizeof(struct ieee80211_channel) * | ||
481 | IWL_NUM_CHANNELS_FAMILY_8000, | ||
482 | GFP_KERNEL); | ||
350 | if (!data) | 483 | if (!data) |
351 | return NULL; | 484 | return NULL; |
352 | 485 | ||
353 | data->nvm_version = le16_to_cpup(nvm_sw + NVM_VERSION); | 486 | data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); |
354 | 487 | ||
355 | radio_cfg = le16_to_cpup(nvm_sw + RADIO_CFG); | 488 | radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw); |
356 | data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg); | 489 | iwl_set_radio_cfg(cfg, data, radio_cfg); |
357 | data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg); | ||
358 | data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg); | ||
359 | data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg); | ||
360 | data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK(radio_cfg); | ||
361 | data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK(radio_cfg); | ||
362 | 490 | ||
363 | sku = le16_to_cpup(nvm_sw + SKU); | 491 | sku = iwl_get_sku(cfg, nvm_sw); |
364 | data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; | 492 | data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; |
365 | data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; | 493 | data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; |
366 | data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; | 494 | data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; |
495 | data->sku_cap_11ac_enable = sku & NVM_SKU_CAP_11AC_ENABLE; | ||
367 | if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) | 496 | if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) |
368 | data->sku_cap_11n_enable = false; | 497 | data->sku_cap_11n_enable = false; |
369 | 498 | ||
@@ -380,22 +509,34 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, | |||
380 | return NULL; | 509 | return NULL; |
381 | } | 510 | } |
382 | 511 | ||
383 | data->n_hw_addrs = le16_to_cpup(nvm_sw + N_HW_ADDRS); | 512 | data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); |
384 | 513 | ||
385 | data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); | 514 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { |
386 | data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); | 515 | /* Checking for required sections */ |
516 | if (!nvm_calib) { | ||
517 | IWL_ERR_DEV(dev, | ||
518 | "Can't parse empty Calib NVM sections\n"); | ||
519 | return NULL; | ||
520 | } | ||
521 | /* in family 8000 Xtal calibration values moved to OTP */ | ||
522 | data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); | ||
523 | data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); | ||
524 | } | ||
387 | 525 | ||
388 | /* The byte order is little endian 16 bit, meaning 214365 */ | 526 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { |
389 | memcpy(hw_addr, nvm_hw + HW_ADDR, ETH_ALEN); | 527 | iwl_set_hw_address(cfg, data, nvm_hw); |
390 | data->hw_addr[0] = hw_addr[1]; | 528 | |
391 | data->hw_addr[1] = hw_addr[0]; | 529 | iwl_init_sbands(dev, cfg, data, nvm_sw, |
392 | data->hw_addr[2] = hw_addr[3]; | 530 | sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, |
393 | data->hw_addr[3] = hw_addr[2]; | 531 | rx_chains); |
394 | data->hw_addr[4] = hw_addr[5]; | 532 | } else { |
395 | data->hw_addr[5] = hw_addr[4]; | 533 | /* MAC address in family 8000 */ |
534 | iwl_set_hw_address(cfg, data, mac_override); | ||
396 | 535 | ||
397 | iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE, | 536 | iwl_init_sbands(dev, cfg, data, regulatory, |
398 | tx_chains, rx_chains); | 537 | sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, |
538 | rx_chains); | ||
539 | } | ||
399 | 540 | ||
400 | data->calib_version = 255; | 541 | data->calib_version = 255; |
401 | 542 | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h index 0c4399aba8c6..c9c45a39d212 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h | |||
@@ -75,6 +75,7 @@ | |||
75 | struct iwl_nvm_data * | 75 | struct iwl_nvm_data * |
76 | iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, | 76 | iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, |
77 | const __le16 *nvm_hw, const __le16 *nvm_sw, | 77 | const __le16 *nvm_hw, const __le16 *nvm_sw, |
78 | const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains); | 78 | const __le16 *nvm_calib, const __le16 *regulatory, |
79 | const __le16 *mac_override, u8 tx_chains, u8 rx_chains); | ||
79 | 80 | ||
80 | #endif /* __iwl_nvm_parse_h__ */ | 81 | #endif /* __iwl_nvm_parse_h__ */ |
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index a7c88f1402e9..4d808a91ea7f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h | |||
@@ -306,7 +306,6 @@ struct iwl_phy_cfg_cmd { | |||
306 | #define PHY_CFG_RX_CHAIN_B BIT(13) | 306 | #define PHY_CFG_RX_CHAIN_B BIT(13) |
307 | #define PHY_CFG_RX_CHAIN_C BIT(14) | 307 | #define PHY_CFG_RX_CHAIN_C BIT(14) |
308 | 308 | ||
309 | #define NVM_MAX_NUM_SECTIONS 11 | ||
310 | 309 | ||
311 | /* Target of the NVM_ACCESS_CMD */ | 310 | /* Target of the NVM_ACCESS_CMD */ |
312 | enum { | 311 | enum { |
@@ -318,8 +317,11 @@ enum { | |||
318 | /* Section types for NVM_ACCESS_CMD */ | 317 | /* Section types for NVM_ACCESS_CMD */ |
319 | enum { | 318 | enum { |
320 | NVM_SECTION_TYPE_SW = 1, | 319 | NVM_SECTION_TYPE_SW = 1, |
320 | NVM_SECTION_TYPE_REGULATORY = 3, | ||
321 | NVM_SECTION_TYPE_CALIBRATION = 4, | 321 | NVM_SECTION_TYPE_CALIBRATION = 4, |
322 | NVM_SECTION_TYPE_PRODUCTION = 5, | 322 | NVM_SECTION_TYPE_PRODUCTION = 5, |
323 | NVM_SECTION_TYPE_MAC_OVERRIDE = 11, | ||
324 | NVM_MAX_NUM_SECTIONS = 12, | ||
323 | }; | 325 | }; |
324 | 326 | ||
325 | /** | 327 | /** |
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 2d5251b3600c..e80e81a3f828 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c | |||
@@ -228,13 +228,23 @@ static struct iwl_nvm_data * | |||
228 | iwl_parse_nvm_sections(struct iwl_mvm *mvm) | 228 | iwl_parse_nvm_sections(struct iwl_mvm *mvm) |
229 | { | 229 | { |
230 | struct iwl_nvm_section *sections = mvm->nvm_sections; | 230 | struct iwl_nvm_section *sections = mvm->nvm_sections; |
231 | const __le16 *hw, *sw, *calib; | 231 | const __le16 *hw, *sw, *calib, *regulatory, *mac_override; |
232 | 232 | ||
233 | /* Checking for required sections */ | 233 | /* Checking for required sections */ |
234 | if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || | 234 | if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { |
235 | !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { | 235 | if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || |
236 | IWL_ERR(mvm, "Can't parse empty NVM sections\n"); | 236 | !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { |
237 | return NULL; | 237 | IWL_ERR(mvm, "Can't parse empty NVM sections\n"); |
238 | return NULL; | ||
239 | } | ||
240 | } else { | ||
241 | if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || | ||
242 | !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data || | ||
243 | !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { | ||
244 | IWL_ERR(mvm, | ||
245 | "Can't parse empty family 8000 NVM sections\n"); | ||
246 | return NULL; | ||
247 | } | ||
238 | } | 248 | } |
239 | 249 | ||
240 | if (WARN_ON(!mvm->cfg)) | 250 | if (WARN_ON(!mvm->cfg)) |
@@ -243,7 +253,12 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) | |||
243 | hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; | 253 | hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; |
244 | sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; | 254 | sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; |
245 | calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; | 255 | calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; |
256 | regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; | ||
257 | mac_override = | ||
258 | (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; | ||
259 | |||
246 | return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, | 260 | return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, |
261 | regulatory, mac_override, | ||
247 | iwl_fw_valid_tx_ant(mvm->fw), | 262 | iwl_fw_valid_tx_ant(mvm->fw), |
248 | iwl_fw_valid_rx_ant(mvm->fw)); | 263 | iwl_fw_valid_rx_ant(mvm->fw)); |
249 | } | 264 | } |
@@ -285,6 +300,8 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) | |||
285 | 300 | ||
286 | #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) | 301 | #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) |
287 | #define NVM_WORD2_ID(x) (x >> 12) | 302 | #define NVM_WORD2_ID(x) (x >> 12) |
303 | #define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8)) | ||
304 | #define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4) | ||
288 | 305 | ||
289 | IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); | 306 | IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); |
290 | 307 | ||
@@ -335,8 +352,16 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) | |||
335 | break; | 352 | break; |
336 | } | 353 | } |
337 | 354 | ||
338 | section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); | 355 | if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { |
339 | section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); | 356 | section_size = |
357 | 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); | ||
358 | section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); | ||
359 | } else { | ||
360 | section_size = 2 * NVM_WORD2_LEN_FAMILY_8000( | ||
361 | le16_to_cpu(file_sec->word2)); | ||
362 | section_id = NVM_WORD1_ID_FAMILY_8000( | ||
363 | le16_to_cpu(file_sec->word1)); | ||
364 | } | ||
340 | 365 | ||
341 | if (section_size > IWL_MAX_NVM_SECTION_SIZE) { | 366 | if (section_size > IWL_MAX_NVM_SECTION_SIZE) { |
342 | IWL_ERR(mvm, "ERROR - section too large (%d)\n", | 367 | IWL_ERR(mvm, "ERROR - section too large (%d)\n", |
@@ -406,6 +431,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm) | |||
406 | { | 431 | { |
407 | int ret, i, section; | 432 | int ret, i, section; |
408 | u8 *nvm_buffer, *temp; | 433 | u8 *nvm_buffer, *temp; |
434 | int nvm_to_read[NVM_MAX_NUM_SECTIONS]; | ||
435 | int num_of_sections_to_read; | ||
409 | 436 | ||
410 | if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) | 437 | if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) |
411 | return -EINVAL; | 438 | return -EINVAL; |
@@ -418,12 +445,20 @@ int iwl_nvm_init(struct iwl_mvm *mvm) | |||
418 | return ret; | 445 | return ret; |
419 | } else { | 446 | } else { |
420 | /* list of NVM sections we are allowed/need to read */ | 447 | /* list of NVM sections we are allowed/need to read */ |
421 | int nvm_to_read[] = { | 448 | if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { |
422 | mvm->cfg->nvm_hw_section_num, | 449 | nvm_to_read[0] = mvm->cfg->nvm_hw_section_num; |
423 | NVM_SECTION_TYPE_SW, | 450 | nvm_to_read[1] = NVM_SECTION_TYPE_SW; |
424 | NVM_SECTION_TYPE_CALIBRATION, | 451 | nvm_to_read[2] = NVM_SECTION_TYPE_CALIBRATION; |
425 | NVM_SECTION_TYPE_PRODUCTION, | 452 | nvm_to_read[3] = NVM_SECTION_TYPE_PRODUCTION; |
426 | }; | 453 | num_of_sections_to_read = 4; |
454 | } else { | ||
455 | nvm_to_read[0] = NVM_SECTION_TYPE_SW; | ||
456 | nvm_to_read[1] = NVM_SECTION_TYPE_CALIBRATION; | ||
457 | nvm_to_read[2] = NVM_SECTION_TYPE_PRODUCTION; | ||
458 | nvm_to_read[3] = NVM_SECTION_TYPE_REGULATORY; | ||
459 | nvm_to_read[4] = NVM_SECTION_TYPE_MAC_OVERRIDE; | ||
460 | num_of_sections_to_read = 5; | ||
461 | } | ||
427 | 462 | ||
428 | /* Read From FW NVM */ | 463 | /* Read From FW NVM */ |
429 | IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); | 464 | IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); |
@@ -433,7 +468,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm) | |||
433 | GFP_KERNEL); | 468 | GFP_KERNEL); |
434 | if (!nvm_buffer) | 469 | if (!nvm_buffer) |
435 | return -ENOMEM; | 470 | return -ENOMEM; |
436 | for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { | 471 | for (i = 0; i < num_of_sections_to_read; i++) { |
437 | section = nvm_to_read[i]; | 472 | section = nvm_to_read[i]; |
438 | /* we override the constness for initial read */ | 473 | /* we override the constness for initial read */ |
439 | ret = iwl_nvm_read_section(mvm, section, nvm_buffer); | 474 | ret = iwl_nvm_read_section(mvm, section, nvm_buffer); |