diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 552 |
1 files changed, 479 insertions, 73 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index ca0174680af9..96e101d79662 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. | 8 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. |
| 9 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | 9 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
| 10 | * Copyright(c) 2016 - 2017 Intel Deutschland GmbH | 10 | * Copyright(c) 2016 - 2017 Intel Deutschland GmbH |
| 11 | * Copyright(c) 2018 Intel Corporation | ||
| 11 | * | 12 | * |
| 12 | * This program is free software; you can redistribute it and/or modify | 13 | * This program is free software; you can redistribute it and/or modify |
| 13 | * it under the terms of version 2 of the GNU General Public License as | 14 | * it under the terms of version 2 of the GNU General Public License as |
| @@ -18,11 +19,6 @@ | |||
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 19 | * General Public License for more details. | 20 | * General Public License for more details. |
| 20 | * | 21 | * |
| 21 | * You should have received a copy of the GNU General Public License | ||
| 22 | * along with this program; if not, write to the Free Software | ||
| 23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, | ||
| 24 | * USA | ||
| 25 | * | ||
| 26 | * The full GNU General Public License is included in this distribution | 22 | * The full GNU General Public License is included in this distribution |
| 27 | * in the file called COPYING. | 23 | * in the file called COPYING. |
| 28 | * | 24 | * |
| @@ -35,6 +31,7 @@ | |||
| 35 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. | 31 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. |
| 36 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | 32 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
| 37 | * Copyright(c) 2016 - 2017 Intel Deutschland GmbH | 33 | * Copyright(c) 2016 - 2017 Intel Deutschland GmbH |
| 34 | * Copyright(c) 2018 Intel Corporation | ||
| 38 | * All rights reserved. | 35 | * All rights reserved. |
| 39 | * | 36 | * |
| 40 | * Redistribution and use in source and binary forms, with or without | 37 | * Redistribution and use in source and binary forms, with or without |
| @@ -68,6 +65,7 @@ | |||
| 68 | #include <linux/export.h> | 65 | #include <linux/export.h> |
| 69 | #include <linux/etherdevice.h> | 66 | #include <linux/etherdevice.h> |
| 70 | #include <linux/pci.h> | 67 | #include <linux/pci.h> |
| 68 | #include <linux/firmware.h> | ||
| 71 | 69 | ||
| 72 | #include "iwl-drv.h" | 70 | #include "iwl-drv.h" |
| 73 | #include "iwl-modparams.h" | 71 | #include "iwl-modparams.h" |
| @@ -77,6 +75,9 @@ | |||
| 77 | #include "iwl-csr.h" | 75 | #include "iwl-csr.h" |
| 78 | #include "fw/acpi.h" | 76 | #include "fw/acpi.h" |
| 79 | #include "fw/api/nvm-reg.h" | 77 | #include "fw/api/nvm-reg.h" |
| 78 | #include "fw/api/commands.h" | ||
| 79 | #include "fw/api/cmdhdr.h" | ||
| 80 | #include "fw/img.h" | ||
| 80 | 81 | ||
| 81 | /* NVM offsets (in words) definitions */ | 82 | /* NVM offsets (in words) definitions */ |
| 82 | enum nvm_offsets { | 83 | enum nvm_offsets { |
| @@ -292,7 +293,7 @@ static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, | |||
| 292 | static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | 293 | static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, |
| 293 | struct iwl_nvm_data *data, | 294 | struct iwl_nvm_data *data, |
| 294 | const __le16 * const nvm_ch_flags, | 295 | const __le16 * const nvm_ch_flags, |
| 295 | bool lar_supported, bool no_wide_in_5ghz) | 296 | u32 sbands_flags) |
| 296 | { | 297 | { |
| 297 | int ch_idx; | 298 | int ch_idx; |
| 298 | int n_channels = 0; | 299 | int n_channels = 0; |
| @@ -316,11 +317,12 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
| 316 | 317 | ||
| 317 | ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); | 318 | ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); |
| 318 | 319 | ||
| 319 | if (is_5ghz && !data->sku_cap_band_52GHz_enable) | 320 | if (is_5ghz && !data->sku_cap_band_52ghz_enable) |
| 320 | continue; | 321 | continue; |
| 321 | 322 | ||
| 322 | /* workaround to disable wide channels in 5GHz */ | 323 | /* workaround to disable wide channels in 5GHz */ |
| 323 | if (no_wide_in_5ghz && is_5ghz) { | 324 | if ((sbands_flags & IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ) && |
| 325 | is_5ghz) { | ||
| 324 | ch_flags &= ~(NVM_CHANNEL_40MHZ | | 326 | ch_flags &= ~(NVM_CHANNEL_40MHZ | |
| 325 | NVM_CHANNEL_80MHZ | | 327 | NVM_CHANNEL_80MHZ | |
| 326 | NVM_CHANNEL_160MHZ); | 328 | NVM_CHANNEL_160MHZ); |
| @@ -329,7 +331,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
| 329 | if (ch_flags & NVM_CHANNEL_160MHZ) | 331 | if (ch_flags & NVM_CHANNEL_160MHZ) |
| 330 | data->vht160_supported = true; | 332 | data->vht160_supported = true; |
| 331 | 333 | ||
| 332 | if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) { | 334 | if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR) && |
| 335 | !(ch_flags & NVM_CHANNEL_VALID)) { | ||
| 333 | /* | 336 | /* |
| 334 | * Channels might become valid later if lar is | 337 | * Channels might become valid later if lar is |
| 335 | * supported, hence we still want to add them to | 338 | * supported, hence we still want to add them to |
| @@ -359,7 +362,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, | |||
| 359 | channel->max_power = IWL_DEFAULT_MAX_TX_POWER; | 362 | channel->max_power = IWL_DEFAULT_MAX_TX_POWER; |
| 360 | 363 | ||
| 361 | /* don't put limitations in case we're using LAR */ | 364 | /* don't put limitations in case we're using LAR */ |
| 362 | if (!lar_supported) | 365 | if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR)) |
| 363 | channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], | 366 | channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], |
| 364 | ch_idx, is_5ghz, | 367 | ch_idx, is_5ghz, |
| 365 | ch_flags, cfg); | 368 | ch_flags, cfg); |
| @@ -422,6 +425,13 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, | |||
| 422 | else | 425 | else |
| 423 | vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; | 426 | vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; |
| 424 | break; | 427 | break; |
| 428 | case IWL_AMSDU_2K: | ||
| 429 | if (cfg->mq_rx_supported) | ||
| 430 | vht_cap->cap |= | ||
| 431 | IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; | ||
| 432 | else | ||
| 433 | WARN(1, "RB size of 2K is not supported by this device\n"); | ||
| 434 | break; | ||
| 425 | case IWL_AMSDU_4K: | 435 | case IWL_AMSDU_4K: |
| 426 | vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; | 436 | vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; |
| 427 | break; | 437 | break; |
| @@ -455,17 +465,137 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, | |||
| 455 | vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; | 465 | vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; |
| 456 | } | 466 | } |
| 457 | 467 | ||
| 458 | void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | 468 | static struct ieee80211_sband_iftype_data iwl_he_capa = { |
| 459 | struct iwl_nvm_data *data, const __le16 *nvm_ch_flags, | 469 | .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), |
| 460 | u8 tx_chains, u8 rx_chains, bool lar_supported, | 470 | .he_cap = { |
| 461 | bool no_wide_in_5ghz) | 471 | .has_he = true, |
| 472 | .he_cap_elem = { | ||
| 473 | .mac_cap_info[0] = | ||
| 474 | IEEE80211_HE_MAC_CAP0_HTC_HE | | ||
| 475 | IEEE80211_HE_MAC_CAP0_TWT_REQ, | ||
| 476 | .mac_cap_info[1] = | ||
| 477 | IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | | ||
| 478 | IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, | ||
| 479 | .mac_cap_info[2] = | ||
| 480 | IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP | | ||
| 481 | IEEE80211_HE_MAC_CAP2_MU_CASCADING | | ||
| 482 | IEEE80211_HE_MAC_CAP2_ACK_EN, | ||
| 483 | .mac_cap_info[3] = | ||
| 484 | IEEE80211_HE_MAC_CAP3_OMI_CONTROL | | ||
| 485 | IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2, | ||
| 486 | .mac_cap_info[4] = | ||
| 487 | IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU | | ||
| 488 | IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39, | ||
| 489 | .mac_cap_info[5] = | ||
| 490 | IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 | | ||
| 491 | IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 | | ||
| 492 | IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU, | ||
| 493 | .phy_cap_info[0] = | ||
| 494 | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | | ||
| 495 | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | | ||
| 496 | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G, | ||
| 497 | .phy_cap_info[1] = | ||
| 498 | IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | | ||
| 499 | IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | | ||
| 500 | IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | | ||
| 501 | IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, | ||
| 502 | .phy_cap_info[2] = | ||
| 503 | IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | | ||
| 504 | IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | | ||
| 505 | IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | | ||
| 506 | IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | | ||
| 507 | IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, | ||
| 508 | .phy_cap_info[3] = | ||
| 509 | IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK | | ||
| 510 | IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 | | ||
| 511 | IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK | | ||
| 512 | IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1, | ||
| 513 | .phy_cap_info[4] = | ||
| 514 | IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | | ||
| 515 | IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 | | ||
| 516 | IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8, | ||
| 517 | .phy_cap_info[5] = | ||
| 518 | IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 | | ||
| 519 | IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2 | | ||
| 520 | IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK | | ||
| 521 | IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK, | ||
| 522 | .phy_cap_info[6] = | ||
| 523 | IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | | ||
| 524 | IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | | ||
| 525 | IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB | | ||
| 526 | IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB | | ||
| 527 | IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | | ||
| 528 | IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO | | ||
| 529 | IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT, | ||
| 530 | .phy_cap_info[7] = | ||
| 531 | IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR | | ||
| 532 | IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI | | ||
| 533 | IEEE80211_HE_PHY_CAP7_MAX_NC_1, | ||
| 534 | .phy_cap_info[8] = | ||
| 535 | IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI | | ||
| 536 | IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | | ||
| 537 | IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | | ||
| 538 | IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU | | ||
| 539 | IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_160_OR_80P80_MHZ, | ||
| 540 | .phy_cap_info[9] = | ||
| 541 | IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | | ||
| 542 | IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | | ||
| 543 | IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB, | ||
| 544 | }, | ||
| 545 | /* | ||
| 546 | * Set default Tx/Rx HE MCS NSS Support field. Indicate support | ||
| 547 | * for up to 2 spatial streams and all MCS, without any special | ||
| 548 | * cases | ||
| 549 | */ | ||
| 550 | .he_mcs_nss_supp = { | ||
| 551 | .rx_mcs_80 = cpu_to_le16(0xfffa), | ||
| 552 | .tx_mcs_80 = cpu_to_le16(0xfffa), | ||
| 553 | .rx_mcs_160 = cpu_to_le16(0xfffa), | ||
| 554 | .tx_mcs_160 = cpu_to_le16(0xfffa), | ||
| 555 | .rx_mcs_80p80 = cpu_to_le16(0xffff), | ||
| 556 | .tx_mcs_80p80 = cpu_to_le16(0xffff), | ||
| 557 | }, | ||
| 558 | /* | ||
| 559 | * Set default PPE thresholds, with PPET16 set to 0, PPET8 set | ||
| 560 | * to 7 | ||
| 561 | */ | ||
| 562 | .ppe_thres = {0x61, 0x1c, 0xc7, 0x71}, | ||
| 563 | }, | ||
| 564 | }; | ||
| 565 | |||
| 566 | static void iwl_init_he_hw_capab(struct ieee80211_supported_band *sband, | ||
| 567 | u8 tx_chains, u8 rx_chains) | ||
| 568 | { | ||
| 569 | if (sband->band == NL80211_BAND_2GHZ || | ||
| 570 | sband->band == NL80211_BAND_5GHZ) | ||
| 571 | sband->iftype_data = &iwl_he_capa; | ||
| 572 | else | ||
| 573 | return; | ||
| 574 | |||
| 575 | sband->n_iftype_data = 1; | ||
| 576 | |||
| 577 | /* If not 2x2, we need to indicate 1x1 in the Midamble RX Max NSTS */ | ||
| 578 | if ((tx_chains & rx_chains) != ANT_AB) { | ||
| 579 | iwl_he_capa.he_cap.he_cap_elem.phy_cap_info[1] &= | ||
| 580 | ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS; | ||
| 581 | iwl_he_capa.he_cap.he_cap_elem.phy_cap_info[2] &= | ||
| 582 | ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS; | ||
| 583 | iwl_he_capa.he_cap.he_cap_elem.phy_cap_info[7] &= | ||
| 584 | ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK; | ||
| 585 | } | ||
| 586 | } | ||
| 587 | |||
| 588 | static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | ||
| 589 | struct iwl_nvm_data *data, | ||
| 590 | const __le16 *nvm_ch_flags, u8 tx_chains, | ||
| 591 | u8 rx_chains, u32 sbands_flags) | ||
| 462 | { | 592 | { |
| 463 | int n_channels; | 593 | int n_channels; |
| 464 | int n_used = 0; | 594 | int n_used = 0; |
| 465 | struct ieee80211_supported_band *sband; | 595 | struct ieee80211_supported_band *sband; |
| 466 | 596 | ||
| 467 | n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags, | 597 | n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags, |
| 468 | lar_supported, no_wide_in_5ghz); | 598 | sbands_flags); |
| 469 | sband = &data->bands[NL80211_BAND_2GHZ]; | 599 | sband = &data->bands[NL80211_BAND_2GHZ]; |
| 470 | sband->band = NL80211_BAND_2GHZ; | 600 | sband->band = NL80211_BAND_2GHZ; |
| 471 | sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; | 601 | sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; |
| @@ -475,6 +605,9 @@ void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | |||
| 475 | iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, NL80211_BAND_2GHZ, | 605 | iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, NL80211_BAND_2GHZ, |
| 476 | tx_chains, rx_chains); | 606 | tx_chains, rx_chains); |
| 477 | 607 | ||
| 608 | if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) | ||
| 609 | iwl_init_he_hw_capab(sband, tx_chains, rx_chains); | ||
| 610 | |||
| 478 | sband = &data->bands[NL80211_BAND_5GHZ]; | 611 | sband = &data->bands[NL80211_BAND_5GHZ]; |
| 479 | sband->band = NL80211_BAND_5GHZ; | 612 | sband->band = NL80211_BAND_5GHZ; |
| 480 | sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; | 613 | sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; |
| @@ -487,11 +620,13 @@ void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, | |||
| 487 | iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, | 620 | iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, |
| 488 | tx_chains, rx_chains); | 621 | tx_chains, rx_chains); |
| 489 | 622 | ||
| 623 | if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) | ||
| 624 | iwl_init_he_hw_capab(sband, tx_chains, rx_chains); | ||
| 625 | |||
| 490 | if (n_channels != n_used) | 626 | if (n_channels != n_used) |
| 491 | IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", | 627 | IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", |
| 492 | n_used, n_channels); | 628 | n_used, n_channels); |
| 493 | } | 629 | } |
| 494 | IWL_EXPORT_SYMBOL(iwl_init_sbands); | ||
| 495 | 630 | ||
| 496 | static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, | 631 | static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, |
| 497 | const __le16 *phy_sku) | 632 | const __le16 *phy_sku) |
| @@ -569,11 +704,15 @@ static void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest) | |||
| 569 | dest[5] = hw_addr[0]; | 704 | dest[5] = hw_addr[0]; |
| 570 | } | 705 | } |
| 571 | 706 | ||
| 572 | void iwl_set_hw_address_from_csr(struct iwl_trans *trans, | 707 | static void iwl_set_hw_address_from_csr(struct iwl_trans *trans, |
| 573 | struct iwl_nvm_data *data) | 708 | struct iwl_nvm_data *data) |
| 574 | { | 709 | { |
| 575 | __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_STRAP)); | 710 | __le32 mac_addr0 = |
| 576 | __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_STRAP)); | 711 | cpu_to_le32(iwl_read32(trans, |
| 712 | trans->cfg->csr->mac_addr0_strap)); | ||
| 713 | __le32 mac_addr1 = | ||
| 714 | cpu_to_le32(iwl_read32(trans, | ||
| 715 | trans->cfg->csr->mac_addr1_strap)); | ||
| 577 | 716 | ||
| 578 | iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); | 717 | iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); |
| 579 | /* | 718 | /* |
| @@ -583,12 +722,13 @@ void iwl_set_hw_address_from_csr(struct iwl_trans *trans, | |||
| 583 | if (is_valid_ether_addr(data->hw_addr)) | 722 | if (is_valid_ether_addr(data->hw_addr)) |
| 584 | return; | 723 | return; |
| 585 | 724 | ||
| 586 | mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP)); | 725 | mac_addr0 = cpu_to_le32(iwl_read32(trans, |
| 587 | mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP)); | 726 | trans->cfg->csr->mac_addr0_otp)); |
| 727 | mac_addr1 = cpu_to_le32(iwl_read32(trans, | ||
| 728 | trans->cfg->csr->mac_addr1_otp)); | ||
| 588 | 729 | ||
| 589 | iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); | 730 | iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); |
| 590 | } | 731 | } |
| 591 | IWL_EXPORT_SYMBOL(iwl_set_hw_address_from_csr); | ||
| 592 | 732 | ||
| 593 | static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, | 733 | static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, |
| 594 | const struct iwl_cfg *cfg, | 734 | const struct iwl_cfg *cfg, |
| @@ -713,8 +853,8 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
| 713 | struct device *dev = trans->dev; | 853 | struct device *dev = trans->dev; |
| 714 | struct iwl_nvm_data *data; | 854 | struct iwl_nvm_data *data; |
| 715 | bool lar_enabled; | 855 | bool lar_enabled; |
| 716 | bool no_wide_in_5ghz = iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw); | ||
| 717 | u32 sku, radio_cfg; | 856 | u32 sku, radio_cfg; |
| 857 | u32 sbands_flags = 0; | ||
| 718 | u16 lar_config; | 858 | u16 lar_config; |
| 719 | const __le16 *ch_section; | 859 | const __le16 *ch_section; |
| 720 | 860 | ||
| @@ -741,8 +881,8 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
| 741 | rx_chains &= data->valid_rx_ant; | 881 | rx_chains &= data->valid_rx_ant; |
| 742 | 882 | ||
| 743 | sku = iwl_get_sku(cfg, nvm_sw, phy_sku); | 883 | sku = iwl_get_sku(cfg, nvm_sw, phy_sku); |
| 744 | data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; | 884 | data->sku_cap_band_24ghz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; |
| 745 | data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; | 885 | data->sku_cap_band_52ghz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; |
| 746 | data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; | 886 | data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; |
| 747 | if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) | 887 | if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) |
| 748 | data->sku_cap_11n_enable = false; | 888 | data->sku_cap_11n_enable = false; |
| @@ -787,8 +927,14 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
| 787 | return NULL; | 927 | return NULL; |
| 788 | } | 928 | } |
| 789 | 929 | ||
| 930 | if (lar_fw_supported && lar_enabled) | ||
| 931 | sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR; | ||
| 932 | |||
| 933 | if (iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw)) | ||
| 934 | sbands_flags |= IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ; | ||
| 935 | |||
| 790 | iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains, | 936 | iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains, |
| 791 | lar_fw_supported && lar_enabled, no_wide_in_5ghz); | 937 | sbands_flags); |
| 792 | data->calib_version = 255; | 938 | data->calib_version = 255; |
| 793 | 939 | ||
| 794 | return data; | 940 | return data; |
| @@ -859,15 +1005,12 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, | |||
| 859 | const u8 *nvm_chan = cfg->nvm_type == IWL_NVM_EXT ? | 1005 | const u8 *nvm_chan = cfg->nvm_type == IWL_NVM_EXT ? |
| 860 | iwl_ext_nvm_channels : iwl_nvm_channels; | 1006 | iwl_ext_nvm_channels : iwl_nvm_channels; |
| 861 | struct ieee80211_regdomain *regd, *copy_rd; | 1007 | struct ieee80211_regdomain *regd, *copy_rd; |
| 862 | int size_of_regd, regd_to_copy, wmms_to_copy; | 1008 | int size_of_regd, regd_to_copy; |
| 863 | int size_of_wmms = 0; | ||
| 864 | struct ieee80211_reg_rule *rule; | 1009 | struct ieee80211_reg_rule *rule; |
| 865 | struct ieee80211_wmm_rule *wmm_rule, *d_wmm, *s_wmm; | ||
| 866 | struct regdb_ptrs *regdb_ptrs; | 1010 | struct regdb_ptrs *regdb_ptrs; |
| 867 | enum nl80211_band band; | 1011 | enum nl80211_band band; |
| 868 | int center_freq, prev_center_freq = 0; | 1012 | int center_freq, prev_center_freq = 0; |
| 869 | int valid_rules = 0, n_wmms = 0; | 1013 | int valid_rules = 0; |
| 870 | int i; | ||
| 871 | bool new_rule; | 1014 | bool new_rule; |
| 872 | int max_num_ch = cfg->nvm_type == IWL_NVM_EXT ? | 1015 | int max_num_ch = cfg->nvm_type == IWL_NVM_EXT ? |
| 873 | IWL_NVM_NUM_CHANNELS_EXT : IWL_NVM_NUM_CHANNELS; | 1016 | IWL_NVM_NUM_CHANNELS_EXT : IWL_NVM_NUM_CHANNELS; |
| @@ -886,11 +1029,7 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, | |||
| 886 | sizeof(struct ieee80211_regdomain) + | 1029 | sizeof(struct ieee80211_regdomain) + |
| 887 | num_of_ch * sizeof(struct ieee80211_reg_rule); | 1030 | num_of_ch * sizeof(struct ieee80211_reg_rule); |
| 888 | 1031 | ||
| 889 | if (geo_info & GEO_WMM_ETSI_5GHZ_INFO) | 1032 | regd = kzalloc(size_of_regd, GFP_KERNEL); |
| 890 | size_of_wmms = | ||
| 891 | num_of_ch * sizeof(struct ieee80211_wmm_rule); | ||
| 892 | |||
| 893 | regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL); | ||
| 894 | if (!regd) | 1033 | if (!regd) |
| 895 | return ERR_PTR(-ENOMEM); | 1034 | return ERR_PTR(-ENOMEM); |
| 896 | 1035 | ||
| @@ -904,8 +1043,6 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, | |||
| 904 | regd->alpha2[0] = fw_mcc >> 8; | 1043 | regd->alpha2[0] = fw_mcc >> 8; |
| 905 | regd->alpha2[1] = fw_mcc & 0xff; | 1044 | regd->alpha2[1] = fw_mcc & 0xff; |
| 906 | 1045 | ||
| 907 | wmm_rule = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd); | ||
| 908 | |||
| 909 | for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { | 1046 | for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { |
| 910 | ch_flags = (u16)__le32_to_cpup(channels + ch_idx); | 1047 | ch_flags = (u16)__le32_to_cpup(channels + ch_idx); |
| 911 | band = (ch_idx < NUM_2GHZ_CHANNELS) ? | 1048 | band = (ch_idx < NUM_2GHZ_CHANNELS) ? |
| @@ -959,26 +1096,10 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, | |||
| 959 | band == NL80211_BAND_2GHZ) | 1096 | band == NL80211_BAND_2GHZ) |
| 960 | continue; | 1097 | continue; |
| 961 | 1098 | ||
| 962 | if (!reg_query_regdb_wmm(regd->alpha2, center_freq, | 1099 | reg_query_regdb_wmm(regd->alpha2, center_freq, rule); |
| 963 | ®db_ptrs[n_wmms].token, wmm_rule)) { | ||
| 964 | /* Add only new rules */ | ||
| 965 | for (i = 0; i < n_wmms; i++) { | ||
| 966 | if (regdb_ptrs[i].token == | ||
| 967 | regdb_ptrs[n_wmms].token) { | ||
| 968 | rule->wmm_rule = regdb_ptrs[i].rule; | ||
| 969 | break; | ||
| 970 | } | ||
| 971 | } | ||
| 972 | if (i == n_wmms) { | ||
| 973 | rule->wmm_rule = wmm_rule; | ||
| 974 | regdb_ptrs[n_wmms++].rule = wmm_rule; | ||
| 975 | wmm_rule++; | ||
| 976 | } | ||
| 977 | } | ||
| 978 | } | 1100 | } |
| 979 | 1101 | ||
| 980 | regd->n_reg_rules = valid_rules; | 1102 | regd->n_reg_rules = valid_rules; |
| 981 | regd->n_wmm_rules = n_wmms; | ||
| 982 | 1103 | ||
| 983 | /* | 1104 | /* |
| 984 | * Narrow down regdom for unused regulatory rules to prevent hole | 1105 | * Narrow down regdom for unused regulatory rules to prevent hole |
| @@ -987,29 +1108,13 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, | |||
| 987 | regd_to_copy = sizeof(struct ieee80211_regdomain) + | 1108 | regd_to_copy = sizeof(struct ieee80211_regdomain) + |
| 988 | valid_rules * sizeof(struct ieee80211_reg_rule); | 1109 | valid_rules * sizeof(struct ieee80211_reg_rule); |
| 989 | 1110 | ||
| 990 | wmms_to_copy = sizeof(struct ieee80211_wmm_rule) * n_wmms; | 1111 | copy_rd = kzalloc(regd_to_copy, GFP_KERNEL); |
| 991 | |||
| 992 | copy_rd = kzalloc(regd_to_copy + wmms_to_copy, GFP_KERNEL); | ||
| 993 | if (!copy_rd) { | 1112 | if (!copy_rd) { |
| 994 | copy_rd = ERR_PTR(-ENOMEM); | 1113 | copy_rd = ERR_PTR(-ENOMEM); |
| 995 | goto out; | 1114 | goto out; |
| 996 | } | 1115 | } |
| 997 | 1116 | ||
| 998 | memcpy(copy_rd, regd, regd_to_copy); | 1117 | memcpy(copy_rd, regd, regd_to_copy); |
| 999 | memcpy((u8 *)copy_rd + regd_to_copy, (u8 *)regd + size_of_regd, | ||
| 1000 | wmms_to_copy); | ||
| 1001 | |||
| 1002 | d_wmm = (struct ieee80211_wmm_rule *)((u8 *)copy_rd + regd_to_copy); | ||
| 1003 | s_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd); | ||
| 1004 | |||
| 1005 | for (i = 0; i < regd->n_reg_rules; i++) { | ||
| 1006 | if (!regd->reg_rules[i].wmm_rule) | ||
| 1007 | continue; | ||
| 1008 | |||
| 1009 | copy_rd->reg_rules[i].wmm_rule = d_wmm + | ||
| 1010 | (regd->reg_rules[i].wmm_rule - s_wmm) / | ||
| 1011 | sizeof(struct ieee80211_wmm_rule); | ||
| 1012 | } | ||
| 1013 | 1118 | ||
| 1014 | out: | 1119 | out: |
| 1015 | kfree(regdb_ptrs); | 1120 | kfree(regdb_ptrs); |
| @@ -1017,3 +1122,304 @@ out: | |||
| 1017 | return copy_rd; | 1122 | return copy_rd; |
| 1018 | } | 1123 | } |
| 1019 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); | 1124 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); |
| 1125 | |||
| 1126 | #define IWL_MAX_NVM_SECTION_SIZE 0x1b58 | ||
| 1127 | #define IWL_MAX_EXT_NVM_SECTION_SIZE 0x1ffc | ||
| 1128 | #define MAX_NVM_FILE_LEN 16384 | ||
| 1129 | |||
| 1130 | void iwl_nvm_fixups(u32 hw_id, unsigned int section, u8 *data, | ||
| 1131 | unsigned int len) | ||
| 1132 | { | ||
| 1133 | #define IWL_4165_DEVICE_ID 0x5501 | ||
| 1134 | #define NVM_SKU_CAP_MIMO_DISABLE BIT(5) | ||
| 1135 | |||
| 1136 | if (section == NVM_SECTION_TYPE_PHY_SKU && | ||
| 1137 | hw_id == IWL_4165_DEVICE_ID && data && len >= 5 && | ||
| 1138 | (data[4] & NVM_SKU_CAP_MIMO_DISABLE)) | ||
| 1139 | /* OTP 0x52 bug work around: it's a 1x1 device */ | ||
| 1140 | data[3] = ANT_B | (ANT_B << 4); | ||
| 1141 | } | ||
| 1142 | IWL_EXPORT_SYMBOL(iwl_nvm_fixups); | ||
| 1143 | |||
| 1144 | /* | ||
| 1145 | * Reads external NVM from a file into mvm->nvm_sections | ||
| 1146 | * | ||
| 1147 | * HOW TO CREATE THE NVM FILE FORMAT: | ||
| 1148 | * ------------------------------ | ||
| 1149 | * 1. create hex file, format: | ||
| 1150 | * 3800 -> header | ||
| 1151 | * 0000 -> header | ||
| 1152 | * 5a40 -> data | ||
| 1153 | * | ||
| 1154 | * rev - 6 bit (word1) | ||
| 1155 | * len - 10 bit (word1) | ||
| 1156 | * id - 4 bit (word2) | ||
| 1157 | * rsv - 12 bit (word2) | ||
| 1158 | * | ||
| 1159 | * 2. flip 8bits with 8 bits per line to get the right NVM file format | ||
| 1160 | * | ||
| 1161 | * 3. create binary file from the hex file | ||
| 1162 | * | ||
| 1163 | * 4. save as "iNVM_xxx.bin" under /lib/firmware | ||
| 1164 | */ | ||
| 1165 | int iwl_read_external_nvm(struct iwl_trans *trans, | ||
| 1166 | const char *nvm_file_name, | ||
| 1167 | struct iwl_nvm_section *nvm_sections) | ||
| 1168 | { | ||
| 1169 | int ret, section_size; | ||
| 1170 | u16 section_id; | ||
| 1171 | const struct firmware *fw_entry; | ||
| 1172 | const struct { | ||
| 1173 | __le16 word1; | ||
| 1174 | __le16 word2; | ||
| 1175 | u8 data[]; | ||
| 1176 | } *file_sec; | ||
| 1177 | const u8 *eof; | ||
| 1178 | u8 *temp; | ||
| 1179 | int max_section_size; | ||
| 1180 | const __le32 *dword_buff; | ||
| 1181 | |||
| 1182 | #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) | ||
| 1183 | #define NVM_WORD2_ID(x) (x >> 12) | ||
| 1184 | #define EXT_NVM_WORD2_LEN(x) (2 * (((x) & 0xFF) << 8 | (x) >> 8)) | ||
| 1185 | #define EXT_NVM_WORD1_ID(x) ((x) >> 4) | ||
| 1186 | #define NVM_HEADER_0 (0x2A504C54) | ||
| 1187 | #define NVM_HEADER_1 (0x4E564D2A) | ||
| 1188 | #define NVM_HEADER_SIZE (4 * sizeof(u32)) | ||
| 1189 | |||
| 1190 | IWL_DEBUG_EEPROM(trans->dev, "Read from external NVM\n"); | ||
| 1191 | |||
| 1192 | /* Maximal size depends on NVM version */ | ||
| 1193 | if (trans->cfg->nvm_type != IWL_NVM_EXT) | ||
| 1194 | max_section_size = IWL_MAX_NVM_SECTION_SIZE; | ||
| 1195 | else | ||
| 1196 | max_section_size = IWL_MAX_EXT_NVM_SECTION_SIZE; | ||
| 1197 | |||
| 1198 | /* | ||
| 1199 | * Obtain NVM image via request_firmware. Since we already used | ||
| 1200 | * request_firmware_nowait() for the firmware binary load and only | ||
| 1201 | * get here after that we assume the NVM request can be satisfied | ||
| 1202 | * synchronously. | ||
| 1203 | */ | ||
| 1204 | ret = request_firmware(&fw_entry, nvm_file_name, trans->dev); | ||
| 1205 | if (ret) { | ||
| 1206 | IWL_ERR(trans, "ERROR: %s isn't available %d\n", | ||
| 1207 | nvm_file_name, ret); | ||
| 1208 | return ret; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | IWL_INFO(trans, "Loaded NVM file %s (%zu bytes)\n", | ||
| 1212 | nvm_file_name, fw_entry->size); | ||
| 1213 | |||
| 1214 | if (fw_entry->size > MAX_NVM_FILE_LEN) { | ||
| 1215 | IWL_ERR(trans, "NVM file too large\n"); | ||
| 1216 | ret = -EINVAL; | ||
| 1217 | goto out; | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | eof = fw_entry->data + fw_entry->size; | ||
| 1221 | dword_buff = (__le32 *)fw_entry->data; | ||
| 1222 | |||
| 1223 | /* some NVM file will contain a header. | ||
| 1224 | * The header is identified by 2 dwords header as follow: | ||
| 1225 | * dword[0] = 0x2A504C54 | ||
| 1226 | * dword[1] = 0x4E564D2A | ||
| 1227 | * | ||
| 1228 | * This header must be skipped when providing the NVM data to the FW. | ||
| 1229 | */ | ||
| 1230 | if (fw_entry->size > NVM_HEADER_SIZE && | ||
| 1231 | dword_buff[0] == cpu_to_le32(NVM_HEADER_0) && | ||
| 1232 | dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) { | ||
| 1233 | file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE); | ||
| 1234 | IWL_INFO(trans, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); | ||
| 1235 | IWL_INFO(trans, "NVM Manufacturing date %08X\n", | ||
| 1236 | le32_to_cpu(dword_buff[3])); | ||
| 1237 | |||
| 1238 | /* nvm file validation, dword_buff[2] holds the file version */ | ||
| 1239 | if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 && | ||
| 1240 | CSR_HW_REV_STEP(trans->hw_rev) == SILICON_C_STEP && | ||
| 1241 | le32_to_cpu(dword_buff[2]) < 0xE4A) { | ||
| 1242 | ret = -EFAULT; | ||
| 1243 | goto out; | ||
| 1244 | } | ||
| 1245 | } else { | ||
| 1246 | file_sec = (void *)fw_entry->data; | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | while (true) { | ||
| 1250 | if (file_sec->data > eof) { | ||
| 1251 | IWL_ERR(trans, | ||
| 1252 | "ERROR - NVM file too short for section header\n"); | ||
| 1253 | ret = -EINVAL; | ||
| 1254 | break; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | /* check for EOF marker */ | ||
| 1258 | if (!file_sec->word1 && !file_sec->word2) { | ||
| 1259 | ret = 0; | ||
| 1260 | break; | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | if (trans->cfg->nvm_type != IWL_NVM_EXT) { | ||
| 1264 | section_size = | ||
| 1265 | 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); | ||
| 1266 | section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); | ||
| 1267 | } else { | ||
| 1268 | section_size = 2 * EXT_NVM_WORD2_LEN( | ||
| 1269 | le16_to_cpu(file_sec->word2)); | ||
| 1270 | section_id = EXT_NVM_WORD1_ID( | ||
| 1271 | le16_to_cpu(file_sec->word1)); | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | if (section_size > max_section_size) { | ||
| 1275 | IWL_ERR(trans, "ERROR - section too large (%d)\n", | ||
| 1276 | section_size); | ||
| 1277 | ret = -EINVAL; | ||
| 1278 | break; | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | if (!section_size) { | ||
| 1282 | IWL_ERR(trans, "ERROR - section empty\n"); | ||
| 1283 | ret = -EINVAL; | ||
| 1284 | break; | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | if (file_sec->data + section_size > eof) { | ||
| 1288 | IWL_ERR(trans, | ||
| 1289 | "ERROR - NVM file too short for section (%d bytes)\n", | ||
| 1290 | section_size); | ||
| 1291 | ret = -EINVAL; | ||
| 1292 | break; | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, | ||
| 1296 | "Invalid NVM section ID %d\n", section_id)) { | ||
| 1297 | ret = -EINVAL; | ||
| 1298 | break; | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); | ||
| 1302 | if (!temp) { | ||
| 1303 | ret = -ENOMEM; | ||
| 1304 | break; | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | iwl_nvm_fixups(trans->hw_id, section_id, temp, section_size); | ||
| 1308 | |||
| 1309 | kfree(nvm_sections[section_id].data); | ||
| 1310 | nvm_sections[section_id].data = temp; | ||
| 1311 | nvm_sections[section_id].length = section_size; | ||
| 1312 | |||
| 1313 | /* advance to the next section */ | ||
| 1314 | file_sec = (void *)(file_sec->data + section_size); | ||
| 1315 | } | ||
| 1316 | out: | ||
| 1317 | release_firmware(fw_entry); | ||
| 1318 | return ret; | ||
| 1319 | } | ||
| 1320 | IWL_EXPORT_SYMBOL(iwl_read_external_nvm); | ||
| 1321 | |||
| 1322 | struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, | ||
| 1323 | const struct iwl_fw *fw) | ||
| 1324 | { | ||
| 1325 | struct iwl_nvm_get_info cmd = {}; | ||
| 1326 | struct iwl_nvm_get_info_rsp *rsp; | ||
| 1327 | struct iwl_nvm_data *nvm; | ||
| 1328 | struct iwl_host_cmd hcmd = { | ||
| 1329 | .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, | ||
| 1330 | .data = { &cmd, }, | ||
| 1331 | .len = { sizeof(cmd) }, | ||
| 1332 | .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO) | ||
| 1333 | }; | ||
| 1334 | int ret; | ||
| 1335 | bool lar_fw_supported = !iwlwifi_mod_params.lar_disable && | ||
| 1336 | fw_has_capa(&fw->ucode_capa, | ||
| 1337 | IWL_UCODE_TLV_CAPA_LAR_SUPPORT); | ||
| 1338 | bool empty_otp; | ||
| 1339 | u32 mac_flags; | ||
| 1340 | u32 sbands_flags = 0; | ||
| 1341 | |||
| 1342 | ret = iwl_trans_send_cmd(trans, &hcmd); | ||
| 1343 | if (ret) | ||
| 1344 | return ERR_PTR(ret); | ||
| 1345 | |||
| 1346 | if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp), | ||
| 1347 | "Invalid payload len in NVM response from FW %d", | ||
| 1348 | iwl_rx_packet_payload_len(hcmd.resp_pkt))) { | ||
| 1349 | ret = -EINVAL; | ||
| 1350 | goto out; | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | rsp = (void *)hcmd.resp_pkt->data; | ||
| 1354 | empty_otp = !!(le32_to_cpu(rsp->general.flags) & | ||
| 1355 | NVM_GENERAL_FLAGS_EMPTY_OTP); | ||
| 1356 | if (empty_otp) | ||
| 1357 | IWL_INFO(trans, "OTP is empty\n"); | ||
| 1358 | |||
| 1359 | nvm = kzalloc(sizeof(*nvm) + | ||
| 1360 | sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, | ||
| 1361 | GFP_KERNEL); | ||
| 1362 | if (!nvm) { | ||
| 1363 | ret = -ENOMEM; | ||
| 1364 | goto out; | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | iwl_set_hw_address_from_csr(trans, nvm); | ||
| 1368 | /* TODO: if platform NVM has MAC address - override it here */ | ||
| 1369 | |||
| 1370 | if (!is_valid_ether_addr(nvm->hw_addr)) { | ||
| 1371 | IWL_ERR(trans, "no valid mac address was found\n"); | ||
| 1372 | ret = -EINVAL; | ||
| 1373 | goto err_free; | ||
| 1374 | } | ||
| 1375 | |||
| 1376 | IWL_INFO(trans, "base HW address: %pM\n", nvm->hw_addr); | ||
| 1377 | |||
| 1378 | /* Initialize general data */ | ||
| 1379 | nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version); | ||
| 1380 | nvm->n_hw_addrs = rsp->general.n_hw_addrs; | ||
| 1381 | if (nvm->n_hw_addrs == 0) | ||
| 1382 | IWL_WARN(trans, | ||
| 1383 | "Firmware declares no reserved mac addresses. OTP is empty: %d\n", | ||
| 1384 | empty_otp); | ||
| 1385 | |||
| 1386 | /* Initialize MAC sku data */ | ||
| 1387 | mac_flags = le32_to_cpu(rsp->mac_sku.mac_sku_flags); | ||
| 1388 | nvm->sku_cap_11ac_enable = | ||
| 1389 | !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AC_ENABLED); | ||
| 1390 | nvm->sku_cap_11n_enable = | ||
| 1391 | !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11N_ENABLED); | ||
| 1392 | nvm->sku_cap_11ax_enable = | ||
| 1393 | !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AX_ENABLED); | ||
| 1394 | nvm->sku_cap_band_24ghz_enable = | ||
| 1395 | !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED); | ||
| 1396 | nvm->sku_cap_band_52ghz_enable = | ||
| 1397 | !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED); | ||
| 1398 | nvm->sku_cap_mimo_disabled = | ||
| 1399 | !!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED); | ||
| 1400 | |||
| 1401 | /* Initialize PHY sku data */ | ||
| 1402 | nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains); | ||
| 1403 | nvm->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains); | ||
| 1404 | |||
| 1405 | if (le32_to_cpu(rsp->regulatory.lar_enabled) && lar_fw_supported) { | ||
| 1406 | nvm->lar_enabled = true; | ||
| 1407 | sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR; | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | iwl_init_sbands(trans->dev, trans->cfg, nvm, | ||
| 1411 | rsp->regulatory.channel_profile, | ||
| 1412 | nvm->valid_tx_ant & fw->valid_tx_ant, | ||
| 1413 | nvm->valid_rx_ant & fw->valid_rx_ant, | ||
| 1414 | sbands_flags); | ||
| 1415 | |||
| 1416 | iwl_free_resp(&hcmd); | ||
| 1417 | return nvm; | ||
| 1418 | |||
| 1419 | err_free: | ||
| 1420 | kfree(nvm); | ||
| 1421 | out: | ||
| 1422 | iwl_free_resp(&hcmd); | ||
| 1423 | return ERR_PTR(ret); | ||
| 1424 | } | ||
| 1425 | IWL_EXPORT_SYMBOL(iwl_get_nvm); | ||
