xref: /openbmc/linux/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28e99ea8dSJohannes Berg /*
384969e0fSIlan Peer  * Copyright (C) 2005-2014, 2018-2023 Intel Corporation
48e99ea8dSJohannes Berg  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
58e99ea8dSJohannes Berg  * Copyright (C) 2016-2017 Intel Deutschland GmbH
68e99ea8dSJohannes Berg  */
7e705c121SKalle Valo #include <linux/types.h>
8e705c121SKalle Valo #include <linux/slab.h>
9e705c121SKalle Valo #include <linux/export.h>
10e705c121SKalle Valo #include <linux/etherdevice.h>
11e705c121SKalle Valo #include <linux/pci.h>
129c4f7d51SShaul Triebitz #include <linux/firmware.h>
13813df5ceSLuca Coelho 
14e705c121SKalle Valo #include "iwl-drv.h"
15e705c121SKalle Valo #include "iwl-modparams.h"
16e705c121SKalle Valo #include "iwl-nvm-parse.h"
17afd5b170SSara Sharon #include "iwl-prph.h"
1817c867bfSSara Sharon #include "iwl-io.h"
1917c867bfSSara Sharon #include "iwl-csr.h"
20813df5ceSLuca Coelho #include "fw/acpi.h"
219c4f7d51SShaul Triebitz #include "fw/api/nvm-reg.h"
224c625c56SShaul Triebitz #include "fw/api/commands.h"
234c625c56SShaul Triebitz #include "fw/api/cmdhdr.h"
244c625c56SShaul Triebitz #include "fw/img.h"
256d19a5ebSEmmanuel Grumbach #include "mei/iwl-mei.h"
26e705c121SKalle Valo 
27e705c121SKalle Valo /* NVM offsets (in words) definitions */
2844fd09daSChaya Rachel Ivgi enum nvm_offsets {
29e705c121SKalle Valo 	/* NVM HW-Section offset (in words) definitions */
3001a9c948SLuca Coelho 	SUBSYSTEM_ID = 0x0A,
31e705c121SKalle Valo 	HW_ADDR = 0x15,
32e705c121SKalle Valo 
33e705c121SKalle Valo 	/* NVM SW-Section offset (in words) definitions */
34e705c121SKalle Valo 	NVM_SW_SECTION = 0x1C0,
35e705c121SKalle Valo 	NVM_VERSION = 0,
36e705c121SKalle Valo 	RADIO_CFG = 1,
37e705c121SKalle Valo 	SKU = 2,
38e705c121SKalle Valo 	N_HW_ADDRS = 3,
39e705c121SKalle Valo 	NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION,
40e705c121SKalle Valo 
41e705c121SKalle Valo 	/* NVM calibration section offset (in words) definitions */
42e705c121SKalle Valo 	NVM_CALIB_SECTION = 0x2B8,
4344fd09daSChaya Rachel Ivgi 	XTAL_CALIB = 0x316 - NVM_CALIB_SECTION,
4444fd09daSChaya Rachel Ivgi 
4544fd09daSChaya Rachel Ivgi 	/* NVM REGULATORY -Section offset (in words) definitions */
4644fd09daSChaya Rachel Ivgi 	NVM_CHANNELS_SDP = 0,
47e705c121SKalle Valo };
48e705c121SKalle Valo 
497042678dSSara Sharon enum ext_nvm_offsets {
50e705c121SKalle Valo 	/* NVM HW-Section offset (in words) definitions */
517042678dSSara Sharon 	MAC_ADDRESS_OVERRIDE_EXT_NVM = 1,
52e705c121SKalle Valo 
53e705c121SKalle Valo 	/* NVM SW-Section offset (in words) definitions */
547042678dSSara Sharon 	NVM_VERSION_EXT_NVM = 0,
557b2829f3SEmmanuel Grumbach 	N_HW_ADDRS_FAMILY_8000 = 3,
567b2829f3SEmmanuel Grumbach 
577b2829f3SEmmanuel Grumbach 	/* NVM PHY_SKU-Section offset (in words) definitions */
587042678dSSara Sharon 	RADIO_CFG_FAMILY_EXT_NVM = 0,
59e705c121SKalle Valo 	SKU_FAMILY_8000 = 2,
60e705c121SKalle Valo 
61e705c121SKalle Valo 	/* NVM REGULATORY -Section offset (in words) definitions */
627042678dSSara Sharon 	NVM_CHANNELS_EXTENDED = 0,
637042678dSSara Sharon 	NVM_LAR_OFFSET_OLD = 0x4C7,
647042678dSSara Sharon 	NVM_LAR_OFFSET = 0x507,
657042678dSSara Sharon 	NVM_LAR_ENABLED = 0x7,
66e705c121SKalle Valo };
67e705c121SKalle Valo 
68e705c121SKalle Valo /* SKU Capabilities (actual values from NVM definition) */
69e705c121SKalle Valo enum nvm_sku_bits {
70e705c121SKalle Valo 	NVM_SKU_CAP_BAND_24GHZ		= BIT(0),
71e705c121SKalle Valo 	NVM_SKU_CAP_BAND_52GHZ		= BIT(1),
72e705c121SKalle Valo 	NVM_SKU_CAP_11N_ENABLE		= BIT(2),
73e705c121SKalle Valo 	NVM_SKU_CAP_11AC_ENABLE		= BIT(3),
74e705c121SKalle Valo 	NVM_SKU_CAP_MIMO_DISABLE	= BIT(5),
75e705c121SKalle Valo };
76e705c121SKalle Valo 
77e705c121SKalle Valo /*
78e705c121SKalle Valo  * These are the channel numbers in the order that they are stored in the NVM
79e705c121SKalle Valo  */
80b15ef67cSShaul Triebitz static const u16 iwl_nvm_channels[] = {
81e705c121SKalle Valo 	/* 2.4 GHz */
82e705c121SKalle Valo 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
83e705c121SKalle Valo 	/* 5 GHz */
84e705c121SKalle Valo 	36, 40, 44, 48, 52, 56, 60, 64,
85e705c121SKalle Valo 	100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
86e705c121SKalle Valo 	149, 153, 157, 161, 165
87e705c121SKalle Valo };
88e705c121SKalle Valo 
89b15ef67cSShaul Triebitz static const u16 iwl_ext_nvm_channels[] = {
90e705c121SKalle Valo 	/* 2.4 GHz */
91e705c121SKalle Valo 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
92e705c121SKalle Valo 	/* 5 GHz */
93e705c121SKalle Valo 	36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
94e705c121SKalle Valo 	96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
95e705c121SKalle Valo 	149, 153, 157, 161, 165, 169, 173, 177, 181
96e705c121SKalle Valo };
97e705c121SKalle Valo 
98b15ef67cSShaul Triebitz static const u16 iwl_uhb_nvm_channels[] = {
99b15ef67cSShaul Triebitz 	/* 2.4 GHz */
100b15ef67cSShaul Triebitz 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
101b15ef67cSShaul Triebitz 	/* 5 GHz */
102b15ef67cSShaul Triebitz 	36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
103b15ef67cSShaul Triebitz 	96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
104b15ef67cSShaul Triebitz 	149, 153, 157, 161, 165, 169, 173, 177, 181,
105b15ef67cSShaul Triebitz 	/* 6-7 GHz */
106222ccf5eSTova Mussai 	1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69,
107222ccf5eSTova Mussai 	73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129,
108222ccf5eSTova Mussai 	133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185,
109222ccf5eSTova Mussai 	189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233
110b15ef67cSShaul Triebitz };
111b15ef67cSShaul Triebitz 
1129c4f7d51SShaul Triebitz #define IWL_NVM_NUM_CHANNELS		ARRAY_SIZE(iwl_nvm_channels)
1139c4f7d51SShaul Triebitz #define IWL_NVM_NUM_CHANNELS_EXT	ARRAY_SIZE(iwl_ext_nvm_channels)
114b15ef67cSShaul Triebitz #define IWL_NVM_NUM_CHANNELS_UHB	ARRAY_SIZE(iwl_uhb_nvm_channels)
115e705c121SKalle Valo #define NUM_2GHZ_CHANNELS		14
116eae94cf8SLuca Coelho #define NUM_5GHZ_CHANNELS		37
117e705c121SKalle Valo #define FIRST_2GHZ_HT_MINUS		5
118e705c121SKalle Valo #define LAST_2GHZ_HT_PLUS		9
119e705c121SKalle Valo #define N_HW_ADDR_MASK			0xF
120e705c121SKalle Valo 
121e705c121SKalle Valo /* rate data (static) */
122e705c121SKalle Valo static struct ieee80211_rate iwl_cfg80211_rates[] = {
123e705c121SKalle Valo 	{ .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, },
124e705c121SKalle Valo 	{ .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1,
125e705c121SKalle Valo 	  .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
126e705c121SKalle Valo 	{ .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2,
127e705c121SKalle Valo 	  .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
128e705c121SKalle Valo 	{ .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3,
129e705c121SKalle Valo 	  .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
130e705c121SKalle Valo 	{ .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, },
131e705c121SKalle Valo 	{ .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, },
132e705c121SKalle Valo 	{ .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, },
133e705c121SKalle Valo 	{ .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, },
134e705c121SKalle Valo 	{ .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, },
135e705c121SKalle Valo 	{ .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, },
136e705c121SKalle Valo 	{ .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, },
137e705c121SKalle Valo 	{ .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, },
138e705c121SKalle Valo };
139e705c121SKalle Valo #define RATES_24_OFFS	0
140e705c121SKalle Valo #define N_RATES_24	ARRAY_SIZE(iwl_cfg80211_rates)
141e705c121SKalle Valo #define RATES_52_OFFS	4
142e705c121SKalle Valo #define N_RATES_52	(N_RATES_24 - RATES_52_OFFS)
143e705c121SKalle Valo 
144e705c121SKalle Valo /**
145e705c121SKalle Valo  * enum iwl_nvm_channel_flags - channel flags in NVM
146e705c121SKalle Valo  * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
147e705c121SKalle Valo  * @NVM_CHANNEL_IBSS: usable as an IBSS channel
148e705c121SKalle Valo  * @NVM_CHANNEL_ACTIVE: active scanning allowed
149e705c121SKalle Valo  * @NVM_CHANNEL_RADAR: radar detection required
150e705c121SKalle Valo  * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed
151e705c121SKalle Valo  * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS
152e705c121SKalle Valo  *	on same channel on 2.4 or same UNII band on 5.2
153b823cf3bSLuca Coelho  * @NVM_CHANNEL_UNIFORM: uniform spreading required
154b823cf3bSLuca Coelho  * @NVM_CHANNEL_20MHZ: 20 MHz channel okay
155b823cf3bSLuca Coelho  * @NVM_CHANNEL_40MHZ: 40 MHz channel okay
156b823cf3bSLuca Coelho  * @NVM_CHANNEL_80MHZ: 80 MHz channel okay
157b823cf3bSLuca Coelho  * @NVM_CHANNEL_160MHZ: 160 MHz channel okay
158b823cf3bSLuca Coelho  * @NVM_CHANNEL_DC_HIGH: DC HIGH required/allowed (?)
159e705c121SKalle Valo  */
160e705c121SKalle Valo enum iwl_nvm_channel_flags {
161e705c121SKalle Valo 	NVM_CHANNEL_VALID		= BIT(0),
162e705c121SKalle Valo 	NVM_CHANNEL_IBSS		= BIT(1),
163e705c121SKalle Valo 	NVM_CHANNEL_ACTIVE		= BIT(3),
164e705c121SKalle Valo 	NVM_CHANNEL_RADAR		= BIT(4),
165e705c121SKalle Valo 	NVM_CHANNEL_INDOOR_ONLY		= BIT(5),
166e705c121SKalle Valo 	NVM_CHANNEL_GO_CONCURRENT	= BIT(6),
167b823cf3bSLuca Coelho 	NVM_CHANNEL_UNIFORM		= BIT(7),
168b823cf3bSLuca Coelho 	NVM_CHANNEL_20MHZ		= BIT(8),
169e705c121SKalle Valo 	NVM_CHANNEL_40MHZ		= BIT(9),
170e705c121SKalle Valo 	NVM_CHANNEL_80MHZ		= BIT(10),
171e705c121SKalle Valo 	NVM_CHANNEL_160MHZ		= BIT(11),
172b823cf3bSLuca Coelho 	NVM_CHANNEL_DC_HIGH		= BIT(12),
173e705c121SKalle Valo };
174e705c121SKalle Valo 
1752763bba6SHaim Dreyfuss /**
176e9b63341SAbhishek Naik  * enum iwl_reg_capa_flags_v1 - global flags applied for the whole regulatory
1772763bba6SHaim Dreyfuss  * domain.
178e9b63341SAbhishek Naik  * @REG_CAPA_V1_BF_CCD_LOW_BAND: Beam-forming or Cyclic Delay Diversity in the
1792763bba6SHaim Dreyfuss  *	2.4Ghz band is allowed.
180e9b63341SAbhishek Naik  * @REG_CAPA_V1_BF_CCD_HIGH_BAND: Beam-forming or Cyclic Delay Diversity in the
1812763bba6SHaim Dreyfuss  *	5Ghz band is allowed.
182e9b63341SAbhishek Naik  * @REG_CAPA_V1_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
1832763bba6SHaim Dreyfuss  *	for this regulatory domain (valid only in 5Ghz).
184e9b63341SAbhishek Naik  * @REG_CAPA_V1_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
1852763bba6SHaim Dreyfuss  *	for this regulatory domain (valid only in 5Ghz).
186e9b63341SAbhishek Naik  * @REG_CAPA_V1_MCS_8_ALLOWED: 11ac with MCS 8 is allowed.
187e9b63341SAbhishek Naik  * @REG_CAPA_V1_MCS_9_ALLOWED: 11ac with MCS 9 is allowed.
188e9b63341SAbhishek Naik  * @REG_CAPA_V1_40MHZ_FORBIDDEN: 11n channel with a width of 40Mhz is forbidden
1892763bba6SHaim Dreyfuss  *	for this regulatory domain (valid only in 5Ghz).
190e9b63341SAbhishek Naik  * @REG_CAPA_V1_DC_HIGH_ENABLED: DC HIGH allowed.
191e9b63341SAbhishek Naik  * @REG_CAPA_V1_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
1922763bba6SHaim Dreyfuss  */
193e9b63341SAbhishek Naik enum iwl_reg_capa_flags_v1 {
194e9b63341SAbhishek Naik 	REG_CAPA_V1_BF_CCD_LOW_BAND	= BIT(0),
195e9b63341SAbhishek Naik 	REG_CAPA_V1_BF_CCD_HIGH_BAND	= BIT(1),
196e9b63341SAbhishek Naik 	REG_CAPA_V1_160MHZ_ALLOWED	= BIT(2),
197e9b63341SAbhishek Naik 	REG_CAPA_V1_80MHZ_ALLOWED	= BIT(3),
198e9b63341SAbhishek Naik 	REG_CAPA_V1_MCS_8_ALLOWED	= BIT(4),
199e9b63341SAbhishek Naik 	REG_CAPA_V1_MCS_9_ALLOWED	= BIT(5),
200e9b63341SAbhishek Naik 	REG_CAPA_V1_40MHZ_FORBIDDEN	= BIT(7),
201e9b63341SAbhishek Naik 	REG_CAPA_V1_DC_HIGH_ENABLED	= BIT(9),
202e9b63341SAbhishek Naik 	REG_CAPA_V1_11AX_DISABLED	= BIT(10),
203e9b63341SAbhishek Naik }; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_1 */
2042763bba6SHaim Dreyfuss 
205e27c506aSGil Adam /**
206e27c506aSGil Adam  * enum iwl_reg_capa_flags_v2 - global flags applied for the whole regulatory
207e27c506aSGil Adam  * domain (version 2).
208e27c506aSGil Adam  * @REG_CAPA_V2_STRADDLE_DISABLED: Straddle channels (144, 142, 138) are
209e27c506aSGil Adam  *	disabled.
210e27c506aSGil Adam  * @REG_CAPA_V2_BF_CCD_LOW_BAND: Beam-forming or Cyclic Delay Diversity in the
211e27c506aSGil Adam  *	2.4Ghz band is allowed.
212e27c506aSGil Adam  * @REG_CAPA_V2_BF_CCD_HIGH_BAND: Beam-forming or Cyclic Delay Diversity in the
213e27c506aSGil Adam  *	5Ghz band is allowed.
214e27c506aSGil Adam  * @REG_CAPA_V2_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
215e27c506aSGil Adam  *	for this regulatory domain (valid only in 5Ghz).
216e27c506aSGil Adam  * @REG_CAPA_V2_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
217e27c506aSGil Adam  *	for this regulatory domain (valid only in 5Ghz).
218e27c506aSGil Adam  * @REG_CAPA_V2_MCS_8_ALLOWED: 11ac with MCS 8 is allowed.
219e27c506aSGil Adam  * @REG_CAPA_V2_MCS_9_ALLOWED: 11ac with MCS 9 is allowed.
220e27c506aSGil Adam  * @REG_CAPA_V2_WEATHER_DISABLED: Weather radar channels (120, 124, 128, 118,
221e27c506aSGil Adam  *	126, 122) are disabled.
222e27c506aSGil Adam  * @REG_CAPA_V2_40MHZ_ALLOWED: 11n channel with a width of 40Mhz is allowed
223e27c506aSGil Adam  *	for this regulatory domain (uvalid only in 5Ghz).
224e27c506aSGil Adam  * @REG_CAPA_V2_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
225e27c506aSGil Adam  */
226e27c506aSGil Adam enum iwl_reg_capa_flags_v2 {
227e27c506aSGil Adam 	REG_CAPA_V2_STRADDLE_DISABLED	= BIT(0),
228e27c506aSGil Adam 	REG_CAPA_V2_BF_CCD_LOW_BAND	= BIT(1),
229e27c506aSGil Adam 	REG_CAPA_V2_BF_CCD_HIGH_BAND	= BIT(2),
230e27c506aSGil Adam 	REG_CAPA_V2_160MHZ_ALLOWED	= BIT(3),
231e27c506aSGil Adam 	REG_CAPA_V2_80MHZ_ALLOWED	= BIT(4),
232e27c506aSGil Adam 	REG_CAPA_V2_MCS_8_ALLOWED	= BIT(5),
233e27c506aSGil Adam 	REG_CAPA_V2_MCS_9_ALLOWED	= BIT(6),
234e27c506aSGil Adam 	REG_CAPA_V2_WEATHER_DISABLED	= BIT(7),
235e27c506aSGil Adam 	REG_CAPA_V2_40MHZ_ALLOWED	= BIT(8),
23607cc40feSLuca Coelho 	REG_CAPA_V2_11AX_DISABLED	= BIT(10),
237e9b63341SAbhishek Naik }; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_2 */
238e9b63341SAbhishek Naik 
239e9b63341SAbhishek Naik /**
240e9b63341SAbhishek Naik  * enum iwl_reg_capa_flags_v4 - global flags applied for the whole regulatory
241e9b63341SAbhishek Naik  * domain.
242e9b63341SAbhishek Naik  * @REG_CAPA_V4_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
243e9b63341SAbhishek Naik  *	for this regulatory domain (valid only in 5Ghz).
244e9b63341SAbhishek Naik  * @REG_CAPA_V4_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
245e9b63341SAbhishek Naik  *	for this regulatory domain (valid only in 5Ghz).
246e9b63341SAbhishek Naik  * @REG_CAPA_V4_MCS_12_ALLOWED: 11ac with MCS 12 is allowed.
247e9b63341SAbhishek Naik  * @REG_CAPA_V4_MCS_13_ALLOWED: 11ac with MCS 13 is allowed.
248e9b63341SAbhishek Naik  * @REG_CAPA_V4_11BE_DISABLED: 11be is forbidden for this regulatory domain.
249e9b63341SAbhishek Naik  * @REG_CAPA_V4_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
250e9b63341SAbhishek Naik  * @REG_CAPA_V4_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed
251e9b63341SAbhishek Naik  *	for this regulatory domain (valid only in 5GHz).
252e9b63341SAbhishek Naik  */
253e9b63341SAbhishek Naik enum iwl_reg_capa_flags_v4 {
254e9b63341SAbhishek Naik 	REG_CAPA_V4_160MHZ_ALLOWED		= BIT(3),
255e9b63341SAbhishek Naik 	REG_CAPA_V4_80MHZ_ALLOWED		= BIT(4),
256e9b63341SAbhishek Naik 	REG_CAPA_V4_MCS_12_ALLOWED		= BIT(5),
257e9b63341SAbhishek Naik 	REG_CAPA_V4_MCS_13_ALLOWED		= BIT(6),
258e9b63341SAbhishek Naik 	REG_CAPA_V4_11BE_DISABLED		= BIT(8),
259e9b63341SAbhishek Naik 	REG_CAPA_V4_11AX_DISABLED		= BIT(13),
260e9b63341SAbhishek Naik 	REG_CAPA_V4_320MHZ_ALLOWED		= BIT(16),
261e9b63341SAbhishek Naik }; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4 */
262e27c506aSGil Adam 
263e27c506aSGil Adam /*
264e27c506aSGil Adam * API v2 for reg_capa_flags is relevant from version 6 and onwards of the
265e27c506aSGil Adam * MCC update command response.
266e27c506aSGil Adam */
267e27c506aSGil Adam #define REG_CAPA_V2_RESP_VER	6
268e27c506aSGil Adam 
269e9b63341SAbhishek Naik /* API v4 for reg_capa_flags is relevant from version 8 and onwards of the
270e9b63341SAbhishek Naik  * MCC update command response.
271e9b63341SAbhishek Naik  */
272e9b63341SAbhishek Naik #define REG_CAPA_V4_RESP_VER	8
273e9b63341SAbhishek Naik 
274e27c506aSGil Adam /**
275e27c506aSGil Adam  * struct iwl_reg_capa - struct for global regulatory capabilities, Used for
276e27c506aSGil Adam  * handling the different APIs of reg_capa_flags.
277e27c506aSGil Adam  *
278e27c506aSGil Adam  * @allow_40mhz: 11n channel with a width of 40Mhz is allowed
279e9b63341SAbhishek Naik  *	for this regulatory domain.
280e27c506aSGil Adam  * @allow_80mhz: 11ac channel with a width of 80Mhz is allowed
281e9b63341SAbhishek Naik  *	for this regulatory domain (valid only in 5 and 6 Ghz).
282e27c506aSGil Adam  * @allow_160mhz: 11ac channel with a width of 160Mhz is allowed
283e9b63341SAbhishek Naik  *	for this regulatory domain (valid only in 5 and 6 Ghz).
284e9b63341SAbhishek Naik  * @allow_320mhz: 11be channel with a width of 320Mhz is allowed
285e9b63341SAbhishek Naik  *	for this regulatory domain (valid only in 6 Ghz).
286e27c506aSGil Adam  * @disable_11ax: 11ax is forbidden for this regulatory domain.
287e9b63341SAbhishek Naik  * @disable_11be: 11be is forbidden for this regulatory domain.
288e27c506aSGil Adam  */
289e27c506aSGil Adam struct iwl_reg_capa {
290e9b63341SAbhishek Naik 	bool allow_40mhz;
291e9b63341SAbhishek Naik 	bool allow_80mhz;
292e9b63341SAbhishek Naik 	bool allow_160mhz;
293e9b63341SAbhishek Naik 	bool allow_320mhz;
294e9b63341SAbhishek Naik 	bool disable_11ax;
295e9b63341SAbhishek Naik 	bool disable_11be;
296e27c506aSGil Adam };
297e27c506aSGil Adam 
iwl_nvm_print_channel_flags(struct device * dev,u32 level,int chan,u32 flags)298d8c73e45SJohannes Berg static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level,
2992785ce00SShaul Triebitz 					       int chan, u32 flags)
300d8c73e45SJohannes Berg {
301e705c121SKalle Valo #define CHECK_AND_PRINT_I(x)	\
302d8c73e45SJohannes Berg 	((flags & NVM_CHANNEL_##x) ? " " #x : "")
303d8c73e45SJohannes Berg 
304d8c73e45SJohannes Berg 	if (!(flags & NVM_CHANNEL_VALID)) {
305d8c73e45SJohannes Berg 		IWL_DEBUG_DEV(dev, level, "Ch. %d: 0x%x: No traffic\n",
306d8c73e45SJohannes Berg 			      chan, flags);
307d8c73e45SJohannes Berg 		return;
308d8c73e45SJohannes Berg 	}
309d8c73e45SJohannes Berg 
310d8c73e45SJohannes Berg 	/* Note: already can print up to 101 characters, 110 is the limit! */
311d8c73e45SJohannes Berg 	IWL_DEBUG_DEV(dev, level,
312d8c73e45SJohannes Berg 		      "Ch. %d: 0x%x:%s%s%s%s%s%s%s%s%s%s%s%s\n",
313d8c73e45SJohannes Berg 		      chan, flags,
314d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(VALID),
315d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(IBSS),
316d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(ACTIVE),
317d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(RADAR),
318d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(INDOOR_ONLY),
319d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(GO_CONCURRENT),
320d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(UNIFORM),
321d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(20MHZ),
322d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(40MHZ),
323d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(80MHZ),
324d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(160MHZ),
325d8c73e45SJohannes Berg 		      CHECK_AND_PRINT_I(DC_HIGH));
326d8c73e45SJohannes Berg #undef CHECK_AND_PRINT_I
327d8c73e45SJohannes Berg }
328e705c121SKalle Valo 
iwl_get_channel_flags(u8 ch_num,int ch_idx,enum nl80211_band band,u32 nvm_flags,const struct iwl_cfg * cfg)329e878325aSTova Mussai static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, enum nl80211_band band,
3302785ce00SShaul Triebitz 				 u32 nvm_flags, const struct iwl_cfg *cfg)
331e705c121SKalle Valo {
332e705c121SKalle Valo 	u32 flags = IEEE80211_CHAN_NO_HT40;
333e705c121SKalle Valo 
334e878325aSTova Mussai 	if (band == NL80211_BAND_2GHZ && (nvm_flags & NVM_CHANNEL_40MHZ)) {
335e705c121SKalle Valo 		if (ch_num <= LAST_2GHZ_HT_PLUS)
336e705c121SKalle Valo 			flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
337e705c121SKalle Valo 		if (ch_num >= FIRST_2GHZ_HT_MINUS)
338e705c121SKalle Valo 			flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
339b15ef67cSShaul Triebitz 	} else if (nvm_flags & NVM_CHANNEL_40MHZ) {
340e705c121SKalle Valo 		if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
341e705c121SKalle Valo 			flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
342e705c121SKalle Valo 		else
343e705c121SKalle Valo 			flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
344e705c121SKalle Valo 	}
345e705c121SKalle Valo 	if (!(nvm_flags & NVM_CHANNEL_80MHZ))
346e705c121SKalle Valo 		flags |= IEEE80211_CHAN_NO_80MHZ;
347e705c121SKalle Valo 	if (!(nvm_flags & NVM_CHANNEL_160MHZ))
348e705c121SKalle Valo 		flags |= IEEE80211_CHAN_NO_160MHZ;
349e705c121SKalle Valo 
350e705c121SKalle Valo 	if (!(nvm_flags & NVM_CHANNEL_IBSS))
351e705c121SKalle Valo 		flags |= IEEE80211_CHAN_NO_IR;
352e705c121SKalle Valo 
353e705c121SKalle Valo 	if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
354e705c121SKalle Valo 		flags |= IEEE80211_CHAN_NO_IR;
355e705c121SKalle Valo 
356e705c121SKalle Valo 	if (nvm_flags & NVM_CHANNEL_RADAR)
357e705c121SKalle Valo 		flags |= IEEE80211_CHAN_RADAR;
358e705c121SKalle Valo 
359e705c121SKalle Valo 	if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
360e705c121SKalle Valo 		flags |= IEEE80211_CHAN_INDOOR_ONLY;
361e705c121SKalle Valo 
362e705c121SKalle Valo 	/* Set the GO concurrent flag only in case that NO_IR is set.
363e705c121SKalle Valo 	 * Otherwise it is meaningless
364e705c121SKalle Valo 	 */
365e705c121SKalle Valo 	if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
366e705c121SKalle Valo 	    (flags & IEEE80211_CHAN_NO_IR))
367e705c121SKalle Valo 		flags |= IEEE80211_CHAN_IR_CONCURRENT;
368e705c121SKalle Valo 
369e705c121SKalle Valo 	return flags;
370e705c121SKalle Valo }
371e705c121SKalle Valo 
iwl_nl80211_band_from_channel_idx(int ch_idx)372e878325aSTova Mussai static enum nl80211_band iwl_nl80211_band_from_channel_idx(int ch_idx)
373e878325aSTova Mussai {
374eae94cf8SLuca Coelho 	if (ch_idx >= NUM_2GHZ_CHANNELS + NUM_5GHZ_CHANNELS) {
375eae94cf8SLuca Coelho 		return NL80211_BAND_6GHZ;
376eae94cf8SLuca Coelho 	}
377eae94cf8SLuca Coelho 
378e878325aSTova Mussai 	if (ch_idx >= NUM_2GHZ_CHANNELS)
379e878325aSTova Mussai 		return NL80211_BAND_5GHZ;
380e878325aSTova Mussai 	return NL80211_BAND_2GHZ;
381e878325aSTova Mussai }
382e878325aSTova Mussai 
iwl_init_channel_map(struct device * dev,const struct iwl_cfg * cfg,struct iwl_nvm_data * data,const void * const nvm_ch_flags,u32 sbands_flags,bool v4)383e705c121SKalle Valo static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
384e705c121SKalle Valo 				struct iwl_nvm_data *data,
3852785ce00SShaul Triebitz 				const void * const nvm_ch_flags,
3862785ce00SShaul Triebitz 				u32 sbands_flags, bool v4)
387e705c121SKalle Valo {
388e705c121SKalle Valo 	int ch_idx;
389e705c121SKalle Valo 	int n_channels = 0;
390e705c121SKalle Valo 	struct ieee80211_channel *channel;
3912785ce00SShaul Triebitz 	u32 ch_flags;
392e878325aSTova Mussai 	int num_of_ch;
393b15ef67cSShaul Triebitz 	const u16 *nvm_chan;
394e705c121SKalle Valo 
395b15ef67cSShaul Triebitz 	if (cfg->uhb_supported) {
396b15ef67cSShaul Triebitz 		num_of_ch = IWL_NVM_NUM_CHANNELS_UHB;
397b15ef67cSShaul Triebitz 		nvm_chan = iwl_uhb_nvm_channels;
398b15ef67cSShaul Triebitz 	} else if (cfg->nvm_type == IWL_NVM_EXT) {
3999c4f7d51SShaul Triebitz 		num_of_ch = IWL_NVM_NUM_CHANNELS_EXT;
400b15ef67cSShaul Triebitz 		nvm_chan = iwl_ext_nvm_channels;
401b15ef67cSShaul Triebitz 	} else {
402b15ef67cSShaul Triebitz 		num_of_ch = IWL_NVM_NUM_CHANNELS;
403b15ef67cSShaul Triebitz 		nvm_chan = iwl_nvm_channels;
404e705c121SKalle Valo 	}
405e705c121SKalle Valo 
406e705c121SKalle Valo 	for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
407e878325aSTova Mussai 		enum nl80211_band band =
408e878325aSTova Mussai 			iwl_nl80211_band_from_channel_idx(ch_idx);
40901a9c948SLuca Coelho 
4102785ce00SShaul Triebitz 		if (v4)
4112785ce00SShaul Triebitz 			ch_flags =
41273c289baSBjoern A. Zeeb 				__le32_to_cpup((const __le32 *)nvm_ch_flags + ch_idx);
4132785ce00SShaul Triebitz 		else
4142785ce00SShaul Triebitz 			ch_flags =
41573c289baSBjoern A. Zeeb 				__le16_to_cpup((const __le16 *)nvm_ch_flags + ch_idx);
416e705c121SKalle Valo 
417e878325aSTova Mussai 		if (band == NL80211_BAND_5GHZ &&
418e878325aSTova Mussai 		    !data->sku_cap_band_52ghz_enable)
419e705c121SKalle Valo 			continue;
420e705c121SKalle Valo 
42101a9c948SLuca Coelho 		/* workaround to disable wide channels in 5GHz */
4224b82455cSLuca Coelho 		if ((sbands_flags & IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ) &&
423e878325aSTova Mussai 		    band == NL80211_BAND_5GHZ) {
42401a9c948SLuca Coelho 			ch_flags &= ~(NVM_CHANNEL_40MHZ |
42501a9c948SLuca Coelho 				     NVM_CHANNEL_80MHZ |
42601a9c948SLuca Coelho 				     NVM_CHANNEL_160MHZ);
42701a9c948SLuca Coelho 		}
42801a9c948SLuca Coelho 
4294896d764SGregory Greenman 		if (ch_flags & NVM_CHANNEL_160MHZ)
4304896d764SGregory Greenman 			data->vht160_supported = true;
4314896d764SGregory Greenman 
4324b82455cSLuca Coelho 		if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR) &&
4334b82455cSLuca Coelho 		    !(ch_flags & NVM_CHANNEL_VALID)) {
434e705c121SKalle Valo 			/*
435e705c121SKalle Valo 			 * Channels might become valid later if lar is
436e705c121SKalle Valo 			 * supported, hence we still want to add them to
437e705c121SKalle Valo 			 * the list of supported channels to cfg80211.
438e705c121SKalle Valo 			 */
439d8c73e45SJohannes Berg 			iwl_nvm_print_channel_flags(dev, IWL_DL_EEPROM,
440d8c73e45SJohannes Berg 						    nvm_chan[ch_idx], ch_flags);
441e705c121SKalle Valo 			continue;
442e705c121SKalle Valo 		}
443e705c121SKalle Valo 
444e705c121SKalle Valo 		channel = &data->channels[n_channels];
445e705c121SKalle Valo 		n_channels++;
446e705c121SKalle Valo 
447e705c121SKalle Valo 		channel->hw_value = nvm_chan[ch_idx];
448e878325aSTova Mussai 		channel->band = band;
449e705c121SKalle Valo 		channel->center_freq =
450e705c121SKalle Valo 			ieee80211_channel_to_frequency(
451e705c121SKalle Valo 				channel->hw_value, channel->band);
452e705c121SKalle Valo 
453e705c121SKalle Valo 		/* Initialize regulatory-based run-time data */
454e705c121SKalle Valo 
455e705c121SKalle Valo 		/*
456e705c121SKalle Valo 		 * Default value - highest tx power value.  max_power
457e705c121SKalle Valo 		 * is not used in mvm, and is used for backwards compatibility
458e705c121SKalle Valo 		 */
459e705c121SKalle Valo 		channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
460e705c121SKalle Valo 
461e705c121SKalle Valo 		/* don't put limitations in case we're using LAR */
4624b82455cSLuca Coelho 		if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR))
463e705c121SKalle Valo 			channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx],
464e878325aSTova Mussai 							       ch_idx, band,
465e705c121SKalle Valo 							       ch_flags, cfg);
466e705c121SKalle Valo 		else
467e705c121SKalle Valo 			channel->flags = 0;
468e705c121SKalle Valo 
469eae94cf8SLuca Coelho 		/* TODO: Don't put limitations on UHB devices as we still don't
470eae94cf8SLuca Coelho 		 * have NVM for them
471eae94cf8SLuca Coelho 		 */
472eae94cf8SLuca Coelho 		if (cfg->uhb_supported)
473eae94cf8SLuca Coelho 			channel->flags = 0;
474d8c73e45SJohannes Berg 		iwl_nvm_print_channel_flags(dev, IWL_DL_EEPROM,
475d8c73e45SJohannes Berg 					    channel->hw_value, ch_flags);
476d8c73e45SJohannes Berg 		IWL_DEBUG_EEPROM(dev, "Ch. %d: %ddBm\n",
477d8c73e45SJohannes Berg 				 channel->hw_value, channel->max_power);
478e705c121SKalle Valo 	}
479e705c121SKalle Valo 
480e705c121SKalle Valo 	return n_channels;
481e705c121SKalle Valo }
482e705c121SKalle Valo 
iwl_init_vht_hw_capab(struct iwl_trans * trans,struct iwl_nvm_data * data,struct ieee80211_sta_vht_cap * vht_cap,u8 tx_chains,u8 rx_chains)483d8913b80SShaul Triebitz static void iwl_init_vht_hw_capab(struct iwl_trans *trans,
484e705c121SKalle Valo 				  struct iwl_nvm_data *data,
485e705c121SKalle Valo 				  struct ieee80211_sta_vht_cap *vht_cap,
486e705c121SKalle Valo 				  u8 tx_chains, u8 rx_chains)
487e705c121SKalle Valo {
488d8913b80SShaul Triebitz 	const struct iwl_cfg *cfg = trans->cfg;
489e705c121SKalle Valo 	int num_rx_ants = num_of_ant(rx_chains);
490e705c121SKalle Valo 	int num_tx_ants = num_of_ant(tx_chains);
491e705c121SKalle Valo 
492e705c121SKalle Valo 	vht_cap->vht_supported = true;
493e705c121SKalle Valo 
494e705c121SKalle Valo 	vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 |
495e705c121SKalle Valo 		       IEEE80211_VHT_CAP_RXSTBC_1 |
496e705c121SKalle Valo 		       IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
497e705c121SKalle Valo 		       3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
498eebe75d1SJohannes Berg 		       IEEE80211_VHT_MAX_AMPDU_1024K <<
499e705c121SKalle Valo 		       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
500e705c121SKalle Valo 
501dbb6f230SGregory Greenman 	if (!trans->cfg->ht_params->stbc)
502dbb6f230SGregory Greenman 		vht_cap->cap &= ~IEEE80211_VHT_CAP_RXSTBC_MASK;
503dbb6f230SGregory Greenman 
5044896d764SGregory Greenman 	if (data->vht160_supported)
505fbbd4859SGregory Greenman 		vht_cap->cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
506fbbd4859SGregory Greenman 				IEEE80211_VHT_CAP_SHORT_GI_160;
5074896d764SGregory Greenman 
508e48c947fSSara Sharon 	if (cfg->vht_mu_mimo_supported)
509e48c947fSSara Sharon 		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
510e48c947fSSara Sharon 
511e705c121SKalle Valo 	if (cfg->ht_params->ldpc)
512e705c121SKalle Valo 		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
513e705c121SKalle Valo 
514e705c121SKalle Valo 	if (data->sku_cap_mimo_disabled) {
515e705c121SKalle Valo 		num_rx_ants = 1;
516e705c121SKalle Valo 		num_tx_ants = 1;
517e705c121SKalle Valo 	}
518e705c121SKalle Valo 
51984969e0fSIlan Peer 	if (trans->cfg->ht_params->stbc && num_tx_ants > 1)
520e705c121SKalle Valo 		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
521e705c121SKalle Valo 	else
522e705c121SKalle Valo 		vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
523e705c121SKalle Valo 
5246c4fbcbcSEmmanuel Grumbach 	switch (iwlwifi_mod_params.amsdu_size) {
5254bdd4dfeSEmmanuel Grumbach 	case IWL_AMSDU_DEF:
5267d34a7d7SLuca Coelho 		if (trans->trans_cfg->mq_rx_supported)
5274bdd4dfeSEmmanuel Grumbach 			vht_cap->cap |=
5284bdd4dfeSEmmanuel Grumbach 				IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
5294bdd4dfeSEmmanuel Grumbach 		else
5304bdd4dfeSEmmanuel Grumbach 			vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
5314bdd4dfeSEmmanuel Grumbach 		break;
5321a4968d1SGolan Ben Ami 	case IWL_AMSDU_2K:
5337d34a7d7SLuca Coelho 		if (trans->trans_cfg->mq_rx_supported)
5341a4968d1SGolan Ben Ami 			vht_cap->cap |=
5351a4968d1SGolan Ben Ami 				IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
5361a4968d1SGolan Ben Ami 		else
5371a4968d1SGolan Ben Ami 			WARN(1, "RB size of 2K is not supported by this device\n");
5381a4968d1SGolan Ben Ami 		break;
5396c4fbcbcSEmmanuel Grumbach 	case IWL_AMSDU_4K:
5406c4fbcbcSEmmanuel Grumbach 		vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
5416c4fbcbcSEmmanuel Grumbach 		break;
5426c4fbcbcSEmmanuel Grumbach 	case IWL_AMSDU_8K:
543e705c121SKalle Valo 		vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
5446c4fbcbcSEmmanuel Grumbach 		break;
5456c4fbcbcSEmmanuel Grumbach 	case IWL_AMSDU_12K:
5466c4fbcbcSEmmanuel Grumbach 		vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
5476c4fbcbcSEmmanuel Grumbach 		break;
5486c4fbcbcSEmmanuel Grumbach 	default:
5496c4fbcbcSEmmanuel Grumbach 		break;
5506c4fbcbcSEmmanuel Grumbach 	}
551e705c121SKalle Valo 
552e705c121SKalle Valo 	vht_cap->vht_mcs.rx_mcs_map =
553e705c121SKalle Valo 		cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
554e705c121SKalle Valo 			    IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
555e705c121SKalle Valo 			    IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 |
556e705c121SKalle Valo 			    IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
557e705c121SKalle Valo 			    IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
558e705c121SKalle Valo 			    IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
559e705c121SKalle Valo 			    IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
560e705c121SKalle Valo 			    IEEE80211_VHT_MCS_NOT_SUPPORTED << 14);
561e705c121SKalle Valo 
562e705c121SKalle Valo 	if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) {
563e705c121SKalle Valo 		vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN;
564e705c121SKalle Valo 		/* this works because NOT_SUPPORTED == 3 */
565e705c121SKalle Valo 		vht_cap->vht_mcs.rx_mcs_map |=
566e705c121SKalle Valo 			cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2);
567e705c121SKalle Valo 	}
568e705c121SKalle Valo 
569e705c121SKalle Valo 	vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
5707691fa69SJohannes Berg 
5717691fa69SJohannes Berg 	vht_cap->vht_mcs.tx_highest |=
5727691fa69SJohannes Berg 		cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE);
573e705c121SKalle Valo }
574e705c121SKalle Valo 
575ee02e598SJohannes Berg static const u8 iwl_vendor_caps[] = {
576ee02e598SJohannes Berg 	0xdd,			/* vendor element */
577ee02e598SJohannes Berg 	0x06,			/* length */
578ee02e598SJohannes Berg 	0x00, 0x17, 0x35,	/* Intel OUI */
579ee02e598SJohannes Berg 	0x08,			/* type (Intel Capabilities) */
580ee02e598SJohannes Berg 	/* followed by 16 bits of capabilities */
581ee02e598SJohannes Berg #define IWL_VENDOR_CAP_IMPROVED_BF_FDBK_HE	BIT(0)
582ee02e598SJohannes Berg 	IWL_VENDOR_CAP_IMPROVED_BF_FDBK_HE,
583ee02e598SJohannes Berg 	0x00
584ee02e598SJohannes Berg };
585ee02e598SJohannes Berg 
58626d7cc0aSIlan Peer static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
58757a3a454SShaul Triebitz 	{
58857a3a454SShaul Triebitz 		.types_mask = BIT(NL80211_IFTYPE_STATION),
589514c3069SLuca Coelho 		.he_cap = {
590514c3069SLuca Coelho 			.has_he = true,
591514c3069SLuca Coelho 			.he_cap_elem = {
592514c3069SLuca Coelho 				.mac_cap_info[0] =
5931db5fcbbSGolan Ben Ami 					IEEE80211_HE_MAC_CAP0_HTC_HE,
594514c3069SLuca Coelho 				.mac_cap_info[1] =
595514c3069SLuca Coelho 					IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
596add7453aSShaul Triebitz 					IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
597514c3069SLuca Coelho 				.mac_cap_info[2] =
59838af8d5aSIlan Peer 					IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP,
599514c3069SLuca Coelho 				.mac_cap_info[3] =
6004e110e79SShaul Triebitz 					IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
6014e110e79SShaul Triebitz 					IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS,
602add7453aSShaul Triebitz 				.mac_cap_info[4] =
6032f516444SJohannes Berg 					IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU |
604add7453aSShaul Triebitz 					IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39,
605add7453aSShaul Triebitz 				.mac_cap_info[5] =
606add7453aSShaul Triebitz 					IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 |
607add7453aSShaul Triebitz 					IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 |
60877ff2c6bSLiad Kaufman 					IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU |
60977ff2c6bSLiad Kaufman 					IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS |
61077ff2c6bSLiad Kaufman 					IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX,
611514c3069SLuca Coelho 				.phy_cap_info[1] =
612add7453aSShaul Triebitz 					IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
613514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
61477ff2c6bSLiad Kaufman 					IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD,
615514c3069SLuca Coelho 				.phy_cap_info[2] =
616f2d1bdf0SMordechay Goodstein 					IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
617f2d1bdf0SMordechay Goodstein 					IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ,
618514c3069SLuca Coelho 				.phy_cap_info[3] =
6190dadd986SMordechay Goodstein 					IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK |
620514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 |
6210dadd986SMordechay Goodstein 					IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK |
622514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1,
623514c3069SLuca Coelho 				.phy_cap_info[4] =
624514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
625514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 |
626514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8,
627514c3069SLuca Coelho 				.phy_cap_info[6] =
62876cf4221SJohannes Berg 					IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
62976cf4221SJohannes Berg 					IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
630514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
631514c3069SLuca Coelho 				.phy_cap_info[7] =
63276cf4221SJohannes Berg 					IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP |
6331381eb5cSJohannes Berg 					IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI,
634514c3069SLuca Coelho 				.phy_cap_info[8] =
635514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
636514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
637514c3069SLuca Coelho 					IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
638add7453aSShaul Triebitz 					IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU |
6391381eb5cSJohannes Berg 					IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242,
640add7453aSShaul Triebitz 				.phy_cap_info[9] =
641add7453aSShaul Triebitz 					IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
64277ff2c6bSLiad Kaufman 					IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB |
64375c5bd68SMiri Korenblit 					(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED <<
64475c5bd68SMiri Korenblit 					IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_POS),
6450eb5a554SJohannes Berg 				.phy_cap_info[10] =
6460eb5a554SJohannes Berg 					IEEE80211_HE_PHY_CAP10_HE_MU_M1RU_MAX_LTF,
647514c3069SLuca Coelho 			},
648514c3069SLuca Coelho 			/*
64957a3a454SShaul Triebitz 			 * Set default Tx/Rx HE MCS NSS Support field.
65057a3a454SShaul Triebitz 			 * Indicate support for up to 2 spatial streams and all
65157a3a454SShaul Triebitz 			 * MCS, without any special cases
652514c3069SLuca Coelho 			 */
653514c3069SLuca Coelho 			.he_mcs_nss_supp = {
654514c3069SLuca Coelho 				.rx_mcs_80 = cpu_to_le16(0xfffa),
655514c3069SLuca Coelho 				.tx_mcs_80 = cpu_to_le16(0xfffa),
656514c3069SLuca Coelho 				.rx_mcs_160 = cpu_to_le16(0xfffa),
657514c3069SLuca Coelho 				.tx_mcs_160 = cpu_to_le16(0xfffa),
658514c3069SLuca Coelho 				.rx_mcs_80p80 = cpu_to_le16(0xffff),
659514c3069SLuca Coelho 				.tx_mcs_80p80 = cpu_to_le16(0xffff),
660514c3069SLuca Coelho 			},
661514c3069SLuca Coelho 			/*
66257a3a454SShaul Triebitz 			 * Set default PPE thresholds, with PPET16 set to 0,
66357a3a454SShaul Triebitz 			 * PPET8 set to 7
664514c3069SLuca Coelho 			 */
665514c3069SLuca Coelho 			.ppe_thres = {0x61, 0x1c, 0xc7, 0x71},
666514c3069SLuca Coelho 		},
66726d7cc0aSIlan Peer 		.eht_cap = {
66826d7cc0aSIlan Peer 			.has_eht = true,
66926d7cc0aSIlan Peer 			.eht_cap_elem = {
67026d7cc0aSIlan Peer 				.mac_cap_info[0] =
67126d7cc0aSIlan Peer 					IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
67226d7cc0aSIlan Peer 					IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 |
67326d7cc0aSIlan Peer 					IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2,
67426d7cc0aSIlan Peer 				.phy_cap_info[0] =
67526d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
67626d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
67726d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO |
67826d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE |
67926d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK,
68026d7cc0aSIlan Peer 				.phy_cap_info[1] =
68126d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK  |
6823a9690d0SJohannes Berg 					IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK,
68326d7cc0aSIlan Peer 				.phy_cap_info[3] =
68426d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
68526d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
68626d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
68726d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
68826d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
68926d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
69026d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK,
69126d7cc0aSIlan Peer 
69226d7cc0aSIlan Peer 				.phy_cap_info[4] =
69326d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO |
69426d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP |
69526d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI,
69626d7cc0aSIlan Peer 				.phy_cap_info[5] =
69726d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
69826d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP |
69926d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP |
70026d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT,
70126d7cc0aSIlan Peer 				.phy_cap_info[6] =
70226d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK |
70326d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP,
70426d7cc0aSIlan Peer 				.phy_cap_info[8] =
70526d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP8_RX_1024QAM_WIDER_BW_DL_OFDMA |
70626d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA,
70726d7cc0aSIlan Peer 			},
70826d7cc0aSIlan Peer 
70926d7cc0aSIlan Peer 			/* For all MCS and bandwidth, set 2 NSS for both Tx and
71026d7cc0aSIlan Peer 			 * Rx - note we don't set the only_20mhz, but due to this
71126d7cc0aSIlan Peer 			 * being a union, it gets set correctly anyway.
71226d7cc0aSIlan Peer 			 */
71326d7cc0aSIlan Peer 			.eht_mcs_nss_supp = {
71426d7cc0aSIlan Peer 				.bw._80 = {
71526d7cc0aSIlan Peer 					.rx_tx_mcs9_max_nss = 0x22,
71626d7cc0aSIlan Peer 					.rx_tx_mcs11_max_nss = 0x22,
71726d7cc0aSIlan Peer 					.rx_tx_mcs13_max_nss = 0x22,
71826d7cc0aSIlan Peer 				},
71926d7cc0aSIlan Peer 				.bw._160 = {
72026d7cc0aSIlan Peer 					.rx_tx_mcs9_max_nss = 0x22,
72126d7cc0aSIlan Peer 					.rx_tx_mcs11_max_nss = 0x22,
72226d7cc0aSIlan Peer 					.rx_tx_mcs13_max_nss = 0x22,
72326d7cc0aSIlan Peer 				},
72426d7cc0aSIlan Peer 				.bw._320 = {
72526d7cc0aSIlan Peer 					.rx_tx_mcs9_max_nss = 0x22,
72626d7cc0aSIlan Peer 					.rx_tx_mcs11_max_nss = 0x22,
72726d7cc0aSIlan Peer 					.rx_tx_mcs13_max_nss = 0x22,
72826d7cc0aSIlan Peer 				},
72926d7cc0aSIlan Peer 			},
73026d7cc0aSIlan Peer 
73126d7cc0aSIlan Peer 			/*
73226d7cc0aSIlan Peer 			 * PPE thresholds for NSS = 2, and RU index bitmap set
73326d7cc0aSIlan Peer 			 * to 0xc.
73426d7cc0aSIlan Peer 			 */
73526d7cc0aSIlan Peer 			.eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
73626d7cc0aSIlan Peer 		},
73757a3a454SShaul Triebitz 	},
73857a3a454SShaul Triebitz 	{
73957a3a454SShaul Triebitz 		.types_mask = BIT(NL80211_IFTYPE_AP),
74057a3a454SShaul Triebitz 		.he_cap = {
74157a3a454SShaul Triebitz 			.has_he = true,
74257a3a454SShaul Triebitz 			.he_cap_elem = {
74357a3a454SShaul Triebitz 				.mac_cap_info[0] =
7447360f99eSEmmanuel Grumbach 					IEEE80211_HE_MAC_CAP0_HTC_HE,
74557a3a454SShaul Triebitz 				.mac_cap_info[1] =
74657a3a454SShaul Triebitz 					IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
74757a3a454SShaul Triebitz 					IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
74857a3a454SShaul Triebitz 				.mac_cap_info[3] =
7491381eb5cSJohannes Berg 					IEEE80211_HE_MAC_CAP3_OMI_CONTROL,
75057a3a454SShaul Triebitz 				.phy_cap_info[1] =
75177ff2c6bSLiad Kaufman 					IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD,
75257a3a454SShaul Triebitz 				.phy_cap_info[2] =
753f2d1bdf0SMordechay Goodstein 					IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
75477ff2c6bSLiad Kaufman 					IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US,
75557a3a454SShaul Triebitz 				.phy_cap_info[3] =
7560dadd986SMordechay Goodstein 					IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK |
75757a3a454SShaul Triebitz 					IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 |
7580dadd986SMordechay Goodstein 					IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK |
75957a3a454SShaul Triebitz 					IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1,
76057a3a454SShaul Triebitz 				.phy_cap_info[6] =
76157a3a454SShaul Triebitz 					IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
76257a3a454SShaul Triebitz 				.phy_cap_info[7] =
7631381eb5cSJohannes Berg 					IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI,
76457a3a454SShaul Triebitz 				.phy_cap_info[8] =
76557a3a454SShaul Triebitz 					IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
7661381eb5cSJohannes Berg 					IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242,
76757a3a454SShaul Triebitz 				.phy_cap_info[9] =
76875c5bd68SMiri Korenblit 					IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED
76975c5bd68SMiri Korenblit 					<< IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_POS,
77057a3a454SShaul Triebitz 			},
77157a3a454SShaul Triebitz 			/*
77257a3a454SShaul Triebitz 			 * Set default Tx/Rx HE MCS NSS Support field.
77357a3a454SShaul Triebitz 			 * Indicate support for up to 2 spatial streams and all
77457a3a454SShaul Triebitz 			 * MCS, without any special cases
77557a3a454SShaul Triebitz 			 */
77657a3a454SShaul Triebitz 			.he_mcs_nss_supp = {
77757a3a454SShaul Triebitz 				.rx_mcs_80 = cpu_to_le16(0xfffa),
77857a3a454SShaul Triebitz 				.tx_mcs_80 = cpu_to_le16(0xfffa),
77957a3a454SShaul Triebitz 				.rx_mcs_160 = cpu_to_le16(0xfffa),
78057a3a454SShaul Triebitz 				.tx_mcs_160 = cpu_to_le16(0xfffa),
78157a3a454SShaul Triebitz 				.rx_mcs_80p80 = cpu_to_le16(0xffff),
78257a3a454SShaul Triebitz 				.tx_mcs_80p80 = cpu_to_le16(0xffff),
78357a3a454SShaul Triebitz 			},
78457a3a454SShaul Triebitz 			/*
78557a3a454SShaul Triebitz 			 * Set default PPE thresholds, with PPET16 set to 0,
78657a3a454SShaul Triebitz 			 * PPET8 set to 7
78757a3a454SShaul Triebitz 			 */
78857a3a454SShaul Triebitz 			.ppe_thres = {0x61, 0x1c, 0xc7, 0x71},
78957a3a454SShaul Triebitz 		},
79026d7cc0aSIlan Peer 		.eht_cap = {
79126d7cc0aSIlan Peer 			.has_eht = true,
79226d7cc0aSIlan Peer 			.eht_cap_elem = {
79326d7cc0aSIlan Peer 				.mac_cap_info[0] =
79426d7cc0aSIlan Peer 					IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
79526d7cc0aSIlan Peer 					IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 |
79626d7cc0aSIlan Peer 					IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2,
79726d7cc0aSIlan Peer 				.phy_cap_info[0] =
79826d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
79926d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI,
80026d7cc0aSIlan Peer 				.phy_cap_info[5] =
80126d7cc0aSIlan Peer 					IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT,
80226d7cc0aSIlan Peer 			},
80326d7cc0aSIlan Peer 
80426d7cc0aSIlan Peer 			/* For all MCS and bandwidth, set 2 NSS for both Tx and
80526d7cc0aSIlan Peer 			 * Rx - note we don't set the only_20mhz, but due to this
80626d7cc0aSIlan Peer 			 * being a union, it gets set correctly anyway.
80726d7cc0aSIlan Peer 			 */
80826d7cc0aSIlan Peer 			.eht_mcs_nss_supp = {
80926d7cc0aSIlan Peer 				.bw._80 = {
81026d7cc0aSIlan Peer 					.rx_tx_mcs9_max_nss = 0x22,
81126d7cc0aSIlan Peer 					.rx_tx_mcs11_max_nss = 0x22,
81226d7cc0aSIlan Peer 					.rx_tx_mcs13_max_nss = 0x22,
81326d7cc0aSIlan Peer 				},
81426d7cc0aSIlan Peer 				.bw._160 = {
81526d7cc0aSIlan Peer 					.rx_tx_mcs9_max_nss = 0x22,
81626d7cc0aSIlan Peer 					.rx_tx_mcs11_max_nss = 0x22,
81726d7cc0aSIlan Peer 					.rx_tx_mcs13_max_nss = 0x22,
81826d7cc0aSIlan Peer 				},
81926d7cc0aSIlan Peer 				.bw._320 = {
82026d7cc0aSIlan Peer 					.rx_tx_mcs9_max_nss = 0x22,
82126d7cc0aSIlan Peer 					.rx_tx_mcs11_max_nss = 0x22,
82226d7cc0aSIlan Peer 					.rx_tx_mcs13_max_nss = 0x22,
82326d7cc0aSIlan Peer 				},
82426d7cc0aSIlan Peer 			},
82526d7cc0aSIlan Peer 
82626d7cc0aSIlan Peer 			/*
82726d7cc0aSIlan Peer 			 * PPE thresholds for NSS = 2, and RU index bitmap set
82826d7cc0aSIlan Peer 			 * to 0xc.
82926d7cc0aSIlan Peer 			 */
83026d7cc0aSIlan Peer 			.eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
83126d7cc0aSIlan Peer 		},
83257a3a454SShaul Triebitz 	},
833514c3069SLuca Coelho };
834514c3069SLuca Coelho 
iwl_init_he_6ghz_capa(struct iwl_trans * trans,struct iwl_nvm_data * data,struct ieee80211_supported_band * sband,u8 tx_chains,u8 rx_chains)835eae94cf8SLuca Coelho static void iwl_init_he_6ghz_capa(struct iwl_trans *trans,
836eae94cf8SLuca Coelho 				  struct iwl_nvm_data *data,
837eae94cf8SLuca Coelho 				  struct ieee80211_supported_band *sband,
838eae94cf8SLuca Coelho 				  u8 tx_chains, u8 rx_chains)
839eae94cf8SLuca Coelho {
840eae94cf8SLuca Coelho 	struct ieee80211_sta_ht_cap ht_cap;
841eae94cf8SLuca Coelho 	struct ieee80211_sta_vht_cap vht_cap = {};
842eae94cf8SLuca Coelho 	struct ieee80211_sband_iftype_data *iftype_data;
843eae94cf8SLuca Coelho 	u16 he_6ghz_capa = 0;
844eae94cf8SLuca Coelho 	u32 exp;
845eae94cf8SLuca Coelho 	int i;
846eae94cf8SLuca Coelho 
847eae94cf8SLuca Coelho 	if (sband->band != NL80211_BAND_6GHZ)
848eae94cf8SLuca Coelho 		return;
849eae94cf8SLuca Coelho 
850eae94cf8SLuca Coelho 	/* grab HT/VHT capabilities and calculate HE 6 GHz capabilities */
851eae94cf8SLuca Coelho 	iwl_init_ht_hw_capab(trans, data, &ht_cap, NL80211_BAND_5GHZ,
852eae94cf8SLuca Coelho 			     tx_chains, rx_chains);
853eae94cf8SLuca Coelho 	WARN_ON(!ht_cap.ht_supported);
854eae94cf8SLuca Coelho 	iwl_init_vht_hw_capab(trans, data, &vht_cap, tx_chains, rx_chains);
855eae94cf8SLuca Coelho 	WARN_ON(!vht_cap.vht_supported);
856eae94cf8SLuca Coelho 
857eae94cf8SLuca Coelho 	he_6ghz_capa |=
858eae94cf8SLuca Coelho 		u16_encode_bits(ht_cap.ampdu_density,
859eae94cf8SLuca Coelho 				IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START);
860eae94cf8SLuca Coelho 	exp = u32_get_bits(vht_cap.cap,
861eae94cf8SLuca Coelho 			   IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
862eae94cf8SLuca Coelho 	he_6ghz_capa |=
863eae94cf8SLuca Coelho 		u16_encode_bits(exp, IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
864eae94cf8SLuca Coelho 	exp = u32_get_bits(vht_cap.cap, IEEE80211_VHT_CAP_MAX_MPDU_MASK);
865eae94cf8SLuca Coelho 	he_6ghz_capa |=
866eae94cf8SLuca Coelho 		u16_encode_bits(exp, IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN);
867eae94cf8SLuca Coelho 	/* we don't support extended_ht_cap_info anywhere, so no RD_RESPONDER */
868eae94cf8SLuca Coelho 	if (vht_cap.cap & IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN)
869eae94cf8SLuca Coelho 		he_6ghz_capa |= IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS;
870eae94cf8SLuca Coelho 	if (vht_cap.cap & IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN)
871eae94cf8SLuca Coelho 		he_6ghz_capa |= IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS;
872eae94cf8SLuca Coelho 
873eae94cf8SLuca Coelho 	IWL_DEBUG_EEPROM(trans->dev, "he_6ghz_capa=0x%x\n", he_6ghz_capa);
874eae94cf8SLuca Coelho 
875eae94cf8SLuca Coelho 	/* we know it's writable - we set it before ourselves */
87686e8e657SJohannes Berg 	iftype_data = (void *)(uintptr_t)sband->iftype_data;
877eae94cf8SLuca Coelho 	for (i = 0; i < sband->n_iftype_data; i++)
878eae94cf8SLuca Coelho 		iftype_data[i].he_6ghz_capa.capa = cpu_to_le16(he_6ghz_capa);
879eae94cf8SLuca Coelho }
880eae94cf8SLuca Coelho 
8811381eb5cSJohannes Berg static void
iwl_nvm_fixup_sband_iftd(struct iwl_trans * trans,struct iwl_nvm_data * data,struct ieee80211_supported_band * sband,struct ieee80211_sband_iftype_data * iftype_data,u8 tx_chains,u8 rx_chains,const struct iwl_fw * fw)8821381eb5cSJohannes Berg iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
88326d7cc0aSIlan Peer 			 struct iwl_nvm_data *data,
8841381eb5cSJohannes Berg 			 struct ieee80211_supported_band *sband,
8851381eb5cSJohannes Berg 			 struct ieee80211_sband_iftype_data *iftype_data,
88603470ba7SShaul Triebitz 			 u8 tx_chains, u8 rx_chains,
88703470ba7SShaul Triebitz 			 const struct iwl_fw *fw)
8881381eb5cSJohannes Berg {
8891381eb5cSJohannes Berg 	bool is_ap = iftype_data->types_mask & BIT(NL80211_IFTYPE_AP);
8903a9690d0SJohannes Berg 	bool no_320;
8913a9690d0SJohannes Berg 
8923a9690d0SJohannes Berg 	no_320 = !trans->trans_cfg->integrated &&
8933a9690d0SJohannes Berg 		 trans->pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB;
8941381eb5cSJohannes Berg 
89526d7cc0aSIlan Peer 	if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be)
89626d7cc0aSIlan Peer 		iftype_data->eht_cap.has_eht = false;
89726d7cc0aSIlan Peer 
8981381eb5cSJohannes Berg 	/* Advertise an A-MPDU exponent extension based on
8991381eb5cSJohannes Berg 	 * operating band
9001381eb5cSJohannes Berg 	 */
901cb75abceSDaniel Gabay 	if (sband->band == NL80211_BAND_6GHZ && iftype_data->eht_cap.has_eht)
902cb75abceSDaniel Gabay 		iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |=
903cb75abceSDaniel Gabay 			IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2;
904cb75abceSDaniel Gabay 	else if (sband->band != NL80211_BAND_2GHZ)
9051381eb5cSJohannes Berg 		iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |=
9061381eb5cSJohannes Berg 			IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1;
9071381eb5cSJohannes Berg 	else
9081381eb5cSJohannes Berg 		iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |=
9091381eb5cSJohannes Berg 			IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3;
9101381eb5cSJohannes Berg 
9113895f160SJohannes Berg 	switch (sband->band) {
9123895f160SJohannes Berg 	case NL80211_BAND_2GHZ:
9133895f160SJohannes Berg 		iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |=
9143895f160SJohannes Berg 			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
9153f44d44fSJohannes Berg 		iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] |=
9163f44d44fSJohannes Berg 			u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454,
9173f44d44fSJohannes Berg 				       IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK);
9183895f160SJohannes Berg 		break;
9193895f160SJohannes Berg 	case NL80211_BAND_6GHZ:
9203a9690d0SJohannes Berg 		if (!no_320) {
92135ea5f61SJohannes Berg 			iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |=
92235ea5f61SJohannes Berg 				IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
9233a9690d0SJohannes Berg 			iftype_data->eht_cap.eht_cap_elem.phy_cap_info[1] |=
9243a9690d0SJohannes Berg 				IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK;
9253a9690d0SJohannes Berg 		}
92635ea5f61SJohannes Berg 		fallthrough;
9273895f160SJohannes Berg 	case NL80211_BAND_5GHZ:
9283895f160SJohannes Berg 		iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |=
9290cc6fb8aSJohannes Berg 			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
9301381eb5cSJohannes Berg 			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
9313895f160SJohannes Berg 		break;
9323895f160SJohannes Berg 	default:
9333895f160SJohannes Berg 		WARN_ON(1);
9343895f160SJohannes Berg 		break;
9353895f160SJohannes Berg 	}
9361381eb5cSJohannes Berg 
9371381eb5cSJohannes Berg 	if ((tx_chains & rx_chains) == ANT_AB) {
938f2d1bdf0SMordechay Goodstein 		iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |=
939f2d1bdf0SMordechay Goodstein 			IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ;
9401381eb5cSJohannes Berg 		iftype_data->he_cap.he_cap_elem.phy_cap_info[5] |=
9411381eb5cSJohannes Berg 			IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 |
9421381eb5cSJohannes Berg 			IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2;
94326d7cc0aSIlan Peer 		if (!is_ap) {
9441381eb5cSJohannes Berg 			iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |=
9451381eb5cSJohannes Berg 				IEEE80211_HE_PHY_CAP7_MAX_NC_2;
94626d7cc0aSIlan Peer 
94726d7cc0aSIlan Peer 			if (iftype_data->eht_cap.has_eht) {
94826d7cc0aSIlan Peer 				/*
94926d7cc0aSIlan Peer 				 * Set the number of sounding dimensions for each
95026d7cc0aSIlan Peer 				 * bandwidth to 1 to indicate the maximal supported
95126d7cc0aSIlan Peer 				 * value of TXVECTOR parameter NUM_STS of 2
95226d7cc0aSIlan Peer 				 */
95326d7cc0aSIlan Peer 				iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] |= 0x49;
95426d7cc0aSIlan Peer 
95526d7cc0aSIlan Peer 				/*
95626d7cc0aSIlan Peer 				 * Set the MAX NC to 1 to indicate sounding feedback of
95726d7cc0aSIlan Peer 				 * 2 supported by the beamfomee.
95826d7cc0aSIlan Peer 				 */
95926d7cc0aSIlan Peer 				iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10;
96026d7cc0aSIlan Peer 			}
96126d7cc0aSIlan Peer 		}
96226d7cc0aSIlan Peer 	} else {
96326d7cc0aSIlan Peer 		if (iftype_data->eht_cap.has_eht) {
96426d7cc0aSIlan Peer 			struct ieee80211_eht_mcs_nss_supp *mcs_nss =
96526d7cc0aSIlan Peer 				&iftype_data->eht_cap.eht_mcs_nss_supp;
96626d7cc0aSIlan Peer 
96726d7cc0aSIlan Peer 			memset(mcs_nss, 0x11, sizeof(*mcs_nss));
96826d7cc0aSIlan Peer 		}
96926d7cc0aSIlan Peer 
97026d7cc0aSIlan Peer 		if (!is_ap) {
9711381eb5cSJohannes Berg 			/* If not 2x2, we need to indicate 1x1 in the
9721381eb5cSJohannes Berg 			 * Midamble RX Max NSTS - but not for AP mode
9731381eb5cSJohannes Berg 			 */
9741381eb5cSJohannes Berg 			iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &=
9751381eb5cSJohannes Berg 				~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
9761381eb5cSJohannes Berg 			iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &=
9771381eb5cSJohannes Berg 				~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS;
9781381eb5cSJohannes Berg 			iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |=
9791381eb5cSJohannes Berg 				IEEE80211_HE_PHY_CAP7_MAX_NC_1;
9801381eb5cSJohannes Berg 		}
98126d7cc0aSIlan Peer 	}
9821381eb5cSJohannes Berg 
983f8f9c311SJohannes Berg 	if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap)
984f8f9c311SJohannes Berg 		iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |=
985f8f9c311SJohannes Berg 			IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
986f8f9c311SJohannes Berg 
9871381eb5cSJohannes Berg 	switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) {
9881381eb5cSJohannes Berg 	case IWL_CFG_RF_TYPE_GF:
9891381eb5cSJohannes Berg 	case IWL_CFG_RF_TYPE_MR:
99085643396SAbhishek Naik 	case IWL_CFG_RF_TYPE_MS:
9910922a710SJohannes Berg 	case IWL_CFG_RF_TYPE_FM:
9920922a710SJohannes Berg 	case IWL_CFG_RF_TYPE_WH:
9931381eb5cSJohannes Berg 		iftype_data->he_cap.he_cap_elem.phy_cap_info[9] |=
9941381eb5cSJohannes Berg 			IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU;
9951381eb5cSJohannes Berg 		if (!is_ap)
9961381eb5cSJohannes Berg 			iftype_data->he_cap.he_cap_elem.phy_cap_info[9] |=
9971381eb5cSJohannes Berg 				IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU;
9981381eb5cSJohannes Berg 		break;
9991381eb5cSJohannes Berg 	}
100003470ba7SShaul Triebitz 
10010e21ec6eSAbhishek Naik 	if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL &&
10020e21ec6eSAbhishek Naik 	    iftype_data->eht_cap.has_eht) {
10030e21ec6eSAbhishek Naik 		iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] &=
10043dce50caSBenjamin Berg 			~(IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 |
10050e21ec6eSAbhishek Naik 			  IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2);
10060e21ec6eSAbhishek Naik 		iftype_data->eht_cap.eht_cap_elem.phy_cap_info[3] &=
10070e21ec6eSAbhishek Naik 			~(IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO |
10080e21ec6eSAbhishek Naik 			  IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
10090e21ec6eSAbhishek Naik 			  IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
10100e21ec6eSAbhishek Naik 			  IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
10110e21ec6eSAbhishek Naik 			  IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
101285e60760SMiri Korenblit 			  IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
101385e60760SMiri Korenblit 			  IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK);
10140e21ec6eSAbhishek Naik 		iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] &=
10150e21ec6eSAbhishek Naik 			~(IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO |
10160e21ec6eSAbhishek Naik 			  IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP);
10170e21ec6eSAbhishek Naik 		iftype_data->eht_cap.eht_cap_elem.phy_cap_info[5] &=
10180e21ec6eSAbhishek Naik 			~IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK;
10190e21ec6eSAbhishek Naik 		iftype_data->eht_cap.eht_cap_elem.phy_cap_info[6] &=
10200e21ec6eSAbhishek Naik 			~(IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK |
10210e21ec6eSAbhishek Naik 			  IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP);
102218c0ffb4SGregory Greenman 		iftype_data->eht_cap.eht_cap_elem.phy_cap_info[5] |=
102318c0ffb4SGregory Greenman 			IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF;
10240e21ec6eSAbhishek Naik 	}
10250e21ec6eSAbhishek Naik 
102603470ba7SShaul Triebitz 	if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_BROADCAST_TWT))
102703470ba7SShaul Triebitz 		iftype_data->he_cap.he_cap_elem.mac_cap_info[2] |=
102803470ba7SShaul Triebitz 			IEEE80211_HE_MAC_CAP2_BCAST_TWT;
1029ee02e598SJohannes Berg 
1030ee02e598SJohannes Berg 	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 &&
1031ee02e598SJohannes Berg 	    !is_ap) {
1032ee02e598SJohannes Berg 		iftype_data->vendor_elems.data = iwl_vendor_caps;
1033ee02e598SJohannes Berg 		iftype_data->vendor_elems.len = ARRAY_SIZE(iwl_vendor_caps);
1034ee02e598SJohannes Berg 	}
1035dbb6f230SGregory Greenman 
1036dbb6f230SGregory Greenman 	if (!trans->cfg->ht_params->stbc) {
1037dbb6f230SGregory Greenman 		iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &=
1038dbb6f230SGregory Greenman 			~IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
1039dbb6f230SGregory Greenman 		iftype_data->he_cap.he_cap_elem.phy_cap_info[7] &=
1040dbb6f230SGregory Greenman 			~IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ;
1041dbb6f230SGregory Greenman 	}
10421381eb5cSJohannes Berg }
10431381eb5cSJohannes Berg 
iwl_init_he_hw_capab(struct iwl_trans * trans,struct iwl_nvm_data * data,struct ieee80211_supported_band * sband,u8 tx_chains,u8 rx_chains,const struct iwl_fw * fw)1044df658908SJohannes Berg static void iwl_init_he_hw_capab(struct iwl_trans *trans,
1045df658908SJohannes Berg 				 struct iwl_nvm_data *data,
1046df658908SJohannes Berg 				 struct ieee80211_supported_band *sband,
104703470ba7SShaul Triebitz 				 u8 tx_chains, u8 rx_chains,
104803470ba7SShaul Triebitz 				 const struct iwl_fw *fw)
1049514c3069SLuca Coelho {
1050d43ab298SJohannes Berg 	struct ieee80211_sband_iftype_data *iftype_data;
10511381eb5cSJohannes Berg 	int i;
1052d43ab298SJohannes Berg 
1053d43ab298SJohannes Berg 	/* should only initialize once */
1054d43ab298SJohannes Berg 	if (WARN_ON(sband->iftype_data))
1055d43ab298SJohannes Berg 		return;
1056d43ab298SJohannes Berg 
105726d7cc0aSIlan Peer 	BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa));
105826d7cc0aSIlan Peer 	BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa));
1059cb75abceSDaniel Gabay 	BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_he_eht_capa));
1060d43ab298SJohannes Berg 
1061d43ab298SJohannes Berg 	switch (sband->band) {
1062d43ab298SJohannes Berg 	case NL80211_BAND_2GHZ:
1063d43ab298SJohannes Berg 		iftype_data = data->iftd.low;
1064d43ab298SJohannes Berg 		break;
1065d43ab298SJohannes Berg 	case NL80211_BAND_5GHZ:
1066d43ab298SJohannes Berg 		iftype_data = data->iftd.high;
1067d43ab298SJohannes Berg 		break;
1068cb75abceSDaniel Gabay 	case NL80211_BAND_6GHZ:
1069cb75abceSDaniel Gabay 		iftype_data = data->iftd.uhb;
1070cb75abceSDaniel Gabay 		break;
1071d43ab298SJohannes Berg 	default:
1072d43ab298SJohannes Berg 		WARN_ON(1);
1073d43ab298SJohannes Berg 		return;
1074d43ab298SJohannes Berg 	}
1075d43ab298SJohannes Berg 
107626d7cc0aSIlan Peer 	memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa));
1077d43ab298SJohannes Berg 
1078d43ab298SJohannes Berg 	sband->iftype_data = iftype_data;
107926d7cc0aSIlan Peer 	sband->n_iftype_data = ARRAY_SIZE(iwl_he_eht_capa);
1080514c3069SLuca Coelho 
10811381eb5cSJohannes Berg 	for (i = 0; i < sband->n_iftype_data; i++)
108226d7cc0aSIlan Peer 		iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i],
108303470ba7SShaul Triebitz 					 tx_chains, rx_chains, fw);
108457a3a454SShaul Triebitz 
1085eae94cf8SLuca Coelho 	iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains);
108657a3a454SShaul Triebitz }
1087514c3069SLuca Coelho 
iwl_init_sbands(struct iwl_trans * trans,struct iwl_nvm_data * data,const void * nvm_ch_flags,u8 tx_chains,u8 rx_chains,u32 sbands_flags,bool v4,const struct iwl_fw * fw)1088d8913b80SShaul Triebitz static void iwl_init_sbands(struct iwl_trans *trans,
10894c625c56SShaul Triebitz 			    struct iwl_nvm_data *data,
10902785ce00SShaul Triebitz 			    const void *nvm_ch_flags, u8 tx_chains,
109103470ba7SShaul Triebitz 			    u8 rx_chains, u32 sbands_flags, bool v4,
109203470ba7SShaul Triebitz 			    const struct iwl_fw *fw)
1093e705c121SKalle Valo {
1094d8913b80SShaul Triebitz 	struct device *dev = trans->dev;
1095d8913b80SShaul Triebitz 	const struct iwl_cfg *cfg = trans->cfg;
1096e705c121SKalle Valo 	int n_channels;
1097e705c121SKalle Valo 	int n_used = 0;
1098e705c121SKalle Valo 	struct ieee80211_supported_band *sband;
1099e705c121SKalle Valo 
110029108495SSara Sharon 	n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags,
11012785ce00SShaul Triebitz 					  sbands_flags, v4);
110257fbcce3SJohannes Berg 	sband = &data->bands[NL80211_BAND_2GHZ];
110357fbcce3SJohannes Berg 	sband->band = NL80211_BAND_2GHZ;
1104e705c121SKalle Valo 	sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
1105e705c121SKalle Valo 	sband->n_bitrates = N_RATES_24;
1106e705c121SKalle Valo 	n_used += iwl_init_sband_channels(data, sband, n_channels,
110757fbcce3SJohannes Berg 					  NL80211_BAND_2GHZ);
11087d34a7d7SLuca Coelho 	iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_2GHZ,
1109e705c121SKalle Valo 			     tx_chains, rx_chains);
1110e705c121SKalle Valo 
1111230ba6c5SLuca Coelho 	if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
111203470ba7SShaul Triebitz 		iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
111303470ba7SShaul Triebitz 				     fw);
1114514c3069SLuca Coelho 
111557fbcce3SJohannes Berg 	sband = &data->bands[NL80211_BAND_5GHZ];
111657fbcce3SJohannes Berg 	sband->band = NL80211_BAND_5GHZ;
1117e705c121SKalle Valo 	sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
1118e705c121SKalle Valo 	sband->n_bitrates = N_RATES_52;
1119e705c121SKalle Valo 	n_used += iwl_init_sband_channels(data, sband, n_channels,
112057fbcce3SJohannes Berg 					  NL80211_BAND_5GHZ);
11217d34a7d7SLuca Coelho 	iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_5GHZ,
1122e705c121SKalle Valo 			     tx_chains, rx_chains);
11230d0985adSAndrei Otcheretianski 	if (data->sku_cap_11ac_enable && !iwlwifi_mod_params.disable_11ac)
1124d8913b80SShaul Triebitz 		iwl_init_vht_hw_capab(trans, data, &sband->vht_cap,
1125e705c121SKalle Valo 				      tx_chains, rx_chains);
1126e705c121SKalle Valo 
1127230ba6c5SLuca Coelho 	if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
112803470ba7SShaul Triebitz 		iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
112903470ba7SShaul Triebitz 				     fw);
1130514c3069SLuca Coelho 
1131eae94cf8SLuca Coelho 	/* 6GHz band. */
1132eae94cf8SLuca Coelho 	sband = &data->bands[NL80211_BAND_6GHZ];
1133eae94cf8SLuca Coelho 	sband->band = NL80211_BAND_6GHZ;
1134eae94cf8SLuca Coelho 	/* use the same rates as 5GHz band */
1135eae94cf8SLuca Coelho 	sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
1136eae94cf8SLuca Coelho 	sband->n_bitrates = N_RATES_52;
1137eae94cf8SLuca Coelho 	n_used += iwl_init_sband_channels(data, sband, n_channels,
1138eae94cf8SLuca Coelho 					  NL80211_BAND_6GHZ);
1139eae94cf8SLuca Coelho 
1140eae94cf8SLuca Coelho 	if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
114103470ba7SShaul Triebitz 		iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
114203470ba7SShaul Triebitz 				     fw);
1143eae94cf8SLuca Coelho 	else
1144eae94cf8SLuca Coelho 		sband->n_channels = 0;
1145e705c121SKalle Valo 	if (n_channels != n_used)
1146e705c121SKalle Valo 		IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
1147e705c121SKalle Valo 			    n_used, n_channels);
1148e705c121SKalle Valo }
1149e705c121SKalle Valo 
iwl_get_sku(const struct iwl_cfg * cfg,const __le16 * nvm_sw,const __le16 * phy_sku)1150e705c121SKalle Valo static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
1151e705c121SKalle Valo 		       const __le16 *phy_sku)
1152e705c121SKalle Valo {
115344fd09daSChaya Rachel Ivgi 	if (cfg->nvm_type != IWL_NVM_EXT)
1154e705c121SKalle Valo 		return le16_to_cpup(nvm_sw + SKU);
1155e705c121SKalle Valo 
115673c289baSBjoern A. Zeeb 	return le32_to_cpup((const __le32 *)(phy_sku + SKU_FAMILY_8000));
1157e705c121SKalle Valo }
1158e705c121SKalle Valo 
iwl_get_nvm_version(const struct iwl_cfg * cfg,const __le16 * nvm_sw)1159e705c121SKalle Valo static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
1160e705c121SKalle Valo {
116144fd09daSChaya Rachel Ivgi 	if (cfg->nvm_type != IWL_NVM_EXT)
1162e705c121SKalle Valo 		return le16_to_cpup(nvm_sw + NVM_VERSION);
1163e705c121SKalle Valo 	else
116473c289baSBjoern A. Zeeb 		return le32_to_cpup((const __le32 *)(nvm_sw +
11657042678dSSara Sharon 						     NVM_VERSION_EXT_NVM));
1166e705c121SKalle Valo }
1167e705c121SKalle Valo 
iwl_get_radio_cfg(const struct iwl_cfg * cfg,const __le16 * nvm_sw,const __le16 * phy_sku)1168e705c121SKalle Valo static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
1169e705c121SKalle Valo 			     const __le16 *phy_sku)
1170e705c121SKalle Valo {
117144fd09daSChaya Rachel Ivgi 	if (cfg->nvm_type != IWL_NVM_EXT)
1172e705c121SKalle Valo 		return le16_to_cpup(nvm_sw + RADIO_CFG);
1173e705c121SKalle Valo 
117473c289baSBjoern A. Zeeb 	return le32_to_cpup((const __le32 *)(phy_sku + RADIO_CFG_FAMILY_EXT_NVM));
1175e705c121SKalle Valo 
1176e705c121SKalle Valo }
1177e705c121SKalle Valo 
iwl_get_n_hw_addrs(const struct iwl_cfg * cfg,const __le16 * nvm_sw)1178e705c121SKalle Valo static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
1179e705c121SKalle Valo {
1180e705c121SKalle Valo 	int n_hw_addr;
1181e705c121SKalle Valo 
118244fd09daSChaya Rachel Ivgi 	if (cfg->nvm_type != IWL_NVM_EXT)
1183e705c121SKalle Valo 		return le16_to_cpup(nvm_sw + N_HW_ADDRS);
1184e705c121SKalle Valo 
118573c289baSBjoern A. Zeeb 	n_hw_addr = le32_to_cpup((const __le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000));
1186e705c121SKalle Valo 
1187e705c121SKalle Valo 	return n_hw_addr & N_HW_ADDR_MASK;
1188e705c121SKalle Valo }
1189e705c121SKalle Valo 
iwl_set_radio_cfg(const struct iwl_cfg * cfg,struct iwl_nvm_data * data,u32 radio_cfg)1190e705c121SKalle Valo static void iwl_set_radio_cfg(const struct iwl_cfg *cfg,
1191e705c121SKalle Valo 			      struct iwl_nvm_data *data,
1192e705c121SKalle Valo 			      u32 radio_cfg)
1193e705c121SKalle Valo {
119444fd09daSChaya Rachel Ivgi 	if (cfg->nvm_type != IWL_NVM_EXT) {
1195e705c121SKalle Valo 		data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg);
1196e705c121SKalle Valo 		data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg);
1197e705c121SKalle Valo 		data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg);
1198e705c121SKalle Valo 		data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg);
1199e705c121SKalle Valo 		return;
1200e705c121SKalle Valo 	}
1201e705c121SKalle Valo 
1202e705c121SKalle Valo 	/* set the radio configuration for family 8000 */
12037042678dSSara Sharon 	data->radio_cfg_type = EXT_NVM_RF_CFG_TYPE_MSK(radio_cfg);
12047042678dSSara Sharon 	data->radio_cfg_step = EXT_NVM_RF_CFG_STEP_MSK(radio_cfg);
12057042678dSSara Sharon 	data->radio_cfg_dash = EXT_NVM_RF_CFG_DASH_MSK(radio_cfg);
12067042678dSSara Sharon 	data->radio_cfg_pnum = EXT_NVM_RF_CFG_FLAVOR_MSK(radio_cfg);
12077042678dSSara Sharon 	data->valid_tx_ant = EXT_NVM_RF_CFG_TX_ANT_MSK(radio_cfg);
12087042678dSSara Sharon 	data->valid_rx_ant = EXT_NVM_RF_CFG_RX_ANT_MSK(radio_cfg);
1209e705c121SKalle Valo }
1210e705c121SKalle Valo 
iwl_flip_hw_address(__le32 mac_addr0,__le32 mac_addr1,u8 * dest)121117c867bfSSara Sharon static void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest)
121217c867bfSSara Sharon {
121317c867bfSSara Sharon 	const u8 *hw_addr;
121417c867bfSSara Sharon 
121517c867bfSSara Sharon 	hw_addr = (const u8 *)&mac_addr0;
121617c867bfSSara Sharon 	dest[0] = hw_addr[3];
121717c867bfSSara Sharon 	dest[1] = hw_addr[2];
121817c867bfSSara Sharon 	dest[2] = hw_addr[1];
121917c867bfSSara Sharon 	dest[3] = hw_addr[0];
122017c867bfSSara Sharon 
122117c867bfSSara Sharon 	hw_addr = (const u8 *)&mac_addr1;
122217c867bfSSara Sharon 	dest[4] = hw_addr[1];
122317c867bfSSara Sharon 	dest[5] = hw_addr[0];
122417c867bfSSara Sharon }
122517c867bfSSara Sharon 
iwl_set_hw_address_from_csr(struct iwl_trans * trans,struct iwl_nvm_data * data)12264c625c56SShaul Triebitz static void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
122717c867bfSSara Sharon 					struct iwl_nvm_data *data)
122817c867bfSSara Sharon {
12297e6dffdaSJohannes Berg 	__le32 mac_addr0 = cpu_to_le32(iwl_read32(trans,
12307e6dffdaSJohannes Berg 						  CSR_MAC_ADDR0_STRAP(trans)));
12317e6dffdaSJohannes Berg 	__le32 mac_addr1 = cpu_to_le32(iwl_read32(trans,
12327e6dffdaSJohannes Berg 						  CSR_MAC_ADDR1_STRAP(trans)));
123317c867bfSSara Sharon 
1234a6c934b3SHaim Dreyfuss 	iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
1235a6c934b3SHaim Dreyfuss 	/*
1236a6c934b3SHaim Dreyfuss 	 * If the OEM fused a valid address, use it instead of the one in the
1237a6c934b3SHaim Dreyfuss 	 * OTP
1238a6c934b3SHaim Dreyfuss 	 */
1239a6c934b3SHaim Dreyfuss 	if (is_valid_ether_addr(data->hw_addr))
1240a6c934b3SHaim Dreyfuss 		return;
1241a6c934b3SHaim Dreyfuss 
12427e6dffdaSJohannes Berg 	mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP(trans)));
12437e6dffdaSJohannes Berg 	mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP(trans)));
124417c867bfSSara Sharon 
124517c867bfSSara Sharon 	iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
124617c867bfSSara Sharon }
124717c867bfSSara Sharon 
iwl_set_hw_address_family_8000(struct iwl_trans * trans,const struct iwl_cfg * cfg,struct iwl_nvm_data * data,const __le16 * mac_override,const __be16 * nvm_hw)1248afd5b170SSara Sharon static void iwl_set_hw_address_family_8000(struct iwl_trans *trans,
1249e705c121SKalle Valo 					   const struct iwl_cfg *cfg,
1250e705c121SKalle Valo 					   struct iwl_nvm_data *data,
1251e705c121SKalle Valo 					   const __le16 *mac_override,
12528fe34b06SLuca Coelho 					   const __be16 *nvm_hw)
1253e705c121SKalle Valo {
1254e705c121SKalle Valo 	const u8 *hw_addr;
1255e705c121SKalle Valo 
1256e705c121SKalle Valo 	if (mac_override) {
1257e705c121SKalle Valo 		static const u8 reserved_mac[] = {
1258e705c121SKalle Valo 			0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00
1259e705c121SKalle Valo 		};
1260e705c121SKalle Valo 
1261e705c121SKalle Valo 		hw_addr = (const u8 *)(mac_override +
12627042678dSSara Sharon 				 MAC_ADDRESS_OVERRIDE_EXT_NVM);
1263e705c121SKalle Valo 
1264e705c121SKalle Valo 		/*
1265e705c121SKalle Valo 		 * Store the MAC address from MAO section.
1266e705c121SKalle Valo 		 * No byte swapping is required in MAO section
1267e705c121SKalle Valo 		 */
1268e705c121SKalle Valo 		memcpy(data->hw_addr, hw_addr, ETH_ALEN);
1269e705c121SKalle Valo 
1270e705c121SKalle Valo 		/*
1271e705c121SKalle Valo 		 * Force the use of the OTP MAC address in case of reserved MAC
1272e705c121SKalle Valo 		 * address in the NVM, or if address is given but invalid.
1273e705c121SKalle Valo 		 */
1274e705c121SKalle Valo 		if (is_valid_ether_addr(data->hw_addr) &&
1275e705c121SKalle Valo 		    memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0)
1276e705c121SKalle Valo 			return;
1277e705c121SKalle Valo 
1278afd5b170SSara Sharon 		IWL_ERR(trans,
1279e705c121SKalle Valo 			"mac address from nvm override section is not valid\n");
1280e705c121SKalle Valo 	}
1281e705c121SKalle Valo 
1282e705c121SKalle Valo 	if (nvm_hw) {
1283afd5b170SSara Sharon 		/* read the mac address from WFMP registers */
1284afd5b170SSara Sharon 		__le32 mac_addr0 = cpu_to_le32(iwl_trans_read_prph(trans,
1285afd5b170SSara Sharon 						WFMP_MAC_ADDR_0));
1286afd5b170SSara Sharon 		__le32 mac_addr1 = cpu_to_le32(iwl_trans_read_prph(trans,
1287afd5b170SSara Sharon 						WFMP_MAC_ADDR_1));
1288e705c121SKalle Valo 
128917c867bfSSara Sharon 		iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
1290e705c121SKalle Valo 
1291e705c121SKalle Valo 		return;
1292e705c121SKalle Valo 	}
1293e705c121SKalle Valo 
1294afd5b170SSara Sharon 	IWL_ERR(trans, "mac address is not found\n");
1295afd5b170SSara Sharon }
1296afd5b170SSara Sharon 
iwl_set_hw_address(struct iwl_trans * trans,const struct iwl_cfg * cfg,struct iwl_nvm_data * data,const __be16 * nvm_hw,const __le16 * mac_override)129717c867bfSSara Sharon static int iwl_set_hw_address(struct iwl_trans *trans,
1298afd5b170SSara Sharon 			      const struct iwl_cfg *cfg,
12998fe34b06SLuca Coelho 			      struct iwl_nvm_data *data, const __be16 *nvm_hw,
1300afd5b170SSara Sharon 			      const __le16 *mac_override)
1301afd5b170SSara Sharon {
130217c867bfSSara Sharon 	if (cfg->mac_addr_from_csr) {
130317c867bfSSara Sharon 		iwl_set_hw_address_from_csr(trans, data);
130444fd09daSChaya Rachel Ivgi 	} else if (cfg->nvm_type != IWL_NVM_EXT) {
1305afd5b170SSara Sharon 		const u8 *hw_addr = (const u8 *)(nvm_hw + HW_ADDR);
1306afd5b170SSara Sharon 
1307afd5b170SSara Sharon 		/* The byte order is little endian 16 bit, meaning 214365 */
1308afd5b170SSara Sharon 		data->hw_addr[0] = hw_addr[1];
1309afd5b170SSara Sharon 		data->hw_addr[1] = hw_addr[0];
1310afd5b170SSara Sharon 		data->hw_addr[2] = hw_addr[3];
1311afd5b170SSara Sharon 		data->hw_addr[3] = hw_addr[2];
1312afd5b170SSara Sharon 		data->hw_addr[4] = hw_addr[5];
1313afd5b170SSara Sharon 		data->hw_addr[5] = hw_addr[4];
1314afd5b170SSara Sharon 	} else {
1315afd5b170SSara Sharon 		iwl_set_hw_address_family_8000(trans, cfg, data,
1316afd5b170SSara Sharon 					       mac_override, nvm_hw);
1317afd5b170SSara Sharon 	}
131817c867bfSSara Sharon 
131917c867bfSSara Sharon 	if (!is_valid_ether_addr(data->hw_addr)) {
132017c867bfSSara Sharon 		IWL_ERR(trans, "no valid mac address was found\n");
132117c867bfSSara Sharon 		return -EINVAL;
132217c867bfSSara Sharon 	}
132317c867bfSSara Sharon 
13243ea839c1SLuca Coelho 	if (!trans->csme_own)
13253ea839c1SLuca Coelho 		IWL_INFO(trans, "base HW address: %pM, OTP minor version: 0x%x\n",
13263ea839c1SLuca Coelho 			 data->hw_addr, iwl_read_prph(trans, REG_OTP_MINOR));
13274409e72bSLuca Coelho 
132817c867bfSSara Sharon 	return 0;
1329e705c121SKalle Valo }
1330e705c121SKalle Valo 
133101a9c948SLuca Coelho static bool
iwl_nvm_no_wide_in_5ghz(struct iwl_trans * trans,const struct iwl_cfg * cfg,const __be16 * nvm_hw)13327d34a7d7SLuca Coelho iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg,
13338fe34b06SLuca Coelho 			const __be16 *nvm_hw)
133401a9c948SLuca Coelho {
133501a9c948SLuca Coelho 	/*
133601a9c948SLuca Coelho 	 * Workaround a bug in Indonesia SKUs where the regulatory in
133701a9c948SLuca Coelho 	 * some 7000-family OTPs erroneously allow wide channels in
133801a9c948SLuca Coelho 	 * 5GHz.  To check for Indonesia, we take the SKU value from
133901a9c948SLuca Coelho 	 * bits 1-4 in the subsystem ID and check if it is either 5 or
134001a9c948SLuca Coelho 	 * 9.  In those cases, we need to force-disable wide channels
134101a9c948SLuca Coelho 	 * in 5GHz otherwise the FW will throw a sysassert when we try
134201a9c948SLuca Coelho 	 * to use them.
134301a9c948SLuca Coelho 	 */
13447d34a7d7SLuca Coelho 	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
134501a9c948SLuca Coelho 		/*
134601a9c948SLuca Coelho 		 * Unlike the other sections in the NVM, the hw
134701a9c948SLuca Coelho 		 * section uses big-endian.
134801a9c948SLuca Coelho 		 */
13498fe34b06SLuca Coelho 		u16 subsystem_id = be16_to_cpup(nvm_hw + SUBSYSTEM_ID);
135001a9c948SLuca Coelho 		u8 sku = (subsystem_id & 0x1e) >> 1;
135101a9c948SLuca Coelho 
135201a9c948SLuca Coelho 		if (sku == 5 || sku == 9) {
13537d34a7d7SLuca Coelho 			IWL_DEBUG_EEPROM(trans->dev,
135401a9c948SLuca Coelho 					 "disabling wide channels in 5GHz (0x%0x %d)\n",
135501a9c948SLuca Coelho 					 subsystem_id, sku);
135601a9c948SLuca Coelho 			return true;
135701a9c948SLuca Coelho 		}
135801a9c948SLuca Coelho 	}
135901a9c948SLuca Coelho 
136001a9c948SLuca Coelho 	return false;
136101a9c948SLuca Coelho }
136201a9c948SLuca Coelho 
1363e705c121SKalle Valo struct iwl_nvm_data *
iwl_parse_mei_nvm_data(struct iwl_trans * trans,const struct iwl_cfg * cfg,const struct iwl_mei_nvm * mei_nvm,const struct iwl_fw * fw)13646d19a5ebSEmmanuel Grumbach iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
13656d19a5ebSEmmanuel Grumbach 		       const struct iwl_mei_nvm *mei_nvm,
13666d19a5ebSEmmanuel Grumbach 		       const struct iwl_fw *fw)
13676d19a5ebSEmmanuel Grumbach {
13686d19a5ebSEmmanuel Grumbach 	struct iwl_nvm_data *data;
13696d19a5ebSEmmanuel Grumbach 	u32 sbands_flags = 0;
13706d19a5ebSEmmanuel Grumbach 	u8 rx_chains = fw->valid_rx_ant;
13716d19a5ebSEmmanuel Grumbach 	u8 tx_chains = fw->valid_rx_ant;
13726d19a5ebSEmmanuel Grumbach 
13736d19a5ebSEmmanuel Grumbach 	if (cfg->uhb_supported)
13746d19a5ebSEmmanuel Grumbach 		data = kzalloc(struct_size(data, channels,
13756d19a5ebSEmmanuel Grumbach 					   IWL_NVM_NUM_CHANNELS_UHB),
13766d19a5ebSEmmanuel Grumbach 					   GFP_KERNEL);
13776d19a5ebSEmmanuel Grumbach 	else
13786d19a5ebSEmmanuel Grumbach 		data = kzalloc(struct_size(data, channels,
13796d19a5ebSEmmanuel Grumbach 					   IWL_NVM_NUM_CHANNELS_EXT),
13806d19a5ebSEmmanuel Grumbach 					   GFP_KERNEL);
13816d19a5ebSEmmanuel Grumbach 	if (!data)
13826d19a5ebSEmmanuel Grumbach 		return NULL;
13836d19a5ebSEmmanuel Grumbach 
13846d19a5ebSEmmanuel Grumbach 	BUILD_BUG_ON(ARRAY_SIZE(mei_nvm->channels) !=
13856d19a5ebSEmmanuel Grumbach 		     IWL_NVM_NUM_CHANNELS_UHB);
13866d19a5ebSEmmanuel Grumbach 	data->nvm_version = mei_nvm->nvm_version;
13876d19a5ebSEmmanuel Grumbach 
13886d19a5ebSEmmanuel Grumbach 	iwl_set_radio_cfg(cfg, data, mei_nvm->radio_cfg);
13896d19a5ebSEmmanuel Grumbach 	if (data->valid_tx_ant)
13906d19a5ebSEmmanuel Grumbach 		tx_chains &= data->valid_tx_ant;
13916d19a5ebSEmmanuel Grumbach 	if (data->valid_rx_ant)
13926d19a5ebSEmmanuel Grumbach 		rx_chains &= data->valid_rx_ant;
13936d19a5ebSEmmanuel Grumbach 
13946d19a5ebSEmmanuel Grumbach 	data->sku_cap_mimo_disabled = false;
13956d19a5ebSEmmanuel Grumbach 	data->sku_cap_band_24ghz_enable = true;
13966d19a5ebSEmmanuel Grumbach 	data->sku_cap_band_52ghz_enable = true;
13976d19a5ebSEmmanuel Grumbach 	data->sku_cap_11n_enable =
13986d19a5ebSEmmanuel Grumbach 		!(iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL);
13996d19a5ebSEmmanuel Grumbach 	data->sku_cap_11ac_enable = true;
14006d19a5ebSEmmanuel Grumbach 	data->sku_cap_11ax_enable =
14016d19a5ebSEmmanuel Grumbach 		mei_nvm->caps & MEI_NVM_CAPS_11AX_SUPPORT;
14026d19a5ebSEmmanuel Grumbach 
14036d19a5ebSEmmanuel Grumbach 	data->lar_enabled = mei_nvm->caps & MEI_NVM_CAPS_LARI_SUPPORT;
14046d19a5ebSEmmanuel Grumbach 
14056d19a5ebSEmmanuel Grumbach 	data->n_hw_addrs = mei_nvm->n_hw_addrs;
14066d19a5ebSEmmanuel Grumbach 	/* If no valid mac address was found - bail out */
14076d19a5ebSEmmanuel Grumbach 	if (iwl_set_hw_address(trans, cfg, data, NULL, NULL)) {
14086d19a5ebSEmmanuel Grumbach 		kfree(data);
14096d19a5ebSEmmanuel Grumbach 		return NULL;
14106d19a5ebSEmmanuel Grumbach 	}
14116d19a5ebSEmmanuel Grumbach 
14126d19a5ebSEmmanuel Grumbach 	if (data->lar_enabled &&
14136d19a5ebSEmmanuel Grumbach 	    fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_LAR_SUPPORT))
14146d19a5ebSEmmanuel Grumbach 		sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR;
14156d19a5ebSEmmanuel Grumbach 
14166d19a5ebSEmmanuel Grumbach 	iwl_init_sbands(trans, data, mei_nvm->channels, tx_chains, rx_chains,
14176d19a5ebSEmmanuel Grumbach 			sbands_flags, true, fw);
14186d19a5ebSEmmanuel Grumbach 
14196d19a5ebSEmmanuel Grumbach 	return data;
14206d19a5ebSEmmanuel Grumbach }
14216d19a5ebSEmmanuel Grumbach IWL_EXPORT_SYMBOL(iwl_parse_mei_nvm_data);
14226d19a5ebSEmmanuel Grumbach 
14236d19a5ebSEmmanuel Grumbach struct iwl_nvm_data *
iwl_parse_nvm_data(struct iwl_trans * trans,const struct iwl_cfg * cfg,const struct iwl_fw * fw,const __be16 * nvm_hw,const __le16 * nvm_sw,const __le16 * nvm_calib,const __le16 * regulatory,const __le16 * mac_override,const __le16 * phy_sku,u8 tx_chains,u8 rx_chains)1424afd5b170SSara Sharon iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
1425f06021a1SLuca Coelho 		   const struct iwl_fw *fw,
14268fe34b06SLuca Coelho 		   const __be16 *nvm_hw, const __le16 *nvm_sw,
1427e705c121SKalle Valo 		   const __le16 *nvm_calib, const __le16 *regulatory,
1428e705c121SKalle Valo 		   const __le16 *mac_override, const __le16 *phy_sku,
1429f06021a1SLuca Coelho 		   u8 tx_chains, u8 rx_chains)
1430e705c121SKalle Valo {
1431e705c121SKalle Valo 	struct iwl_nvm_data *data;
1432afd5b170SSara Sharon 	bool lar_enabled;
1433afd5b170SSara Sharon 	u32 sku, radio_cfg;
14344b82455cSLuca Coelho 	u32 sbands_flags = 0;
1435e705c121SKalle Valo 	u16 lar_config;
1436afd5b170SSara Sharon 	const __le16 *ch_section;
1437e705c121SKalle Valo 
1438c8cfa08eSTova Mussai 	if (cfg->uhb_supported)
1439c8cfa08eSTova Mussai 		data = kzalloc(struct_size(data, channels,
1440c8cfa08eSTova Mussai 					   IWL_NVM_NUM_CHANNELS_UHB),
1441c8cfa08eSTova Mussai 					   GFP_KERNEL);
1442c8cfa08eSTova Mussai 	else if (cfg->nvm_type != IWL_NVM_EXT)
14436b367c9fSGustavo A. R. Silva 		data = kzalloc(struct_size(data, channels,
14446b367c9fSGustavo A. R. Silva 					   IWL_NVM_NUM_CHANNELS),
1445e705c121SKalle Valo 					   GFP_KERNEL);
1446e705c121SKalle Valo 	else
14476b367c9fSGustavo A. R. Silva 		data = kzalloc(struct_size(data, channels,
14486b367c9fSGustavo A. R. Silva 					   IWL_NVM_NUM_CHANNELS_EXT),
1449e705c121SKalle Valo 					   GFP_KERNEL);
1450e705c121SKalle Valo 	if (!data)
1451e705c121SKalle Valo 		return NULL;
1452e705c121SKalle Valo 
1453e705c121SKalle Valo 	data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw);
1454e705c121SKalle Valo 
1455e705c121SKalle Valo 	radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku);
1456e705c121SKalle Valo 	iwl_set_radio_cfg(cfg, data, radio_cfg);
1457e705c121SKalle Valo 	if (data->valid_tx_ant)
1458e705c121SKalle Valo 		tx_chains &= data->valid_tx_ant;
1459e705c121SKalle Valo 	if (data->valid_rx_ant)
1460e705c121SKalle Valo 		rx_chains &= data->valid_rx_ant;
1461e705c121SKalle Valo 
1462e705c121SKalle Valo 	sku = iwl_get_sku(cfg, nvm_sw, phy_sku);
146328e9c00fSLuca Coelho 	data->sku_cap_band_24ghz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
146428e9c00fSLuca Coelho 	data->sku_cap_band_52ghz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
1465e705c121SKalle Valo 	data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
1466e705c121SKalle Valo 	if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
1467e705c121SKalle Valo 		data->sku_cap_11n_enable = false;
1468e705c121SKalle Valo 	data->sku_cap_11ac_enable = data->sku_cap_11n_enable &&
1469e705c121SKalle Valo 				    (sku & NVM_SKU_CAP_11AC_ENABLE);
1470e705c121SKalle Valo 	data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE;
1471e705c121SKalle Valo 
1472e705c121SKalle Valo 	data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
1473e705c121SKalle Valo 
147444fd09daSChaya Rachel Ivgi 	if (cfg->nvm_type != IWL_NVM_EXT) {
1475e705c121SKalle Valo 		/* Checking for required sections */
1476e705c121SKalle Valo 		if (!nvm_calib) {
1477afd5b170SSara Sharon 			IWL_ERR(trans,
1478e705c121SKalle Valo 				"Can't parse empty Calib NVM sections\n");
1479e705c121SKalle Valo 			kfree(data);
1480e705c121SKalle Valo 			return NULL;
1481e705c121SKalle Valo 		}
148244fd09daSChaya Rachel Ivgi 
148344fd09daSChaya Rachel Ivgi 		ch_section = cfg->nvm_type == IWL_NVM_SDP ?
148444fd09daSChaya Rachel Ivgi 			     &regulatory[NVM_CHANNELS_SDP] :
148544fd09daSChaya Rachel Ivgi 			     &nvm_sw[NVM_CHANNELS];
148644fd09daSChaya Rachel Ivgi 
1487e705c121SKalle Valo 		/* in family 8000 Xtal calibration values moved to OTP */
1488e705c121SKalle Valo 		data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
1489e705c121SKalle Valo 		data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
1490afd5b170SSara Sharon 		lar_enabled = true;
1491e705c121SKalle Valo 	} else {
1492e705c121SKalle Valo 		u16 lar_offset = data->nvm_version < 0xE39 ?
14937042678dSSara Sharon 				 NVM_LAR_OFFSET_OLD :
14947042678dSSara Sharon 				 NVM_LAR_OFFSET;
1495e705c121SKalle Valo 
1496e705c121SKalle Valo 		lar_config = le16_to_cpup(regulatory + lar_offset);
1497e705c121SKalle Valo 		data->lar_enabled = !!(lar_config &
14987042678dSSara Sharon 				       NVM_LAR_ENABLED);
1499afd5b170SSara Sharon 		lar_enabled = data->lar_enabled;
15007042678dSSara Sharon 		ch_section = &regulatory[NVM_CHANNELS_EXTENDED];
1501e705c121SKalle Valo 	}
1502e705c121SKalle Valo 
150317c867bfSSara Sharon 	/* If no valid mac address was found - bail out */
150417c867bfSSara Sharon 	if (iwl_set_hw_address(trans, cfg, data, nvm_hw, mac_override)) {
150517c867bfSSara Sharon 		kfree(data);
150617c867bfSSara Sharon 		return NULL;
150717c867bfSSara Sharon 	}
150817c867bfSSara Sharon 
1509f06021a1SLuca Coelho 	if (lar_enabled &&
1510f06021a1SLuca Coelho 	    fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_LAR_SUPPORT))
15114b82455cSLuca Coelho 		sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR;
15124b82455cSLuca Coelho 
15137d34a7d7SLuca Coelho 	if (iwl_nvm_no_wide_in_5ghz(trans, cfg, nvm_hw))
15144b82455cSLuca Coelho 		sbands_flags |= IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ;
15154b82455cSLuca Coelho 
1516d8913b80SShaul Triebitz 	iwl_init_sbands(trans, data, ch_section, tx_chains, rx_chains,
151703470ba7SShaul Triebitz 			sbands_flags, false, fw);
1518e705c121SKalle Valo 	data->calib_version = 255;
1519e705c121SKalle Valo 
1520e705c121SKalle Valo 	return data;
1521e705c121SKalle Valo }
1522e705c121SKalle Valo IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
1523e705c121SKalle Valo 
iwl_nvm_get_regdom_bw_flags(const u16 * nvm_chan,int ch_idx,u16 nvm_flags,struct iwl_reg_capa reg_capa,const struct iwl_cfg * cfg)1524b15ef67cSShaul Triebitz static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
1525e705c121SKalle Valo 				       int ch_idx, u16 nvm_flags,
1526e27c506aSGil Adam 				       struct iwl_reg_capa reg_capa,
1527e705c121SKalle Valo 				       const struct iwl_cfg *cfg)
1528e705c121SKalle Valo {
1529e705c121SKalle Valo 	u32 flags = NL80211_RRF_NO_HT40;
1530e705c121SKalle Valo 
1531e705c121SKalle Valo 	if (ch_idx < NUM_2GHZ_CHANNELS &&
1532e705c121SKalle Valo 	    (nvm_flags & NVM_CHANNEL_40MHZ)) {
1533e705c121SKalle Valo 		if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
1534e705c121SKalle Valo 			flags &= ~NL80211_RRF_NO_HT40PLUS;
1535e705c121SKalle Valo 		if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
1536e705c121SKalle Valo 			flags &= ~NL80211_RRF_NO_HT40MINUS;
1537b15ef67cSShaul Triebitz 	} else if (nvm_flags & NVM_CHANNEL_40MHZ) {
1538e705c121SKalle Valo 		if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
1539e705c121SKalle Valo 			flags &= ~NL80211_RRF_NO_HT40PLUS;
1540e705c121SKalle Valo 		else
1541e705c121SKalle Valo 			flags &= ~NL80211_RRF_NO_HT40MINUS;
1542e705c121SKalle Valo 	}
1543e705c121SKalle Valo 
1544e705c121SKalle Valo 	if (!(nvm_flags & NVM_CHANNEL_80MHZ))
1545e705c121SKalle Valo 		flags |= NL80211_RRF_NO_80MHZ;
1546e705c121SKalle Valo 	if (!(nvm_flags & NVM_CHANNEL_160MHZ))
1547e705c121SKalle Valo 		flags |= NL80211_RRF_NO_160MHZ;
1548e705c121SKalle Valo 
1549e705c121SKalle Valo 	if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
1550e705c121SKalle Valo 		flags |= NL80211_RRF_NO_IR;
1551e705c121SKalle Valo 
1552e705c121SKalle Valo 	if (nvm_flags & NVM_CHANNEL_RADAR)
1553e705c121SKalle Valo 		flags |= NL80211_RRF_DFS;
1554e705c121SKalle Valo 
1555e705c121SKalle Valo 	if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
1556e705c121SKalle Valo 		flags |= NL80211_RRF_NO_OUTDOOR;
1557e705c121SKalle Valo 
1558e705c121SKalle Valo 	/* Set the GO concurrent flag only in case that NO_IR is set.
1559e705c121SKalle Valo 	 * Otherwise it is meaningless
1560e705c121SKalle Valo 	 */
1561e705c121SKalle Valo 	if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
1562e705c121SKalle Valo 	    (flags & NL80211_RRF_NO_IR))
1563e705c121SKalle Valo 		flags |= NL80211_RRF_GO_CONCURRENT;
1564e705c121SKalle Valo 
15652763bba6SHaim Dreyfuss 	/*
1566e27c506aSGil Adam 	 * reg_capa is per regulatory domain so apply it for every channel
15672763bba6SHaim Dreyfuss 	 */
15682763bba6SHaim Dreyfuss 	if (ch_idx >= NUM_2GHZ_CHANNELS) {
1569e27c506aSGil Adam 		if (!reg_capa.allow_40mhz)
15702763bba6SHaim Dreyfuss 			flags |= NL80211_RRF_NO_HT40;
15712763bba6SHaim Dreyfuss 
1572e27c506aSGil Adam 		if (!reg_capa.allow_80mhz)
15732763bba6SHaim Dreyfuss 			flags |= NL80211_RRF_NO_80MHZ;
15742763bba6SHaim Dreyfuss 
1575e27c506aSGil Adam 		if (!reg_capa.allow_160mhz)
15762763bba6SHaim Dreyfuss 			flags |= NL80211_RRF_NO_160MHZ;
1577592fef3eSJohannes Berg 
1578592fef3eSJohannes Berg 		if (!reg_capa.allow_320mhz)
1579592fef3eSJohannes Berg 			flags |= NL80211_RRF_NO_320MHZ;
15802763bba6SHaim Dreyfuss 	}
1581592fef3eSJohannes Berg 
1582e27c506aSGil Adam 	if (reg_capa.disable_11ax)
1583a224883cSHaim Dreyfuss 		flags |= NL80211_RRF_NO_HE;
1584a224883cSHaim Dreyfuss 
1585592fef3eSJohannes Berg 	if (reg_capa.disable_11be)
1586592fef3eSJohannes Berg 		flags |= NL80211_RRF_NO_EHT;
1587592fef3eSJohannes Berg 
1588e705c121SKalle Valo 	return flags;
1589e705c121SKalle Valo }
1590e705c121SKalle Valo 
iwl_get_reg_capa(u32 flags,u8 resp_ver)1591e9b63341SAbhishek Naik static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver)
1592e27c506aSGil Adam {
1593e9b63341SAbhishek Naik 	struct iwl_reg_capa reg_capa = {};
1594e27c506aSGil Adam 
1595e9b63341SAbhishek Naik 	if (resp_ver >= REG_CAPA_V4_RESP_VER) {
1596e9b63341SAbhishek Naik 		reg_capa.allow_40mhz = true;
1597e9b63341SAbhishek Naik 		reg_capa.allow_80mhz = flags & REG_CAPA_V4_80MHZ_ALLOWED;
1598e9b63341SAbhishek Naik 		reg_capa.allow_160mhz = flags & REG_CAPA_V4_160MHZ_ALLOWED;
1599e9b63341SAbhishek Naik 		reg_capa.allow_320mhz = flags & REG_CAPA_V4_320MHZ_ALLOWED;
1600e9b63341SAbhishek Naik 		reg_capa.disable_11ax = flags & REG_CAPA_V4_11AX_DISABLED;
1601e9b63341SAbhishek Naik 		reg_capa.disable_11be = flags & REG_CAPA_V4_11BE_DISABLED;
1602e9b63341SAbhishek Naik 	} else if (resp_ver >= REG_CAPA_V2_RESP_VER) {
1603e27c506aSGil Adam 		reg_capa.allow_40mhz = flags & REG_CAPA_V2_40MHZ_ALLOWED;
1604e27c506aSGil Adam 		reg_capa.allow_80mhz = flags & REG_CAPA_V2_80MHZ_ALLOWED;
1605e27c506aSGil Adam 		reg_capa.allow_160mhz = flags & REG_CAPA_V2_160MHZ_ALLOWED;
1606e27c506aSGil Adam 		reg_capa.disable_11ax = flags & REG_CAPA_V2_11AX_DISABLED;
1607e27c506aSGil Adam 	} else {
1608e9b63341SAbhishek Naik 		reg_capa.allow_40mhz = !(flags & REG_CAPA_V1_40MHZ_FORBIDDEN);
1609e9b63341SAbhishek Naik 		reg_capa.allow_80mhz = flags & REG_CAPA_V1_80MHZ_ALLOWED;
1610e9b63341SAbhishek Naik 		reg_capa.allow_160mhz = flags & REG_CAPA_V1_160MHZ_ALLOWED;
1611e9b63341SAbhishek Naik 		reg_capa.disable_11ax = flags & REG_CAPA_V1_11AX_DISABLED;
1612e27c506aSGil Adam 	}
1613e27c506aSGil Adam 	return reg_capa;
1614e27c506aSGil Adam }
1615e27c506aSGil Adam 
1616e705c121SKalle Valo struct ieee80211_regdomain *
iwl_parse_nvm_mcc_info(struct device * dev,const struct iwl_cfg * cfg,int num_of_ch,__le32 * channels,u16 fw_mcc,u16 geo_info,u32 cap,u8 resp_ver)1617e705c121SKalle Valo iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
161877e30e10SHaim Dreyfuss 		       int num_of_ch, __le32 *channels, u16 fw_mcc,
1619e9b63341SAbhishek Naik 		       u16 geo_info, u32 cap, u8 resp_ver)
1620e705c121SKalle Valo {
1621e705c121SKalle Valo 	int ch_idx;
162292b0f7b2SEmmanuel Grumbach 	u16 ch_flags;
162392b0f7b2SEmmanuel Grumbach 	u32 reg_rule_flags, prev_reg_rule_flags = 0;
1624b15ef67cSShaul Triebitz 	const u16 *nvm_chan;
162577e30e10SHaim Dreyfuss 	struct ieee80211_regdomain *regd, *copy_rd;
1626e705c121SKalle Valo 	struct ieee80211_reg_rule *rule;
162757fbcce3SJohannes Berg 	enum nl80211_band band;
1628e705c121SKalle Valo 	int center_freq, prev_center_freq = 0;
162938cb87eeSStanislaw Gruszka 	int valid_rules = 0;
1630e705c121SKalle Valo 	bool new_rule;
1631b15ef67cSShaul Triebitz 	int max_num_ch;
1632e27c506aSGil Adam 	struct iwl_reg_capa reg_capa;
1633b15ef67cSShaul Triebitz 
1634b15ef67cSShaul Triebitz 	if (cfg->uhb_supported) {
1635b15ef67cSShaul Triebitz 		max_num_ch = IWL_NVM_NUM_CHANNELS_UHB;
1636b15ef67cSShaul Triebitz 		nvm_chan = iwl_uhb_nvm_channels;
1637b15ef67cSShaul Triebitz 	} else if (cfg->nvm_type == IWL_NVM_EXT) {
1638b15ef67cSShaul Triebitz 		max_num_ch = IWL_NVM_NUM_CHANNELS_EXT;
1639b15ef67cSShaul Triebitz 		nvm_chan = iwl_ext_nvm_channels;
1640b15ef67cSShaul Triebitz 	} else {
1641b15ef67cSShaul Triebitz 		max_num_ch = IWL_NVM_NUM_CHANNELS;
1642b15ef67cSShaul Triebitz 		nvm_chan = iwl_nvm_channels;
1643b15ef67cSShaul Triebitz 	}
1644e705c121SKalle Valo 
1645853450a6SMiri Korenblit 	if (num_of_ch > max_num_ch) {
1646853450a6SMiri Korenblit 		IWL_DEBUG_DEV(dev, IWL_DL_LAR,
1647853450a6SMiri Korenblit 			      "Num of channels (%d) is greater than expected. Truncating to %d\n",
1648853450a6SMiri Korenblit 			      num_of_ch, max_num_ch);
1649e705c121SKalle Valo 		num_of_ch = max_num_ch;
1650853450a6SMiri Korenblit 	}
1651e705c121SKalle Valo 
16524c816b21SShaul Triebitz 	if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES))
16534c816b21SShaul Triebitz 		return ERR_PTR(-EINVAL);
16544c816b21SShaul Triebitz 
1655e705c121SKalle Valo 	IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n",
1656e705c121SKalle Valo 		      num_of_ch);
1657e705c121SKalle Valo 
1658e705c121SKalle Valo 	/* build a regdomain rule for every valid channel */
165978d722b1SYueHaibing 	regd = kzalloc(struct_size(regd, reg_rules, num_of_ch), GFP_KERNEL);
1660e705c121SKalle Valo 	if (!regd)
1661e705c121SKalle Valo 		return ERR_PTR(-ENOMEM);
1662e705c121SKalle Valo 
166377e30e10SHaim Dreyfuss 	/* set alpha2 from FW. */
166477e30e10SHaim Dreyfuss 	regd->alpha2[0] = fw_mcc >> 8;
166577e30e10SHaim Dreyfuss 	regd->alpha2[1] = fw_mcc & 0xff;
166677e30e10SHaim Dreyfuss 
1667e27c506aSGil Adam 	/* parse regulatory capability flags */
1668e27c506aSGil Adam 	reg_capa = iwl_get_reg_capa(cap, resp_ver);
1669e27c506aSGil Adam 
1670e705c121SKalle Valo 	for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
1671e705c121SKalle Valo 		ch_flags = (u16)__le32_to_cpup(channels + ch_idx);
1672c2cf318dSTova Mussai 		band = iwl_nl80211_band_from_channel_idx(ch_idx);
1673e705c121SKalle Valo 		center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx],
1674e705c121SKalle Valo 							     band);
1675e705c121SKalle Valo 		new_rule = false;
1676e705c121SKalle Valo 
1677e705c121SKalle Valo 		if (!(ch_flags & NVM_CHANNEL_VALID)) {
1678d8c73e45SJohannes Berg 			iwl_nvm_print_channel_flags(dev, IWL_DL_LAR,
1679d8c73e45SJohannes Berg 						    nvm_chan[ch_idx], ch_flags);
1680e705c121SKalle Valo 			continue;
1681e705c121SKalle Valo 		}
1682e705c121SKalle Valo 
168392b0f7b2SEmmanuel Grumbach 		reg_rule_flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx,
1684e27c506aSGil Adam 							     ch_flags, reg_capa,
16852763bba6SHaim Dreyfuss 							     cfg);
168692b0f7b2SEmmanuel Grumbach 
1687e705c121SKalle Valo 		/* we can't continue the same rule */
168892b0f7b2SEmmanuel Grumbach 		if (ch_idx == 0 || prev_reg_rule_flags != reg_rule_flags ||
1689e705c121SKalle Valo 		    center_freq - prev_center_freq > 20) {
1690e705c121SKalle Valo 			valid_rules++;
1691e705c121SKalle Valo 			new_rule = true;
1692e705c121SKalle Valo 		}
1693e705c121SKalle Valo 
1694e705c121SKalle Valo 		rule = &regd->reg_rules[valid_rules - 1];
1695e705c121SKalle Valo 
1696e705c121SKalle Valo 		if (new_rule)
1697e705c121SKalle Valo 			rule->freq_range.start_freq_khz =
1698e705c121SKalle Valo 						MHZ_TO_KHZ(center_freq - 10);
1699e705c121SKalle Valo 
1700e705c121SKalle Valo 		rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10);
1701e705c121SKalle Valo 
1702e705c121SKalle Valo 		/* this doesn't matter - not used by FW */
1703e705c121SKalle Valo 		rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
1704e705c121SKalle Valo 		rule->power_rule.max_eirp =
1705e705c121SKalle Valo 			DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER);
1706e705c121SKalle Valo 
170792b0f7b2SEmmanuel Grumbach 		rule->flags = reg_rule_flags;
1708e705c121SKalle Valo 
1709e705c121SKalle Valo 		/* rely on auto-calculation to merge BW of contiguous chans */
1710e705c121SKalle Valo 		rule->flags |= NL80211_RRF_AUTO_BW;
1711e705c121SKalle Valo 		rule->freq_range.max_bandwidth_khz = 0;
1712e705c121SKalle Valo 
1713e705c121SKalle Valo 		prev_center_freq = center_freq;
171492b0f7b2SEmmanuel Grumbach 		prev_reg_rule_flags = reg_rule_flags;
1715e705c121SKalle Valo 
1716d8c73e45SJohannes Berg 		iwl_nvm_print_channel_flags(dev, IWL_DL_LAR,
1717d8c73e45SJohannes Berg 					    nvm_chan[ch_idx], ch_flags);
171877e30e10SHaim Dreyfuss 
171977e30e10SHaim Dreyfuss 		if (!(geo_info & GEO_WMM_ETSI_5GHZ_INFO) ||
172077e30e10SHaim Dreyfuss 		    band == NL80211_BAND_2GHZ)
172177e30e10SHaim Dreyfuss 			continue;
172277e30e10SHaim Dreyfuss 
172338cb87eeSStanislaw Gruszka 		reg_query_regdb_wmm(regd->alpha2, center_freq, rule);
1724e705c121SKalle Valo 	}
1725e705c121SKalle Valo 
1726eb09ae93SMiri Korenblit 	/*
1727eb09ae93SMiri Korenblit 	 * Certain firmware versions might report no valid channels
1728eb09ae93SMiri Korenblit 	 * if booted in RF-kill, i.e. not all calibrations etc. are
1729eb09ae93SMiri Korenblit 	 * running. We'll get out of this situation later when the
1730eb09ae93SMiri Korenblit 	 * rfkill is removed and we update the regdomain again, but
1731eb09ae93SMiri Korenblit 	 * since cfg80211 doesn't accept an empty regdomain, add a
1732eb09ae93SMiri Korenblit 	 * dummy (unusable) rule here in this case so we can init.
1733eb09ae93SMiri Korenblit 	 */
1734eb09ae93SMiri Korenblit 	if (!valid_rules) {
1735eb09ae93SMiri Korenblit 		valid_rules = 1;
1736eb09ae93SMiri Korenblit 		rule = &regd->reg_rules[valid_rules - 1];
1737eb09ae93SMiri Korenblit 		rule->freq_range.start_freq_khz = MHZ_TO_KHZ(2412);
1738eb09ae93SMiri Korenblit 		rule->freq_range.end_freq_khz = MHZ_TO_KHZ(2413);
1739eb09ae93SMiri Korenblit 		rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(1);
1740eb09ae93SMiri Korenblit 		rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
1741eb09ae93SMiri Korenblit 		rule->power_rule.max_eirp =
1742eb09ae93SMiri Korenblit 			DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER);
1743eb09ae93SMiri Korenblit 	}
1744eb09ae93SMiri Korenblit 
1745e705c121SKalle Valo 	regd->n_reg_rules = valid_rules;
1746e705c121SKalle Valo 
174777e30e10SHaim Dreyfuss 	/*
174877e30e10SHaim Dreyfuss 	 * Narrow down regdom for unused regulatory rules to prevent hole
174977e30e10SHaim Dreyfuss 	 * between reg rules to wmm rules.
175077e30e10SHaim Dreyfuss 	 */
175178d722b1SYueHaibing 	copy_rd = kmemdup(regd, struct_size(regd, reg_rules, valid_rules),
175278d722b1SYueHaibing 			  GFP_KERNEL);
1753a2a120a9SLuca Coelho 	if (!copy_rd)
175477e30e10SHaim Dreyfuss 		copy_rd = ERR_PTR(-ENOMEM);
175577e30e10SHaim Dreyfuss 
175677e30e10SHaim Dreyfuss 	kfree(regd);
175777e30e10SHaim Dreyfuss 	return copy_rd;
1758e705c121SKalle Valo }
1759e705c121SKalle Valo IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);
17609c4f7d51SShaul Triebitz 
17619c4f7d51SShaul Triebitz #define IWL_MAX_NVM_SECTION_SIZE	0x1b58
17629c4f7d51SShaul Triebitz #define IWL_MAX_EXT_NVM_SECTION_SIZE	0x1ffc
17639c4f7d51SShaul Triebitz #define MAX_NVM_FILE_LEN	16384
17649c4f7d51SShaul Triebitz 
iwl_nvm_fixups(u32 hw_id,unsigned int section,u8 * data,unsigned int len)17659c4f7d51SShaul Triebitz void iwl_nvm_fixups(u32 hw_id, unsigned int section, u8 *data,
17669c4f7d51SShaul Triebitz 		    unsigned int len)
17679c4f7d51SShaul Triebitz {
17689c4f7d51SShaul Triebitz #define IWL_4165_DEVICE_ID	0x5501
17699c4f7d51SShaul Triebitz #define NVM_SKU_CAP_MIMO_DISABLE BIT(5)
17709c4f7d51SShaul Triebitz 
17719c4f7d51SShaul Triebitz 	if (section == NVM_SECTION_TYPE_PHY_SKU &&
17729c4f7d51SShaul Triebitz 	    hw_id == IWL_4165_DEVICE_ID && data && len >= 5 &&
17739c4f7d51SShaul Triebitz 	    (data[4] & NVM_SKU_CAP_MIMO_DISABLE))
17749c4f7d51SShaul Triebitz 		/* OTP 0x52 bug work around: it's a 1x1 device */
17759c4f7d51SShaul Triebitz 		data[3] = ANT_B | (ANT_B << 4);
17769c4f7d51SShaul Triebitz }
17779c4f7d51SShaul Triebitz IWL_EXPORT_SYMBOL(iwl_nvm_fixups);
17789c4f7d51SShaul Triebitz 
17799c4f7d51SShaul Triebitz /*
17809c4f7d51SShaul Triebitz  * Reads external NVM from a file into mvm->nvm_sections
17819c4f7d51SShaul Triebitz  *
17829c4f7d51SShaul Triebitz  * HOW TO CREATE THE NVM FILE FORMAT:
17839c4f7d51SShaul Triebitz  * ------------------------------
17849c4f7d51SShaul Triebitz  * 1. create hex file, format:
17859c4f7d51SShaul Triebitz  *      3800 -> header
17869c4f7d51SShaul Triebitz  *      0000 -> header
17879c4f7d51SShaul Triebitz  *      5a40 -> data
17889c4f7d51SShaul Triebitz  *
17899c4f7d51SShaul Triebitz  *   rev - 6 bit (word1)
17909c4f7d51SShaul Triebitz  *   len - 10 bit (word1)
17919c4f7d51SShaul Triebitz  *   id - 4 bit (word2)
17929c4f7d51SShaul Triebitz  *   rsv - 12 bit (word2)
17939c4f7d51SShaul Triebitz  *
17949c4f7d51SShaul Triebitz  * 2. flip 8bits with 8 bits per line to get the right NVM file format
17959c4f7d51SShaul Triebitz  *
17969c4f7d51SShaul Triebitz  * 3. create binary file from the hex file
17979c4f7d51SShaul Triebitz  *
17989c4f7d51SShaul Triebitz  * 4. save as "iNVM_xxx.bin" under /lib/firmware
17999c4f7d51SShaul Triebitz  */
iwl_read_external_nvm(struct iwl_trans * trans,const char * nvm_file_name,struct iwl_nvm_section * nvm_sections)18009c4f7d51SShaul Triebitz int iwl_read_external_nvm(struct iwl_trans *trans,
18019c4f7d51SShaul Triebitz 			  const char *nvm_file_name,
18029c4f7d51SShaul Triebitz 			  struct iwl_nvm_section *nvm_sections)
18039c4f7d51SShaul Triebitz {
18049c4f7d51SShaul Triebitz 	int ret, section_size;
18059c4f7d51SShaul Triebitz 	u16 section_id;
18069c4f7d51SShaul Triebitz 	const struct firmware *fw_entry;
18079c4f7d51SShaul Triebitz 	const struct {
18089c4f7d51SShaul Triebitz 		__le16 word1;
18099c4f7d51SShaul Triebitz 		__le16 word2;
18109c4f7d51SShaul Triebitz 		u8 data[];
18119c4f7d51SShaul Triebitz 	} *file_sec;
18129c4f7d51SShaul Triebitz 	const u8 *eof;
18139c4f7d51SShaul Triebitz 	u8 *temp;
18149c4f7d51SShaul Triebitz 	int max_section_size;
18159c4f7d51SShaul Triebitz 	const __le32 *dword_buff;
18169c4f7d51SShaul Triebitz 
18179c4f7d51SShaul Triebitz #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
18189c4f7d51SShaul Triebitz #define NVM_WORD2_ID(x) (x >> 12)
18199c4f7d51SShaul Triebitz #define EXT_NVM_WORD2_LEN(x) (2 * (((x) & 0xFF) << 8 | (x) >> 8))
18209c4f7d51SShaul Triebitz #define EXT_NVM_WORD1_ID(x) ((x) >> 4)
18219c4f7d51SShaul Triebitz #define NVM_HEADER_0	(0x2A504C54)
18229c4f7d51SShaul Triebitz #define NVM_HEADER_1	(0x4E564D2A)
18239c4f7d51SShaul Triebitz #define NVM_HEADER_SIZE	(4 * sizeof(u32))
18249c4f7d51SShaul Triebitz 
18259c4f7d51SShaul Triebitz 	IWL_DEBUG_EEPROM(trans->dev, "Read from external NVM\n");
18269c4f7d51SShaul Triebitz 
18279c4f7d51SShaul Triebitz 	/* Maximal size depends on NVM version */
18289c4f7d51SShaul Triebitz 	if (trans->cfg->nvm_type != IWL_NVM_EXT)
18299c4f7d51SShaul Triebitz 		max_section_size = IWL_MAX_NVM_SECTION_SIZE;
18309c4f7d51SShaul Triebitz 	else
18319c4f7d51SShaul Triebitz 		max_section_size = IWL_MAX_EXT_NVM_SECTION_SIZE;
18329c4f7d51SShaul Triebitz 
18339c4f7d51SShaul Triebitz 	/*
18349c4f7d51SShaul Triebitz 	 * Obtain NVM image via request_firmware. Since we already used
18359c4f7d51SShaul Triebitz 	 * request_firmware_nowait() for the firmware binary load and only
18369c4f7d51SShaul Triebitz 	 * get here after that we assume the NVM request can be satisfied
18379c4f7d51SShaul Triebitz 	 * synchronously.
18389c4f7d51SShaul Triebitz 	 */
18399c4f7d51SShaul Triebitz 	ret = request_firmware(&fw_entry, nvm_file_name, trans->dev);
18409c4f7d51SShaul Triebitz 	if (ret) {
18419c4f7d51SShaul Triebitz 		IWL_ERR(trans, "ERROR: %s isn't available %d\n",
18429c4f7d51SShaul Triebitz 			nvm_file_name, ret);
18439c4f7d51SShaul Triebitz 		return ret;
18449c4f7d51SShaul Triebitz 	}
18459c4f7d51SShaul Triebitz 
18469c4f7d51SShaul Triebitz 	IWL_INFO(trans, "Loaded NVM file %s (%zu bytes)\n",
18479c4f7d51SShaul Triebitz 		 nvm_file_name, fw_entry->size);
18489c4f7d51SShaul Triebitz 
18499c4f7d51SShaul Triebitz 	if (fw_entry->size > MAX_NVM_FILE_LEN) {
18509c4f7d51SShaul Triebitz 		IWL_ERR(trans, "NVM file too large\n");
18519c4f7d51SShaul Triebitz 		ret = -EINVAL;
18529c4f7d51SShaul Triebitz 		goto out;
18539c4f7d51SShaul Triebitz 	}
18549c4f7d51SShaul Triebitz 
18559c4f7d51SShaul Triebitz 	eof = fw_entry->data + fw_entry->size;
185673c289baSBjoern A. Zeeb 	dword_buff = (const __le32 *)fw_entry->data;
18579c4f7d51SShaul Triebitz 
18589c4f7d51SShaul Triebitz 	/* some NVM file will contain a header.
18599c4f7d51SShaul Triebitz 	 * The header is identified by 2 dwords header as follow:
18609c4f7d51SShaul Triebitz 	 * dword[0] = 0x2A504C54
18619c4f7d51SShaul Triebitz 	 * dword[1] = 0x4E564D2A
18629c4f7d51SShaul Triebitz 	 *
18639c4f7d51SShaul Triebitz 	 * This header must be skipped when providing the NVM data to the FW.
18649c4f7d51SShaul Triebitz 	 */
18659c4f7d51SShaul Triebitz 	if (fw_entry->size > NVM_HEADER_SIZE &&
18669c4f7d51SShaul Triebitz 	    dword_buff[0] == cpu_to_le32(NVM_HEADER_0) &&
18679c4f7d51SShaul Triebitz 	    dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) {
186873c289baSBjoern A. Zeeb 		file_sec = (const void *)(fw_entry->data + NVM_HEADER_SIZE);
18699c4f7d51SShaul Triebitz 		IWL_INFO(trans, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
18709c4f7d51SShaul Triebitz 		IWL_INFO(trans, "NVM Manufacturing date %08X\n",
18719c4f7d51SShaul Triebitz 			 le32_to_cpu(dword_buff[3]));
18729c4f7d51SShaul Triebitz 
18739c4f7d51SShaul Triebitz 		/* nvm file validation, dword_buff[2] holds the file version */
1874286ca8ebSLuca Coelho 		if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
187555c6d8f8SMike Golant 		    trans->hw_rev_step == SILICON_C_STEP &&
18769c4f7d51SShaul Triebitz 		    le32_to_cpu(dword_buff[2]) < 0xE4A) {
18779c4f7d51SShaul Triebitz 			ret = -EFAULT;
18789c4f7d51SShaul Triebitz 			goto out;
18799c4f7d51SShaul Triebitz 		}
18809c4f7d51SShaul Triebitz 	} else {
188173c289baSBjoern A. Zeeb 		file_sec = (const void *)fw_entry->data;
18829c4f7d51SShaul Triebitz 	}
18839c4f7d51SShaul Triebitz 
18849c4f7d51SShaul Triebitz 	while (true) {
18859c4f7d51SShaul Triebitz 		if (file_sec->data > eof) {
18869c4f7d51SShaul Triebitz 			IWL_ERR(trans,
18879c4f7d51SShaul Triebitz 				"ERROR - NVM file too short for section header\n");
18889c4f7d51SShaul Triebitz 			ret = -EINVAL;
18899c4f7d51SShaul Triebitz 			break;
18909c4f7d51SShaul Triebitz 		}
18919c4f7d51SShaul Triebitz 
18929c4f7d51SShaul Triebitz 		/* check for EOF marker */
18939c4f7d51SShaul Triebitz 		if (!file_sec->word1 && !file_sec->word2) {
18949c4f7d51SShaul Triebitz 			ret = 0;
18959c4f7d51SShaul Triebitz 			break;
18969c4f7d51SShaul Triebitz 		}
18979c4f7d51SShaul Triebitz 
18989c4f7d51SShaul Triebitz 		if (trans->cfg->nvm_type != IWL_NVM_EXT) {
18999c4f7d51SShaul Triebitz 			section_size =
19009c4f7d51SShaul Triebitz 				2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
19019c4f7d51SShaul Triebitz 			section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
19029c4f7d51SShaul Triebitz 		} else {
19039c4f7d51SShaul Triebitz 			section_size = 2 * EXT_NVM_WORD2_LEN(
19049c4f7d51SShaul Triebitz 						le16_to_cpu(file_sec->word2));
19059c4f7d51SShaul Triebitz 			section_id = EXT_NVM_WORD1_ID(
19069c4f7d51SShaul Triebitz 						le16_to_cpu(file_sec->word1));
19079c4f7d51SShaul Triebitz 		}
19089c4f7d51SShaul Triebitz 
19099c4f7d51SShaul Triebitz 		if (section_size > max_section_size) {
19109c4f7d51SShaul Triebitz 			IWL_ERR(trans, "ERROR - section too large (%d)\n",
19119c4f7d51SShaul Triebitz 				section_size);
19129c4f7d51SShaul Triebitz 			ret = -EINVAL;
19139c4f7d51SShaul Triebitz 			break;
19149c4f7d51SShaul Triebitz 		}
19159c4f7d51SShaul Triebitz 
19169c4f7d51SShaul Triebitz 		if (!section_size) {
19179c4f7d51SShaul Triebitz 			IWL_ERR(trans, "ERROR - section empty\n");
19189c4f7d51SShaul Triebitz 			ret = -EINVAL;
19199c4f7d51SShaul Triebitz 			break;
19209c4f7d51SShaul Triebitz 		}
19219c4f7d51SShaul Triebitz 
19229c4f7d51SShaul Triebitz 		if (file_sec->data + section_size > eof) {
19239c4f7d51SShaul Triebitz 			IWL_ERR(trans,
19249c4f7d51SShaul Triebitz 				"ERROR - NVM file too short for section (%d bytes)\n",
19259c4f7d51SShaul Triebitz 				section_size);
19269c4f7d51SShaul Triebitz 			ret = -EINVAL;
19279c4f7d51SShaul Triebitz 			break;
19289c4f7d51SShaul Triebitz 		}
19299c4f7d51SShaul Triebitz 
19309c4f7d51SShaul Triebitz 		if (WARN(section_id >= NVM_MAX_NUM_SECTIONS,
19319c4f7d51SShaul Triebitz 			 "Invalid NVM section ID %d\n", section_id)) {
19329c4f7d51SShaul Triebitz 			ret = -EINVAL;
19339c4f7d51SShaul Triebitz 			break;
19349c4f7d51SShaul Triebitz 		}
19359c4f7d51SShaul Triebitz 
19369c4f7d51SShaul Triebitz 		temp = kmemdup(file_sec->data, section_size, GFP_KERNEL);
19379c4f7d51SShaul Triebitz 		if (!temp) {
19389c4f7d51SShaul Triebitz 			ret = -ENOMEM;
19399c4f7d51SShaul Triebitz 			break;
19409c4f7d51SShaul Triebitz 		}
19419c4f7d51SShaul Triebitz 
19429c4f7d51SShaul Triebitz 		iwl_nvm_fixups(trans->hw_id, section_id, temp, section_size);
19439c4f7d51SShaul Triebitz 
19449c4f7d51SShaul Triebitz 		kfree(nvm_sections[section_id].data);
19459c4f7d51SShaul Triebitz 		nvm_sections[section_id].data = temp;
19469c4f7d51SShaul Triebitz 		nvm_sections[section_id].length = section_size;
19479c4f7d51SShaul Triebitz 
19489c4f7d51SShaul Triebitz 		/* advance to the next section */
194973c289baSBjoern A. Zeeb 		file_sec = (const void *)(file_sec->data + section_size);
19509c4f7d51SShaul Triebitz 	}
19519c4f7d51SShaul Triebitz out:
19529c4f7d51SShaul Triebitz 	release_firmware(fw_entry);
19539c4f7d51SShaul Triebitz 	return ret;
19549c4f7d51SShaul Triebitz }
19559c4f7d51SShaul Triebitz IWL_EXPORT_SYMBOL(iwl_read_external_nvm);
19564c625c56SShaul Triebitz 
iwl_get_nvm(struct iwl_trans * trans,const struct iwl_fw * fw)19574c625c56SShaul Triebitz struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
19584c625c56SShaul Triebitz 				 const struct iwl_fw *fw)
19594c625c56SShaul Triebitz {
19604c625c56SShaul Triebitz 	struct iwl_nvm_get_info cmd = {};
19614c625c56SShaul Triebitz 	struct iwl_nvm_data *nvm;
19624c625c56SShaul Triebitz 	struct iwl_host_cmd hcmd = {
19634c625c56SShaul Triebitz 		.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
19644c625c56SShaul Triebitz 		.data = { &cmd, },
19654c625c56SShaul Triebitz 		.len = { sizeof(cmd) },
19664c625c56SShaul Triebitz 		.id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO)
19674c625c56SShaul Triebitz 	};
19684c625c56SShaul Triebitz 	int  ret;
1969e7eeee08SNaftali Goldstein 	bool empty_otp;
19704c625c56SShaul Triebitz 	u32 mac_flags;
19714c625c56SShaul Triebitz 	u32 sbands_flags = 0;
19722785ce00SShaul Triebitz 	/*
19732785ce00SShaul Triebitz 	 * All the values in iwl_nvm_get_info_rsp v4 are the same as
19742785ce00SShaul Triebitz 	 * in v3, except for the channel profile part of the
19752785ce00SShaul Triebitz 	 * regulatory.  So we can just access the new struct, with the
19762785ce00SShaul Triebitz 	 * exception of the latter.
19772785ce00SShaul Triebitz 	 */
19782785ce00SShaul Triebitz 	struct iwl_nvm_get_info_rsp *rsp;
19792785ce00SShaul Triebitz 	struct iwl_nvm_get_info_rsp_v3 *rsp_v3;
19802785ce00SShaul Triebitz 	bool v4 = fw_has_api(&fw->ucode_capa,
19812785ce00SShaul Triebitz 			     IWL_UCODE_TLV_API_REGULATORY_NVM_INFO);
19822785ce00SShaul Triebitz 	size_t rsp_size = v4 ? sizeof(*rsp) : sizeof(*rsp_v3);
19832785ce00SShaul Triebitz 	void *channel_profile;
19844c625c56SShaul Triebitz 
19854c625c56SShaul Triebitz 	ret = iwl_trans_send_cmd(trans, &hcmd);
19864c625c56SShaul Triebitz 	if (ret)
19874c625c56SShaul Triebitz 		return ERR_PTR(ret);
19884c625c56SShaul Triebitz 
19892785ce00SShaul Triebitz 	if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != rsp_size,
19904c625c56SShaul Triebitz 		 "Invalid payload len in NVM response from FW %d",
19914c625c56SShaul Triebitz 		 iwl_rx_packet_payload_len(hcmd.resp_pkt))) {
19924c625c56SShaul Triebitz 		ret = -EINVAL;
19934c625c56SShaul Triebitz 		goto out;
19944c625c56SShaul Triebitz 	}
19954c625c56SShaul Triebitz 
19964c625c56SShaul Triebitz 	rsp = (void *)hcmd.resp_pkt->data;
1997e7eeee08SNaftali Goldstein 	empty_otp = !!(le32_to_cpu(rsp->general.flags) &
1998e7eeee08SNaftali Goldstein 		       NVM_GENERAL_FLAGS_EMPTY_OTP);
1999e7eeee08SNaftali Goldstein 	if (empty_otp)
20004c625c56SShaul Triebitz 		IWL_INFO(trans, "OTP is empty\n");
20014c625c56SShaul Triebitz 
20026b367c9fSGustavo A. R. Silva 	nvm = kzalloc(struct_size(nvm, channels, IWL_NUM_CHANNELS), GFP_KERNEL);
20034c625c56SShaul Triebitz 	if (!nvm) {
20044c625c56SShaul Triebitz 		ret = -ENOMEM;
20054c625c56SShaul Triebitz 		goto out;
20064c625c56SShaul Triebitz 	}
20074c625c56SShaul Triebitz 
20084c625c56SShaul Triebitz 	iwl_set_hw_address_from_csr(trans, nvm);
20094c625c56SShaul Triebitz 	/* TODO: if platform NVM has MAC address - override it here */
20104c625c56SShaul Triebitz 
20114c625c56SShaul Triebitz 	if (!is_valid_ether_addr(nvm->hw_addr)) {
20124c625c56SShaul Triebitz 		IWL_ERR(trans, "no valid mac address was found\n");
20134c625c56SShaul Triebitz 		ret = -EINVAL;
20144c625c56SShaul Triebitz 		goto err_free;
20154c625c56SShaul Triebitz 	}
20164c625c56SShaul Triebitz 
20174c625c56SShaul Triebitz 	IWL_INFO(trans, "base HW address: %pM\n", nvm->hw_addr);
20184c625c56SShaul Triebitz 
20194c625c56SShaul Triebitz 	/* Initialize general data */
20204c625c56SShaul Triebitz 	nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version);
2021e7eeee08SNaftali Goldstein 	nvm->n_hw_addrs = rsp->general.n_hw_addrs;
2022e7eeee08SNaftali Goldstein 	if (nvm->n_hw_addrs == 0)
2023e7eeee08SNaftali Goldstein 		IWL_WARN(trans,
2024e7eeee08SNaftali Goldstein 			 "Firmware declares no reserved mac addresses. OTP is empty: %d\n",
2025e7eeee08SNaftali Goldstein 			 empty_otp);
20264c625c56SShaul Triebitz 
20274c625c56SShaul Triebitz 	/* Initialize MAC sku data */
20284c625c56SShaul Triebitz 	mac_flags = le32_to_cpu(rsp->mac_sku.mac_sku_flags);
20294c625c56SShaul Triebitz 	nvm->sku_cap_11ac_enable =
20304c625c56SShaul Triebitz 		!!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AC_ENABLED);
20314c625c56SShaul Triebitz 	nvm->sku_cap_11n_enable =
20324c625c56SShaul Triebitz 		!!(mac_flags & NVM_MAC_SKU_FLAGS_802_11N_ENABLED);
2033514c3069SLuca Coelho 	nvm->sku_cap_11ax_enable =
2034514c3069SLuca Coelho 		!!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AX_ENABLED);
20354c625c56SShaul Triebitz 	nvm->sku_cap_band_24ghz_enable =
20364c625c56SShaul Triebitz 		!!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED);
20374c625c56SShaul Triebitz 	nvm->sku_cap_band_52ghz_enable =
20384c625c56SShaul Triebitz 		!!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED);
20394c625c56SShaul Triebitz 	nvm->sku_cap_mimo_disabled =
20404c625c56SShaul Triebitz 		!!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED);
2041*7559a34fSMiri Korenblit 	if (CSR_HW_RFID_TYPE(trans->hw_rf_id) >= IWL_CFG_RF_TYPE_FM)
20428ca67e3dSJohannes Berg 		nvm->sku_cap_11be_enable = true;
20434c625c56SShaul Triebitz 
20444c625c56SShaul Triebitz 	/* Initialize PHY sku data */
20454c625c56SShaul Triebitz 	nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains);
20464c625c56SShaul Triebitz 	nvm->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains);
20474c625c56SShaul Triebitz 
2048f06021a1SLuca Coelho 	if (le32_to_cpu(rsp->regulatory.lar_enabled) &&
2049f06021a1SLuca Coelho 	    fw_has_capa(&fw->ucode_capa,
2050f06021a1SLuca Coelho 			IWL_UCODE_TLV_CAPA_LAR_SUPPORT)) {
20514c625c56SShaul Triebitz 		nvm->lar_enabled = true;
20524c625c56SShaul Triebitz 		sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR;
20534c625c56SShaul Triebitz 	}
20544c625c56SShaul Triebitz 
20552785ce00SShaul Triebitz 	rsp_v3 = (void *)rsp;
20562785ce00SShaul Triebitz 	channel_profile = v4 ? (void *)rsp->regulatory.channel_profile :
20572785ce00SShaul Triebitz 			  (void *)rsp_v3->regulatory.channel_profile;
20582785ce00SShaul Triebitz 
2059d8913b80SShaul Triebitz 	iwl_init_sbands(trans, nvm,
206014cf9bc6SYueHaibing 			channel_profile,
20614c625c56SShaul Triebitz 			nvm->valid_tx_ant & fw->valid_tx_ant,
20624c625c56SShaul Triebitz 			nvm->valid_rx_ant & fw->valid_rx_ant,
206303470ba7SShaul Triebitz 			sbands_flags, v4, fw);
20644c625c56SShaul Triebitz 
20654c625c56SShaul Triebitz 	iwl_free_resp(&hcmd);
20664c625c56SShaul Triebitz 	return nvm;
20674c625c56SShaul Triebitz 
20684c625c56SShaul Triebitz err_free:
20694c625c56SShaul Triebitz 	kfree(nvm);
20704c625c56SShaul Triebitz out:
20714c625c56SShaul Triebitz 	iwl_free_resp(&hcmd);
20724c625c56SShaul Triebitz 	return ERR_PTR(ret);
20734c625c56SShaul Triebitz }
20744c625c56SShaul Triebitz IWL_EXPORT_SYMBOL(iwl_get_nvm);
2075