162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005-2014, 2018-2020 Intel Corporation 462306a36Sopenharmony_ci * Copyright (C) 2015 Intel Mobile Communications GmbH 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/types.h> 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include "iwl-drv.h" 1062306a36Sopenharmony_ci#include "iwl-modparams.h" 1162306a36Sopenharmony_ci#include "iwl-eeprom-parse.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IWLDVM) 1462306a36Sopenharmony_ci/* EEPROM offset definitions */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* indirect access definitions */ 1762306a36Sopenharmony_ci#define ADDRESS_MSK 0x0000FFFF 1862306a36Sopenharmony_ci#define INDIRECT_TYPE_MSK 0x000F0000 1962306a36Sopenharmony_ci#define INDIRECT_HOST 0x00010000 2062306a36Sopenharmony_ci#define INDIRECT_GENERAL 0x00020000 2162306a36Sopenharmony_ci#define INDIRECT_REGULATORY 0x00030000 2262306a36Sopenharmony_ci#define INDIRECT_CALIBRATION 0x00040000 2362306a36Sopenharmony_ci#define INDIRECT_PROCESS_ADJST 0x00050000 2462306a36Sopenharmony_ci#define INDIRECT_OTHERS 0x00060000 2562306a36Sopenharmony_ci#define INDIRECT_TXP_LIMIT 0x00070000 2662306a36Sopenharmony_ci#define INDIRECT_TXP_LIMIT_SIZE 0x00080000 2762306a36Sopenharmony_ci#define INDIRECT_ADDRESS 0x00100000 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* corresponding link offsets in EEPROM */ 3062306a36Sopenharmony_ci#define EEPROM_LINK_HOST (2*0x64) 3162306a36Sopenharmony_ci#define EEPROM_LINK_GENERAL (2*0x65) 3262306a36Sopenharmony_ci#define EEPROM_LINK_REGULATORY (2*0x66) 3362306a36Sopenharmony_ci#define EEPROM_LINK_CALIBRATION (2*0x67) 3462306a36Sopenharmony_ci#define EEPROM_LINK_PROCESS_ADJST (2*0x68) 3562306a36Sopenharmony_ci#define EEPROM_LINK_OTHERS (2*0x69) 3662306a36Sopenharmony_ci#define EEPROM_LINK_TXP_LIMIT (2*0x6a) 3762306a36Sopenharmony_ci#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* General */ 4062306a36Sopenharmony_ci#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ 4162306a36Sopenharmony_ci#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */ 4262306a36Sopenharmony_ci#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ 4362306a36Sopenharmony_ci#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ 4462306a36Sopenharmony_ci#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ 4562306a36Sopenharmony_ci#define EEPROM_VERSION (2*0x44) /* 2 bytes */ 4662306a36Sopenharmony_ci#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ 4762306a36Sopenharmony_ci#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ 4862306a36Sopenharmony_ci#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ 4962306a36Sopenharmony_ci#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* calibration */ 5262306a36Sopenharmony_cistruct iwl_eeprom_calib_hdr { 5362306a36Sopenharmony_ci u8 version; 5462306a36Sopenharmony_ci u8 pa_type; 5562306a36Sopenharmony_ci __le16 voltage; 5662306a36Sopenharmony_ci} __packed; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) 5962306a36Sopenharmony_ci#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* temperature */ 6262306a36Sopenharmony_ci#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) 6362306a36Sopenharmony_ci#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* SKU Capabilities (actual values from EEPROM definition) */ 6662306a36Sopenharmony_cienum eeprom_sku_bits { 6762306a36Sopenharmony_ci EEPROM_SKU_CAP_BAND_24GHZ = BIT(4), 6862306a36Sopenharmony_ci EEPROM_SKU_CAP_BAND_52GHZ = BIT(5), 6962306a36Sopenharmony_ci EEPROM_SKU_CAP_11N_ENABLE = BIT(6), 7062306a36Sopenharmony_ci EEPROM_SKU_CAP_AMT_ENABLE = BIT(7), 7162306a36Sopenharmony_ci EEPROM_SKU_CAP_IPAN_ENABLE = BIT(8) 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* radio config bits (actual values from EEPROM definition) */ 7562306a36Sopenharmony_ci#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ 7662306a36Sopenharmony_ci#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ 7762306a36Sopenharmony_ci#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ 7862306a36Sopenharmony_ci#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ 7962306a36Sopenharmony_ci#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ 8062306a36Sopenharmony_ci#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * EEPROM bands 8562306a36Sopenharmony_ci * These are the channel numbers from each band in the order 8662306a36Sopenharmony_ci * that they are stored in the EEPROM band information. Note 8762306a36Sopenharmony_ci * that EEPROM bands aren't the same as mac80211 bands, and 8862306a36Sopenharmony_ci * there are even special "ht40 bands" in the EEPROM. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic const u8 iwl_eeprom_band_1[14] = { /* 2.4 GHz */ 9162306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */ 9562306a36Sopenharmony_ci 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */ 9962306a36Sopenharmony_ci 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */ 10362306a36Sopenharmony_ci 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ 10762306a36Sopenharmony_ci 145, 149, 153, 157, 161, 165 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ 11162306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ 11562306a36Sopenharmony_ci 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define IWL_NUM_CHANNELS (ARRAY_SIZE(iwl_eeprom_band_1) + \ 11962306a36Sopenharmony_ci ARRAY_SIZE(iwl_eeprom_band_2) + \ 12062306a36Sopenharmony_ci ARRAY_SIZE(iwl_eeprom_band_3) + \ 12162306a36Sopenharmony_ci ARRAY_SIZE(iwl_eeprom_band_4) + \ 12262306a36Sopenharmony_ci ARRAY_SIZE(iwl_eeprom_band_5)) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* rate data (static) */ 12562306a36Sopenharmony_cistatic struct ieee80211_rate iwl_cfg80211_rates[] = { 12662306a36Sopenharmony_ci { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, 12762306a36Sopenharmony_ci { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, 12862306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, 12962306a36Sopenharmony_ci { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, 13062306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, 13162306a36Sopenharmony_ci { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, 13262306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, 13362306a36Sopenharmony_ci { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, 13462306a36Sopenharmony_ci { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, 13562306a36Sopenharmony_ci { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, 13662306a36Sopenharmony_ci { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, 13762306a36Sopenharmony_ci { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, 13862306a36Sopenharmony_ci { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, 13962306a36Sopenharmony_ci { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, 14062306a36Sopenharmony_ci { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci#define RATES_24_OFFS 0 14362306a36Sopenharmony_ci#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) 14462306a36Sopenharmony_ci#define RATES_52_OFFS 4 14562306a36Sopenharmony_ci#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* EEPROM reading functions */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci if (WARN_ON(offset + sizeof(u16) > eeprom_size)) 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci return le16_to_cpup((__le16 *)(eeprom + offset)); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size, 15762306a36Sopenharmony_ci u32 address) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci u16 offset = 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if ((address & INDIRECT_ADDRESS) == 0) 16262306a36Sopenharmony_ci return address; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci switch (address & INDIRECT_TYPE_MSK) { 16562306a36Sopenharmony_ci case INDIRECT_HOST: 16662306a36Sopenharmony_ci offset = iwl_eeprom_query16(eeprom, eeprom_size, 16762306a36Sopenharmony_ci EEPROM_LINK_HOST); 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case INDIRECT_GENERAL: 17062306a36Sopenharmony_ci offset = iwl_eeprom_query16(eeprom, eeprom_size, 17162306a36Sopenharmony_ci EEPROM_LINK_GENERAL); 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case INDIRECT_REGULATORY: 17462306a36Sopenharmony_ci offset = iwl_eeprom_query16(eeprom, eeprom_size, 17562306a36Sopenharmony_ci EEPROM_LINK_REGULATORY); 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci case INDIRECT_TXP_LIMIT: 17862306a36Sopenharmony_ci offset = iwl_eeprom_query16(eeprom, eeprom_size, 17962306a36Sopenharmony_ci EEPROM_LINK_TXP_LIMIT); 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci case INDIRECT_TXP_LIMIT_SIZE: 18262306a36Sopenharmony_ci offset = iwl_eeprom_query16(eeprom, eeprom_size, 18362306a36Sopenharmony_ci EEPROM_LINK_TXP_LIMIT_SIZE); 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci case INDIRECT_CALIBRATION: 18662306a36Sopenharmony_ci offset = iwl_eeprom_query16(eeprom, eeprom_size, 18762306a36Sopenharmony_ci EEPROM_LINK_CALIBRATION); 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case INDIRECT_PROCESS_ADJST: 19062306a36Sopenharmony_ci offset = iwl_eeprom_query16(eeprom, eeprom_size, 19162306a36Sopenharmony_ci EEPROM_LINK_PROCESS_ADJST); 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci case INDIRECT_OTHERS: 19462306a36Sopenharmony_ci offset = iwl_eeprom_query16(eeprom, eeprom_size, 19562306a36Sopenharmony_ci EEPROM_LINK_OTHERS); 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci default: 19862306a36Sopenharmony_ci WARN_ON(1); 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* translate the offset from words to byte */ 20362306a36Sopenharmony_ci return (address & ADDRESS_MSK) + (offset << 1); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size, 20762306a36Sopenharmony_ci u32 offset) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (WARN_ON(address >= eeprom_size)) 21262306a36Sopenharmony_ci return NULL; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return &eeprom[address]; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size, 21862306a36Sopenharmony_ci struct iwl_nvm_data *data) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct iwl_eeprom_calib_hdr *hdr; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, 22362306a36Sopenharmony_ci EEPROM_CALIB_ALL); 22462306a36Sopenharmony_ci if (!hdr) 22562306a36Sopenharmony_ci return -ENODATA; 22662306a36Sopenharmony_ci data->calib_version = hdr->version; 22762306a36Sopenharmony_ci data->calib_voltage = hdr->voltage; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/** 23362306a36Sopenharmony_ci * enum iwl_eeprom_channel_flags - channel flags in EEPROM 23462306a36Sopenharmony_ci * @EEPROM_CHANNEL_VALID: channel is usable for this SKU/geo 23562306a36Sopenharmony_ci * @EEPROM_CHANNEL_IBSS: usable as an IBSS channel 23662306a36Sopenharmony_ci * @EEPROM_CHANNEL_ACTIVE: active scanning allowed 23762306a36Sopenharmony_ci * @EEPROM_CHANNEL_RADAR: radar detection required 23862306a36Sopenharmony_ci * @EEPROM_CHANNEL_WIDE: 20 MHz channel okay (?) 23962306a36Sopenharmony_ci * @EEPROM_CHANNEL_DFS: dynamic freq selection candidate 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_cienum iwl_eeprom_channel_flags { 24262306a36Sopenharmony_ci EEPROM_CHANNEL_VALID = BIT(0), 24362306a36Sopenharmony_ci EEPROM_CHANNEL_IBSS = BIT(1), 24462306a36Sopenharmony_ci EEPROM_CHANNEL_ACTIVE = BIT(3), 24562306a36Sopenharmony_ci EEPROM_CHANNEL_RADAR = BIT(4), 24662306a36Sopenharmony_ci EEPROM_CHANNEL_WIDE = BIT(5), 24762306a36Sopenharmony_ci EEPROM_CHANNEL_DFS = BIT(7), 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/** 25162306a36Sopenharmony_ci * struct iwl_eeprom_channel - EEPROM channel data 25262306a36Sopenharmony_ci * @flags: %EEPROM_CHANNEL_* flags 25362306a36Sopenharmony_ci * @max_power_avg: max power (in dBm) on this channel, at most 31 dBm 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_cistruct iwl_eeprom_channel { 25662306a36Sopenharmony_ci u8 flags; 25762306a36Sopenharmony_ci s8 max_power_avg; 25862306a36Sopenharmony_ci} __packed; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cienum iwl_eeprom_enhanced_txpwr_flags { 26262306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0), 26362306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1), 26462306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2), 26562306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3), 26662306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4), 26762306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5), 26862306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6), 26962306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7), 27062306a36Sopenharmony_ci}; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/** 27362306a36Sopenharmony_ci * struct iwl_eeprom_enhanced_txpwr 27462306a36Sopenharmony_ci * @flags: entry flags 27562306a36Sopenharmony_ci * @channel: channel number 27662306a36Sopenharmony_ci * @chain_a_max: chain a max power in 1/2 dBm 27762306a36Sopenharmony_ci * @chain_b_max: chain b max power in 1/2 dBm 27862306a36Sopenharmony_ci * @chain_c_max: chain c max power in 1/2 dBm 27962306a36Sopenharmony_ci * @delta_20_in_40: 20-in-40 deltas (hi/lo) 28062306a36Sopenharmony_ci * @mimo2_max: mimo2 max power in 1/2 dBm 28162306a36Sopenharmony_ci * @mimo3_max: mimo3 max power in 1/2 dBm 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * This structure presents the enhanced regulatory tx power limit layout 28462306a36Sopenharmony_ci * in an EEPROM image. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_cistruct iwl_eeprom_enhanced_txpwr { 28762306a36Sopenharmony_ci u8 flags; 28862306a36Sopenharmony_ci u8 channel; 28962306a36Sopenharmony_ci s8 chain_a_max; 29062306a36Sopenharmony_ci s8 chain_b_max; 29162306a36Sopenharmony_ci s8 chain_c_max; 29262306a36Sopenharmony_ci u8 delta_20_in_40; 29362306a36Sopenharmony_ci s8 mimo2_max; 29462306a36Sopenharmony_ci s8 mimo3_max; 29562306a36Sopenharmony_ci} __packed; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data, 29862306a36Sopenharmony_ci struct iwl_eeprom_enhanced_txpwr *txp) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci s8 result = 0; /* (.5 dBm) */ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Take the highest tx power from any valid chains */ 30362306a36Sopenharmony_ci if (data->valid_tx_ant & ANT_A && txp->chain_a_max > result) 30462306a36Sopenharmony_ci result = txp->chain_a_max; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (data->valid_tx_ant & ANT_B && txp->chain_b_max > result) 30762306a36Sopenharmony_ci result = txp->chain_b_max; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (data->valid_tx_ant & ANT_C && txp->chain_c_max > result) 31062306a36Sopenharmony_ci result = txp->chain_c_max; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if ((data->valid_tx_ant == ANT_AB || 31362306a36Sopenharmony_ci data->valid_tx_ant == ANT_BC || 31462306a36Sopenharmony_ci data->valid_tx_ant == ANT_AC) && txp->mimo2_max > result) 31562306a36Sopenharmony_ci result = txp->mimo2_max; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (data->valid_tx_ant == ANT_ABC && txp->mimo3_max > result) 31862306a36Sopenharmony_ci result = txp->mimo3_max; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return result; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) 32462306a36Sopenharmony_ci#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) 32562306a36Sopenharmony_ci#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci#define TXP_CHECK_AND_PRINT(x) \ 32862306a36Sopenharmony_ci ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) ? # x " " : "") 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void 33162306a36Sopenharmony_ciiwl_eeprom_enh_txp_read_element(struct iwl_nvm_data *data, 33262306a36Sopenharmony_ci struct iwl_eeprom_enhanced_txpwr *txp, 33362306a36Sopenharmony_ci int n_channels, s8 max_txpower_avg) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci int ch_idx; 33662306a36Sopenharmony_ci enum nl80211_band band; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? 33962306a36Sopenharmony_ci NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci for (ch_idx = 0; ch_idx < n_channels; ch_idx++) { 34262306a36Sopenharmony_ci struct ieee80211_channel *chan = &data->channels[ch_idx]; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* update matching channel or from common data only */ 34562306a36Sopenharmony_ci if (txp->channel != 0 && chan->hw_value != txp->channel) 34662306a36Sopenharmony_ci continue; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* update matching band only */ 34962306a36Sopenharmony_ci if (band != chan->band) 35062306a36Sopenharmony_ci continue; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (chan->max_power < max_txpower_avg && 35362306a36Sopenharmony_ci !(txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ)) 35462306a36Sopenharmony_ci chan->max_power = max_txpower_avg; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void iwl_eeprom_enhanced_txpower(struct device *dev, 35962306a36Sopenharmony_ci struct iwl_nvm_data *data, 36062306a36Sopenharmony_ci const u8 *eeprom, size_t eeprom_size, 36162306a36Sopenharmony_ci int n_channels) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; 36462306a36Sopenharmony_ci int idx, entries; 36562306a36Sopenharmony_ci __le16 *txp_len; 36662306a36Sopenharmony_ci s8 max_txp_avg_halfdbm; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* the length is in 16-bit words, but we want entries */ 37162306a36Sopenharmony_ci txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size, 37262306a36Sopenharmony_ci EEPROM_TXP_SZ_OFFS); 37362306a36Sopenharmony_ci entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, 37662306a36Sopenharmony_ci EEPROM_TXP_OFFS); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci for (idx = 0; idx < entries; idx++) { 37962306a36Sopenharmony_ci txp = &txp_array[idx]; 38062306a36Sopenharmony_ci /* skip invalid entries */ 38162306a36Sopenharmony_ci if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) 38262306a36Sopenharmony_ci continue; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci IWL_DEBUG_EEPROM(dev, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", 38562306a36Sopenharmony_ci (txp->channel && (txp->flags & 38662306a36Sopenharmony_ci IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? 38762306a36Sopenharmony_ci "Common " : (txp->channel) ? 38862306a36Sopenharmony_ci "Channel" : "Common", 38962306a36Sopenharmony_ci (txp->channel), 39062306a36Sopenharmony_ci TXP_CHECK_AND_PRINT(VALID), 39162306a36Sopenharmony_ci TXP_CHECK_AND_PRINT(BAND_52G), 39262306a36Sopenharmony_ci TXP_CHECK_AND_PRINT(OFDM), 39362306a36Sopenharmony_ci TXP_CHECK_AND_PRINT(40MHZ), 39462306a36Sopenharmony_ci TXP_CHECK_AND_PRINT(HT_AP), 39562306a36Sopenharmony_ci TXP_CHECK_AND_PRINT(RES1), 39662306a36Sopenharmony_ci TXP_CHECK_AND_PRINT(RES2), 39762306a36Sopenharmony_ci TXP_CHECK_AND_PRINT(COMMON_TYPE), 39862306a36Sopenharmony_ci txp->flags); 39962306a36Sopenharmony_ci IWL_DEBUG_EEPROM(dev, 40062306a36Sopenharmony_ci "\t\t chain_A: %d chain_B: %d chain_C: %d\n", 40162306a36Sopenharmony_ci txp->chain_a_max, txp->chain_b_max, 40262306a36Sopenharmony_ci txp->chain_c_max); 40362306a36Sopenharmony_ci IWL_DEBUG_EEPROM(dev, 40462306a36Sopenharmony_ci "\t\t MIMO2: %d MIMO3: %d High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n", 40562306a36Sopenharmony_ci txp->mimo2_max, txp->mimo3_max, 40662306a36Sopenharmony_ci ((txp->delta_20_in_40 & 0xf0) >> 4), 40762306a36Sopenharmony_ci (txp->delta_20_in_40 & 0x0f)); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci max_txp_avg_halfdbm = iwl_get_max_txpwr_half_dbm(data, txp); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci iwl_eeprom_enh_txp_read_element(data, txp, n_channels, 41262306a36Sopenharmony_ci DIV_ROUND_UP(max_txp_avg_halfdbm, 2)); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (max_txp_avg_halfdbm > data->max_tx_pwr_half_dbm) 41562306a36Sopenharmony_ci data->max_tx_pwr_half_dbm = max_txp_avg_halfdbm; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void iwl_init_band_reference(const struct iwl_cfg *cfg, 42062306a36Sopenharmony_ci const u8 *eeprom, size_t eeprom_size, 42162306a36Sopenharmony_ci int eeprom_band, int *eeprom_ch_count, 42262306a36Sopenharmony_ci const struct iwl_eeprom_channel **ch_info, 42362306a36Sopenharmony_ci const u8 **eeprom_ch_array) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci u32 offset = cfg->eeprom_params->regulatory_bands[eeprom_band - 1]; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci switch (eeprom_band) { 43262306a36Sopenharmony_ci case 1: /* 2.4GHz band */ 43362306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); 43462306a36Sopenharmony_ci *eeprom_ch_array = iwl_eeprom_band_1; 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci case 2: /* 4.9GHz band */ 43762306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); 43862306a36Sopenharmony_ci *eeprom_ch_array = iwl_eeprom_band_2; 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci case 3: /* 5.2GHz band */ 44162306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); 44262306a36Sopenharmony_ci *eeprom_ch_array = iwl_eeprom_band_3; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case 4: /* 5.5GHz band */ 44562306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); 44662306a36Sopenharmony_ci *eeprom_ch_array = iwl_eeprom_band_4; 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci case 5: /* 5.7GHz band */ 44962306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); 45062306a36Sopenharmony_ci *eeprom_ch_array = iwl_eeprom_band_5; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci case 6: /* 2.4GHz ht40 channels */ 45362306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); 45462306a36Sopenharmony_ci *eeprom_ch_array = iwl_eeprom_band_6; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case 7: /* 5 GHz ht40 channels */ 45762306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); 45862306a36Sopenharmony_ci *eeprom_ch_array = iwl_eeprom_band_7; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci default: 46162306a36Sopenharmony_ci *eeprom_ch_count = 0; 46262306a36Sopenharmony_ci *eeprom_ch_array = NULL; 46362306a36Sopenharmony_ci WARN_ON(1); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci#define CHECK_AND_PRINT(x) \ 46862306a36Sopenharmony_ci ((eeprom_ch->flags & EEPROM_CHANNEL_##x) ? # x " " : "") 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void iwl_mod_ht40_chan_info(struct device *dev, 47162306a36Sopenharmony_ci struct iwl_nvm_data *data, int n_channels, 47262306a36Sopenharmony_ci enum nl80211_band band, u16 channel, 47362306a36Sopenharmony_ci const struct iwl_eeprom_channel *eeprom_ch, 47462306a36Sopenharmony_ci u8 clear_ht40_extension_channel) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct ieee80211_channel *chan = NULL; 47762306a36Sopenharmony_ci int i; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci for (i = 0; i < n_channels; i++) { 48062306a36Sopenharmony_ci if (data->channels[i].band != band) 48162306a36Sopenharmony_ci continue; 48262306a36Sopenharmony_ci if (data->channels[i].hw_value != channel) 48362306a36Sopenharmony_ci continue; 48462306a36Sopenharmony_ci chan = &data->channels[i]; 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (!chan) 48962306a36Sopenharmony_ci return; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci IWL_DEBUG_EEPROM(dev, 49262306a36Sopenharmony_ci "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", 49362306a36Sopenharmony_ci channel, 49462306a36Sopenharmony_ci band == NL80211_BAND_5GHZ ? "5.2" : "2.4", 49562306a36Sopenharmony_ci CHECK_AND_PRINT(IBSS), 49662306a36Sopenharmony_ci CHECK_AND_PRINT(ACTIVE), 49762306a36Sopenharmony_ci CHECK_AND_PRINT(RADAR), 49862306a36Sopenharmony_ci CHECK_AND_PRINT(WIDE), 49962306a36Sopenharmony_ci CHECK_AND_PRINT(DFS), 50062306a36Sopenharmony_ci eeprom_ch->flags, 50162306a36Sopenharmony_ci eeprom_ch->max_power_avg, 50262306a36Sopenharmony_ci ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && 50362306a36Sopenharmony_ci !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" 50462306a36Sopenharmony_ci : "not "); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) 50762306a36Sopenharmony_ci chan->flags &= ~clear_ht40_extension_channel; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci#define CHECK_AND_PRINT_I(x) \ 51162306a36Sopenharmony_ci ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "") 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, 51462306a36Sopenharmony_ci struct iwl_nvm_data *data, 51562306a36Sopenharmony_ci const u8 *eeprom, size_t eeprom_size) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci int band, ch_idx; 51862306a36Sopenharmony_ci const struct iwl_eeprom_channel *eeprom_ch_info; 51962306a36Sopenharmony_ci const u8 *eeprom_ch_array; 52062306a36Sopenharmony_ci int eeprom_ch_count; 52162306a36Sopenharmony_ci int n_channels = 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* 52462306a36Sopenharmony_ci * Loop through the 5 EEPROM bands and add them to the parse list 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci for (band = 1; band <= 5; band++) { 52762306a36Sopenharmony_ci struct ieee80211_channel *channel; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci iwl_init_band_reference(cfg, eeprom, eeprom_size, band, 53062306a36Sopenharmony_ci &eeprom_ch_count, &eeprom_ch_info, 53162306a36Sopenharmony_ci &eeprom_ch_array); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* Loop through each band adding each of the channels */ 53462306a36Sopenharmony_ci for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { 53562306a36Sopenharmony_ci const struct iwl_eeprom_channel *eeprom_ch; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci eeprom_ch = &eeprom_ch_info[ch_idx]; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (!(eeprom_ch->flags & EEPROM_CHANNEL_VALID)) { 54062306a36Sopenharmony_ci IWL_DEBUG_EEPROM(dev, 54162306a36Sopenharmony_ci "Ch. %d Flags %x [%sGHz] - No traffic\n", 54262306a36Sopenharmony_ci eeprom_ch_array[ch_idx], 54362306a36Sopenharmony_ci eeprom_ch_info[ch_idx].flags, 54462306a36Sopenharmony_ci (band != 1) ? "5.2" : "2.4"); 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci channel = &data->channels[n_channels]; 54962306a36Sopenharmony_ci n_channels++; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci channel->hw_value = eeprom_ch_array[ch_idx]; 55262306a36Sopenharmony_ci channel->band = (band == 1) ? NL80211_BAND_2GHZ 55362306a36Sopenharmony_ci : NL80211_BAND_5GHZ; 55462306a36Sopenharmony_ci channel->center_freq = 55562306a36Sopenharmony_ci ieee80211_channel_to_frequency( 55662306a36Sopenharmony_ci channel->hw_value, channel->band); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* set no-HT40, will enable as appropriate later */ 55962306a36Sopenharmony_ci channel->flags = IEEE80211_CHAN_NO_HT40; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (!(eeprom_ch->flags & EEPROM_CHANNEL_IBSS)) 56262306a36Sopenharmony_ci channel->flags |= IEEE80211_CHAN_NO_IR; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (!(eeprom_ch->flags & EEPROM_CHANNEL_ACTIVE)) 56562306a36Sopenharmony_ci channel->flags |= IEEE80211_CHAN_NO_IR; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (eeprom_ch->flags & EEPROM_CHANNEL_RADAR) 56862306a36Sopenharmony_ci channel->flags |= IEEE80211_CHAN_RADAR; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Initialize regulatory-based run-time data */ 57162306a36Sopenharmony_ci channel->max_power = 57262306a36Sopenharmony_ci eeprom_ch_info[ch_idx].max_power_avg; 57362306a36Sopenharmony_ci IWL_DEBUG_EEPROM(dev, 57462306a36Sopenharmony_ci "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", 57562306a36Sopenharmony_ci channel->hw_value, 57662306a36Sopenharmony_ci (band != 1) ? "5.2" : "2.4", 57762306a36Sopenharmony_ci CHECK_AND_PRINT_I(VALID), 57862306a36Sopenharmony_ci CHECK_AND_PRINT_I(IBSS), 57962306a36Sopenharmony_ci CHECK_AND_PRINT_I(ACTIVE), 58062306a36Sopenharmony_ci CHECK_AND_PRINT_I(RADAR), 58162306a36Sopenharmony_ci CHECK_AND_PRINT_I(WIDE), 58262306a36Sopenharmony_ci CHECK_AND_PRINT_I(DFS), 58362306a36Sopenharmony_ci eeprom_ch_info[ch_idx].flags, 58462306a36Sopenharmony_ci eeprom_ch_info[ch_idx].max_power_avg, 58562306a36Sopenharmony_ci ((eeprom_ch_info[ch_idx].flags & 58662306a36Sopenharmony_ci EEPROM_CHANNEL_IBSS) && 58762306a36Sopenharmony_ci !(eeprom_ch_info[ch_idx].flags & 58862306a36Sopenharmony_ci EEPROM_CHANNEL_RADAR)) 58962306a36Sopenharmony_ci ? "" : "not "); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (cfg->eeprom_params->enhanced_txpower) { 59462306a36Sopenharmony_ci /* 59562306a36Sopenharmony_ci * for newer device (6000 series and up) 59662306a36Sopenharmony_ci * EEPROM contain enhanced tx power information 59762306a36Sopenharmony_ci * driver need to process addition information 59862306a36Sopenharmony_ci * to determine the max channel tx power limits 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci iwl_eeprom_enhanced_txpower(dev, data, eeprom, eeprom_size, 60162306a36Sopenharmony_ci n_channels); 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci /* All others use data from channel map */ 60462306a36Sopenharmony_ci int i; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci data->max_tx_pwr_half_dbm = -128; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci for (i = 0; i < n_channels; i++) 60962306a36Sopenharmony_ci data->max_tx_pwr_half_dbm = 61062306a36Sopenharmony_ci max_t(s8, data->max_tx_pwr_half_dbm, 61162306a36Sopenharmony_ci data->channels[i].max_power * 2); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* Check if we do have HT40 channels */ 61562306a36Sopenharmony_ci if (cfg->eeprom_params->regulatory_bands[5] == 61662306a36Sopenharmony_ci EEPROM_REGULATORY_BAND_NO_HT40 && 61762306a36Sopenharmony_ci cfg->eeprom_params->regulatory_bands[6] == 61862306a36Sopenharmony_ci EEPROM_REGULATORY_BAND_NO_HT40) 61962306a36Sopenharmony_ci return n_channels; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ 62262306a36Sopenharmony_ci for (band = 6; band <= 7; band++) { 62362306a36Sopenharmony_ci enum nl80211_band ieeeband; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci iwl_init_band_reference(cfg, eeprom, eeprom_size, band, 62662306a36Sopenharmony_ci &eeprom_ch_count, &eeprom_ch_info, 62762306a36Sopenharmony_ci &eeprom_ch_array); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ 63062306a36Sopenharmony_ci ieeeband = (band == 6) ? NL80211_BAND_2GHZ 63162306a36Sopenharmony_ci : NL80211_BAND_5GHZ; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Loop through each band adding each of the channels */ 63462306a36Sopenharmony_ci for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { 63562306a36Sopenharmony_ci /* Set up driver's info for lower half */ 63662306a36Sopenharmony_ci iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, 63762306a36Sopenharmony_ci eeprom_ch_array[ch_idx], 63862306a36Sopenharmony_ci &eeprom_ch_info[ch_idx], 63962306a36Sopenharmony_ci IEEE80211_CHAN_NO_HT40PLUS); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Set up driver's info for upper half */ 64262306a36Sopenharmony_ci iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, 64362306a36Sopenharmony_ci eeprom_ch_array[ch_idx] + 4, 64462306a36Sopenharmony_ci &eeprom_ch_info[ch_idx], 64562306a36Sopenharmony_ci IEEE80211_CHAN_NO_HT40MINUS); 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return n_channels; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci#endif 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ciint iwl_init_sband_channels(struct iwl_nvm_data *data, 65462306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 65562306a36Sopenharmony_ci int n_channels, enum nl80211_band band) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct ieee80211_channel *chan = &data->channels[0]; 65862306a36Sopenharmony_ci int n = 0, idx = 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci while (idx < n_channels && chan->band != band) 66162306a36Sopenharmony_ci chan = &data->channels[++idx]; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci sband->channels = &data->channels[idx]; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci while (idx < n_channels && chan->band == band) { 66662306a36Sopenharmony_ci chan = &data->channels[++idx]; 66762306a36Sopenharmony_ci n++; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci sband->n_channels = n; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return n; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ 67662306a36Sopenharmony_ci#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_civoid iwl_init_ht_hw_capab(struct iwl_trans *trans, 67962306a36Sopenharmony_ci struct iwl_nvm_data *data, 68062306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_info, 68162306a36Sopenharmony_ci enum nl80211_band band, 68262306a36Sopenharmony_ci u8 tx_chains, u8 rx_chains) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci const struct iwl_cfg *cfg = trans->cfg; 68562306a36Sopenharmony_ci int max_bit_rate = 0; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci tx_chains = hweight8(tx_chains); 68862306a36Sopenharmony_ci if (cfg->rx_with_siso_diversity) 68962306a36Sopenharmony_ci rx_chains = 1; 69062306a36Sopenharmony_ci else 69162306a36Sopenharmony_ci rx_chains = hweight8(rx_chains); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (!(data->sku_cap_11n_enable) || 69462306a36Sopenharmony_ci (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) || 69562306a36Sopenharmony_ci !cfg->ht_params) { 69662306a36Sopenharmony_ci ht_info->ht_supported = false; 69762306a36Sopenharmony_ci return; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (data->sku_cap_mimo_disabled) 70162306a36Sopenharmony_ci rx_chains = 1; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci ht_info->ht_supported = true; 70462306a36Sopenharmony_ci ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (cfg->ht_params->stbc) { 70762306a36Sopenharmony_ci ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (tx_chains > 1) 71062306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (cfg->ht_params->ldpc) 71462306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (trans->trans_cfg->mq_rx_supported || 71762306a36Sopenharmony_ci iwlwifi_mod_params.amsdu_size >= IWL_AMSDU_8K) 71862306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; 72162306a36Sopenharmony_ci ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci ht_info->mcs.rx_mask[0] = 0xFF; 72462306a36Sopenharmony_ci if (rx_chains >= 2) 72562306a36Sopenharmony_ci ht_info->mcs.rx_mask[1] = 0xFF; 72662306a36Sopenharmony_ci if (rx_chains >= 3) 72762306a36Sopenharmony_ci ht_info->mcs.rx_mask[2] = 0xFF; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (cfg->ht_params->ht_greenfield_support) 73062306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; 73162306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SGI_20; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci max_bit_rate = MAX_BIT_RATE_20_MHZ; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (cfg->ht_params->ht40_bands & BIT(band)) { 73662306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; 73762306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SGI_40; 73862306a36Sopenharmony_ci max_bit_rate = MAX_BIT_RATE_40_MHZ; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Highest supported Rx data rate */ 74262306a36Sopenharmony_ci max_bit_rate *= rx_chains; 74362306a36Sopenharmony_ci WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); 74462306a36Sopenharmony_ci ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Tx MCS capabilities */ 74762306a36Sopenharmony_ci ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; 74862306a36Sopenharmony_ci if (tx_chains != rx_chains) { 74962306a36Sopenharmony_ci ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; 75062306a36Sopenharmony_ci ht_info->mcs.tx_params |= ((tx_chains - 1) << 75162306a36Sopenharmony_ci IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IWLDVM) 75662306a36Sopenharmony_cistatic void iwl_init_sbands(struct iwl_trans *trans, const struct iwl_cfg *cfg, 75762306a36Sopenharmony_ci struct iwl_nvm_data *data, 75862306a36Sopenharmony_ci const u8 *eeprom, size_t eeprom_size) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct device *dev = trans->dev; 76162306a36Sopenharmony_ci int n_channels = iwl_init_channel_map(dev, cfg, data, 76262306a36Sopenharmony_ci eeprom, eeprom_size); 76362306a36Sopenharmony_ci int n_used = 0; 76462306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci sband = &data->bands[NL80211_BAND_2GHZ]; 76762306a36Sopenharmony_ci sband->band = NL80211_BAND_2GHZ; 76862306a36Sopenharmony_ci sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; 76962306a36Sopenharmony_ci sband->n_bitrates = N_RATES_24; 77062306a36Sopenharmony_ci n_used += iwl_init_sband_channels(data, sband, n_channels, 77162306a36Sopenharmony_ci NL80211_BAND_2GHZ); 77262306a36Sopenharmony_ci iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_2GHZ, 77362306a36Sopenharmony_ci data->valid_tx_ant, data->valid_rx_ant); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci sband = &data->bands[NL80211_BAND_5GHZ]; 77662306a36Sopenharmony_ci sband->band = NL80211_BAND_5GHZ; 77762306a36Sopenharmony_ci sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; 77862306a36Sopenharmony_ci sband->n_bitrates = N_RATES_52; 77962306a36Sopenharmony_ci n_used += iwl_init_sband_channels(data, sband, n_channels, 78062306a36Sopenharmony_ci NL80211_BAND_5GHZ); 78162306a36Sopenharmony_ci iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_5GHZ, 78262306a36Sopenharmony_ci data->valid_tx_ant, data->valid_rx_ant); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (n_channels != n_used) 78562306a36Sopenharmony_ci IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n", 78662306a36Sopenharmony_ci n_used, n_channels); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci/* EEPROM data functions */ 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistruct iwl_nvm_data * 79262306a36Sopenharmony_ciiwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, 79362306a36Sopenharmony_ci const u8 *eeprom, size_t eeprom_size) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct iwl_nvm_data *data; 79662306a36Sopenharmony_ci struct device *dev = trans->dev; 79762306a36Sopenharmony_ci const void *tmp; 79862306a36Sopenharmony_ci u16 radio_cfg, sku; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (WARN_ON(!cfg || !cfg->eeprom_params)) 80162306a36Sopenharmony_ci return NULL; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci data = kzalloc(struct_size(data, channels, IWL_NUM_CHANNELS), 80462306a36Sopenharmony_ci GFP_KERNEL); 80562306a36Sopenharmony_ci if (!data) 80662306a36Sopenharmony_ci return NULL; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* get MAC address(es) */ 80962306a36Sopenharmony_ci tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_MAC_ADDRESS); 81062306a36Sopenharmony_ci if (!tmp) 81162306a36Sopenharmony_ci goto err_free; 81262306a36Sopenharmony_ci memcpy(data->hw_addr, tmp, ETH_ALEN); 81362306a36Sopenharmony_ci data->n_hw_addrs = iwl_eeprom_query16(eeprom, eeprom_size, 81462306a36Sopenharmony_ci EEPROM_NUM_MAC_ADDRESS); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (iwl_eeprom_read_calib(eeprom, eeprom_size, data)) 81762306a36Sopenharmony_ci goto err_free; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_XTAL); 82062306a36Sopenharmony_ci if (!tmp) 82162306a36Sopenharmony_ci goto err_free; 82262306a36Sopenharmony_ci memcpy(data->xtal_calib, tmp, sizeof(data->xtal_calib)); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, 82562306a36Sopenharmony_ci EEPROM_RAW_TEMPERATURE); 82662306a36Sopenharmony_ci if (!tmp) 82762306a36Sopenharmony_ci goto err_free; 82862306a36Sopenharmony_ci data->raw_temperature = *(__le16 *)tmp; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, 83162306a36Sopenharmony_ci EEPROM_KELVIN_TEMPERATURE); 83262306a36Sopenharmony_ci if (!tmp) 83362306a36Sopenharmony_ci goto err_free; 83462306a36Sopenharmony_ci data->kelvin_temperature = *(__le16 *)tmp; 83562306a36Sopenharmony_ci data->kelvin_voltage = *((__le16 *)tmp + 1); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci radio_cfg = iwl_eeprom_query16(eeprom, eeprom_size, 83862306a36Sopenharmony_ci EEPROM_RADIO_CONFIG); 83962306a36Sopenharmony_ci data->radio_cfg_dash = EEPROM_RF_CFG_DASH_MSK(radio_cfg); 84062306a36Sopenharmony_ci data->radio_cfg_pnum = EEPROM_RF_CFG_PNUM_MSK(radio_cfg); 84162306a36Sopenharmony_ci data->radio_cfg_step = EEPROM_RF_CFG_STEP_MSK(radio_cfg); 84262306a36Sopenharmony_ci data->radio_cfg_type = EEPROM_RF_CFG_TYPE_MSK(radio_cfg); 84362306a36Sopenharmony_ci data->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); 84462306a36Sopenharmony_ci data->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci sku = iwl_eeprom_query16(eeprom, eeprom_size, 84762306a36Sopenharmony_ci EEPROM_SKU_CAP); 84862306a36Sopenharmony_ci data->sku_cap_11n_enable = sku & EEPROM_SKU_CAP_11N_ENABLE; 84962306a36Sopenharmony_ci data->sku_cap_amt_enable = sku & EEPROM_SKU_CAP_AMT_ENABLE; 85062306a36Sopenharmony_ci data->sku_cap_band_24ghz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ; 85162306a36Sopenharmony_ci data->sku_cap_band_52ghz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ; 85262306a36Sopenharmony_ci data->sku_cap_ipan_enable = sku & EEPROM_SKU_CAP_IPAN_ENABLE; 85362306a36Sopenharmony_ci if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) 85462306a36Sopenharmony_ci data->sku_cap_11n_enable = false; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci data->nvm_version = iwl_eeprom_query16(eeprom, eeprom_size, 85762306a36Sopenharmony_ci EEPROM_VERSION); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* check overrides (some devices have wrong EEPROM) */ 86062306a36Sopenharmony_ci if (cfg->valid_tx_ant) 86162306a36Sopenharmony_ci data->valid_tx_ant = cfg->valid_tx_ant; 86262306a36Sopenharmony_ci if (cfg->valid_rx_ant) 86362306a36Sopenharmony_ci data->valid_rx_ant = cfg->valid_rx_ant; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (!data->valid_tx_ant || !data->valid_rx_ant) { 86662306a36Sopenharmony_ci IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n", 86762306a36Sopenharmony_ci data->valid_tx_ant, data->valid_rx_ant); 86862306a36Sopenharmony_ci goto err_free; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci iwl_init_sbands(trans, cfg, data, eeprom, eeprom_size); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return data; 87462306a36Sopenharmony_ci err_free: 87562306a36Sopenharmony_ci kfree(data); 87662306a36Sopenharmony_ci return NULL; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_parse_eeprom_data); 87962306a36Sopenharmony_ci#endif 880