diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-nvm-parse.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 411 |
1 files changed, 304 insertions, 107 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index c74f1a4edf23..774637746427 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | |||
@@ -103,8 +103,16 @@ enum family_8000_nvm_offsets { | |||
103 | SKU_FAMILY_8000 = 4, | 103 | SKU_FAMILY_8000 = 4, |
104 | N_HW_ADDRS_FAMILY_8000 = 5, | 104 | N_HW_ADDRS_FAMILY_8000 = 5, |
105 | 105 | ||
106 | /* NVM PHY-SKU-Section offset (in words) for B0 */ | ||
107 | RADIO_CFG_FAMILY_8000_B0 = 0, | ||
108 | SKU_FAMILY_8000_B0 = 2, | ||
109 | N_HW_ADDRS_FAMILY_8000_B0 = 3, | ||
110 | |||
106 | /* NVM REGULATORY -Section offset (in words) definitions */ | 111 | /* NVM REGULATORY -Section offset (in words) definitions */ |
107 | NVM_CHANNELS_FAMILY_8000 = 0, | 112 | NVM_CHANNELS_FAMILY_8000 = 0, |
113 | NVM_LAR_OFFSET_FAMILY_8000_OLD = 0x4C7, | ||
114 | NVM_LAR_OFFSET_FAMILY_8000 = 0x507, | ||
115 | NVM_LAR_ENABLED_FAMILY_8000 = 0x7, | ||
108 | 116 | ||
109 | /* NVM calibration section offset (in words) definitions */ | 117 | /* NVM calibration section offset (in words) definitions */ |
110 | NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8, | 118 | NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8, |
@@ -146,7 +154,9 @@ static const u8 iwl_nvm_channels_family_8000[] = { | |||
146 | #define NUM_2GHZ_CHANNELS_FAMILY_8000 14 | 154 | #define NUM_2GHZ_CHANNELS_FAMILY_8000 14 |
147 | #define FIRST_2GHZ_HT_MINUS 5 | 155 | #define FIRST_2GHZ_HT_MINUS 5 |
148 | #define LAST_2GHZ_HT_PLUS 9 | 156 | #define LAST_2GHZ_HT_PLUS 9 |
149 | #define LAST_5GHZ_HT 161 | 157 | #define LAST_5GHZ_HT 165 |
158 | #define LAST_5GHZ_HT_FAMILY_8000 181 | ||
159 | #define N_HW_ADDR_MASK 0xF | ||
150 | 160 | ||
151 | /* rate data (static) */ | 161 | /* rate data (static) */ |
152 | static struct ieee80211_rate iwl_cfg80211_rates[] = { | 162 | static struct ieee80211_rate iwl_cfg80211_rates[] = { |
@@ -201,9 +211,57 @@ enum iwl_nvm_channel_flags { | |||
201 | #define CHECK_AND_PRINT_I(x) \ | 211 | #define CHECK_AND_PRINT_I(x) \ |
202 | ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "") | 212 | ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "") |
203 | 213 | ||
214 | static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, | ||
215 | u16 nvm_flags, const struct iwl_cfg *cfg) | ||
216 | { | ||
217 | u32 flags = IEEE80211_CHAN_NO_HT40; | ||
218 | u32 last_5ghz_ht = LAST_5GHZ_HT; | ||
219 | |||
220 | if (cfg->device_family == IWL_DEVICE_FAMILY_8000) | ||
221 | last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; | ||
222 | |||
223 | if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) { | ||
224 | if (ch_num <= LAST_2GHZ_HT_PLUS) | ||
225 | flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | ||
226 | if (ch_num >= FIRST_2GHZ_HT_MINUS) | ||
227 | flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | ||
228 | } else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) { | ||
229 | if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) | ||
230 | flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | ||
231 | else | ||
232 | flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | ||
233 | } | ||
234 | if (!(nvm_flags & NVM_CHANNEL_80MHZ)) | ||
235 | flags |= IEEE80211_CHAN_NO_80MHZ; | ||
236 | if (!(nvm_flags & NVM_CHANNEL_160MHZ)) | ||
237 | flags |= IEEE80211_CHAN_NO_160MHZ; | ||
238 | |||
239 | if (!(nvm_flags & NVM_CHANNEL_IBSS)) | ||
240 | flags |= IEEE80211_CHAN_NO_IR; | ||
241 | |||
242 | if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) | ||
243 | flags |= IEEE80211_CHAN_NO_IR; | ||
244 | |||
245 | if (nvm_flags & NVM_CHANNEL_RADAR) | ||
246 | flags |= IEEE80211_CHAN_RADAR; | ||
247 | |||
248 | if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) | ||
249 | flags |= IEEE80211_CHAN_INDOOR_ONLY; | ||
250 | |||
251 | /* Set the GO concurrent flag only in case that NO_IR is set. | ||
252 | * Otherwise it is meaningless | ||
253 | */ | ||
254 | if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && | ||
255 | (flags & IEEE80211_CHAN_NO_IR)) | ||
256 | flags |= IEEE80211_CHAN_GO_CONCURRENT; | ||
257 | |||
258 | return flags; | ||
259 | } | ||
260 | |||
204 | static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | 261 | static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, |
205 | struct iwl_nvm_data *data, | 262 | struct iwl_nvm_data *data, |
206 | const __le16 * const nvm_ch_flags) | 263 | const __le16 * const nvm_ch_flags, |
264 | bool lar_supported) | ||
207 | { | 265 | { |
208 | int ch_idx; | 266 | int ch_idx; |
209 | int n_channels = 0; | 267 | int n_channels = 0; |
@@ -228,9 +286,14 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
228 | 286 | ||
229 | if (ch_idx >= num_2ghz_channels && | 287 | if (ch_idx >= num_2ghz_channels && |
230 | !data->sku_cap_band_52GHz_enable) | 288 | !data->sku_cap_band_52GHz_enable) |
231 | ch_flags &= ~NVM_CHANNEL_VALID; | 289 | continue; |
232 | 290 | ||
233 | if (!(ch_flags & NVM_CHANNEL_VALID)) { | 291 | if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) { |
292 | /* | ||
293 | * Channels might become valid later if lar is | ||
294 | * supported, hence we still want to add them to | ||
295 | * the list of supported channels to cfg80211. | ||
296 | */ | ||
234 | IWL_DEBUG_EEPROM(dev, | 297 | IWL_DEBUG_EEPROM(dev, |
235 | "Ch. %d Flags %x [%sGHz] - No traffic\n", | 298 | "Ch. %d Flags %x [%sGHz] - No traffic\n", |
236 | nvm_chan[ch_idx], | 299 | nvm_chan[ch_idx], |
@@ -250,45 +313,6 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
250 | ieee80211_channel_to_frequency( | 313 | ieee80211_channel_to_frequency( |
251 | channel->hw_value, channel->band); | 314 | channel->hw_value, channel->band); |
252 | 315 | ||
253 | /* TODO: Need to be dependent to the NVM */ | ||
254 | channel->flags = IEEE80211_CHAN_NO_HT40; | ||
255 | if (ch_idx < num_2ghz_channels && | ||
256 | (ch_flags & NVM_CHANNEL_40MHZ)) { | ||
257 | if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) | ||
258 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | ||
259 | if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) | ||
260 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | ||
261 | } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT && | ||
262 | (ch_flags & NVM_CHANNEL_40MHZ)) { | ||
263 | if ((ch_idx - num_2ghz_channels) % 2 == 0) | ||
264 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | ||
265 | else | ||
266 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | ||
267 | } | ||
268 | if (!(ch_flags & NVM_CHANNEL_80MHZ)) | ||
269 | channel->flags |= IEEE80211_CHAN_NO_80MHZ; | ||
270 | if (!(ch_flags & NVM_CHANNEL_160MHZ)) | ||
271 | channel->flags |= IEEE80211_CHAN_NO_160MHZ; | ||
272 | |||
273 | if (!(ch_flags & NVM_CHANNEL_IBSS)) | ||
274 | channel->flags |= IEEE80211_CHAN_NO_IR; | ||
275 | |||
276 | if (!(ch_flags & NVM_CHANNEL_ACTIVE)) | ||
277 | channel->flags |= IEEE80211_CHAN_NO_IR; | ||
278 | |||
279 | if (ch_flags & NVM_CHANNEL_RADAR) | ||
280 | channel->flags |= IEEE80211_CHAN_RADAR; | ||
281 | |||
282 | if (ch_flags & NVM_CHANNEL_INDOOR_ONLY) | ||
283 | channel->flags |= IEEE80211_CHAN_INDOOR_ONLY; | ||
284 | |||
285 | /* Set the GO concurrent flag only in case that NO_IR is set. | ||
286 | * Otherwise it is meaningless | ||
287 | */ | ||
288 | if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) && | ||
289 | (channel->flags & IEEE80211_CHAN_NO_IR)) | ||
290 | channel->flags |= IEEE80211_CHAN_GO_CONCURRENT; | ||
291 | |||
292 | /* Initialize regulatory-based run-time data */ | 316 | /* Initialize regulatory-based run-time data */ |
293 | 317 | ||
294 | /* | 318 | /* |
@@ -297,6 +321,15 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
297 | */ | 321 | */ |
298 | channel->max_power = IWL_DEFAULT_MAX_TX_POWER; | 322 | channel->max_power = IWL_DEFAULT_MAX_TX_POWER; |
299 | is_5ghz = channel->band == IEEE80211_BAND_5GHZ; | 323 | is_5ghz = channel->band == IEEE80211_BAND_5GHZ; |
324 | |||
325 | /* don't put limitations in case we're using LAR */ | ||
326 | if (!lar_supported) | ||
327 | channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], | ||
328 | ch_idx, is_5ghz, | ||
329 | ch_flags, cfg); | ||
330 | else | ||
331 | channel->flags = 0; | ||
332 | |||
300 | IWL_DEBUG_EEPROM(dev, | 333 | IWL_DEBUG_EEPROM(dev, |
301 | "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", | 334 | "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", |
302 | channel->hw_value, | 335 | channel->hw_value, |
@@ -370,8 +403,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, | |||
370 | 403 | ||
371 | static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | 404 | static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, |
372 | struct iwl_nvm_data *data, | 405 | struct iwl_nvm_data *data, |
373 | const __le16 *ch_section, bool enable_vht, | 406 | const __le16 *ch_section, |
374 | u8 tx_chains, u8 rx_chains) | 407 | u8 tx_chains, u8 rx_chains, bool lar_supported) |
375 | { | 408 | { |
376 | int n_channels; | 409 | int n_channels; |
377 | int n_used = 0; | 410 | int n_used = 0; |
@@ -380,11 +413,12 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | |||
380 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | 413 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) |
381 | n_channels = iwl_init_channel_map( | 414 | n_channels = iwl_init_channel_map( |
382 | dev, cfg, data, | 415 | dev, cfg, data, |
383 | &ch_section[NVM_CHANNELS]); | 416 | &ch_section[NVM_CHANNELS], lar_supported); |
384 | else | 417 | else |
385 | n_channels = iwl_init_channel_map( | 418 | n_channels = iwl_init_channel_map( |
386 | dev, cfg, data, | 419 | dev, cfg, data, |
387 | &ch_section[NVM_CHANNELS_FAMILY_8000]); | 420 | &ch_section[NVM_CHANNELS_FAMILY_8000], |
421 | lar_supported); | ||
388 | 422 | ||
389 | sband = &data->bands[IEEE80211_BAND_2GHZ]; | 423 | sband = &data->bands[IEEE80211_BAND_2GHZ]; |
390 | sband->band = IEEE80211_BAND_2GHZ; | 424 | sband->band = IEEE80211_BAND_2GHZ; |
@@ -403,7 +437,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | |||
403 | IEEE80211_BAND_5GHZ); | 437 | IEEE80211_BAND_5GHZ); |
404 | iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, | 438 | iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, |
405 | tx_chains, rx_chains); | 439 | tx_chains, rx_chains); |
406 | if (enable_vht) | 440 | if (data->sku_cap_11ac_enable) |
407 | iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, | 441 | iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, |
408 | tx_chains, rx_chains); | 442 | tx_chains, rx_chains); |
409 | 443 | ||
@@ -413,10 +447,15 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | |||
413 | } | 447 | } |
414 | 448 | ||
415 | static int iwl_get_sku(const struct iwl_cfg *cfg, | 449 | static int iwl_get_sku(const struct iwl_cfg *cfg, |
416 | const __le16 *nvm_sw) | 450 | const __le16 *nvm_sw, const __le16 *phy_sku, |
451 | bool is_family_8000_a_step) | ||
417 | { | 452 | { |
418 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | 453 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) |
419 | return le16_to_cpup(nvm_sw + SKU); | 454 | return le16_to_cpup(nvm_sw + SKU); |
455 | |||
456 | if (!is_family_8000_a_step) | ||
457 | return le32_to_cpup((__le32 *)(phy_sku + | ||
458 | SKU_FAMILY_8000_B0)); | ||
420 | else | 459 | else |
421 | return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000)); | 460 | return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000)); |
422 | } | 461 | } |
@@ -432,23 +471,36 @@ static int iwl_get_nvm_version(const struct iwl_cfg *cfg, | |||
432 | } | 471 | } |
433 | 472 | ||
434 | static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, | 473 | static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, |
435 | const __le16 *nvm_sw) | 474 | const __le16 *nvm_sw, const __le16 *phy_sku, |
475 | bool is_family_8000_a_step) | ||
436 | { | 476 | { |
437 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | 477 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) |
438 | return le16_to_cpup(nvm_sw + RADIO_CFG); | 478 | return le16_to_cpup(nvm_sw + RADIO_CFG); |
479 | |||
480 | if (!is_family_8000_a_step) | ||
481 | return le32_to_cpup((__le32 *)(phy_sku + | ||
482 | RADIO_CFG_FAMILY_8000_B0)); | ||
439 | else | 483 | else |
440 | return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000)); | 484 | return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000)); |
485 | |||
441 | } | 486 | } |
442 | 487 | ||
443 | #define N_HW_ADDRS_MASK_FAMILY_8000 0xF | ||
444 | static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, | 488 | static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, |
445 | const __le16 *nvm_sw) | 489 | const __le16 *nvm_sw, bool is_family_8000_a_step) |
446 | { | 490 | { |
491 | int n_hw_addr; | ||
492 | |||
447 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | 493 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) |
448 | return le16_to_cpup(nvm_sw + N_HW_ADDRS); | 494 | return le16_to_cpup(nvm_sw + N_HW_ADDRS); |
495 | |||
496 | if (!is_family_8000_a_step) | ||
497 | n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + | ||
498 | N_HW_ADDRS_FAMILY_8000_B0)); | ||
449 | else | 499 | else |
450 | return le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)) | 500 | n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + |
451 | & N_HW_ADDRS_MASK_FAMILY_8000; | 501 | N_HW_ADDRS_FAMILY_8000)); |
502 | |||
503 | return n_hw_addr & N_HW_ADDR_MASK; | ||
452 | } | 504 | } |
453 | 505 | ||
454 | static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, | 506 | static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, |
@@ -491,7 +543,8 @@ static void iwl_set_hw_address_family_8000(struct device *dev, | |||
491 | const struct iwl_cfg *cfg, | 543 | const struct iwl_cfg *cfg, |
492 | struct iwl_nvm_data *data, | 544 | struct iwl_nvm_data *data, |
493 | const __le16 *mac_override, | 545 | const __le16 *mac_override, |
494 | const __le16 *nvm_hw) | 546 | const __le16 *nvm_hw, |
547 | u32 mac_addr0, u32 mac_addr1) | ||
495 | { | 548 | { |
496 | const u8 *hw_addr; | 549 | const u8 *hw_addr; |
497 | 550 | ||
@@ -515,48 +568,17 @@ static void iwl_set_hw_address_family_8000(struct device *dev, | |||
515 | } | 568 | } |
516 | 569 | ||
517 | if (nvm_hw) { | 570 | if (nvm_hw) { |
518 | /* read the MAC address from OTP */ | 571 | /* read the MAC address from HW resisters */ |
519 | if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) { | 572 | hw_addr = (const u8 *)&mac_addr0; |
520 | /* read the mac address from the WFPM location */ | 573 | data->hw_addr[0] = hw_addr[3]; |
521 | hw_addr = (const u8 *)(nvm_hw + | 574 | data->hw_addr[1] = hw_addr[2]; |
522 | HW_ADDR0_WFPM_FAMILY_8000); | 575 | data->hw_addr[2] = hw_addr[1]; |
523 | data->hw_addr[0] = hw_addr[3]; | 576 | data->hw_addr[3] = hw_addr[0]; |
524 | data->hw_addr[1] = hw_addr[2]; | 577 | |
525 | data->hw_addr[2] = hw_addr[1]; | 578 | hw_addr = (const u8 *)&mac_addr1; |
526 | data->hw_addr[3] = hw_addr[0]; | 579 | data->hw_addr[4] = hw_addr[1]; |
527 | 580 | data->hw_addr[5] = hw_addr[0]; | |
528 | hw_addr = (const u8 *)(nvm_hw + | 581 | |
529 | HW_ADDR1_WFPM_FAMILY_8000); | ||
530 | data->hw_addr[4] = hw_addr[1]; | ||
531 | data->hw_addr[5] = hw_addr[0]; | ||
532 | } else if ((data->nvm_version >= 0xE08) && | ||
533 | (data->nvm_version < 0xE0B)) { | ||
534 | /* read "reverse order" from the PCIe location */ | ||
535 | hw_addr = (const u8 *)(nvm_hw + | ||
536 | HW_ADDR0_PCIE_FAMILY_8000); | ||
537 | data->hw_addr[5] = hw_addr[2]; | ||
538 | data->hw_addr[4] = hw_addr[1]; | ||
539 | data->hw_addr[3] = hw_addr[0]; | ||
540 | |||
541 | hw_addr = (const u8 *)(nvm_hw + | ||
542 | HW_ADDR1_PCIE_FAMILY_8000); | ||
543 | data->hw_addr[2] = hw_addr[3]; | ||
544 | data->hw_addr[1] = hw_addr[2]; | ||
545 | data->hw_addr[0] = hw_addr[1]; | ||
546 | } else { | ||
547 | /* read from the PCIe location */ | ||
548 | hw_addr = (const u8 *)(nvm_hw + | ||
549 | HW_ADDR0_PCIE_FAMILY_8000); | ||
550 | data->hw_addr[5] = hw_addr[0]; | ||
551 | data->hw_addr[4] = hw_addr[1]; | ||
552 | data->hw_addr[3] = hw_addr[2]; | ||
553 | |||
554 | hw_addr = (const u8 *)(nvm_hw + | ||
555 | HW_ADDR1_PCIE_FAMILY_8000); | ||
556 | data->hw_addr[2] = hw_addr[1]; | ||
557 | data->hw_addr[1] = hw_addr[2]; | ||
558 | data->hw_addr[0] = hw_addr[3]; | ||
559 | } | ||
560 | if (!is_valid_ether_addr(data->hw_addr)) | 582 | if (!is_valid_ether_addr(data->hw_addr)) |
561 | IWL_ERR_DEV(dev, | 583 | IWL_ERR_DEV(dev, |
562 | "mac address from hw section is not valid\n"); | 584 | "mac address from hw section is not valid\n"); |
@@ -571,11 +593,15 @@ struct iwl_nvm_data * | |||
571 | iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, | 593 | iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, |
572 | const __le16 *nvm_hw, const __le16 *nvm_sw, | 594 | const __le16 *nvm_hw, const __le16 *nvm_sw, |
573 | const __le16 *nvm_calib, const __le16 *regulatory, | 595 | const __le16 *nvm_calib, const __le16 *regulatory, |
574 | const __le16 *mac_override, u8 tx_chains, u8 rx_chains) | 596 | const __le16 *mac_override, const __le16 *phy_sku, |
597 | u8 tx_chains, u8 rx_chains, | ||
598 | bool lar_fw_supported, bool is_family_8000_a_step, | ||
599 | u32 mac_addr0, u32 mac_addr1) | ||
575 | { | 600 | { |
576 | struct iwl_nvm_data *data; | 601 | struct iwl_nvm_data *data; |
577 | u32 sku; | 602 | u32 sku; |
578 | u32 radio_cfg; | 603 | u32 radio_cfg; |
604 | u16 lar_config; | ||
579 | 605 | ||
580 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) | 606 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) |
581 | data = kzalloc(sizeof(*data) + | 607 | data = kzalloc(sizeof(*data) + |
@@ -592,22 +618,25 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, | |||
592 | 618 | ||
593 | data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); | 619 | data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); |
594 | 620 | ||
595 | radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw); | 621 | radio_cfg = |
622 | iwl_get_radio_cfg(cfg, nvm_sw, phy_sku, is_family_8000_a_step); | ||
596 | iwl_set_radio_cfg(cfg, data, radio_cfg); | 623 | iwl_set_radio_cfg(cfg, data, radio_cfg); |
597 | if (data->valid_tx_ant) | 624 | if (data->valid_tx_ant) |
598 | tx_chains &= data->valid_tx_ant; | 625 | tx_chains &= data->valid_tx_ant; |
599 | if (data->valid_rx_ant) | 626 | if (data->valid_rx_ant) |
600 | rx_chains &= data->valid_rx_ant; | 627 | rx_chains &= data->valid_rx_ant; |
601 | 628 | ||
602 | sku = iwl_get_sku(cfg, nvm_sw); | 629 | sku = iwl_get_sku(cfg, nvm_sw, phy_sku, is_family_8000_a_step); |
603 | data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; | 630 | data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; |
604 | data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; | 631 | data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; |
605 | data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; | 632 | data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; |
606 | data->sku_cap_11ac_enable = sku & NVM_SKU_CAP_11AC_ENABLE; | ||
607 | if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) | 633 | if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) |
608 | data->sku_cap_11n_enable = false; | 634 | data->sku_cap_11n_enable = false; |
635 | data->sku_cap_11ac_enable = data->sku_cap_11n_enable && | ||
636 | (sku & NVM_SKU_CAP_11AC_ENABLE); | ||
609 | 637 | ||
610 | data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); | 638 | data->n_hw_addrs = |
639 | iwl_get_n_hw_addrs(cfg, nvm_sw, is_family_8000_a_step); | ||
611 | 640 | ||
612 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { | 641 | if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { |
613 | /* Checking for required sections */ | 642 | /* Checking for required sections */ |
@@ -626,16 +655,23 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, | |||
626 | iwl_set_hw_address(cfg, data, nvm_hw); | 655 | iwl_set_hw_address(cfg, data, nvm_hw); |
627 | 656 | ||
628 | iwl_init_sbands(dev, cfg, data, nvm_sw, | 657 | iwl_init_sbands(dev, cfg, data, nvm_sw, |
629 | sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, | 658 | tx_chains, rx_chains, lar_fw_supported); |
630 | rx_chains); | ||
631 | } else { | 659 | } else { |
660 | u16 lar_offset = data->nvm_version < 0xE39 ? | ||
661 | NVM_LAR_OFFSET_FAMILY_8000_OLD : | ||
662 | NVM_LAR_OFFSET_FAMILY_8000; | ||
663 | |||
664 | lar_config = le16_to_cpup(regulatory + lar_offset); | ||
665 | data->lar_enabled = !!(lar_config & | ||
666 | NVM_LAR_ENABLED_FAMILY_8000); | ||
667 | |||
632 | /* MAC address in family 8000 */ | 668 | /* MAC address in family 8000 */ |
633 | iwl_set_hw_address_family_8000(dev, cfg, data, mac_override, | 669 | iwl_set_hw_address_family_8000(dev, cfg, data, mac_override, |
634 | nvm_hw); | 670 | nvm_hw, mac_addr0, mac_addr1); |
635 | 671 | ||
636 | iwl_init_sbands(dev, cfg, data, regulatory, | 672 | iwl_init_sbands(dev, cfg, data, regulatory, |
637 | sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, | 673 | tx_chains, rx_chains, |
638 | rx_chains); | 674 | lar_fw_supported && data->lar_enabled); |
639 | } | 675 | } |
640 | 676 | ||
641 | data->calib_version = 255; | 677 | data->calib_version = 255; |
@@ -643,3 +679,164 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, | |||
643 | return data; | 679 | return data; |
644 | } | 680 | } |
645 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); | 681 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); |
682 | |||
683 | static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, | ||
684 | int ch_idx, u16 nvm_flags, | ||
685 | const struct iwl_cfg *cfg) | ||
686 | { | ||
687 | u32 flags = NL80211_RRF_NO_HT40; | ||
688 | u32 last_5ghz_ht = LAST_5GHZ_HT; | ||
689 | |||
690 | if (cfg->device_family == IWL_DEVICE_FAMILY_8000) | ||
691 | last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; | ||
692 | |||
693 | if (ch_idx < NUM_2GHZ_CHANNELS && | ||
694 | (nvm_flags & NVM_CHANNEL_40MHZ)) { | ||
695 | if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) | ||
696 | flags &= ~NL80211_RRF_NO_HT40PLUS; | ||
697 | if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) | ||
698 | flags &= ~NL80211_RRF_NO_HT40MINUS; | ||
699 | } else if (nvm_chan[ch_idx] <= last_5ghz_ht && | ||
700 | (nvm_flags & NVM_CHANNEL_40MHZ)) { | ||
701 | if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) | ||
702 | flags &= ~NL80211_RRF_NO_HT40PLUS; | ||
703 | else | ||
704 | flags &= ~NL80211_RRF_NO_HT40MINUS; | ||
705 | } | ||
706 | |||
707 | if (!(nvm_flags & NVM_CHANNEL_80MHZ)) | ||
708 | flags |= NL80211_RRF_NO_80MHZ; | ||
709 | if (!(nvm_flags & NVM_CHANNEL_160MHZ)) | ||
710 | flags |= NL80211_RRF_NO_160MHZ; | ||
711 | |||
712 | if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) | ||
713 | flags |= NL80211_RRF_NO_IR; | ||
714 | |||
715 | if (nvm_flags & NVM_CHANNEL_RADAR) | ||
716 | flags |= NL80211_RRF_DFS; | ||
717 | |||
718 | if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) | ||
719 | flags |= NL80211_RRF_NO_OUTDOOR; | ||
720 | |||
721 | /* Set the GO concurrent flag only in case that NO_IR is set. | ||
722 | * Otherwise it is meaningless | ||
723 | */ | ||
724 | if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && | ||
725 | (flags & NL80211_RRF_NO_IR)) | ||
726 | flags |= NL80211_RRF_GO_CONCURRENT; | ||
727 | |||
728 | return flags; | ||
729 | } | ||
730 | |||
731 | struct ieee80211_regdomain * | ||
732 | iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, | ||
733 | int num_of_ch, __le32 *channels, u16 fw_mcc) | ||
734 | { | ||
735 | int ch_idx; | ||
736 | u16 ch_flags, prev_ch_flags = 0; | ||
737 | const u8 *nvm_chan = cfg->device_family == IWL_DEVICE_FAMILY_8000 ? | ||
738 | iwl_nvm_channels_family_8000 : iwl_nvm_channels; | ||
739 | struct ieee80211_regdomain *regd; | ||
740 | int size_of_regd; | ||
741 | struct ieee80211_reg_rule *rule; | ||
742 | enum ieee80211_band band; | ||
743 | int center_freq, prev_center_freq = 0; | ||
744 | int valid_rules = 0; | ||
745 | bool new_rule; | ||
746 | int max_num_ch = cfg->device_family == IWL_DEVICE_FAMILY_8000 ? | ||
747 | IWL_NUM_CHANNELS_FAMILY_8000 : IWL_NUM_CHANNELS; | ||
748 | |||
749 | if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES)) | ||
750 | return ERR_PTR(-EINVAL); | ||
751 | |||
752 | if (WARN_ON(num_of_ch > max_num_ch)) | ||
753 | num_of_ch = max_num_ch; | ||
754 | |||
755 | IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n", | ||
756 | num_of_ch); | ||
757 | |||
758 | /* build a regdomain rule for every valid channel */ | ||
759 | size_of_regd = | ||
760 | sizeof(struct ieee80211_regdomain) + | ||
761 | num_of_ch * sizeof(struct ieee80211_reg_rule); | ||
762 | |||
763 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
764 | if (!regd) | ||
765 | return ERR_PTR(-ENOMEM); | ||
766 | |||
767 | for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { | ||
768 | ch_flags = (u16)__le32_to_cpup(channels + ch_idx); | ||
769 | band = (ch_idx < NUM_2GHZ_CHANNELS) ? | ||
770 | IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; | ||
771 | center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx], | ||
772 | band); | ||
773 | new_rule = false; | ||
774 | |||
775 | if (!(ch_flags & NVM_CHANNEL_VALID)) { | ||
776 | IWL_DEBUG_DEV(dev, IWL_DL_LAR, | ||
777 | "Ch. %d Flags %x [%sGHz] - No traffic\n", | ||
778 | nvm_chan[ch_idx], | ||
779 | ch_flags, | ||
780 | (ch_idx >= NUM_2GHZ_CHANNELS) ? | ||
781 | "5.2" : "2.4"); | ||
782 | continue; | ||
783 | } | ||
784 | |||
785 | /* we can't continue the same rule */ | ||
786 | if (ch_idx == 0 || prev_ch_flags != ch_flags || | ||
787 | center_freq - prev_center_freq > 20) { | ||
788 | valid_rules++; | ||
789 | new_rule = true; | ||
790 | } | ||
791 | |||
792 | rule = ®d->reg_rules[valid_rules - 1]; | ||
793 | |||
794 | if (new_rule) | ||
795 | rule->freq_range.start_freq_khz = | ||
796 | MHZ_TO_KHZ(center_freq - 10); | ||
797 | |||
798 | rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10); | ||
799 | |||
800 | /* this doesn't matter - not used by FW */ | ||
801 | rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); | ||
802 | rule->power_rule.max_eirp = | ||
803 | DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER); | ||
804 | |||
805 | rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, | ||
806 | ch_flags, cfg); | ||
807 | |||
808 | /* rely on auto-calculation to merge BW of contiguous chans */ | ||
809 | rule->flags |= NL80211_RRF_AUTO_BW; | ||
810 | rule->freq_range.max_bandwidth_khz = 0; | ||
811 | |||
812 | prev_ch_flags = ch_flags; | ||
813 | prev_center_freq = center_freq; | ||
814 | |||
815 | IWL_DEBUG_DEV(dev, IWL_DL_LAR, | ||
816 | "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n", | ||
817 | center_freq, | ||
818 | band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", | ||
819 | CHECK_AND_PRINT_I(VALID), | ||
820 | CHECK_AND_PRINT_I(ACTIVE), | ||
821 | CHECK_AND_PRINT_I(RADAR), | ||
822 | CHECK_AND_PRINT_I(WIDE), | ||
823 | CHECK_AND_PRINT_I(40MHZ), | ||
824 | CHECK_AND_PRINT_I(80MHZ), | ||
825 | CHECK_AND_PRINT_I(160MHZ), | ||
826 | CHECK_AND_PRINT_I(INDOOR_ONLY), | ||
827 | CHECK_AND_PRINT_I(GO_CONCURRENT), | ||
828 | ch_flags, | ||
829 | ((ch_flags & NVM_CHANNEL_ACTIVE) && | ||
830 | !(ch_flags & NVM_CHANNEL_RADAR)) | ||
831 | ? "" : "not "); | ||
832 | } | ||
833 | |||
834 | regd->n_reg_rules = valid_rules; | ||
835 | |||
836 | /* set alpha2 from FW. */ | ||
837 | regd->alpha2[0] = fw_mcc >> 8; | ||
838 | regd->alpha2[1] = fw_mcc & 0xff; | ||
839 | |||
840 | return regd; | ||
841 | } | ||
842 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); | ||