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 ®ulatory[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 = ®ulatory[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 = ®d->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 = ®d->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