162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005-2014, 2018-2023 Intel Corporation 462306a36Sopenharmony_ci * Copyright (C) 2013-2015 Intel Mobile Communications GmbH 562306a36Sopenharmony_ci * Copyright (C) 2016-2017 Intel Deutschland GmbH 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/types.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/etherdevice.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/firmware.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "iwl-drv.h" 1562306a36Sopenharmony_ci#include "iwl-modparams.h" 1662306a36Sopenharmony_ci#include "iwl-nvm-parse.h" 1762306a36Sopenharmony_ci#include "iwl-prph.h" 1862306a36Sopenharmony_ci#include "iwl-io.h" 1962306a36Sopenharmony_ci#include "iwl-csr.h" 2062306a36Sopenharmony_ci#include "fw/acpi.h" 2162306a36Sopenharmony_ci#include "fw/api/nvm-reg.h" 2262306a36Sopenharmony_ci#include "fw/api/commands.h" 2362306a36Sopenharmony_ci#include "fw/api/cmdhdr.h" 2462306a36Sopenharmony_ci#include "fw/img.h" 2562306a36Sopenharmony_ci#include "mei/iwl-mei.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* NVM offsets (in words) definitions */ 2862306a36Sopenharmony_cienum nvm_offsets { 2962306a36Sopenharmony_ci /* NVM HW-Section offset (in words) definitions */ 3062306a36Sopenharmony_ci SUBSYSTEM_ID = 0x0A, 3162306a36Sopenharmony_ci HW_ADDR = 0x15, 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* NVM SW-Section offset (in words) definitions */ 3462306a36Sopenharmony_ci NVM_SW_SECTION = 0x1C0, 3562306a36Sopenharmony_ci NVM_VERSION = 0, 3662306a36Sopenharmony_ci RADIO_CFG = 1, 3762306a36Sopenharmony_ci SKU = 2, 3862306a36Sopenharmony_ci N_HW_ADDRS = 3, 3962306a36Sopenharmony_ci NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION, 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* NVM calibration section offset (in words) definitions */ 4262306a36Sopenharmony_ci NVM_CALIB_SECTION = 0x2B8, 4362306a36Sopenharmony_ci XTAL_CALIB = 0x316 - NVM_CALIB_SECTION, 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* NVM REGULATORY -Section offset (in words) definitions */ 4662306a36Sopenharmony_ci NVM_CHANNELS_SDP = 0, 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cienum ext_nvm_offsets { 5062306a36Sopenharmony_ci /* NVM HW-Section offset (in words) definitions */ 5162306a36Sopenharmony_ci MAC_ADDRESS_OVERRIDE_EXT_NVM = 1, 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* NVM SW-Section offset (in words) definitions */ 5462306a36Sopenharmony_ci NVM_VERSION_EXT_NVM = 0, 5562306a36Sopenharmony_ci N_HW_ADDRS_FAMILY_8000 = 3, 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* NVM PHY_SKU-Section offset (in words) definitions */ 5862306a36Sopenharmony_ci RADIO_CFG_FAMILY_EXT_NVM = 0, 5962306a36Sopenharmony_ci SKU_FAMILY_8000 = 2, 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* NVM REGULATORY -Section offset (in words) definitions */ 6262306a36Sopenharmony_ci NVM_CHANNELS_EXTENDED = 0, 6362306a36Sopenharmony_ci NVM_LAR_OFFSET_OLD = 0x4C7, 6462306a36Sopenharmony_ci NVM_LAR_OFFSET = 0x507, 6562306a36Sopenharmony_ci NVM_LAR_ENABLED = 0x7, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* SKU Capabilities (actual values from NVM definition) */ 6962306a36Sopenharmony_cienum nvm_sku_bits { 7062306a36Sopenharmony_ci NVM_SKU_CAP_BAND_24GHZ = BIT(0), 7162306a36Sopenharmony_ci NVM_SKU_CAP_BAND_52GHZ = BIT(1), 7262306a36Sopenharmony_ci NVM_SKU_CAP_11N_ENABLE = BIT(2), 7362306a36Sopenharmony_ci NVM_SKU_CAP_11AC_ENABLE = BIT(3), 7462306a36Sopenharmony_ci NVM_SKU_CAP_MIMO_DISABLE = BIT(5), 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* 7862306a36Sopenharmony_ci * These are the channel numbers in the order that they are stored in the NVM 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic const u16 iwl_nvm_channels[] = { 8162306a36Sopenharmony_ci /* 2.4 GHz */ 8262306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 8362306a36Sopenharmony_ci /* 5 GHz */ 8462306a36Sopenharmony_ci 36, 40, 44, 48, 52, 56, 60, 64, 8562306a36Sopenharmony_ci 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 8662306a36Sopenharmony_ci 149, 153, 157, 161, 165 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const u16 iwl_ext_nvm_channels[] = { 9062306a36Sopenharmony_ci /* 2.4 GHz */ 9162306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 9262306a36Sopenharmony_ci /* 5 GHz */ 9362306a36Sopenharmony_ci 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 9462306a36Sopenharmony_ci 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 9562306a36Sopenharmony_ci 149, 153, 157, 161, 165, 169, 173, 177, 181 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic const u16 iwl_uhb_nvm_channels[] = { 9962306a36Sopenharmony_ci /* 2.4 GHz */ 10062306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 10162306a36Sopenharmony_ci /* 5 GHz */ 10262306a36Sopenharmony_ci 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 10362306a36Sopenharmony_ci 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 10462306a36Sopenharmony_ci 149, 153, 157, 161, 165, 169, 173, 177, 181, 10562306a36Sopenharmony_ci /* 6-7 GHz */ 10662306a36Sopenharmony_ci 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 10762306a36Sopenharmony_ci 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 10862306a36Sopenharmony_ci 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 10962306a36Sopenharmony_ci 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define IWL_NVM_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) 11362306a36Sopenharmony_ci#define IWL_NVM_NUM_CHANNELS_EXT ARRAY_SIZE(iwl_ext_nvm_channels) 11462306a36Sopenharmony_ci#define IWL_NVM_NUM_CHANNELS_UHB ARRAY_SIZE(iwl_uhb_nvm_channels) 11562306a36Sopenharmony_ci#define NUM_2GHZ_CHANNELS 14 11662306a36Sopenharmony_ci#define NUM_5GHZ_CHANNELS 37 11762306a36Sopenharmony_ci#define FIRST_2GHZ_HT_MINUS 5 11862306a36Sopenharmony_ci#define LAST_2GHZ_HT_PLUS 9 11962306a36Sopenharmony_ci#define N_HW_ADDR_MASK 0xF 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* rate data (static) */ 12262306a36Sopenharmony_cistatic struct ieee80211_rate iwl_cfg80211_rates[] = { 12362306a36Sopenharmony_ci { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, 12462306a36Sopenharmony_ci { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, 12562306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, 12662306a36Sopenharmony_ci { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, 12762306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, 12862306a36Sopenharmony_ci { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, 12962306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, 13062306a36Sopenharmony_ci { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, 13162306a36Sopenharmony_ci { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, 13262306a36Sopenharmony_ci { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, 13362306a36Sopenharmony_ci { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, 13462306a36Sopenharmony_ci { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, 13562306a36Sopenharmony_ci { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, 13662306a36Sopenharmony_ci { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, 13762306a36Sopenharmony_ci { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci#define RATES_24_OFFS 0 14062306a36Sopenharmony_ci#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) 14162306a36Sopenharmony_ci#define RATES_52_OFFS 4 14262306a36Sopenharmony_ci#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/** 14562306a36Sopenharmony_ci * enum iwl_nvm_channel_flags - channel flags in NVM 14662306a36Sopenharmony_ci * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo 14762306a36Sopenharmony_ci * @NVM_CHANNEL_IBSS: usable as an IBSS channel 14862306a36Sopenharmony_ci * @NVM_CHANNEL_ACTIVE: active scanning allowed 14962306a36Sopenharmony_ci * @NVM_CHANNEL_RADAR: radar detection required 15062306a36Sopenharmony_ci * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed 15162306a36Sopenharmony_ci * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS 15262306a36Sopenharmony_ci * on same channel on 2.4 or same UNII band on 5.2 15362306a36Sopenharmony_ci * @NVM_CHANNEL_UNIFORM: uniform spreading required 15462306a36Sopenharmony_ci * @NVM_CHANNEL_20MHZ: 20 MHz channel okay 15562306a36Sopenharmony_ci * @NVM_CHANNEL_40MHZ: 40 MHz channel okay 15662306a36Sopenharmony_ci * @NVM_CHANNEL_80MHZ: 80 MHz channel okay 15762306a36Sopenharmony_ci * @NVM_CHANNEL_160MHZ: 160 MHz channel okay 15862306a36Sopenharmony_ci * @NVM_CHANNEL_DC_HIGH: DC HIGH required/allowed (?) 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_cienum iwl_nvm_channel_flags { 16162306a36Sopenharmony_ci NVM_CHANNEL_VALID = BIT(0), 16262306a36Sopenharmony_ci NVM_CHANNEL_IBSS = BIT(1), 16362306a36Sopenharmony_ci NVM_CHANNEL_ACTIVE = BIT(3), 16462306a36Sopenharmony_ci NVM_CHANNEL_RADAR = BIT(4), 16562306a36Sopenharmony_ci NVM_CHANNEL_INDOOR_ONLY = BIT(5), 16662306a36Sopenharmony_ci NVM_CHANNEL_GO_CONCURRENT = BIT(6), 16762306a36Sopenharmony_ci NVM_CHANNEL_UNIFORM = BIT(7), 16862306a36Sopenharmony_ci NVM_CHANNEL_20MHZ = BIT(8), 16962306a36Sopenharmony_ci NVM_CHANNEL_40MHZ = BIT(9), 17062306a36Sopenharmony_ci NVM_CHANNEL_80MHZ = BIT(10), 17162306a36Sopenharmony_ci NVM_CHANNEL_160MHZ = BIT(11), 17262306a36Sopenharmony_ci NVM_CHANNEL_DC_HIGH = BIT(12), 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/** 17662306a36Sopenharmony_ci * enum iwl_reg_capa_flags_v1 - global flags applied for the whole regulatory 17762306a36Sopenharmony_ci * domain. 17862306a36Sopenharmony_ci * @REG_CAPA_V1_BF_CCD_LOW_BAND: Beam-forming or Cyclic Delay Diversity in the 17962306a36Sopenharmony_ci * 2.4Ghz band is allowed. 18062306a36Sopenharmony_ci * @REG_CAPA_V1_BF_CCD_HIGH_BAND: Beam-forming or Cyclic Delay Diversity in the 18162306a36Sopenharmony_ci * 5Ghz band is allowed. 18262306a36Sopenharmony_ci * @REG_CAPA_V1_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed 18362306a36Sopenharmony_ci * for this regulatory domain (valid only in 5Ghz). 18462306a36Sopenharmony_ci * @REG_CAPA_V1_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed 18562306a36Sopenharmony_ci * for this regulatory domain (valid only in 5Ghz). 18662306a36Sopenharmony_ci * @REG_CAPA_V1_MCS_8_ALLOWED: 11ac with MCS 8 is allowed. 18762306a36Sopenharmony_ci * @REG_CAPA_V1_MCS_9_ALLOWED: 11ac with MCS 9 is allowed. 18862306a36Sopenharmony_ci * @REG_CAPA_V1_40MHZ_FORBIDDEN: 11n channel with a width of 40Mhz is forbidden 18962306a36Sopenharmony_ci * for this regulatory domain (valid only in 5Ghz). 19062306a36Sopenharmony_ci * @REG_CAPA_V1_DC_HIGH_ENABLED: DC HIGH allowed. 19162306a36Sopenharmony_ci * @REG_CAPA_V1_11AX_DISABLED: 11ax is forbidden for this regulatory domain. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cienum iwl_reg_capa_flags_v1 { 19462306a36Sopenharmony_ci REG_CAPA_V1_BF_CCD_LOW_BAND = BIT(0), 19562306a36Sopenharmony_ci REG_CAPA_V1_BF_CCD_HIGH_BAND = BIT(1), 19662306a36Sopenharmony_ci REG_CAPA_V1_160MHZ_ALLOWED = BIT(2), 19762306a36Sopenharmony_ci REG_CAPA_V1_80MHZ_ALLOWED = BIT(3), 19862306a36Sopenharmony_ci REG_CAPA_V1_MCS_8_ALLOWED = BIT(4), 19962306a36Sopenharmony_ci REG_CAPA_V1_MCS_9_ALLOWED = BIT(5), 20062306a36Sopenharmony_ci REG_CAPA_V1_40MHZ_FORBIDDEN = BIT(7), 20162306a36Sopenharmony_ci REG_CAPA_V1_DC_HIGH_ENABLED = BIT(9), 20262306a36Sopenharmony_ci REG_CAPA_V1_11AX_DISABLED = BIT(10), 20362306a36Sopenharmony_ci}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_1 */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/** 20662306a36Sopenharmony_ci * enum iwl_reg_capa_flags_v2 - global flags applied for the whole regulatory 20762306a36Sopenharmony_ci * domain (version 2). 20862306a36Sopenharmony_ci * @REG_CAPA_V2_STRADDLE_DISABLED: Straddle channels (144, 142, 138) are 20962306a36Sopenharmony_ci * disabled. 21062306a36Sopenharmony_ci * @REG_CAPA_V2_BF_CCD_LOW_BAND: Beam-forming or Cyclic Delay Diversity in the 21162306a36Sopenharmony_ci * 2.4Ghz band is allowed. 21262306a36Sopenharmony_ci * @REG_CAPA_V2_BF_CCD_HIGH_BAND: Beam-forming or Cyclic Delay Diversity in the 21362306a36Sopenharmony_ci * 5Ghz band is allowed. 21462306a36Sopenharmony_ci * @REG_CAPA_V2_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed 21562306a36Sopenharmony_ci * for this regulatory domain (valid only in 5Ghz). 21662306a36Sopenharmony_ci * @REG_CAPA_V2_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed 21762306a36Sopenharmony_ci * for this regulatory domain (valid only in 5Ghz). 21862306a36Sopenharmony_ci * @REG_CAPA_V2_MCS_8_ALLOWED: 11ac with MCS 8 is allowed. 21962306a36Sopenharmony_ci * @REG_CAPA_V2_MCS_9_ALLOWED: 11ac with MCS 9 is allowed. 22062306a36Sopenharmony_ci * @REG_CAPA_V2_WEATHER_DISABLED: Weather radar channels (120, 124, 128, 118, 22162306a36Sopenharmony_ci * 126, 122) are disabled. 22262306a36Sopenharmony_ci * @REG_CAPA_V2_40MHZ_ALLOWED: 11n channel with a width of 40Mhz is allowed 22362306a36Sopenharmony_ci * for this regulatory domain (uvalid only in 5Ghz). 22462306a36Sopenharmony_ci * @REG_CAPA_V2_11AX_DISABLED: 11ax is forbidden for this regulatory domain. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_cienum iwl_reg_capa_flags_v2 { 22762306a36Sopenharmony_ci REG_CAPA_V2_STRADDLE_DISABLED = BIT(0), 22862306a36Sopenharmony_ci REG_CAPA_V2_BF_CCD_LOW_BAND = BIT(1), 22962306a36Sopenharmony_ci REG_CAPA_V2_BF_CCD_HIGH_BAND = BIT(2), 23062306a36Sopenharmony_ci REG_CAPA_V2_160MHZ_ALLOWED = BIT(3), 23162306a36Sopenharmony_ci REG_CAPA_V2_80MHZ_ALLOWED = BIT(4), 23262306a36Sopenharmony_ci REG_CAPA_V2_MCS_8_ALLOWED = BIT(5), 23362306a36Sopenharmony_ci REG_CAPA_V2_MCS_9_ALLOWED = BIT(6), 23462306a36Sopenharmony_ci REG_CAPA_V2_WEATHER_DISABLED = BIT(7), 23562306a36Sopenharmony_ci REG_CAPA_V2_40MHZ_ALLOWED = BIT(8), 23662306a36Sopenharmony_ci REG_CAPA_V2_11AX_DISABLED = BIT(10), 23762306a36Sopenharmony_ci}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_2 */ 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/** 24062306a36Sopenharmony_ci * enum iwl_reg_capa_flags_v4 - global flags applied for the whole regulatory 24162306a36Sopenharmony_ci * domain. 24262306a36Sopenharmony_ci * @REG_CAPA_V4_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed 24362306a36Sopenharmony_ci * for this regulatory domain (valid only in 5Ghz). 24462306a36Sopenharmony_ci * @REG_CAPA_V4_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed 24562306a36Sopenharmony_ci * for this regulatory domain (valid only in 5Ghz). 24662306a36Sopenharmony_ci * @REG_CAPA_V4_MCS_12_ALLOWED: 11ac with MCS 12 is allowed. 24762306a36Sopenharmony_ci * @REG_CAPA_V4_MCS_13_ALLOWED: 11ac with MCS 13 is allowed. 24862306a36Sopenharmony_ci * @REG_CAPA_V4_11BE_DISABLED: 11be is forbidden for this regulatory domain. 24962306a36Sopenharmony_ci * @REG_CAPA_V4_11AX_DISABLED: 11ax is forbidden for this regulatory domain. 25062306a36Sopenharmony_ci * @REG_CAPA_V4_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed 25162306a36Sopenharmony_ci * for this regulatory domain (valid only in 5GHz). 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_cienum iwl_reg_capa_flags_v4 { 25462306a36Sopenharmony_ci REG_CAPA_V4_160MHZ_ALLOWED = BIT(3), 25562306a36Sopenharmony_ci REG_CAPA_V4_80MHZ_ALLOWED = BIT(4), 25662306a36Sopenharmony_ci REG_CAPA_V4_MCS_12_ALLOWED = BIT(5), 25762306a36Sopenharmony_ci REG_CAPA_V4_MCS_13_ALLOWED = BIT(6), 25862306a36Sopenharmony_ci REG_CAPA_V4_11BE_DISABLED = BIT(8), 25962306a36Sopenharmony_ci REG_CAPA_V4_11AX_DISABLED = BIT(13), 26062306a36Sopenharmony_ci REG_CAPA_V4_320MHZ_ALLOWED = BIT(16), 26162306a36Sopenharmony_ci}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4 */ 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/* 26462306a36Sopenharmony_ci* API v2 for reg_capa_flags is relevant from version 6 and onwards of the 26562306a36Sopenharmony_ci* MCC update command response. 26662306a36Sopenharmony_ci*/ 26762306a36Sopenharmony_ci#define REG_CAPA_V2_RESP_VER 6 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* API v4 for reg_capa_flags is relevant from version 8 and onwards of the 27062306a36Sopenharmony_ci * MCC update command response. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci#define REG_CAPA_V4_RESP_VER 8 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/** 27562306a36Sopenharmony_ci * struct iwl_reg_capa - struct for global regulatory capabilities, Used for 27662306a36Sopenharmony_ci * handling the different APIs of reg_capa_flags. 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * @allow_40mhz: 11n channel with a width of 40Mhz is allowed 27962306a36Sopenharmony_ci * for this regulatory domain. 28062306a36Sopenharmony_ci * @allow_80mhz: 11ac channel with a width of 80Mhz is allowed 28162306a36Sopenharmony_ci * for this regulatory domain (valid only in 5 and 6 Ghz). 28262306a36Sopenharmony_ci * @allow_160mhz: 11ac channel with a width of 160Mhz is allowed 28362306a36Sopenharmony_ci * for this regulatory domain (valid only in 5 and 6 Ghz). 28462306a36Sopenharmony_ci * @allow_320mhz: 11be channel with a width of 320Mhz is allowed 28562306a36Sopenharmony_ci * for this regulatory domain (valid only in 6 Ghz). 28662306a36Sopenharmony_ci * @disable_11ax: 11ax is forbidden for this regulatory domain. 28762306a36Sopenharmony_ci * @disable_11be: 11be is forbidden for this regulatory domain. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_cistruct iwl_reg_capa { 29062306a36Sopenharmony_ci bool allow_40mhz; 29162306a36Sopenharmony_ci bool allow_80mhz; 29262306a36Sopenharmony_ci bool allow_160mhz; 29362306a36Sopenharmony_ci bool allow_320mhz; 29462306a36Sopenharmony_ci bool disable_11ax; 29562306a36Sopenharmony_ci bool disable_11be; 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, 29962306a36Sopenharmony_ci int chan, u32 flags) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci#define CHECK_AND_PRINT_I(x) \ 30262306a36Sopenharmony_ci ((flags & NVM_CHANNEL_##x) ? " " #x : "") 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (!(flags & NVM_CHANNEL_VALID)) { 30562306a36Sopenharmony_ci IWL_DEBUG_DEV(dev, level, "Ch. %d: 0x%x: No traffic\n", 30662306a36Sopenharmony_ci chan, flags); 30762306a36Sopenharmony_ci return; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Note: already can print up to 101 characters, 110 is the limit! */ 31162306a36Sopenharmony_ci IWL_DEBUG_DEV(dev, level, 31262306a36Sopenharmony_ci "Ch. %d: 0x%x:%s%s%s%s%s%s%s%s%s%s%s%s\n", 31362306a36Sopenharmony_ci chan, flags, 31462306a36Sopenharmony_ci CHECK_AND_PRINT_I(VALID), 31562306a36Sopenharmony_ci CHECK_AND_PRINT_I(IBSS), 31662306a36Sopenharmony_ci CHECK_AND_PRINT_I(ACTIVE), 31762306a36Sopenharmony_ci CHECK_AND_PRINT_I(RADAR), 31862306a36Sopenharmony_ci CHECK_AND_PRINT_I(INDOOR_ONLY), 31962306a36Sopenharmony_ci CHECK_AND_PRINT_I(GO_CONCURRENT), 32062306a36Sopenharmony_ci CHECK_AND_PRINT_I(UNIFORM), 32162306a36Sopenharmony_ci CHECK_AND_PRINT_I(20MHZ), 32262306a36Sopenharmony_ci CHECK_AND_PRINT_I(40MHZ), 32362306a36Sopenharmony_ci CHECK_AND_PRINT_I(80MHZ), 32462306a36Sopenharmony_ci CHECK_AND_PRINT_I(160MHZ), 32562306a36Sopenharmony_ci CHECK_AND_PRINT_I(DC_HIGH)); 32662306a36Sopenharmony_ci#undef CHECK_AND_PRINT_I 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, enum nl80211_band band, 33062306a36Sopenharmony_ci u32 nvm_flags, const struct iwl_cfg *cfg) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci u32 flags = IEEE80211_CHAN_NO_HT40; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (band == NL80211_BAND_2GHZ && (nvm_flags & NVM_CHANNEL_40MHZ)) { 33562306a36Sopenharmony_ci if (ch_num <= LAST_2GHZ_HT_PLUS) 33662306a36Sopenharmony_ci flags &= ~IEEE80211_CHAN_NO_HT40PLUS; 33762306a36Sopenharmony_ci if (ch_num >= FIRST_2GHZ_HT_MINUS) 33862306a36Sopenharmony_ci flags &= ~IEEE80211_CHAN_NO_HT40MINUS; 33962306a36Sopenharmony_ci } else if (nvm_flags & NVM_CHANNEL_40MHZ) { 34062306a36Sopenharmony_ci if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) 34162306a36Sopenharmony_ci flags &= ~IEEE80211_CHAN_NO_HT40PLUS; 34262306a36Sopenharmony_ci else 34362306a36Sopenharmony_ci flags &= ~IEEE80211_CHAN_NO_HT40MINUS; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci if (!(nvm_flags & NVM_CHANNEL_80MHZ)) 34662306a36Sopenharmony_ci flags |= IEEE80211_CHAN_NO_80MHZ; 34762306a36Sopenharmony_ci if (!(nvm_flags & NVM_CHANNEL_160MHZ)) 34862306a36Sopenharmony_ci flags |= IEEE80211_CHAN_NO_160MHZ; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (!(nvm_flags & NVM_CHANNEL_IBSS)) 35162306a36Sopenharmony_ci flags |= IEEE80211_CHAN_NO_IR; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) 35462306a36Sopenharmony_ci flags |= IEEE80211_CHAN_NO_IR; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (nvm_flags & NVM_CHANNEL_RADAR) 35762306a36Sopenharmony_ci flags |= IEEE80211_CHAN_RADAR; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) 36062306a36Sopenharmony_ci flags |= IEEE80211_CHAN_INDOOR_ONLY; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Set the GO concurrent flag only in case that NO_IR is set. 36362306a36Sopenharmony_ci * Otherwise it is meaningless 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && 36662306a36Sopenharmony_ci (flags & IEEE80211_CHAN_NO_IR)) 36762306a36Sopenharmony_ci flags |= IEEE80211_CHAN_IR_CONCURRENT; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return flags; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic enum nl80211_band iwl_nl80211_band_from_channel_idx(int ch_idx) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci if (ch_idx >= NUM_2GHZ_CHANNELS + NUM_5GHZ_CHANNELS) { 37562306a36Sopenharmony_ci return NL80211_BAND_6GHZ; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (ch_idx >= NUM_2GHZ_CHANNELS) 37962306a36Sopenharmony_ci return NL80211_BAND_5GHZ; 38062306a36Sopenharmony_ci return NL80211_BAND_2GHZ; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, 38462306a36Sopenharmony_ci struct iwl_nvm_data *data, 38562306a36Sopenharmony_ci const void * const nvm_ch_flags, 38662306a36Sopenharmony_ci u32 sbands_flags, bool v4) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci int ch_idx; 38962306a36Sopenharmony_ci int n_channels = 0; 39062306a36Sopenharmony_ci struct ieee80211_channel *channel; 39162306a36Sopenharmony_ci u32 ch_flags; 39262306a36Sopenharmony_ci int num_of_ch; 39362306a36Sopenharmony_ci const u16 *nvm_chan; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (cfg->uhb_supported) { 39662306a36Sopenharmony_ci num_of_ch = IWL_NVM_NUM_CHANNELS_UHB; 39762306a36Sopenharmony_ci nvm_chan = iwl_uhb_nvm_channels; 39862306a36Sopenharmony_ci } else if (cfg->nvm_type == IWL_NVM_EXT) { 39962306a36Sopenharmony_ci num_of_ch = IWL_NVM_NUM_CHANNELS_EXT; 40062306a36Sopenharmony_ci nvm_chan = iwl_ext_nvm_channels; 40162306a36Sopenharmony_ci } else { 40262306a36Sopenharmony_ci num_of_ch = IWL_NVM_NUM_CHANNELS; 40362306a36Sopenharmony_ci nvm_chan = iwl_nvm_channels; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { 40762306a36Sopenharmony_ci enum nl80211_band band = 40862306a36Sopenharmony_ci iwl_nl80211_band_from_channel_idx(ch_idx); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (v4) 41162306a36Sopenharmony_ci ch_flags = 41262306a36Sopenharmony_ci __le32_to_cpup((const __le32 *)nvm_ch_flags + ch_idx); 41362306a36Sopenharmony_ci else 41462306a36Sopenharmony_ci ch_flags = 41562306a36Sopenharmony_ci __le16_to_cpup((const __le16 *)nvm_ch_flags + ch_idx); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ && 41862306a36Sopenharmony_ci !data->sku_cap_band_52ghz_enable) 41962306a36Sopenharmony_ci continue; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* workaround to disable wide channels in 5GHz */ 42262306a36Sopenharmony_ci if ((sbands_flags & IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ) && 42362306a36Sopenharmony_ci band == NL80211_BAND_5GHZ) { 42462306a36Sopenharmony_ci ch_flags &= ~(NVM_CHANNEL_40MHZ | 42562306a36Sopenharmony_ci NVM_CHANNEL_80MHZ | 42662306a36Sopenharmony_ci NVM_CHANNEL_160MHZ); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (ch_flags & NVM_CHANNEL_160MHZ) 43062306a36Sopenharmony_ci data->vht160_supported = true; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR) && 43362306a36Sopenharmony_ci !(ch_flags & NVM_CHANNEL_VALID)) { 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * Channels might become valid later if lar is 43662306a36Sopenharmony_ci * supported, hence we still want to add them to 43762306a36Sopenharmony_ci * the list of supported channels to cfg80211. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci iwl_nvm_print_channel_flags(dev, IWL_DL_EEPROM, 44062306a36Sopenharmony_ci nvm_chan[ch_idx], ch_flags); 44162306a36Sopenharmony_ci continue; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci channel = &data->channels[n_channels]; 44562306a36Sopenharmony_ci n_channels++; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci channel->hw_value = nvm_chan[ch_idx]; 44862306a36Sopenharmony_ci channel->band = band; 44962306a36Sopenharmony_ci channel->center_freq = 45062306a36Sopenharmony_ci ieee80211_channel_to_frequency( 45162306a36Sopenharmony_ci channel->hw_value, channel->band); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Initialize regulatory-based run-time data */ 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * Default value - highest tx power value. max_power 45762306a36Sopenharmony_ci * is not used in mvm, and is used for backwards compatibility 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci channel->max_power = IWL_DEFAULT_MAX_TX_POWER; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* don't put limitations in case we're using LAR */ 46262306a36Sopenharmony_ci if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR)) 46362306a36Sopenharmony_ci channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], 46462306a36Sopenharmony_ci ch_idx, band, 46562306a36Sopenharmony_ci ch_flags, cfg); 46662306a36Sopenharmony_ci else 46762306a36Sopenharmony_ci channel->flags = 0; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* TODO: Don't put limitations on UHB devices as we still don't 47062306a36Sopenharmony_ci * have NVM for them 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci if (cfg->uhb_supported) 47362306a36Sopenharmony_ci channel->flags = 0; 47462306a36Sopenharmony_ci iwl_nvm_print_channel_flags(dev, IWL_DL_EEPROM, 47562306a36Sopenharmony_ci channel->hw_value, ch_flags); 47662306a36Sopenharmony_ci IWL_DEBUG_EEPROM(dev, "Ch. %d: %ddBm\n", 47762306a36Sopenharmony_ci channel->hw_value, channel->max_power); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return n_channels; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void iwl_init_vht_hw_capab(struct iwl_trans *trans, 48462306a36Sopenharmony_ci struct iwl_nvm_data *data, 48562306a36Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap, 48662306a36Sopenharmony_ci u8 tx_chains, u8 rx_chains) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci const struct iwl_cfg *cfg = trans->cfg; 48962306a36Sopenharmony_ci int num_rx_ants = num_of_ant(rx_chains); 49062306a36Sopenharmony_ci int num_tx_ants = num_of_ant(tx_chains); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci vht_cap->vht_supported = true; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | 49562306a36Sopenharmony_ci IEEE80211_VHT_CAP_RXSTBC_1 | 49662306a36Sopenharmony_ci IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | 49762306a36Sopenharmony_ci 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | 49862306a36Sopenharmony_ci IEEE80211_VHT_MAX_AMPDU_1024K << 49962306a36Sopenharmony_ci IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!trans->cfg->ht_params->stbc) 50262306a36Sopenharmony_ci vht_cap->cap &= ~IEEE80211_VHT_CAP_RXSTBC_MASK; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (data->vht160_supported) 50562306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | 50662306a36Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_160; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (cfg->vht_mu_mimo_supported) 50962306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (cfg->ht_params->ldpc) 51262306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (data->sku_cap_mimo_disabled) { 51562306a36Sopenharmony_ci num_rx_ants = 1; 51662306a36Sopenharmony_ci num_tx_ants = 1; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (trans->cfg->ht_params->stbc && num_tx_ants > 1) 52062306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; 52162306a36Sopenharmony_ci else 52262306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci switch (iwlwifi_mod_params.amsdu_size) { 52562306a36Sopenharmony_ci case IWL_AMSDU_DEF: 52662306a36Sopenharmony_ci if (trans->trans_cfg->mq_rx_supported) 52762306a36Sopenharmony_ci vht_cap->cap |= 52862306a36Sopenharmony_ci IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci case IWL_AMSDU_2K: 53362306a36Sopenharmony_ci if (trans->trans_cfg->mq_rx_supported) 53462306a36Sopenharmony_ci vht_cap->cap |= 53562306a36Sopenharmony_ci IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; 53662306a36Sopenharmony_ci else 53762306a36Sopenharmony_ci WARN(1, "RB size of 2K is not supported by this device\n"); 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci case IWL_AMSDU_4K: 54062306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci case IWL_AMSDU_8K: 54362306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci case IWL_AMSDU_12K: 54662306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci default: 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci vht_cap->vht_mcs.rx_mcs_map = 55362306a36Sopenharmony_ci cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | 55462306a36Sopenharmony_ci IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | 55562306a36Sopenharmony_ci IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | 55662306a36Sopenharmony_ci IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | 55762306a36Sopenharmony_ci IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | 55862306a36Sopenharmony_ci IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | 55962306a36Sopenharmony_ci IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | 56062306a36Sopenharmony_ci IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) { 56362306a36Sopenharmony_ci vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; 56462306a36Sopenharmony_ci /* this works because NOT_SUPPORTED == 3 */ 56562306a36Sopenharmony_ci vht_cap->vht_mcs.rx_mcs_map |= 56662306a36Sopenharmony_ci cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci vht_cap->vht_mcs.tx_highest |= 57262306a36Sopenharmony_ci cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic const u8 iwl_vendor_caps[] = { 57662306a36Sopenharmony_ci 0xdd, /* vendor element */ 57762306a36Sopenharmony_ci 0x06, /* length */ 57862306a36Sopenharmony_ci 0x00, 0x17, 0x35, /* Intel OUI */ 57962306a36Sopenharmony_ci 0x08, /* type (Intel Capabilities) */ 58062306a36Sopenharmony_ci /* followed by 16 bits of capabilities */ 58162306a36Sopenharmony_ci#define IWL_VENDOR_CAP_IMPROVED_BF_FDBK_HE BIT(0) 58262306a36Sopenharmony_ci IWL_VENDOR_CAP_IMPROVED_BF_FDBK_HE, 58362306a36Sopenharmony_ci 0x00 58462306a36Sopenharmony_ci}; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { 58762306a36Sopenharmony_ci { 58862306a36Sopenharmony_ci .types_mask = BIT(NL80211_IFTYPE_STATION), 58962306a36Sopenharmony_ci .he_cap = { 59062306a36Sopenharmony_ci .has_he = true, 59162306a36Sopenharmony_ci .he_cap_elem = { 59262306a36Sopenharmony_ci .mac_cap_info[0] = 59362306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP0_HTC_HE, 59462306a36Sopenharmony_ci .mac_cap_info[1] = 59562306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | 59662306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, 59762306a36Sopenharmony_ci .mac_cap_info[2] = 59862306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP, 59962306a36Sopenharmony_ci .mac_cap_info[3] = 60062306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP3_OMI_CONTROL | 60162306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS, 60262306a36Sopenharmony_ci .mac_cap_info[4] = 60362306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU | 60462306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39, 60562306a36Sopenharmony_ci .mac_cap_info[5] = 60662306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 | 60762306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 | 60862306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU | 60962306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS | 61062306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX, 61162306a36Sopenharmony_ci .phy_cap_info[1] = 61262306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | 61362306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | 61462306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD, 61562306a36Sopenharmony_ci .phy_cap_info[2] = 61662306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | 61762306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ, 61862306a36Sopenharmony_ci .phy_cap_info[3] = 61962306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK | 62062306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 | 62162306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK | 62262306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1, 62362306a36Sopenharmony_ci .phy_cap_info[4] = 62462306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | 62562306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 | 62662306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8, 62762306a36Sopenharmony_ci .phy_cap_info[6] = 62862306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | 62962306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB | 63062306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT, 63162306a36Sopenharmony_ci .phy_cap_info[7] = 63262306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP | 63362306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI, 63462306a36Sopenharmony_ci .phy_cap_info[8] = 63562306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI | 63662306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | 63762306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | 63862306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU | 63962306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242, 64062306a36Sopenharmony_ci .phy_cap_info[9] = 64162306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | 64262306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB | 64362306a36Sopenharmony_ci (IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED << 64462306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_POS), 64562306a36Sopenharmony_ci .phy_cap_info[10] = 64662306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP10_HE_MU_M1RU_MAX_LTF, 64762306a36Sopenharmony_ci }, 64862306a36Sopenharmony_ci /* 64962306a36Sopenharmony_ci * Set default Tx/Rx HE MCS NSS Support field. 65062306a36Sopenharmony_ci * Indicate support for up to 2 spatial streams and all 65162306a36Sopenharmony_ci * MCS, without any special cases 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_ci .he_mcs_nss_supp = { 65462306a36Sopenharmony_ci .rx_mcs_80 = cpu_to_le16(0xfffa), 65562306a36Sopenharmony_ci .tx_mcs_80 = cpu_to_le16(0xfffa), 65662306a36Sopenharmony_ci .rx_mcs_160 = cpu_to_le16(0xfffa), 65762306a36Sopenharmony_ci .tx_mcs_160 = cpu_to_le16(0xfffa), 65862306a36Sopenharmony_ci .rx_mcs_80p80 = cpu_to_le16(0xffff), 65962306a36Sopenharmony_ci .tx_mcs_80p80 = cpu_to_le16(0xffff), 66062306a36Sopenharmony_ci }, 66162306a36Sopenharmony_ci /* 66262306a36Sopenharmony_ci * Set default PPE thresholds, with PPET16 set to 0, 66362306a36Sopenharmony_ci * PPET8 set to 7 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci .ppe_thres = {0x61, 0x1c, 0xc7, 0x71}, 66662306a36Sopenharmony_ci }, 66762306a36Sopenharmony_ci .eht_cap = { 66862306a36Sopenharmony_ci .has_eht = true, 66962306a36Sopenharmony_ci .eht_cap_elem = { 67062306a36Sopenharmony_ci .mac_cap_info[0] = 67162306a36Sopenharmony_ci IEEE80211_EHT_MAC_CAP0_OM_CONTROL | 67262306a36Sopenharmony_ci IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | 67362306a36Sopenharmony_ci IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2, 67462306a36Sopenharmony_ci .phy_cap_info[0] = 67562306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | 67662306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | 67762306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | 67862306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | 67962306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, 68062306a36Sopenharmony_ci .phy_cap_info[1] = 68162306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | 68262306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK, 68362306a36Sopenharmony_ci .phy_cap_info[3] = 68462306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | 68562306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | 68662306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | 68762306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | 68862306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | 68962306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | 69062306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci .phy_cap_info[4] = 69362306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | 69462306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | 69562306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI, 69662306a36Sopenharmony_ci .phy_cap_info[5] = 69762306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | 69862306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | 69962306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | 70062306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT, 70162306a36Sopenharmony_ci .phy_cap_info[6] = 70262306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | 70362306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, 70462306a36Sopenharmony_ci .phy_cap_info[8] = 70562306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP8_RX_1024QAM_WIDER_BW_DL_OFDMA | 70662306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA, 70762306a36Sopenharmony_ci }, 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* For all MCS and bandwidth, set 2 NSS for both Tx and 71062306a36Sopenharmony_ci * Rx - note we don't set the only_20mhz, but due to this 71162306a36Sopenharmony_ci * being a union, it gets set correctly anyway. 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci .eht_mcs_nss_supp = { 71462306a36Sopenharmony_ci .bw._80 = { 71562306a36Sopenharmony_ci .rx_tx_mcs9_max_nss = 0x22, 71662306a36Sopenharmony_ci .rx_tx_mcs11_max_nss = 0x22, 71762306a36Sopenharmony_ci .rx_tx_mcs13_max_nss = 0x22, 71862306a36Sopenharmony_ci }, 71962306a36Sopenharmony_ci .bw._160 = { 72062306a36Sopenharmony_ci .rx_tx_mcs9_max_nss = 0x22, 72162306a36Sopenharmony_ci .rx_tx_mcs11_max_nss = 0x22, 72262306a36Sopenharmony_ci .rx_tx_mcs13_max_nss = 0x22, 72362306a36Sopenharmony_ci }, 72462306a36Sopenharmony_ci .bw._320 = { 72562306a36Sopenharmony_ci .rx_tx_mcs9_max_nss = 0x22, 72662306a36Sopenharmony_ci .rx_tx_mcs11_max_nss = 0x22, 72762306a36Sopenharmony_ci .rx_tx_mcs13_max_nss = 0x22, 72862306a36Sopenharmony_ci }, 72962306a36Sopenharmony_ci }, 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * PPE thresholds for NSS = 2, and RU index bitmap set 73362306a36Sopenharmony_ci * to 0xc. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ci .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } 73662306a36Sopenharmony_ci }, 73762306a36Sopenharmony_ci }, 73862306a36Sopenharmony_ci { 73962306a36Sopenharmony_ci .types_mask = BIT(NL80211_IFTYPE_AP), 74062306a36Sopenharmony_ci .he_cap = { 74162306a36Sopenharmony_ci .has_he = true, 74262306a36Sopenharmony_ci .he_cap_elem = { 74362306a36Sopenharmony_ci .mac_cap_info[0] = 74462306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP0_HTC_HE, 74562306a36Sopenharmony_ci .mac_cap_info[1] = 74662306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | 74762306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, 74862306a36Sopenharmony_ci .mac_cap_info[3] = 74962306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP3_OMI_CONTROL, 75062306a36Sopenharmony_ci .phy_cap_info[1] = 75162306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD, 75262306a36Sopenharmony_ci .phy_cap_info[2] = 75362306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | 75462306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US, 75562306a36Sopenharmony_ci .phy_cap_info[3] = 75662306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK | 75762306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 | 75862306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK | 75962306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1, 76062306a36Sopenharmony_ci .phy_cap_info[6] = 76162306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT, 76262306a36Sopenharmony_ci .phy_cap_info[7] = 76362306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI, 76462306a36Sopenharmony_ci .phy_cap_info[8] = 76562306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI | 76662306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242, 76762306a36Sopenharmony_ci .phy_cap_info[9] = 76862306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED 76962306a36Sopenharmony_ci << IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_POS, 77062306a36Sopenharmony_ci }, 77162306a36Sopenharmony_ci /* 77262306a36Sopenharmony_ci * Set default Tx/Rx HE MCS NSS Support field. 77362306a36Sopenharmony_ci * Indicate support for up to 2 spatial streams and all 77462306a36Sopenharmony_ci * MCS, without any special cases 77562306a36Sopenharmony_ci */ 77662306a36Sopenharmony_ci .he_mcs_nss_supp = { 77762306a36Sopenharmony_ci .rx_mcs_80 = cpu_to_le16(0xfffa), 77862306a36Sopenharmony_ci .tx_mcs_80 = cpu_to_le16(0xfffa), 77962306a36Sopenharmony_ci .rx_mcs_160 = cpu_to_le16(0xfffa), 78062306a36Sopenharmony_ci .tx_mcs_160 = cpu_to_le16(0xfffa), 78162306a36Sopenharmony_ci .rx_mcs_80p80 = cpu_to_le16(0xffff), 78262306a36Sopenharmony_ci .tx_mcs_80p80 = cpu_to_le16(0xffff), 78362306a36Sopenharmony_ci }, 78462306a36Sopenharmony_ci /* 78562306a36Sopenharmony_ci * Set default PPE thresholds, with PPET16 set to 0, 78662306a36Sopenharmony_ci * PPET8 set to 7 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci .ppe_thres = {0x61, 0x1c, 0xc7, 0x71}, 78962306a36Sopenharmony_ci }, 79062306a36Sopenharmony_ci .eht_cap = { 79162306a36Sopenharmony_ci .has_eht = true, 79262306a36Sopenharmony_ci .eht_cap_elem = { 79362306a36Sopenharmony_ci .mac_cap_info[0] = 79462306a36Sopenharmony_ci IEEE80211_EHT_MAC_CAP0_OM_CONTROL | 79562306a36Sopenharmony_ci IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | 79662306a36Sopenharmony_ci IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2, 79762306a36Sopenharmony_ci .phy_cap_info[0] = 79862306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | 79962306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI, 80062306a36Sopenharmony_ci .phy_cap_info[5] = 80162306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT, 80262306a36Sopenharmony_ci }, 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* For all MCS and bandwidth, set 2 NSS for both Tx and 80562306a36Sopenharmony_ci * Rx - note we don't set the only_20mhz, but due to this 80662306a36Sopenharmony_ci * being a union, it gets set correctly anyway. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci .eht_mcs_nss_supp = { 80962306a36Sopenharmony_ci .bw._80 = { 81062306a36Sopenharmony_ci .rx_tx_mcs9_max_nss = 0x22, 81162306a36Sopenharmony_ci .rx_tx_mcs11_max_nss = 0x22, 81262306a36Sopenharmony_ci .rx_tx_mcs13_max_nss = 0x22, 81362306a36Sopenharmony_ci }, 81462306a36Sopenharmony_ci .bw._160 = { 81562306a36Sopenharmony_ci .rx_tx_mcs9_max_nss = 0x22, 81662306a36Sopenharmony_ci .rx_tx_mcs11_max_nss = 0x22, 81762306a36Sopenharmony_ci .rx_tx_mcs13_max_nss = 0x22, 81862306a36Sopenharmony_ci }, 81962306a36Sopenharmony_ci .bw._320 = { 82062306a36Sopenharmony_ci .rx_tx_mcs9_max_nss = 0x22, 82162306a36Sopenharmony_ci .rx_tx_mcs11_max_nss = 0x22, 82262306a36Sopenharmony_ci .rx_tx_mcs13_max_nss = 0x22, 82362306a36Sopenharmony_ci }, 82462306a36Sopenharmony_ci }, 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* 82762306a36Sopenharmony_ci * PPE thresholds for NSS = 2, and RU index bitmap set 82862306a36Sopenharmony_ci * to 0xc. 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_ci .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } 83162306a36Sopenharmony_ci }, 83262306a36Sopenharmony_ci }, 83362306a36Sopenharmony_ci}; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic void iwl_init_he_6ghz_capa(struct iwl_trans *trans, 83662306a36Sopenharmony_ci struct iwl_nvm_data *data, 83762306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 83862306a36Sopenharmony_ci u8 tx_chains, u8 rx_chains) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct ieee80211_sta_ht_cap ht_cap; 84162306a36Sopenharmony_ci struct ieee80211_sta_vht_cap vht_cap = {}; 84262306a36Sopenharmony_ci struct ieee80211_sband_iftype_data *iftype_data; 84362306a36Sopenharmony_ci u16 he_6ghz_capa = 0; 84462306a36Sopenharmony_ci u32 exp; 84562306a36Sopenharmony_ci int i; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (sband->band != NL80211_BAND_6GHZ) 84862306a36Sopenharmony_ci return; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* grab HT/VHT capabilities and calculate HE 6 GHz capabilities */ 85162306a36Sopenharmony_ci iwl_init_ht_hw_capab(trans, data, &ht_cap, NL80211_BAND_5GHZ, 85262306a36Sopenharmony_ci tx_chains, rx_chains); 85362306a36Sopenharmony_ci WARN_ON(!ht_cap.ht_supported); 85462306a36Sopenharmony_ci iwl_init_vht_hw_capab(trans, data, &vht_cap, tx_chains, rx_chains); 85562306a36Sopenharmony_ci WARN_ON(!vht_cap.vht_supported); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci he_6ghz_capa |= 85862306a36Sopenharmony_ci u16_encode_bits(ht_cap.ampdu_density, 85962306a36Sopenharmony_ci IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); 86062306a36Sopenharmony_ci exp = u32_get_bits(vht_cap.cap, 86162306a36Sopenharmony_ci IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); 86262306a36Sopenharmony_ci he_6ghz_capa |= 86362306a36Sopenharmony_ci u16_encode_bits(exp, IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); 86462306a36Sopenharmony_ci exp = u32_get_bits(vht_cap.cap, IEEE80211_VHT_CAP_MAX_MPDU_MASK); 86562306a36Sopenharmony_ci he_6ghz_capa |= 86662306a36Sopenharmony_ci u16_encode_bits(exp, IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); 86762306a36Sopenharmony_ci /* we don't support extended_ht_cap_info anywhere, so no RD_RESPONDER */ 86862306a36Sopenharmony_ci if (vht_cap.cap & IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN) 86962306a36Sopenharmony_ci he_6ghz_capa |= IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS; 87062306a36Sopenharmony_ci if (vht_cap.cap & IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN) 87162306a36Sopenharmony_ci he_6ghz_capa |= IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci IWL_DEBUG_EEPROM(trans->dev, "he_6ghz_capa=0x%x\n", he_6ghz_capa); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* we know it's writable - we set it before ourselves */ 87662306a36Sopenharmony_ci iftype_data = (void *)(uintptr_t)sband->iftype_data; 87762306a36Sopenharmony_ci for (i = 0; i < sband->n_iftype_data; i++) 87862306a36Sopenharmony_ci iftype_data[i].he_6ghz_capa.capa = cpu_to_le16(he_6ghz_capa); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic void 88262306a36Sopenharmony_ciiwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, 88362306a36Sopenharmony_ci struct iwl_nvm_data *data, 88462306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 88562306a36Sopenharmony_ci struct ieee80211_sband_iftype_data *iftype_data, 88662306a36Sopenharmony_ci u8 tx_chains, u8 rx_chains, 88762306a36Sopenharmony_ci const struct iwl_fw *fw) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci bool is_ap = iftype_data->types_mask & BIT(NL80211_IFTYPE_AP); 89062306a36Sopenharmony_ci bool no_320; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci no_320 = !trans->trans_cfg->integrated && 89362306a36Sopenharmony_ci trans->pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be) 89662306a36Sopenharmony_ci iftype_data->eht_cap.has_eht = false; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* Advertise an A-MPDU exponent extension based on 89962306a36Sopenharmony_ci * operating band 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_ci if (sband->band == NL80211_BAND_6GHZ && iftype_data->eht_cap.has_eht) 90262306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |= 90362306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2; 90462306a36Sopenharmony_ci else if (sband->band != NL80211_BAND_2GHZ) 90562306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |= 90662306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1; 90762306a36Sopenharmony_ci else 90862306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |= 90962306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci switch (sband->band) { 91262306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 91362306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= 91462306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; 91562306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] |= 91662306a36Sopenharmony_ci u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454, 91762306a36Sopenharmony_ci IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); 91862306a36Sopenharmony_ci break; 91962306a36Sopenharmony_ci case NL80211_BAND_6GHZ: 92062306a36Sopenharmony_ci if (!no_320) { 92162306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= 92262306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; 92362306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[1] |= 92462306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci fallthrough; 92762306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 92862306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= 92962306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | 93062306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; 93162306a36Sopenharmony_ci break; 93262306a36Sopenharmony_ci default: 93362306a36Sopenharmony_ci WARN_ON(1); 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if ((tx_chains & rx_chains) == ANT_AB) { 93862306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |= 93962306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ; 94062306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[5] |= 94162306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 | 94262306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2; 94362306a36Sopenharmony_ci if (!is_ap) { 94462306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= 94562306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP7_MAX_NC_2; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (iftype_data->eht_cap.has_eht) { 94862306a36Sopenharmony_ci /* 94962306a36Sopenharmony_ci * Set the number of sounding dimensions for each 95062306a36Sopenharmony_ci * bandwidth to 1 to indicate the maximal supported 95162306a36Sopenharmony_ci * value of TXVECTOR parameter NUM_STS of 2 95262306a36Sopenharmony_ci */ 95362306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] |= 0x49; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* 95662306a36Sopenharmony_ci * Set the MAX NC to 1 to indicate sounding feedback of 95762306a36Sopenharmony_ci * 2 supported by the beamfomee. 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci } else { 96362306a36Sopenharmony_ci if (iftype_data->eht_cap.has_eht) { 96462306a36Sopenharmony_ci struct ieee80211_eht_mcs_nss_supp *mcs_nss = 96562306a36Sopenharmony_ci &iftype_data->eht_cap.eht_mcs_nss_supp; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci memset(mcs_nss, 0x11, sizeof(*mcs_nss)); 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (!is_ap) { 97162306a36Sopenharmony_ci /* If not 2x2, we need to indicate 1x1 in the 97262306a36Sopenharmony_ci * Midamble RX Max NSTS - but not for AP mode 97362306a36Sopenharmony_ci */ 97462306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &= 97562306a36Sopenharmony_ci ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS; 97662306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &= 97762306a36Sopenharmony_ci ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS; 97862306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= 97962306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP7_MAX_NC_1; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap) 98462306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |= 98562306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { 98862306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_GF: 98962306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_MR: 99062306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_MS: 99162306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_FM: 99262306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_WH: 99362306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[9] |= 99462306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU; 99562306a36Sopenharmony_ci if (!is_ap) 99662306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[9] |= 99762306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; 99862306a36Sopenharmony_ci break; 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && 100262306a36Sopenharmony_ci iftype_data->eht_cap.has_eht) { 100362306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] &= 100462306a36Sopenharmony_ci ~(IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | 100562306a36Sopenharmony_ci IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2); 100662306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[3] &= 100762306a36Sopenharmony_ci ~(IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | 100862306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | 100962306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | 101062306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | 101162306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | 101262306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | 101362306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK); 101462306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] &= 101562306a36Sopenharmony_ci ~(IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | 101662306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP); 101762306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[5] &= 101862306a36Sopenharmony_ci ~IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK; 101962306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[6] &= 102062306a36Sopenharmony_ci ~(IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | 102162306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP); 102262306a36Sopenharmony_ci iftype_data->eht_cap.eht_cap_elem.phy_cap_info[5] |= 102362306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_BROADCAST_TWT)) 102762306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.mac_cap_info[2] |= 102862306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP2_BCAST_TWT; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 && 103162306a36Sopenharmony_ci !is_ap) { 103262306a36Sopenharmony_ci iftype_data->vendor_elems.data = iwl_vendor_caps; 103362306a36Sopenharmony_ci iftype_data->vendor_elems.len = ARRAY_SIZE(iwl_vendor_caps); 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (!trans->cfg->ht_params->stbc) { 103762306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &= 103862306a36Sopenharmony_ci ~IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ; 103962306a36Sopenharmony_ci iftype_data->he_cap.he_cap_elem.phy_cap_info[7] &= 104062306a36Sopenharmony_ci ~IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic void iwl_init_he_hw_capab(struct iwl_trans *trans, 104562306a36Sopenharmony_ci struct iwl_nvm_data *data, 104662306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 104762306a36Sopenharmony_ci u8 tx_chains, u8 rx_chains, 104862306a36Sopenharmony_ci const struct iwl_fw *fw) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci struct ieee80211_sband_iftype_data *iftype_data; 105162306a36Sopenharmony_ci int i; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* should only initialize once */ 105462306a36Sopenharmony_ci if (WARN_ON(sband->iftype_data)) 105562306a36Sopenharmony_ci return; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa)); 105862306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa)); 105962306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_he_eht_capa)); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci switch (sband->band) { 106262306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 106362306a36Sopenharmony_ci iftype_data = data->iftd.low; 106462306a36Sopenharmony_ci break; 106562306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 106662306a36Sopenharmony_ci iftype_data = data->iftd.high; 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci case NL80211_BAND_6GHZ: 106962306a36Sopenharmony_ci iftype_data = data->iftd.uhb; 107062306a36Sopenharmony_ci break; 107162306a36Sopenharmony_ci default: 107262306a36Sopenharmony_ci WARN_ON(1); 107362306a36Sopenharmony_ci return; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa)); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci sband->iftype_data = iftype_data; 107962306a36Sopenharmony_ci sband->n_iftype_data = ARRAY_SIZE(iwl_he_eht_capa); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci for (i = 0; i < sband->n_iftype_data; i++) 108262306a36Sopenharmony_ci iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i], 108362306a36Sopenharmony_ci tx_chains, rx_chains, fw); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains); 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic void iwl_init_sbands(struct iwl_trans *trans, 108962306a36Sopenharmony_ci struct iwl_nvm_data *data, 109062306a36Sopenharmony_ci const void *nvm_ch_flags, u8 tx_chains, 109162306a36Sopenharmony_ci u8 rx_chains, u32 sbands_flags, bool v4, 109262306a36Sopenharmony_ci const struct iwl_fw *fw) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct device *dev = trans->dev; 109562306a36Sopenharmony_ci const struct iwl_cfg *cfg = trans->cfg; 109662306a36Sopenharmony_ci int n_channels; 109762306a36Sopenharmony_ci int n_used = 0; 109862306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags, 110162306a36Sopenharmony_ci sbands_flags, v4); 110262306a36Sopenharmony_ci sband = &data->bands[NL80211_BAND_2GHZ]; 110362306a36Sopenharmony_ci sband->band = NL80211_BAND_2GHZ; 110462306a36Sopenharmony_ci sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; 110562306a36Sopenharmony_ci sband->n_bitrates = N_RATES_24; 110662306a36Sopenharmony_ci n_used += iwl_init_sband_channels(data, sband, n_channels, 110762306a36Sopenharmony_ci NL80211_BAND_2GHZ); 110862306a36Sopenharmony_ci iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_2GHZ, 110962306a36Sopenharmony_ci tx_chains, rx_chains); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) 111262306a36Sopenharmony_ci iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains, 111362306a36Sopenharmony_ci fw); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci sband = &data->bands[NL80211_BAND_5GHZ]; 111662306a36Sopenharmony_ci sband->band = NL80211_BAND_5GHZ; 111762306a36Sopenharmony_ci sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; 111862306a36Sopenharmony_ci sband->n_bitrates = N_RATES_52; 111962306a36Sopenharmony_ci n_used += iwl_init_sband_channels(data, sband, n_channels, 112062306a36Sopenharmony_ci NL80211_BAND_5GHZ); 112162306a36Sopenharmony_ci iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_5GHZ, 112262306a36Sopenharmony_ci tx_chains, rx_chains); 112362306a36Sopenharmony_ci if (data->sku_cap_11ac_enable && !iwlwifi_mod_params.disable_11ac) 112462306a36Sopenharmony_ci iwl_init_vht_hw_capab(trans, data, &sband->vht_cap, 112562306a36Sopenharmony_ci tx_chains, rx_chains); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) 112862306a36Sopenharmony_ci iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains, 112962306a36Sopenharmony_ci fw); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* 6GHz band. */ 113262306a36Sopenharmony_ci sband = &data->bands[NL80211_BAND_6GHZ]; 113362306a36Sopenharmony_ci sband->band = NL80211_BAND_6GHZ; 113462306a36Sopenharmony_ci /* use the same rates as 5GHz band */ 113562306a36Sopenharmony_ci sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; 113662306a36Sopenharmony_ci sband->n_bitrates = N_RATES_52; 113762306a36Sopenharmony_ci n_used += iwl_init_sband_channels(data, sband, n_channels, 113862306a36Sopenharmony_ci NL80211_BAND_6GHZ); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) 114162306a36Sopenharmony_ci iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains, 114262306a36Sopenharmony_ci fw); 114362306a36Sopenharmony_ci else 114462306a36Sopenharmony_ci sband->n_channels = 0; 114562306a36Sopenharmony_ci if (n_channels != n_used) 114662306a36Sopenharmony_ci IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", 114762306a36Sopenharmony_ci n_used, n_channels); 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, 115162306a36Sopenharmony_ci const __le16 *phy_sku) 115262306a36Sopenharmony_ci{ 115362306a36Sopenharmony_ci if (cfg->nvm_type != IWL_NVM_EXT) 115462306a36Sopenharmony_ci return le16_to_cpup(nvm_sw + SKU); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci return le32_to_cpup((const __le32 *)(phy_sku + SKU_FAMILY_8000)); 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci if (cfg->nvm_type != IWL_NVM_EXT) 116262306a36Sopenharmony_ci return le16_to_cpup(nvm_sw + NVM_VERSION); 116362306a36Sopenharmony_ci else 116462306a36Sopenharmony_ci return le32_to_cpup((const __le32 *)(nvm_sw + 116562306a36Sopenharmony_ci NVM_VERSION_EXT_NVM)); 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw, 116962306a36Sopenharmony_ci const __le16 *phy_sku) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci if (cfg->nvm_type != IWL_NVM_EXT) 117262306a36Sopenharmony_ci return le16_to_cpup(nvm_sw + RADIO_CFG); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci return le32_to_cpup((const __le32 *)(phy_sku + RADIO_CFG_FAMILY_EXT_NVM)); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci int n_hw_addr; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (cfg->nvm_type != IWL_NVM_EXT) 118362306a36Sopenharmony_ci return le16_to_cpup(nvm_sw + N_HW_ADDRS); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci n_hw_addr = le32_to_cpup((const __le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci return n_hw_addr & N_HW_ADDR_MASK; 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_cistatic void iwl_set_radio_cfg(const struct iwl_cfg *cfg, 119162306a36Sopenharmony_ci struct iwl_nvm_data *data, 119262306a36Sopenharmony_ci u32 radio_cfg) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci if (cfg->nvm_type != IWL_NVM_EXT) { 119562306a36Sopenharmony_ci data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg); 119662306a36Sopenharmony_ci data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg); 119762306a36Sopenharmony_ci data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg); 119862306a36Sopenharmony_ci data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg); 119962306a36Sopenharmony_ci return; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* set the radio configuration for family 8000 */ 120362306a36Sopenharmony_ci data->radio_cfg_type = EXT_NVM_RF_CFG_TYPE_MSK(radio_cfg); 120462306a36Sopenharmony_ci data->radio_cfg_step = EXT_NVM_RF_CFG_STEP_MSK(radio_cfg); 120562306a36Sopenharmony_ci data->radio_cfg_dash = EXT_NVM_RF_CFG_DASH_MSK(radio_cfg); 120662306a36Sopenharmony_ci data->radio_cfg_pnum = EXT_NVM_RF_CFG_FLAVOR_MSK(radio_cfg); 120762306a36Sopenharmony_ci data->valid_tx_ant = EXT_NVM_RF_CFG_TX_ANT_MSK(radio_cfg); 120862306a36Sopenharmony_ci data->valid_rx_ant = EXT_NVM_RF_CFG_RX_ANT_MSK(radio_cfg); 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci const u8 *hw_addr; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci hw_addr = (const u8 *)&mac_addr0; 121662306a36Sopenharmony_ci dest[0] = hw_addr[3]; 121762306a36Sopenharmony_ci dest[1] = hw_addr[2]; 121862306a36Sopenharmony_ci dest[2] = hw_addr[1]; 121962306a36Sopenharmony_ci dest[3] = hw_addr[0]; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci hw_addr = (const u8 *)&mac_addr1; 122262306a36Sopenharmony_ci dest[4] = hw_addr[1]; 122362306a36Sopenharmony_ci dest[5] = hw_addr[0]; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic void iwl_set_hw_address_from_csr(struct iwl_trans *trans, 122762306a36Sopenharmony_ci struct iwl_nvm_data *data) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, 123062306a36Sopenharmony_ci CSR_MAC_ADDR0_STRAP(trans))); 123162306a36Sopenharmony_ci __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, 123262306a36Sopenharmony_ci CSR_MAC_ADDR1_STRAP(trans))); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); 123562306a36Sopenharmony_ci /* 123662306a36Sopenharmony_ci * If the OEM fused a valid address, use it instead of the one in the 123762306a36Sopenharmony_ci * OTP 123862306a36Sopenharmony_ci */ 123962306a36Sopenharmony_ci if (is_valid_ether_addr(data->hw_addr)) 124062306a36Sopenharmony_ci return; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP(trans))); 124362306a36Sopenharmony_ci mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP(trans))); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic void iwl_set_hw_address_family_8000(struct iwl_trans *trans, 124962306a36Sopenharmony_ci const struct iwl_cfg *cfg, 125062306a36Sopenharmony_ci struct iwl_nvm_data *data, 125162306a36Sopenharmony_ci const __le16 *mac_override, 125262306a36Sopenharmony_ci const __be16 *nvm_hw) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci const u8 *hw_addr; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (mac_override) { 125762306a36Sopenharmony_ci static const u8 reserved_mac[] = { 125862306a36Sopenharmony_ci 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 125962306a36Sopenharmony_ci }; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci hw_addr = (const u8 *)(mac_override + 126262306a36Sopenharmony_ci MAC_ADDRESS_OVERRIDE_EXT_NVM); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci /* 126562306a36Sopenharmony_ci * Store the MAC address from MAO section. 126662306a36Sopenharmony_ci * No byte swapping is required in MAO section 126762306a36Sopenharmony_ci */ 126862306a36Sopenharmony_ci memcpy(data->hw_addr, hw_addr, ETH_ALEN); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci /* 127162306a36Sopenharmony_ci * Force the use of the OTP MAC address in case of reserved MAC 127262306a36Sopenharmony_ci * address in the NVM, or if address is given but invalid. 127362306a36Sopenharmony_ci */ 127462306a36Sopenharmony_ci if (is_valid_ether_addr(data->hw_addr) && 127562306a36Sopenharmony_ci memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0) 127662306a36Sopenharmony_ci return; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci IWL_ERR(trans, 127962306a36Sopenharmony_ci "mac address from nvm override section is not valid\n"); 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (nvm_hw) { 128362306a36Sopenharmony_ci /* read the mac address from WFMP registers */ 128462306a36Sopenharmony_ci __le32 mac_addr0 = cpu_to_le32(iwl_trans_read_prph(trans, 128562306a36Sopenharmony_ci WFMP_MAC_ADDR_0)); 128662306a36Sopenharmony_ci __le32 mac_addr1 = cpu_to_le32(iwl_trans_read_prph(trans, 128762306a36Sopenharmony_ci WFMP_MAC_ADDR_1)); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci return; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci IWL_ERR(trans, "mac address is not found\n"); 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic int iwl_set_hw_address(struct iwl_trans *trans, 129862306a36Sopenharmony_ci const struct iwl_cfg *cfg, 129962306a36Sopenharmony_ci struct iwl_nvm_data *data, const __be16 *nvm_hw, 130062306a36Sopenharmony_ci const __le16 *mac_override) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci if (cfg->mac_addr_from_csr) { 130362306a36Sopenharmony_ci iwl_set_hw_address_from_csr(trans, data); 130462306a36Sopenharmony_ci } else if (cfg->nvm_type != IWL_NVM_EXT) { 130562306a36Sopenharmony_ci const u8 *hw_addr = (const u8 *)(nvm_hw + HW_ADDR); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* The byte order is little endian 16 bit, meaning 214365 */ 130862306a36Sopenharmony_ci data->hw_addr[0] = hw_addr[1]; 130962306a36Sopenharmony_ci data->hw_addr[1] = hw_addr[0]; 131062306a36Sopenharmony_ci data->hw_addr[2] = hw_addr[3]; 131162306a36Sopenharmony_ci data->hw_addr[3] = hw_addr[2]; 131262306a36Sopenharmony_ci data->hw_addr[4] = hw_addr[5]; 131362306a36Sopenharmony_ci data->hw_addr[5] = hw_addr[4]; 131462306a36Sopenharmony_ci } else { 131562306a36Sopenharmony_ci iwl_set_hw_address_family_8000(trans, cfg, data, 131662306a36Sopenharmony_ci mac_override, nvm_hw); 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci if (!is_valid_ether_addr(data->hw_addr)) { 132062306a36Sopenharmony_ci IWL_ERR(trans, "no valid mac address was found\n"); 132162306a36Sopenharmony_ci return -EINVAL; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (!trans->csme_own) 132562306a36Sopenharmony_ci IWL_INFO(trans, "base HW address: %pM, OTP minor version: 0x%x\n", 132662306a36Sopenharmony_ci data->hw_addr, iwl_read_prph(trans, REG_OTP_MINOR)); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci return 0; 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_cistatic bool 133262306a36Sopenharmony_ciiwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg, 133362306a36Sopenharmony_ci const __be16 *nvm_hw) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci /* 133662306a36Sopenharmony_ci * Workaround a bug in Indonesia SKUs where the regulatory in 133762306a36Sopenharmony_ci * some 7000-family OTPs erroneously allow wide channels in 133862306a36Sopenharmony_ci * 5GHz. To check for Indonesia, we take the SKU value from 133962306a36Sopenharmony_ci * bits 1-4 in the subsystem ID and check if it is either 5 or 134062306a36Sopenharmony_ci * 9. In those cases, we need to force-disable wide channels 134162306a36Sopenharmony_ci * in 5GHz otherwise the FW will throw a sysassert when we try 134262306a36Sopenharmony_ci * to use them. 134362306a36Sopenharmony_ci */ 134462306a36Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) { 134562306a36Sopenharmony_ci /* 134662306a36Sopenharmony_ci * Unlike the other sections in the NVM, the hw 134762306a36Sopenharmony_ci * section uses big-endian. 134862306a36Sopenharmony_ci */ 134962306a36Sopenharmony_ci u16 subsystem_id = be16_to_cpup(nvm_hw + SUBSYSTEM_ID); 135062306a36Sopenharmony_ci u8 sku = (subsystem_id & 0x1e) >> 1; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (sku == 5 || sku == 9) { 135362306a36Sopenharmony_ci IWL_DEBUG_EEPROM(trans->dev, 135462306a36Sopenharmony_ci "disabling wide channels in 5GHz (0x%0x %d)\n", 135562306a36Sopenharmony_ci subsystem_id, sku); 135662306a36Sopenharmony_ci return true; 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci return false; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistruct iwl_nvm_data * 136462306a36Sopenharmony_ciiwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, 136562306a36Sopenharmony_ci const struct iwl_mei_nvm *mei_nvm, 136662306a36Sopenharmony_ci const struct iwl_fw *fw) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci struct iwl_nvm_data *data; 136962306a36Sopenharmony_ci u32 sbands_flags = 0; 137062306a36Sopenharmony_ci u8 rx_chains = fw->valid_rx_ant; 137162306a36Sopenharmony_ci u8 tx_chains = fw->valid_rx_ant; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (cfg->uhb_supported) 137462306a36Sopenharmony_ci data = kzalloc(struct_size(data, channels, 137562306a36Sopenharmony_ci IWL_NVM_NUM_CHANNELS_UHB), 137662306a36Sopenharmony_ci GFP_KERNEL); 137762306a36Sopenharmony_ci else 137862306a36Sopenharmony_ci data = kzalloc(struct_size(data, channels, 137962306a36Sopenharmony_ci IWL_NVM_NUM_CHANNELS_EXT), 138062306a36Sopenharmony_ci GFP_KERNEL); 138162306a36Sopenharmony_ci if (!data) 138262306a36Sopenharmony_ci return NULL; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(mei_nvm->channels) != 138562306a36Sopenharmony_ci IWL_NVM_NUM_CHANNELS_UHB); 138662306a36Sopenharmony_ci data->nvm_version = mei_nvm->nvm_version; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci iwl_set_radio_cfg(cfg, data, mei_nvm->radio_cfg); 138962306a36Sopenharmony_ci if (data->valid_tx_ant) 139062306a36Sopenharmony_ci tx_chains &= data->valid_tx_ant; 139162306a36Sopenharmony_ci if (data->valid_rx_ant) 139262306a36Sopenharmony_ci rx_chains &= data->valid_rx_ant; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci data->sku_cap_mimo_disabled = false; 139562306a36Sopenharmony_ci data->sku_cap_band_24ghz_enable = true; 139662306a36Sopenharmony_ci data->sku_cap_band_52ghz_enable = true; 139762306a36Sopenharmony_ci data->sku_cap_11n_enable = 139862306a36Sopenharmony_ci !(iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL); 139962306a36Sopenharmony_ci data->sku_cap_11ac_enable = true; 140062306a36Sopenharmony_ci data->sku_cap_11ax_enable = 140162306a36Sopenharmony_ci mei_nvm->caps & MEI_NVM_CAPS_11AX_SUPPORT; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci data->lar_enabled = mei_nvm->caps & MEI_NVM_CAPS_LARI_SUPPORT; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci data->n_hw_addrs = mei_nvm->n_hw_addrs; 140662306a36Sopenharmony_ci /* If no valid mac address was found - bail out */ 140762306a36Sopenharmony_ci if (iwl_set_hw_address(trans, cfg, data, NULL, NULL)) { 140862306a36Sopenharmony_ci kfree(data); 140962306a36Sopenharmony_ci return NULL; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (data->lar_enabled && 141362306a36Sopenharmony_ci fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_LAR_SUPPORT)) 141462306a36Sopenharmony_ci sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci iwl_init_sbands(trans, data, mei_nvm->channels, tx_chains, rx_chains, 141762306a36Sopenharmony_ci sbands_flags, true, fw); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci return data; 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_parse_mei_nvm_data); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_cistruct iwl_nvm_data * 142462306a36Sopenharmony_ciiwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, 142562306a36Sopenharmony_ci const struct iwl_fw *fw, 142662306a36Sopenharmony_ci const __be16 *nvm_hw, const __le16 *nvm_sw, 142762306a36Sopenharmony_ci const __le16 *nvm_calib, const __le16 *regulatory, 142862306a36Sopenharmony_ci const __le16 *mac_override, const __le16 *phy_sku, 142962306a36Sopenharmony_ci u8 tx_chains, u8 rx_chains) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci struct iwl_nvm_data *data; 143262306a36Sopenharmony_ci bool lar_enabled; 143362306a36Sopenharmony_ci u32 sku, radio_cfg; 143462306a36Sopenharmony_ci u32 sbands_flags = 0; 143562306a36Sopenharmony_ci u16 lar_config; 143662306a36Sopenharmony_ci const __le16 *ch_section; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (cfg->uhb_supported) 143962306a36Sopenharmony_ci data = kzalloc(struct_size(data, channels, 144062306a36Sopenharmony_ci IWL_NVM_NUM_CHANNELS_UHB), 144162306a36Sopenharmony_ci GFP_KERNEL); 144262306a36Sopenharmony_ci else if (cfg->nvm_type != IWL_NVM_EXT) 144362306a36Sopenharmony_ci data = kzalloc(struct_size(data, channels, 144462306a36Sopenharmony_ci IWL_NVM_NUM_CHANNELS), 144562306a36Sopenharmony_ci GFP_KERNEL); 144662306a36Sopenharmony_ci else 144762306a36Sopenharmony_ci data = kzalloc(struct_size(data, channels, 144862306a36Sopenharmony_ci IWL_NVM_NUM_CHANNELS_EXT), 144962306a36Sopenharmony_ci GFP_KERNEL); 145062306a36Sopenharmony_ci if (!data) 145162306a36Sopenharmony_ci return NULL; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku); 145662306a36Sopenharmony_ci iwl_set_radio_cfg(cfg, data, radio_cfg); 145762306a36Sopenharmony_ci if (data->valid_tx_ant) 145862306a36Sopenharmony_ci tx_chains &= data->valid_tx_ant; 145962306a36Sopenharmony_ci if (data->valid_rx_ant) 146062306a36Sopenharmony_ci rx_chains &= data->valid_rx_ant; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci sku = iwl_get_sku(cfg, nvm_sw, phy_sku); 146362306a36Sopenharmony_ci data->sku_cap_band_24ghz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; 146462306a36Sopenharmony_ci data->sku_cap_band_52ghz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; 146562306a36Sopenharmony_ci data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; 146662306a36Sopenharmony_ci if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) 146762306a36Sopenharmony_ci data->sku_cap_11n_enable = false; 146862306a36Sopenharmony_ci data->sku_cap_11ac_enable = data->sku_cap_11n_enable && 146962306a36Sopenharmony_ci (sku & NVM_SKU_CAP_11AC_ENABLE); 147062306a36Sopenharmony_ci data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci if (cfg->nvm_type != IWL_NVM_EXT) { 147562306a36Sopenharmony_ci /* Checking for required sections */ 147662306a36Sopenharmony_ci if (!nvm_calib) { 147762306a36Sopenharmony_ci IWL_ERR(trans, 147862306a36Sopenharmony_ci "Can't parse empty Calib NVM sections\n"); 147962306a36Sopenharmony_ci kfree(data); 148062306a36Sopenharmony_ci return NULL; 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci ch_section = cfg->nvm_type == IWL_NVM_SDP ? 148462306a36Sopenharmony_ci ®ulatory[NVM_CHANNELS_SDP] : 148562306a36Sopenharmony_ci &nvm_sw[NVM_CHANNELS]; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* in family 8000 Xtal calibration values moved to OTP */ 148862306a36Sopenharmony_ci data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); 148962306a36Sopenharmony_ci data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); 149062306a36Sopenharmony_ci lar_enabled = true; 149162306a36Sopenharmony_ci } else { 149262306a36Sopenharmony_ci u16 lar_offset = data->nvm_version < 0xE39 ? 149362306a36Sopenharmony_ci NVM_LAR_OFFSET_OLD : 149462306a36Sopenharmony_ci NVM_LAR_OFFSET; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci lar_config = le16_to_cpup(regulatory + lar_offset); 149762306a36Sopenharmony_ci data->lar_enabled = !!(lar_config & 149862306a36Sopenharmony_ci NVM_LAR_ENABLED); 149962306a36Sopenharmony_ci lar_enabled = data->lar_enabled; 150062306a36Sopenharmony_ci ch_section = ®ulatory[NVM_CHANNELS_EXTENDED]; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci /* If no valid mac address was found - bail out */ 150462306a36Sopenharmony_ci if (iwl_set_hw_address(trans, cfg, data, nvm_hw, mac_override)) { 150562306a36Sopenharmony_ci kfree(data); 150662306a36Sopenharmony_ci return NULL; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci if (lar_enabled && 151062306a36Sopenharmony_ci fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_LAR_SUPPORT)) 151162306a36Sopenharmony_ci sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci if (iwl_nvm_no_wide_in_5ghz(trans, cfg, nvm_hw)) 151462306a36Sopenharmony_ci sbands_flags |= IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci iwl_init_sbands(trans, data, ch_section, tx_chains, rx_chains, 151762306a36Sopenharmony_ci sbands_flags, false, fw); 151862306a36Sopenharmony_ci data->calib_version = 255; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci return data; 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_parse_nvm_data); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan, 152562306a36Sopenharmony_ci int ch_idx, u16 nvm_flags, 152662306a36Sopenharmony_ci struct iwl_reg_capa reg_capa, 152762306a36Sopenharmony_ci const struct iwl_cfg *cfg) 152862306a36Sopenharmony_ci{ 152962306a36Sopenharmony_ci u32 flags = NL80211_RRF_NO_HT40; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if (ch_idx < NUM_2GHZ_CHANNELS && 153262306a36Sopenharmony_ci (nvm_flags & NVM_CHANNEL_40MHZ)) { 153362306a36Sopenharmony_ci if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) 153462306a36Sopenharmony_ci flags &= ~NL80211_RRF_NO_HT40PLUS; 153562306a36Sopenharmony_ci if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) 153662306a36Sopenharmony_ci flags &= ~NL80211_RRF_NO_HT40MINUS; 153762306a36Sopenharmony_ci } else if (nvm_flags & NVM_CHANNEL_40MHZ) { 153862306a36Sopenharmony_ci if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) 153962306a36Sopenharmony_ci flags &= ~NL80211_RRF_NO_HT40PLUS; 154062306a36Sopenharmony_ci else 154162306a36Sopenharmony_ci flags &= ~NL80211_RRF_NO_HT40MINUS; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci if (!(nvm_flags & NVM_CHANNEL_80MHZ)) 154562306a36Sopenharmony_ci flags |= NL80211_RRF_NO_80MHZ; 154662306a36Sopenharmony_ci if (!(nvm_flags & NVM_CHANNEL_160MHZ)) 154762306a36Sopenharmony_ci flags |= NL80211_RRF_NO_160MHZ; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) 155062306a36Sopenharmony_ci flags |= NL80211_RRF_NO_IR; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (nvm_flags & NVM_CHANNEL_RADAR) 155362306a36Sopenharmony_ci flags |= NL80211_RRF_DFS; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) 155662306a36Sopenharmony_ci flags |= NL80211_RRF_NO_OUTDOOR; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci /* Set the GO concurrent flag only in case that NO_IR is set. 155962306a36Sopenharmony_ci * Otherwise it is meaningless 156062306a36Sopenharmony_ci */ 156162306a36Sopenharmony_ci if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && 156262306a36Sopenharmony_ci (flags & NL80211_RRF_NO_IR)) 156362306a36Sopenharmony_ci flags |= NL80211_RRF_GO_CONCURRENT; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci /* 156662306a36Sopenharmony_ci * reg_capa is per regulatory domain so apply it for every channel 156762306a36Sopenharmony_ci */ 156862306a36Sopenharmony_ci if (ch_idx >= NUM_2GHZ_CHANNELS) { 156962306a36Sopenharmony_ci if (!reg_capa.allow_40mhz) 157062306a36Sopenharmony_ci flags |= NL80211_RRF_NO_HT40; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci if (!reg_capa.allow_80mhz) 157362306a36Sopenharmony_ci flags |= NL80211_RRF_NO_80MHZ; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci if (!reg_capa.allow_160mhz) 157662306a36Sopenharmony_ci flags |= NL80211_RRF_NO_160MHZ; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci if (!reg_capa.allow_320mhz) 157962306a36Sopenharmony_ci flags |= NL80211_RRF_NO_320MHZ; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (reg_capa.disable_11ax) 158362306a36Sopenharmony_ci flags |= NL80211_RRF_NO_HE; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci if (reg_capa.disable_11be) 158662306a36Sopenharmony_ci flags |= NL80211_RRF_NO_EHT; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return flags; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_cistatic struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci struct iwl_reg_capa reg_capa = {}; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (resp_ver >= REG_CAPA_V4_RESP_VER) { 159662306a36Sopenharmony_ci reg_capa.allow_40mhz = true; 159762306a36Sopenharmony_ci reg_capa.allow_80mhz = flags & REG_CAPA_V4_80MHZ_ALLOWED; 159862306a36Sopenharmony_ci reg_capa.allow_160mhz = flags & REG_CAPA_V4_160MHZ_ALLOWED; 159962306a36Sopenharmony_ci reg_capa.allow_320mhz = flags & REG_CAPA_V4_320MHZ_ALLOWED; 160062306a36Sopenharmony_ci reg_capa.disable_11ax = flags & REG_CAPA_V4_11AX_DISABLED; 160162306a36Sopenharmony_ci reg_capa.disable_11be = flags & REG_CAPA_V4_11BE_DISABLED; 160262306a36Sopenharmony_ci } else if (resp_ver >= REG_CAPA_V2_RESP_VER) { 160362306a36Sopenharmony_ci reg_capa.allow_40mhz = flags & REG_CAPA_V2_40MHZ_ALLOWED; 160462306a36Sopenharmony_ci reg_capa.allow_80mhz = flags & REG_CAPA_V2_80MHZ_ALLOWED; 160562306a36Sopenharmony_ci reg_capa.allow_160mhz = flags & REG_CAPA_V2_160MHZ_ALLOWED; 160662306a36Sopenharmony_ci reg_capa.disable_11ax = flags & REG_CAPA_V2_11AX_DISABLED; 160762306a36Sopenharmony_ci } else { 160862306a36Sopenharmony_ci reg_capa.allow_40mhz = !(flags & REG_CAPA_V1_40MHZ_FORBIDDEN); 160962306a36Sopenharmony_ci reg_capa.allow_80mhz = flags & REG_CAPA_V1_80MHZ_ALLOWED; 161062306a36Sopenharmony_ci reg_capa.allow_160mhz = flags & REG_CAPA_V1_160MHZ_ALLOWED; 161162306a36Sopenharmony_ci reg_capa.disable_11ax = flags & REG_CAPA_V1_11AX_DISABLED; 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci return reg_capa; 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cistruct ieee80211_regdomain * 161762306a36Sopenharmony_ciiwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, 161862306a36Sopenharmony_ci int num_of_ch, __le32 *channels, u16 fw_mcc, 161962306a36Sopenharmony_ci u16 geo_info, u32 cap, u8 resp_ver) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci int ch_idx; 162262306a36Sopenharmony_ci u16 ch_flags; 162362306a36Sopenharmony_ci u32 reg_rule_flags, prev_reg_rule_flags = 0; 162462306a36Sopenharmony_ci const u16 *nvm_chan; 162562306a36Sopenharmony_ci struct ieee80211_regdomain *regd, *copy_rd; 162662306a36Sopenharmony_ci struct ieee80211_reg_rule *rule; 162762306a36Sopenharmony_ci enum nl80211_band band; 162862306a36Sopenharmony_ci int center_freq, prev_center_freq = 0; 162962306a36Sopenharmony_ci int valid_rules = 0; 163062306a36Sopenharmony_ci bool new_rule; 163162306a36Sopenharmony_ci int max_num_ch; 163262306a36Sopenharmony_ci struct iwl_reg_capa reg_capa; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci if (cfg->uhb_supported) { 163562306a36Sopenharmony_ci max_num_ch = IWL_NVM_NUM_CHANNELS_UHB; 163662306a36Sopenharmony_ci nvm_chan = iwl_uhb_nvm_channels; 163762306a36Sopenharmony_ci } else if (cfg->nvm_type == IWL_NVM_EXT) { 163862306a36Sopenharmony_ci max_num_ch = IWL_NVM_NUM_CHANNELS_EXT; 163962306a36Sopenharmony_ci nvm_chan = iwl_ext_nvm_channels; 164062306a36Sopenharmony_ci } else { 164162306a36Sopenharmony_ci max_num_ch = IWL_NVM_NUM_CHANNELS; 164262306a36Sopenharmony_ci nvm_chan = iwl_nvm_channels; 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci if (num_of_ch > max_num_ch) { 164662306a36Sopenharmony_ci IWL_DEBUG_DEV(dev, IWL_DL_LAR, 164762306a36Sopenharmony_ci "Num of channels (%d) is greater than expected. Truncating to %d\n", 164862306a36Sopenharmony_ci num_of_ch, max_num_ch); 164962306a36Sopenharmony_ci num_of_ch = max_num_ch; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES)) 165362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n", 165662306a36Sopenharmony_ci num_of_ch); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* build a regdomain rule for every valid channel */ 165962306a36Sopenharmony_ci regd = kzalloc(struct_size(regd, reg_rules, num_of_ch), GFP_KERNEL); 166062306a36Sopenharmony_ci if (!regd) 166162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci /* set alpha2 from FW. */ 166462306a36Sopenharmony_ci regd->alpha2[0] = fw_mcc >> 8; 166562306a36Sopenharmony_ci regd->alpha2[1] = fw_mcc & 0xff; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci /* parse regulatory capability flags */ 166862306a36Sopenharmony_ci reg_capa = iwl_get_reg_capa(cap, resp_ver); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { 167162306a36Sopenharmony_ci ch_flags = (u16)__le32_to_cpup(channels + ch_idx); 167262306a36Sopenharmony_ci band = iwl_nl80211_band_from_channel_idx(ch_idx); 167362306a36Sopenharmony_ci center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx], 167462306a36Sopenharmony_ci band); 167562306a36Sopenharmony_ci new_rule = false; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci if (!(ch_flags & NVM_CHANNEL_VALID)) { 167862306a36Sopenharmony_ci iwl_nvm_print_channel_flags(dev, IWL_DL_LAR, 167962306a36Sopenharmony_ci nvm_chan[ch_idx], ch_flags); 168062306a36Sopenharmony_ci continue; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci reg_rule_flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, 168462306a36Sopenharmony_ci ch_flags, reg_capa, 168562306a36Sopenharmony_ci cfg); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci /* we can't continue the same rule */ 168862306a36Sopenharmony_ci if (ch_idx == 0 || prev_reg_rule_flags != reg_rule_flags || 168962306a36Sopenharmony_ci center_freq - prev_center_freq > 20) { 169062306a36Sopenharmony_ci valid_rules++; 169162306a36Sopenharmony_ci new_rule = true; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci rule = ®d->reg_rules[valid_rules - 1]; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci if (new_rule) 169762306a36Sopenharmony_ci rule->freq_range.start_freq_khz = 169862306a36Sopenharmony_ci MHZ_TO_KHZ(center_freq - 10); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci /* this doesn't matter - not used by FW */ 170362306a36Sopenharmony_ci rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); 170462306a36Sopenharmony_ci rule->power_rule.max_eirp = 170562306a36Sopenharmony_ci DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci rule->flags = reg_rule_flags; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci /* rely on auto-calculation to merge BW of contiguous chans */ 171062306a36Sopenharmony_ci rule->flags |= NL80211_RRF_AUTO_BW; 171162306a36Sopenharmony_ci rule->freq_range.max_bandwidth_khz = 0; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci prev_center_freq = center_freq; 171462306a36Sopenharmony_ci prev_reg_rule_flags = reg_rule_flags; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci iwl_nvm_print_channel_flags(dev, IWL_DL_LAR, 171762306a36Sopenharmony_ci nvm_chan[ch_idx], ch_flags); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (!(geo_info & GEO_WMM_ETSI_5GHZ_INFO) || 172062306a36Sopenharmony_ci band == NL80211_BAND_2GHZ) 172162306a36Sopenharmony_ci continue; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci reg_query_regdb_wmm(regd->alpha2, center_freq, rule); 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* 172762306a36Sopenharmony_ci * Certain firmware versions might report no valid channels 172862306a36Sopenharmony_ci * if booted in RF-kill, i.e. not all calibrations etc. are 172962306a36Sopenharmony_ci * running. We'll get out of this situation later when the 173062306a36Sopenharmony_ci * rfkill is removed and we update the regdomain again, but 173162306a36Sopenharmony_ci * since cfg80211 doesn't accept an empty regdomain, add a 173262306a36Sopenharmony_ci * dummy (unusable) rule here in this case so we can init. 173362306a36Sopenharmony_ci */ 173462306a36Sopenharmony_ci if (!valid_rules) { 173562306a36Sopenharmony_ci valid_rules = 1; 173662306a36Sopenharmony_ci rule = ®d->reg_rules[valid_rules - 1]; 173762306a36Sopenharmony_ci rule->freq_range.start_freq_khz = MHZ_TO_KHZ(2412); 173862306a36Sopenharmony_ci rule->freq_range.end_freq_khz = MHZ_TO_KHZ(2413); 173962306a36Sopenharmony_ci rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(1); 174062306a36Sopenharmony_ci rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); 174162306a36Sopenharmony_ci rule->power_rule.max_eirp = 174262306a36Sopenharmony_ci DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER); 174362306a36Sopenharmony_ci } 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci regd->n_reg_rules = valid_rules; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci /* 174862306a36Sopenharmony_ci * Narrow down regdom for unused regulatory rules to prevent hole 174962306a36Sopenharmony_ci * between reg rules to wmm rules. 175062306a36Sopenharmony_ci */ 175162306a36Sopenharmony_ci copy_rd = kmemdup(regd, struct_size(regd, reg_rules, valid_rules), 175262306a36Sopenharmony_ci GFP_KERNEL); 175362306a36Sopenharmony_ci if (!copy_rd) 175462306a36Sopenharmony_ci copy_rd = ERR_PTR(-ENOMEM); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci kfree(regd); 175762306a36Sopenharmony_ci return copy_rd; 175862306a36Sopenharmony_ci} 175962306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci#define IWL_MAX_NVM_SECTION_SIZE 0x1b58 176262306a36Sopenharmony_ci#define IWL_MAX_EXT_NVM_SECTION_SIZE 0x1ffc 176362306a36Sopenharmony_ci#define MAX_NVM_FILE_LEN 16384 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_civoid iwl_nvm_fixups(u32 hw_id, unsigned int section, u8 *data, 176662306a36Sopenharmony_ci unsigned int len) 176762306a36Sopenharmony_ci{ 176862306a36Sopenharmony_ci#define IWL_4165_DEVICE_ID 0x5501 176962306a36Sopenharmony_ci#define NVM_SKU_CAP_MIMO_DISABLE BIT(5) 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci if (section == NVM_SECTION_TYPE_PHY_SKU && 177262306a36Sopenharmony_ci hw_id == IWL_4165_DEVICE_ID && data && len >= 5 && 177362306a36Sopenharmony_ci (data[4] & NVM_SKU_CAP_MIMO_DISABLE)) 177462306a36Sopenharmony_ci /* OTP 0x52 bug work around: it's a 1x1 device */ 177562306a36Sopenharmony_ci data[3] = ANT_B | (ANT_B << 4); 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_nvm_fixups); 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci/* 178062306a36Sopenharmony_ci * Reads external NVM from a file into mvm->nvm_sections 178162306a36Sopenharmony_ci * 178262306a36Sopenharmony_ci * HOW TO CREATE THE NVM FILE FORMAT: 178362306a36Sopenharmony_ci * ------------------------------ 178462306a36Sopenharmony_ci * 1. create hex file, format: 178562306a36Sopenharmony_ci * 3800 -> header 178662306a36Sopenharmony_ci * 0000 -> header 178762306a36Sopenharmony_ci * 5a40 -> data 178862306a36Sopenharmony_ci * 178962306a36Sopenharmony_ci * rev - 6 bit (word1) 179062306a36Sopenharmony_ci * len - 10 bit (word1) 179162306a36Sopenharmony_ci * id - 4 bit (word2) 179262306a36Sopenharmony_ci * rsv - 12 bit (word2) 179362306a36Sopenharmony_ci * 179462306a36Sopenharmony_ci * 2. flip 8bits with 8 bits per line to get the right NVM file format 179562306a36Sopenharmony_ci * 179662306a36Sopenharmony_ci * 3. create binary file from the hex file 179762306a36Sopenharmony_ci * 179862306a36Sopenharmony_ci * 4. save as "iNVM_xxx.bin" under /lib/firmware 179962306a36Sopenharmony_ci */ 180062306a36Sopenharmony_ciint iwl_read_external_nvm(struct iwl_trans *trans, 180162306a36Sopenharmony_ci const char *nvm_file_name, 180262306a36Sopenharmony_ci struct iwl_nvm_section *nvm_sections) 180362306a36Sopenharmony_ci{ 180462306a36Sopenharmony_ci int ret, section_size; 180562306a36Sopenharmony_ci u16 section_id; 180662306a36Sopenharmony_ci const struct firmware *fw_entry; 180762306a36Sopenharmony_ci const struct { 180862306a36Sopenharmony_ci __le16 word1; 180962306a36Sopenharmony_ci __le16 word2; 181062306a36Sopenharmony_ci u8 data[]; 181162306a36Sopenharmony_ci } *file_sec; 181262306a36Sopenharmony_ci const u8 *eof; 181362306a36Sopenharmony_ci u8 *temp; 181462306a36Sopenharmony_ci int max_section_size; 181562306a36Sopenharmony_ci const __le32 *dword_buff; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) 181862306a36Sopenharmony_ci#define NVM_WORD2_ID(x) (x >> 12) 181962306a36Sopenharmony_ci#define EXT_NVM_WORD2_LEN(x) (2 * (((x) & 0xFF) << 8 | (x) >> 8)) 182062306a36Sopenharmony_ci#define EXT_NVM_WORD1_ID(x) ((x) >> 4) 182162306a36Sopenharmony_ci#define NVM_HEADER_0 (0x2A504C54) 182262306a36Sopenharmony_ci#define NVM_HEADER_1 (0x4E564D2A) 182362306a36Sopenharmony_ci#define NVM_HEADER_SIZE (4 * sizeof(u32)) 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci IWL_DEBUG_EEPROM(trans->dev, "Read from external NVM\n"); 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci /* Maximal size depends on NVM version */ 182862306a36Sopenharmony_ci if (trans->cfg->nvm_type != IWL_NVM_EXT) 182962306a36Sopenharmony_ci max_section_size = IWL_MAX_NVM_SECTION_SIZE; 183062306a36Sopenharmony_ci else 183162306a36Sopenharmony_ci max_section_size = IWL_MAX_EXT_NVM_SECTION_SIZE; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* 183462306a36Sopenharmony_ci * Obtain NVM image via request_firmware. Since we already used 183562306a36Sopenharmony_ci * request_firmware_nowait() for the firmware binary load and only 183662306a36Sopenharmony_ci * get here after that we assume the NVM request can be satisfied 183762306a36Sopenharmony_ci * synchronously. 183862306a36Sopenharmony_ci */ 183962306a36Sopenharmony_ci ret = request_firmware(&fw_entry, nvm_file_name, trans->dev); 184062306a36Sopenharmony_ci if (ret) { 184162306a36Sopenharmony_ci IWL_ERR(trans, "ERROR: %s isn't available %d\n", 184262306a36Sopenharmony_ci nvm_file_name, ret); 184362306a36Sopenharmony_ci return ret; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci IWL_INFO(trans, "Loaded NVM file %s (%zu bytes)\n", 184762306a36Sopenharmony_ci nvm_file_name, fw_entry->size); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci if (fw_entry->size > MAX_NVM_FILE_LEN) { 185062306a36Sopenharmony_ci IWL_ERR(trans, "NVM file too large\n"); 185162306a36Sopenharmony_ci ret = -EINVAL; 185262306a36Sopenharmony_ci goto out; 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci eof = fw_entry->data + fw_entry->size; 185662306a36Sopenharmony_ci dword_buff = (const __le32 *)fw_entry->data; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci /* some NVM file will contain a header. 185962306a36Sopenharmony_ci * The header is identified by 2 dwords header as follow: 186062306a36Sopenharmony_ci * dword[0] = 0x2A504C54 186162306a36Sopenharmony_ci * dword[1] = 0x4E564D2A 186262306a36Sopenharmony_ci * 186362306a36Sopenharmony_ci * This header must be skipped when providing the NVM data to the FW. 186462306a36Sopenharmony_ci */ 186562306a36Sopenharmony_ci if (fw_entry->size > NVM_HEADER_SIZE && 186662306a36Sopenharmony_ci dword_buff[0] == cpu_to_le32(NVM_HEADER_0) && 186762306a36Sopenharmony_ci dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) { 186862306a36Sopenharmony_ci file_sec = (const void *)(fw_entry->data + NVM_HEADER_SIZE); 186962306a36Sopenharmony_ci IWL_INFO(trans, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); 187062306a36Sopenharmony_ci IWL_INFO(trans, "NVM Manufacturing date %08X\n", 187162306a36Sopenharmony_ci le32_to_cpu(dword_buff[3])); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci /* nvm file validation, dword_buff[2] holds the file version */ 187462306a36Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_8000 && 187562306a36Sopenharmony_ci trans->hw_rev_step == SILICON_C_STEP && 187662306a36Sopenharmony_ci le32_to_cpu(dword_buff[2]) < 0xE4A) { 187762306a36Sopenharmony_ci ret = -EFAULT; 187862306a36Sopenharmony_ci goto out; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci } else { 188162306a36Sopenharmony_ci file_sec = (const void *)fw_entry->data; 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci while (true) { 188562306a36Sopenharmony_ci if (file_sec->data > eof) { 188662306a36Sopenharmony_ci IWL_ERR(trans, 188762306a36Sopenharmony_ci "ERROR - NVM file too short for section header\n"); 188862306a36Sopenharmony_ci ret = -EINVAL; 188962306a36Sopenharmony_ci break; 189062306a36Sopenharmony_ci } 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci /* check for EOF marker */ 189362306a36Sopenharmony_ci if (!file_sec->word1 && !file_sec->word2) { 189462306a36Sopenharmony_ci ret = 0; 189562306a36Sopenharmony_ci break; 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci if (trans->cfg->nvm_type != IWL_NVM_EXT) { 189962306a36Sopenharmony_ci section_size = 190062306a36Sopenharmony_ci 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); 190162306a36Sopenharmony_ci section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); 190262306a36Sopenharmony_ci } else { 190362306a36Sopenharmony_ci section_size = 2 * EXT_NVM_WORD2_LEN( 190462306a36Sopenharmony_ci le16_to_cpu(file_sec->word2)); 190562306a36Sopenharmony_ci section_id = EXT_NVM_WORD1_ID( 190662306a36Sopenharmony_ci le16_to_cpu(file_sec->word1)); 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci if (section_size > max_section_size) { 191062306a36Sopenharmony_ci IWL_ERR(trans, "ERROR - section too large (%d)\n", 191162306a36Sopenharmony_ci section_size); 191262306a36Sopenharmony_ci ret = -EINVAL; 191362306a36Sopenharmony_ci break; 191462306a36Sopenharmony_ci } 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci if (!section_size) { 191762306a36Sopenharmony_ci IWL_ERR(trans, "ERROR - section empty\n"); 191862306a36Sopenharmony_ci ret = -EINVAL; 191962306a36Sopenharmony_ci break; 192062306a36Sopenharmony_ci } 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci if (file_sec->data + section_size > eof) { 192362306a36Sopenharmony_ci IWL_ERR(trans, 192462306a36Sopenharmony_ci "ERROR - NVM file too short for section (%d bytes)\n", 192562306a36Sopenharmony_ci section_size); 192662306a36Sopenharmony_ci ret = -EINVAL; 192762306a36Sopenharmony_ci break; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, 193162306a36Sopenharmony_ci "Invalid NVM section ID %d\n", section_id)) { 193262306a36Sopenharmony_ci ret = -EINVAL; 193362306a36Sopenharmony_ci break; 193462306a36Sopenharmony_ci } 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); 193762306a36Sopenharmony_ci if (!temp) { 193862306a36Sopenharmony_ci ret = -ENOMEM; 193962306a36Sopenharmony_ci break; 194062306a36Sopenharmony_ci } 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci iwl_nvm_fixups(trans->hw_id, section_id, temp, section_size); 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci kfree(nvm_sections[section_id].data); 194562306a36Sopenharmony_ci nvm_sections[section_id].data = temp; 194662306a36Sopenharmony_ci nvm_sections[section_id].length = section_size; 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci /* advance to the next section */ 194962306a36Sopenharmony_ci file_sec = (const void *)(file_sec->data + section_size); 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ciout: 195262306a36Sopenharmony_ci release_firmware(fw_entry); 195362306a36Sopenharmony_ci return ret; 195462306a36Sopenharmony_ci} 195562306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_read_external_nvm); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_cistruct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, 195862306a36Sopenharmony_ci const struct iwl_fw *fw) 195962306a36Sopenharmony_ci{ 196062306a36Sopenharmony_ci struct iwl_nvm_get_info cmd = {}; 196162306a36Sopenharmony_ci struct iwl_nvm_data *nvm; 196262306a36Sopenharmony_ci struct iwl_host_cmd hcmd = { 196362306a36Sopenharmony_ci .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, 196462306a36Sopenharmony_ci .data = { &cmd, }, 196562306a36Sopenharmony_ci .len = { sizeof(cmd) }, 196662306a36Sopenharmony_ci .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO) 196762306a36Sopenharmony_ci }; 196862306a36Sopenharmony_ci int ret; 196962306a36Sopenharmony_ci bool empty_otp; 197062306a36Sopenharmony_ci u32 mac_flags; 197162306a36Sopenharmony_ci u32 sbands_flags = 0; 197262306a36Sopenharmony_ci /* 197362306a36Sopenharmony_ci * All the values in iwl_nvm_get_info_rsp v4 are the same as 197462306a36Sopenharmony_ci * in v3, except for the channel profile part of the 197562306a36Sopenharmony_ci * regulatory. So we can just access the new struct, with the 197662306a36Sopenharmony_ci * exception of the latter. 197762306a36Sopenharmony_ci */ 197862306a36Sopenharmony_ci struct iwl_nvm_get_info_rsp *rsp; 197962306a36Sopenharmony_ci struct iwl_nvm_get_info_rsp_v3 *rsp_v3; 198062306a36Sopenharmony_ci bool v4 = fw_has_api(&fw->ucode_capa, 198162306a36Sopenharmony_ci IWL_UCODE_TLV_API_REGULATORY_NVM_INFO); 198262306a36Sopenharmony_ci size_t rsp_size = v4 ? sizeof(*rsp) : sizeof(*rsp_v3); 198362306a36Sopenharmony_ci void *channel_profile; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci ret = iwl_trans_send_cmd(trans, &hcmd); 198662306a36Sopenharmony_ci if (ret) 198762306a36Sopenharmony_ci return ERR_PTR(ret); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != rsp_size, 199062306a36Sopenharmony_ci "Invalid payload len in NVM response from FW %d", 199162306a36Sopenharmony_ci iwl_rx_packet_payload_len(hcmd.resp_pkt))) { 199262306a36Sopenharmony_ci ret = -EINVAL; 199362306a36Sopenharmony_ci goto out; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci rsp = (void *)hcmd.resp_pkt->data; 199762306a36Sopenharmony_ci empty_otp = !!(le32_to_cpu(rsp->general.flags) & 199862306a36Sopenharmony_ci NVM_GENERAL_FLAGS_EMPTY_OTP); 199962306a36Sopenharmony_ci if (empty_otp) 200062306a36Sopenharmony_ci IWL_INFO(trans, "OTP is empty\n"); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci nvm = kzalloc(struct_size(nvm, channels, IWL_NUM_CHANNELS), GFP_KERNEL); 200362306a36Sopenharmony_ci if (!nvm) { 200462306a36Sopenharmony_ci ret = -ENOMEM; 200562306a36Sopenharmony_ci goto out; 200662306a36Sopenharmony_ci } 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci iwl_set_hw_address_from_csr(trans, nvm); 200962306a36Sopenharmony_ci /* TODO: if platform NVM has MAC address - override it here */ 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci if (!is_valid_ether_addr(nvm->hw_addr)) { 201262306a36Sopenharmony_ci IWL_ERR(trans, "no valid mac address was found\n"); 201362306a36Sopenharmony_ci ret = -EINVAL; 201462306a36Sopenharmony_ci goto err_free; 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci IWL_INFO(trans, "base HW address: %pM\n", nvm->hw_addr); 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci /* Initialize general data */ 202062306a36Sopenharmony_ci nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version); 202162306a36Sopenharmony_ci nvm->n_hw_addrs = rsp->general.n_hw_addrs; 202262306a36Sopenharmony_ci if (nvm->n_hw_addrs == 0) 202362306a36Sopenharmony_ci IWL_WARN(trans, 202462306a36Sopenharmony_ci "Firmware declares no reserved mac addresses. OTP is empty: %d\n", 202562306a36Sopenharmony_ci empty_otp); 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci /* Initialize MAC sku data */ 202862306a36Sopenharmony_ci mac_flags = le32_to_cpu(rsp->mac_sku.mac_sku_flags); 202962306a36Sopenharmony_ci nvm->sku_cap_11ac_enable = 203062306a36Sopenharmony_ci !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AC_ENABLED); 203162306a36Sopenharmony_ci nvm->sku_cap_11n_enable = 203262306a36Sopenharmony_ci !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11N_ENABLED); 203362306a36Sopenharmony_ci nvm->sku_cap_11ax_enable = 203462306a36Sopenharmony_ci !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AX_ENABLED); 203562306a36Sopenharmony_ci nvm->sku_cap_band_24ghz_enable = 203662306a36Sopenharmony_ci !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED); 203762306a36Sopenharmony_ci nvm->sku_cap_band_52ghz_enable = 203862306a36Sopenharmony_ci !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED); 203962306a36Sopenharmony_ci nvm->sku_cap_mimo_disabled = 204062306a36Sopenharmony_ci !!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED); 204162306a36Sopenharmony_ci if (CSR_HW_RFID_TYPE(trans->hw_rf_id) >= IWL_CFG_RF_TYPE_FM) 204262306a36Sopenharmony_ci nvm->sku_cap_11be_enable = true; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci /* Initialize PHY sku data */ 204562306a36Sopenharmony_ci nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains); 204662306a36Sopenharmony_ci nvm->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains); 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci if (le32_to_cpu(rsp->regulatory.lar_enabled) && 204962306a36Sopenharmony_ci fw_has_capa(&fw->ucode_capa, 205062306a36Sopenharmony_ci IWL_UCODE_TLV_CAPA_LAR_SUPPORT)) { 205162306a36Sopenharmony_ci nvm->lar_enabled = true; 205262306a36Sopenharmony_ci sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR; 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci rsp_v3 = (void *)rsp; 205662306a36Sopenharmony_ci channel_profile = v4 ? (void *)rsp->regulatory.channel_profile : 205762306a36Sopenharmony_ci (void *)rsp_v3->regulatory.channel_profile; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci iwl_init_sbands(trans, nvm, 206062306a36Sopenharmony_ci channel_profile, 206162306a36Sopenharmony_ci nvm->valid_tx_ant & fw->valid_tx_ant, 206262306a36Sopenharmony_ci nvm->valid_rx_ant & fw->valid_rx_ant, 206362306a36Sopenharmony_ci sbands_flags, v4, fw); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci iwl_free_resp(&hcmd); 206662306a36Sopenharmony_ci return nvm; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_cierr_free: 206962306a36Sopenharmony_ci kfree(nvm); 207062306a36Sopenharmony_ciout: 207162306a36Sopenharmony_ci iwl_free_resp(&hcmd); 207262306a36Sopenharmony_ci return ERR_PTR(ret); 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_get_nvm); 2075