Commit | Line | Data |
---|---|---|
b1e1adfa JB |
1 | /****************************************************************************** |
2 | * | |
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
4 | * redistributing this file, you may do so under either license. | |
5 | * | |
6 | * GPL LICENSE SUMMARY | |
7 | * | |
51368bf7 | 8 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. |
5f0d98f2 | 9 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
29108495 | 10 | * Copyright(c) 2016 - 2017 Intel Deutschland GmbH |
b1e1adfa JB |
11 | * |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of version 2 of the GNU General Public License as | |
14 | * published by the Free Software Foundation. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, but | |
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 | * General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program; if not, write to the Free Software | |
23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, | |
24 | * USA | |
25 | * | |
26 | * The full GNU General Public License is included in this distribution | |
410dc5aa | 27 | * in the file called COPYING. |
b1e1adfa JB |
28 | * |
29 | * Contact Information: | |
d01c5366 | 30 | * Intel Linux Wireless <linuxwifi@intel.com> |
b1e1adfa JB |
31 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
32 | * | |
33 | * BSD LICENSE | |
34 | * | |
51368bf7 | 35 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. |
5f0d98f2 | 36 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
29108495 | 37 | * Copyright(c) 2016 - 2017 Intel Deutschland GmbH |
b1e1adfa JB |
38 | * All rights reserved. |
39 | * | |
40 | * Redistribution and use in source and binary forms, with or without | |
41 | * modification, are permitted provided that the following conditions | |
42 | * are met: | |
43 | * | |
44 | * * Redistributions of source code must retain the above copyright | |
45 | * notice, this list of conditions and the following disclaimer. | |
46 | * * Redistributions in binary form must reproduce the above copyright | |
47 | * notice, this list of conditions and the following disclaimer in | |
48 | * the documentation and/or other materials provided with the | |
49 | * distribution. | |
50 | * * Neither the name Intel Corporation nor the names of its | |
51 | * contributors may be used to endorse or promote products derived | |
52 | * from this software without specific prior written permission. | |
53 | * | |
54 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
55 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
56 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
57 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
58 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
59 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
60 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
61 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
62 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
63 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
64 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
65 | *****************************************************************************/ | |
66 | #include <linux/types.h> | |
67 | #include <linux/slab.h> | |
68 | #include <linux/export.h> | |
9f32e017 | 69 | #include <linux/etherdevice.h> |
1e0b393a | 70 | #include <linux/pci.h> |
813df5ce | 71 | |
48e29340 | 72 | #include "iwl-drv.h" |
b1e1adfa JB |
73 | #include "iwl-modparams.h" |
74 | #include "iwl-nvm-parse.h" | |
afd5b170 | 75 | #include "iwl-prph.h" |
17c867bf SS |
76 | #include "iwl-io.h" |
77 | #include "iwl-csr.h" | |
813df5ce | 78 | #include "fw/acpi.h" |
77e30e10 | 79 | #include "fw/api/nvm-reg.h" |
b1e1adfa JB |
80 | |
81 | /* NVM offsets (in words) definitions */ | |
44fd09da | 82 | enum nvm_offsets { |
b1e1adfa | 83 | /* NVM HW-Section offset (in words) definitions */ |
01a9c948 | 84 | SUBSYSTEM_ID = 0x0A, |
b1e1adfa JB |
85 | HW_ADDR = 0x15, |
86 | ||
77db0a3c | 87 | /* NVM SW-Section offset (in words) definitions */ |
b1e1adfa JB |
88 | NVM_SW_SECTION = 0x1C0, |
89 | NVM_VERSION = 0, | |
90 | RADIO_CFG = 1, | |
91 | SKU = 2, | |
92 | N_HW_ADDRS = 3, | |
93 | NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION, | |
94 | ||
77db0a3c | 95 | /* NVM calibration section offset (in words) definitions */ |
b1e1adfa | 96 | NVM_CALIB_SECTION = 0x2B8, |
44fd09da CRI |
97 | XTAL_CALIB = 0x316 - NVM_CALIB_SECTION, |
98 | ||
99 | /* NVM REGULATORY -Section offset (in words) definitions */ | |
100 | NVM_CHANNELS_SDP = 0, | |
b1e1adfa JB |
101 | }; |
102 | ||
7042678d | 103 | enum ext_nvm_offsets { |
77db0a3c | 104 | /* NVM HW-Section offset (in words) definitions */ |
7042678d | 105 | MAC_ADDRESS_OVERRIDE_EXT_NVM = 1, |
77db0a3c EH |
106 | |
107 | /* NVM SW-Section offset (in words) definitions */ | |
7042678d SS |
108 | NVM_VERSION_EXT_NVM = 0, |
109 | RADIO_CFG_FAMILY_EXT_NVM = 0, | |
5dd9c68a EG |
110 | SKU_FAMILY_8000 = 2, |
111 | N_HW_ADDRS_FAMILY_8000 = 3, | |
ce500071 | 112 | |
77db0a3c | 113 | /* NVM REGULATORY -Section offset (in words) definitions */ |
7042678d SS |
114 | NVM_CHANNELS_EXTENDED = 0, |
115 | NVM_LAR_OFFSET_OLD = 0x4C7, | |
116 | NVM_LAR_OFFSET = 0x507, | |
117 | NVM_LAR_ENABLED = 0x7, | |
77db0a3c EH |
118 | }; |
119 | ||
b1e1adfa JB |
120 | /* SKU Capabilities (actual values from NVM definition) */ |
121 | enum nvm_sku_bits { | |
5f0d98f2 EG |
122 | NVM_SKU_CAP_BAND_24GHZ = BIT(0), |
123 | NVM_SKU_CAP_BAND_52GHZ = BIT(1), | |
124 | NVM_SKU_CAP_11N_ENABLE = BIT(2), | |
125 | NVM_SKU_CAP_11AC_ENABLE = BIT(3), | |
126 | NVM_SKU_CAP_MIMO_DISABLE = BIT(5), | |
b1e1adfa JB |
127 | }; |
128 | ||
b1e1adfa JB |
129 | /* |
130 | * These are the channel numbers in the order that they are stored in the NVM | |
131 | */ | |
132 | static const u8 iwl_nvm_channels[] = { | |
133 | /* 2.4 GHz */ | |
134 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
135 | /* 5 GHz */ | |
136 | 36, 40, 44 , 48, 52, 56, 60, 64, | |
137 | 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, | |
138 | 149, 153, 157, 161, 165 | |
139 | }; | |
140 | ||
7042678d | 141 | static const u8 iwl_ext_nvm_channels[] = { |
77db0a3c | 142 | /* 2.4 GHz */ |
9b1c9a66 | 143 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
77db0a3c EH |
144 | /* 5 GHz */ |
145 | 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, | |
146 | 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, | |
147 | 149, 153, 157, 161, 165, 169, 173, 177, 181 | |
148 | }; | |
149 | ||
77e30e10 HD |
150 | #define IWL_NVM_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) |
151 | #define IWL_NVM_NUM_CHANNELS_EXT ARRAY_SIZE(iwl_ext_nvm_channels) | |
749f1fe1 | 152 | #define NUM_2GHZ_CHANNELS 14 |
7042678d | 153 | #define NUM_2GHZ_CHANNELS_EXT 14 |
749f1fe1 EH |
154 | #define FIRST_2GHZ_HT_MINUS 5 |
155 | #define LAST_2GHZ_HT_PLUS 9 | |
b281c93d MG |
156 | #define LAST_5GHZ_HT 165 |
157 | #define LAST_5GHZ_HT_FAMILY_8000 181 | |
ce500071 | 158 | #define N_HW_ADDR_MASK 0xF |
b1e1adfa | 159 | |
b1e1adfa JB |
160 | /* rate data (static) */ |
161 | static struct ieee80211_rate iwl_cfg80211_rates[] = { | |
162 | { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, | |
163 | { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, | |
164 | .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, | |
165 | { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, | |
166 | .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, | |
167 | { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, | |
168 | .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, | |
169 | { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, | |
170 | { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, | |
171 | { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, | |
172 | { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, | |
173 | { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, | |
174 | { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, | |
175 | { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, | |
176 | { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, | |
177 | }; | |
178 | #define RATES_24_OFFS 0 | |
179 | #define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) | |
180 | #define RATES_52_OFFS 4 | |
181 | #define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) | |
182 | ||
183 | /** | |
184 | * enum iwl_nvm_channel_flags - channel flags in NVM | |
185 | * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo | |
186 | * @NVM_CHANNEL_IBSS: usable as an IBSS channel | |
187 | * @NVM_CHANNEL_ACTIVE: active scanning allowed | |
188 | * @NVM_CHANNEL_RADAR: radar detection required | |
9ee6dace DS |
189 | * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed |
190 | * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS | |
191 | * on same channel on 2.4 or same UNII band on 5.2 | |
b823cf3b LC |
192 | * @NVM_CHANNEL_UNIFORM: uniform spreading required |
193 | * @NVM_CHANNEL_20MHZ: 20 MHz channel okay | |
194 | * @NVM_CHANNEL_40MHZ: 40 MHz channel okay | |
195 | * @NVM_CHANNEL_80MHZ: 80 MHz channel okay | |
196 | * @NVM_CHANNEL_160MHZ: 160 MHz channel okay | |
197 | * @NVM_CHANNEL_DC_HIGH: DC HIGH required/allowed (?) | |
b1e1adfa JB |
198 | */ |
199 | enum iwl_nvm_channel_flags { | |
b823cf3b LC |
200 | NVM_CHANNEL_VALID = BIT(0), |
201 | NVM_CHANNEL_IBSS = BIT(1), | |
202 | NVM_CHANNEL_ACTIVE = BIT(3), | |
203 | NVM_CHANNEL_RADAR = BIT(4), | |
204 | NVM_CHANNEL_INDOOR_ONLY = BIT(5), | |
205 | NVM_CHANNEL_GO_CONCURRENT = BIT(6), | |
206 | NVM_CHANNEL_UNIFORM = BIT(7), | |
207 | NVM_CHANNEL_20MHZ = BIT(8), | |
208 | NVM_CHANNEL_40MHZ = BIT(9), | |
209 | NVM_CHANNEL_80MHZ = BIT(10), | |
210 | NVM_CHANNEL_160MHZ = BIT(11), | |
211 | NVM_CHANNEL_DC_HIGH = BIT(12), | |
b1e1adfa JB |
212 | }; |
213 | ||
d8c73e45 JB |
214 | static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, |
215 | int chan, u16 flags) | |
216 | { | |
b1e1adfa | 217 | #define CHECK_AND_PRINT_I(x) \ |
d8c73e45 JB |
218 | ((flags & NVM_CHANNEL_##x) ? " " #x : "") |
219 | ||
220 | if (!(flags & NVM_CHANNEL_VALID)) { | |
221 | IWL_DEBUG_DEV(dev, level, "Ch. %d: 0x%x: No traffic\n", | |
222 | chan, flags); | |
223 | return; | |
224 | } | |
225 | ||
226 | /* Note: already can print up to 101 characters, 110 is the limit! */ | |
227 | IWL_DEBUG_DEV(dev, level, | |
228 | "Ch. %d: 0x%x:%s%s%s%s%s%s%s%s%s%s%s%s\n", | |
229 | chan, flags, | |
230 | CHECK_AND_PRINT_I(VALID), | |
231 | CHECK_AND_PRINT_I(IBSS), | |
232 | CHECK_AND_PRINT_I(ACTIVE), | |
233 | CHECK_AND_PRINT_I(RADAR), | |
234 | CHECK_AND_PRINT_I(INDOOR_ONLY), | |
235 | CHECK_AND_PRINT_I(GO_CONCURRENT), | |
236 | CHECK_AND_PRINT_I(UNIFORM), | |
237 | CHECK_AND_PRINT_I(20MHZ), | |
238 | CHECK_AND_PRINT_I(40MHZ), | |
239 | CHECK_AND_PRINT_I(80MHZ), | |
240 | CHECK_AND_PRINT_I(160MHZ), | |
241 | CHECK_AND_PRINT_I(DC_HIGH)); | |
242 | #undef CHECK_AND_PRINT_I | |
243 | } | |
b1e1adfa | 244 | |
770ceda6 | 245 | static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, |
b281c93d | 246 | u16 nvm_flags, const struct iwl_cfg *cfg) |
770ceda6 AN |
247 | { |
248 | u32 flags = IEEE80211_CHAN_NO_HT40; | |
b281c93d MG |
249 | u32 last_5ghz_ht = LAST_5GHZ_HT; |
250 | ||
44fd09da | 251 | if (cfg->nvm_type == IWL_NVM_EXT) |
b281c93d | 252 | last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; |
770ceda6 AN |
253 | |
254 | if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) { | |
255 | if (ch_num <= LAST_2GHZ_HT_PLUS) | |
256 | flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | |
257 | if (ch_num >= FIRST_2GHZ_HT_MINUS) | |
258 | flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | |
b281c93d | 259 | } else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) { |
770ceda6 AN |
260 | if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) |
261 | flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | |
262 | else | |
263 | flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | |
264 | } | |
265 | if (!(nvm_flags & NVM_CHANNEL_80MHZ)) | |
266 | flags |= IEEE80211_CHAN_NO_80MHZ; | |
267 | if (!(nvm_flags & NVM_CHANNEL_160MHZ)) | |
268 | flags |= IEEE80211_CHAN_NO_160MHZ; | |
269 | ||
270 | if (!(nvm_flags & NVM_CHANNEL_IBSS)) | |
271 | flags |= IEEE80211_CHAN_NO_IR; | |
272 | ||
273 | if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) | |
274 | flags |= IEEE80211_CHAN_NO_IR; | |
275 | ||
276 | if (nvm_flags & NVM_CHANNEL_RADAR) | |
277 | flags |= IEEE80211_CHAN_RADAR; | |
278 | ||
279 | if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) | |
280 | flags |= IEEE80211_CHAN_INDOOR_ONLY; | |
281 | ||
282 | /* Set the GO concurrent flag only in case that NO_IR is set. | |
283 | * Otherwise it is meaningless | |
284 | */ | |
285 | if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && | |
286 | (flags & IEEE80211_CHAN_NO_IR)) | |
06f207fc | 287 | flags |= IEEE80211_CHAN_IR_CONCURRENT; |
770ceda6 AN |
288 | |
289 | return flags; | |
290 | } | |
291 | ||
b1e1adfa JB |
292 | static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, |
293 | struct iwl_nvm_data *data, | |
770ceda6 | 294 | const __le16 * const nvm_ch_flags, |
01a9c948 | 295 | bool lar_supported, bool no_wide_in_5ghz) |
b1e1adfa JB |
296 | { |
297 | int ch_idx; | |
298 | int n_channels = 0; | |
299 | struct ieee80211_channel *channel; | |
300 | u16 ch_flags; | |
749f1fe1 | 301 | int num_of_ch, num_2ghz_channels; |
77db0a3c EH |
302 | const u8 *nvm_chan; |
303 | ||
44fd09da | 304 | if (cfg->nvm_type != IWL_NVM_EXT) { |
77e30e10 | 305 | num_of_ch = IWL_NVM_NUM_CHANNELS; |
77db0a3c | 306 | nvm_chan = &iwl_nvm_channels[0]; |
749f1fe1 | 307 | num_2ghz_channels = NUM_2GHZ_CHANNELS; |
77db0a3c | 308 | } else { |
77e30e10 | 309 | num_of_ch = IWL_NVM_NUM_CHANNELS_EXT; |
7042678d SS |
310 | nvm_chan = &iwl_ext_nvm_channels[0]; |
311 | num_2ghz_channels = NUM_2GHZ_CHANNELS_EXT; | |
77db0a3c | 312 | } |
b1e1adfa | 313 | |
77db0a3c | 314 | for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { |
01a9c948 LC |
315 | bool is_5ghz = (ch_idx >= num_2ghz_channels); |
316 | ||
b1e1adfa | 317 | ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); |
c5128654 | 318 | |
01a9c948 | 319 | if (is_5ghz && !data->sku_cap_band_52GHz_enable) |
a76f3bfe | 320 | continue; |
c5128654 | 321 | |
01a9c948 LC |
322 | /* workaround to disable wide channels in 5GHz */ |
323 | if (no_wide_in_5ghz && is_5ghz) { | |
324 | ch_flags &= ~(NVM_CHANNEL_40MHZ | | |
325 | NVM_CHANNEL_80MHZ | | |
326 | NVM_CHANNEL_160MHZ); | |
327 | } | |
328 | ||
4896d764 GG |
329 | if (ch_flags & NVM_CHANNEL_160MHZ) |
330 | data->vht160_supported = true; | |
331 | ||
770ceda6 | 332 | if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) { |
a76f3bfe EP |
333 | /* |
334 | * Channels might become valid later if lar is | |
335 | * supported, hence we still want to add them to | |
336 | * the list of supported channels to cfg80211. | |
337 | */ | |
d8c73e45 JB |
338 | iwl_nvm_print_channel_flags(dev, IWL_DL_EEPROM, |
339 | nvm_chan[ch_idx], ch_flags); | |
b1e1adfa JB |
340 | continue; |
341 | } | |
342 | ||
343 | channel = &data->channels[n_channels]; | |
344 | n_channels++; | |
345 | ||
77db0a3c | 346 | channel->hw_value = nvm_chan[ch_idx]; |
01a9c948 LC |
347 | channel->band = is_5ghz ? |
348 | NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; | |
b1e1adfa JB |
349 | channel->center_freq = |
350 | ieee80211_channel_to_frequency( | |
351 | channel->hw_value, channel->band); | |
352 | ||
b1e1adfa JB |
353 | /* Initialize regulatory-based run-time data */ |
354 | ||
88f2fd73 MG |
355 | /* |
356 | * Default value - highest tx power value. max_power | |
357 | * is not used in mvm, and is used for backwards compatibility | |
358 | */ | |
22d059a5 | 359 | channel->max_power = IWL_DEFAULT_MAX_TX_POWER; |
770ceda6 AN |
360 | |
361 | /* don't put limitations in case we're using LAR */ | |
362 | if (!lar_supported) | |
363 | channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], | |
364 | ch_idx, is_5ghz, | |
b281c93d | 365 | ch_flags, cfg); |
770ceda6 AN |
366 | else |
367 | channel->flags = 0; | |
368 | ||
d8c73e45 JB |
369 | iwl_nvm_print_channel_flags(dev, IWL_DL_EEPROM, |
370 | channel->hw_value, ch_flags); | |
371 | IWL_DEBUG_EEPROM(dev, "Ch. %d: %ddBm\n", | |
372 | channel->hw_value, channel->max_power); | |
b1e1adfa JB |
373 | } |
374 | ||
375 | return n_channels; | |
376 | } | |
377 | ||
33158fef EL |
378 | static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, |
379 | struct iwl_nvm_data *data, | |
6ca89f1f JB |
380 | struct ieee80211_sta_vht_cap *vht_cap, |
381 | u8 tx_chains, u8 rx_chains) | |
33158fef | 382 | { |
6ca89f1f JB |
383 | int num_rx_ants = num_of_ant(rx_chains); |
384 | int num_tx_ants = num_of_ant(tx_chains); | |
c064ddf3 EH |
385 | unsigned int max_ampdu_exponent = (cfg->max_vht_ampdu_exponent ?: |
386 | IEEE80211_VHT_MAX_AMPDU_1024K); | |
48e6de61 | 387 | |
33158fef EL |
388 | vht_cap->vht_supported = true; |
389 | ||
390 | vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | | |
391 | IEEE80211_VHT_CAP_RXSTBC_1 | | |
392 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | | |
e36b766d | 393 | 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | |
c064ddf3 EH |
394 | max_ampdu_exponent << |
395 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; | |
33158fef | 396 | |
4896d764 | 397 | if (data->vht160_supported) |
fbbd4859 GG |
398 | vht_cap->cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | |
399 | IEEE80211_VHT_CAP_SHORT_GI_160; | |
4896d764 | 400 | |
e48c947f SS |
401 | if (cfg->vht_mu_mimo_supported) |
402 | vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; | |
403 | ||
a3576ff2 ES |
404 | if (cfg->ht_params->ldpc) |
405 | vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; | |
406 | ||
5f0d98f2 EG |
407 | if (data->sku_cap_mimo_disabled) { |
408 | num_rx_ants = 1; | |
409 | num_tx_ants = 1; | |
410 | } | |
411 | ||
6ca89f1f | 412 | if (num_tx_ants > 1) |
5f7a6f9b | 413 | vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; |
6ca89f1f JB |
414 | else |
415 | vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; | |
5f7a6f9b | 416 | |
6c4fbcbc | 417 | switch (iwlwifi_mod_params.amsdu_size) { |
4bdd4dfe EG |
418 | case IWL_AMSDU_DEF: |
419 | if (cfg->mq_rx_supported) | |
420 | vht_cap->cap |= | |
421 | IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; | |
422 | else | |
423 | vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; | |
424 | break; | |
6c4fbcbc EG |
425 | case IWL_AMSDU_4K: |
426 | vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; | |
427 | break; | |
428 | case IWL_AMSDU_8K: | |
33158fef | 429 | vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; |
6c4fbcbc EG |
430 | break; |
431 | case IWL_AMSDU_12K: | |
432 | vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; | |
433 | break; | |
434 | default: | |
435 | break; | |
436 | } | |
33158fef EL |
437 | |
438 | vht_cap->vht_mcs.rx_mcs_map = | |
439 | cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | | |
440 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | | |
441 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | | |
442 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | | |
443 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | | |
444 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | | |
445 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | | |
446 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); | |
447 | ||
6ca89f1f JB |
448 | if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) { |
449 | vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; | |
33158fef EL |
450 | /* this works because NOT_SUPPORTED == 3 */ |
451 | vht_cap->vht_mcs.rx_mcs_map |= | |
452 | cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); | |
453 | } | |
454 | ||
455 | vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; | |
456 | } | |
457 | ||
29108495 SS |
458 | void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, |
459 | struct iwl_nvm_data *data, const __le16 *nvm_ch_flags, | |
01a9c948 LC |
460 | u8 tx_chains, u8 rx_chains, bool lar_supported, |
461 | bool no_wide_in_5ghz) | |
b1e1adfa | 462 | { |
77db0a3c | 463 | int n_channels; |
b1e1adfa JB |
464 | int n_used = 0; |
465 | struct ieee80211_supported_band *sband; | |
466 | ||
29108495 | 467 | n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags, |
01a9c948 | 468 | lar_supported, no_wide_in_5ghz); |
57fbcce3 JB |
469 | sband = &data->bands[NL80211_BAND_2GHZ]; |
470 | sband->band = NL80211_BAND_2GHZ; | |
b1e1adfa JB |
471 | sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; |
472 | sband->n_bitrates = N_RATES_24; | |
473 | n_used += iwl_init_sband_channels(data, sband, n_channels, | |
57fbcce3 JB |
474 | NL80211_BAND_2GHZ); |
475 | iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, NL80211_BAND_2GHZ, | |
9ce4fa72 | 476 | tx_chains, rx_chains); |
b1e1adfa | 477 | |
57fbcce3 JB |
478 | sband = &data->bands[NL80211_BAND_5GHZ]; |
479 | sband->band = NL80211_BAND_5GHZ; | |
b1e1adfa JB |
480 | sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; |
481 | sband->n_bitrates = N_RATES_52; | |
482 | n_used += iwl_init_sband_channels(data, sband, n_channels, | |
57fbcce3 JB |
483 | NL80211_BAND_5GHZ); |
484 | iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, NL80211_BAND_5GHZ, | |
9ce4fa72 | 485 | tx_chains, rx_chains); |
0d0985ad | 486 | if (data->sku_cap_11ac_enable && !iwlwifi_mod_params.disable_11ac) |
6ca89f1f JB |
487 | iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, |
488 | tx_chains, rx_chains); | |
b1e1adfa JB |
489 | |
490 | if (n_channels != n_used) | |
491 | IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", | |
492 | n_used, n_channels); | |
493 | } | |
29108495 | 494 | IWL_EXPORT_SYMBOL(iwl_init_sbands); |
b1e1adfa | 495 | |
5dd9c68a EG |
496 | static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, |
497 | const __le16 *phy_sku) | |
77db0a3c | 498 | { |
44fd09da | 499 | if (cfg->nvm_type != IWL_NVM_EXT) |
77db0a3c | 500 | return le16_to_cpup(nvm_sw + SKU); |
ce500071 | 501 | |
5dd9c68a | 502 | return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000)); |
77db0a3c EH |
503 | } |
504 | ||
5dd9c68a | 505 | static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw) |
77db0a3c | 506 | { |
44fd09da | 507 | if (cfg->nvm_type != IWL_NVM_EXT) |
77db0a3c EH |
508 | return le16_to_cpup(nvm_sw + NVM_VERSION); |
509 | else | |
510 | return le32_to_cpup((__le32 *)(nvm_sw + | |
7042678d | 511 | NVM_VERSION_EXT_NVM)); |
77db0a3c EH |
512 | } |
513 | ||
5dd9c68a EG |
514 | static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw, |
515 | const __le16 *phy_sku) | |
77db0a3c | 516 | { |
44fd09da | 517 | if (cfg->nvm_type != IWL_NVM_EXT) |
77db0a3c | 518 | return le16_to_cpup(nvm_sw + RADIO_CFG); |
ce500071 | 519 | |
7042678d | 520 | return le32_to_cpup((__le32 *)(phy_sku + RADIO_CFG_FAMILY_EXT_NVM)); |
ce500071 | 521 | |
77db0a3c EH |
522 | } |
523 | ||
5dd9c68a | 524 | static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw) |
77db0a3c | 525 | { |
ce500071 EH |
526 | int n_hw_addr; |
527 | ||
44fd09da | 528 | if (cfg->nvm_type != IWL_NVM_EXT) |
77db0a3c | 529 | return le16_to_cpup(nvm_sw + N_HW_ADDRS); |
ce500071 | 530 | |
5dd9c68a | 531 | n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)); |
ce500071 EH |
532 | |
533 | return n_hw_addr & N_HW_ADDR_MASK; | |
77db0a3c EH |
534 | } |
535 | ||
536 | static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, | |
537 | struct iwl_nvm_data *data, | |
538 | u32 radio_cfg) | |
539 | { | |
44fd09da | 540 | if (cfg->nvm_type != IWL_NVM_EXT) { |
77db0a3c EH |
541 | data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg); |
542 | data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg); | |
543 | data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg); | |
544 | data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg); | |
77db0a3c EH |
545 | return; |
546 | } | |
547 | ||
548 | /* set the radio configuration for family 8000 */ | |
7042678d SS |
549 | data->radio_cfg_type = EXT_NVM_RF_CFG_TYPE_MSK(radio_cfg); |
550 | data->radio_cfg_step = EXT_NVM_RF_CFG_STEP_MSK(radio_cfg); | |
551 | data->radio_cfg_dash = EXT_NVM_RF_CFG_DASH_MSK(radio_cfg); | |
552 | data->radio_cfg_pnum = EXT_NVM_RF_CFG_FLAVOR_MSK(radio_cfg); | |
553 | data->valid_tx_ant = EXT_NVM_RF_CFG_TX_ANT_MSK(radio_cfg); | |
554 | data->valid_rx_ant = EXT_NVM_RF_CFG_RX_ANT_MSK(radio_cfg); | |
77db0a3c EH |
555 | } |
556 | ||
17c867bf SS |
557 | static void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest) |
558 | { | |
559 | const u8 *hw_addr; | |
560 | ||
561 | hw_addr = (const u8 *)&mac_addr0; | |
562 | dest[0] = hw_addr[3]; | |
563 | dest[1] = hw_addr[2]; | |
564 | dest[2] = hw_addr[1]; | |
565 | dest[3] = hw_addr[0]; | |
566 | ||
567 | hw_addr = (const u8 *)&mac_addr1; | |
568 | dest[4] = hw_addr[1]; | |
569 | dest[5] = hw_addr[0]; | |
570 | } | |
571 | ||
29108495 SS |
572 | void iwl_set_hw_address_from_csr(struct iwl_trans *trans, |
573 | struct iwl_nvm_data *data) | |
17c867bf SS |
574 | { |
575 | __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_STRAP)); | |
576 | __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_STRAP)); | |
577 | ||
a6c934b3 HD |
578 | iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); |
579 | /* | |
580 | * If the OEM fused a valid address, use it instead of the one in the | |
581 | * OTP | |
582 | */ | |
583 | if (is_valid_ether_addr(data->hw_addr)) | |
584 | return; | |
585 | ||
586 | mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP)); | |
587 | mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP)); | |
17c867bf SS |
588 | |
589 | iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); | |
590 | } | |
29108495 | 591 | IWL_EXPORT_SYMBOL(iwl_set_hw_address_from_csr); |
17c867bf | 592 | |
afd5b170 | 593 | static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, |
6a68a39f | 594 | const struct iwl_cfg *cfg, |
9f32e017 EH |
595 | struct iwl_nvm_data *data, |
596 | const __le16 *mac_override, | |
8fe34b06 | 597 | const __be16 *nvm_hw) |
9f32e017 EH |
598 | { |
599 | const u8 *hw_addr; | |
600 | ||
601 | if (mac_override) { | |
18f84673 LK |
602 | static const u8 reserved_mac[] = { |
603 | 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 | |
604 | }; | |
605 | ||
9f32e017 | 606 | hw_addr = (const u8 *)(mac_override + |
7042678d | 607 | MAC_ADDRESS_OVERRIDE_EXT_NVM); |
9f32e017 | 608 | |
be88a1ad LK |
609 | /* |
610 | * Store the MAC address from MAO section. | |
611 | * No byte swapping is required in MAO section | |
612 | */ | |
613 | memcpy(data->hw_addr, hw_addr, ETH_ALEN); | |
9f32e017 | 614 | |
18f84673 LK |
615 | /* |
616 | * Force the use of the OTP MAC address in case of reserved MAC | |
617 | * address in the NVM, or if address is given but invalid. | |
618 | */ | |
619 | if (is_valid_ether_addr(data->hw_addr) && | |
620 | memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0) | |
9f32e017 | 621 | return; |
6a68a39f | 622 | |
afd5b170 SS |
623 | IWL_ERR(trans, |
624 | "mac address from nvm override section is not valid\n"); | |
9f32e017 EH |
625 | } |
626 | ||
6a68a39f | 627 | if (nvm_hw) { |
afd5b170 SS |
628 | /* read the mac address from WFMP registers */ |
629 | __le32 mac_addr0 = cpu_to_le32(iwl_trans_read_prph(trans, | |
630 | WFMP_MAC_ADDR_0)); | |
631 | __le32 mac_addr1 = cpu_to_le32(iwl_trans_read_prph(trans, | |
632 | WFMP_MAC_ADDR_1)); | |
17c867bf SS |
633 | |
634 | iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); | |
1e0b393a | 635 | |
6a68a39f EH |
636 | return; |
637 | } | |
9f32e017 | 638 | |
afd5b170 SS |
639 | IWL_ERR(trans, "mac address is not found\n"); |
640 | } | |
641 | ||
17c867bf SS |
642 | static int iwl_set_hw_address(struct iwl_trans *trans, |
643 | const struct iwl_cfg *cfg, | |
8fe34b06 | 644 | struct iwl_nvm_data *data, const __be16 *nvm_hw, |
17c867bf | 645 | const __le16 *mac_override) |
afd5b170 | 646 | { |
17c867bf SS |
647 | if (cfg->mac_addr_from_csr) { |
648 | iwl_set_hw_address_from_csr(trans, data); | |
44fd09da | 649 | } else if (cfg->nvm_type != IWL_NVM_EXT) { |
afd5b170 SS |
650 | const u8 *hw_addr = (const u8 *)(nvm_hw + HW_ADDR); |
651 | ||
652 | /* The byte order is little endian 16 bit, meaning 214365 */ | |
653 | data->hw_addr[0] = hw_addr[1]; | |
654 | data->hw_addr[1] = hw_addr[0]; | |
655 | data->hw_addr[2] = hw_addr[3]; | |
656 | data->hw_addr[3] = hw_addr[2]; | |
657 | data->hw_addr[4] = hw_addr[5]; | |
658 | data->hw_addr[5] = hw_addr[4]; | |
659 | } else { | |
660 | iwl_set_hw_address_family_8000(trans, cfg, data, | |
661 | mac_override, nvm_hw); | |
662 | } | |
17c867bf SS |
663 | |
664 | if (!is_valid_ether_addr(data->hw_addr)) { | |
665 | IWL_ERR(trans, "no valid mac address was found\n"); | |
666 | return -EINVAL; | |
667 | } | |
668 | ||
4409e72b LC |
669 | IWL_INFO(trans, "base HW address: %pM\n", data->hw_addr); |
670 | ||
17c867bf | 671 | return 0; |
9f32e017 EH |
672 | } |
673 | ||
01a9c948 LC |
674 | static bool |
675 | iwl_nvm_no_wide_in_5ghz(struct device *dev, const struct iwl_cfg *cfg, | |
8fe34b06 | 676 | const __be16 *nvm_hw) |
01a9c948 LC |
677 | { |
678 | /* | |
679 | * Workaround a bug in Indonesia SKUs where the regulatory in | |
680 | * some 7000-family OTPs erroneously allow wide channels in | |
681 | * 5GHz. To check for Indonesia, we take the SKU value from | |
682 | * bits 1-4 in the subsystem ID and check if it is either 5 or | |
683 | * 9. In those cases, we need to force-disable wide channels | |
684 | * in 5GHz otherwise the FW will throw a sysassert when we try | |
685 | * to use them. | |
686 | */ | |
687 | if (cfg->device_family == IWL_DEVICE_FAMILY_7000) { | |
688 | /* | |
689 | * Unlike the other sections in the NVM, the hw | |
690 | * section uses big-endian. | |
691 | */ | |
8fe34b06 | 692 | u16 subsystem_id = be16_to_cpup(nvm_hw + SUBSYSTEM_ID); |
01a9c948 LC |
693 | u8 sku = (subsystem_id & 0x1e) >> 1; |
694 | ||
695 | if (sku == 5 || sku == 9) { | |
696 | IWL_DEBUG_EEPROM(dev, | |
697 | "disabling wide channels in 5GHz (0x%0x %d)\n", | |
698 | subsystem_id, sku); | |
699 | return true; | |
700 | } | |
701 | } | |
702 | ||
703 | return false; | |
704 | } | |
705 | ||
b1e1adfa | 706 | struct iwl_nvm_data * |
afd5b170 | 707 | iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, |
8fe34b06 | 708 | const __be16 *nvm_hw, const __le16 *nvm_sw, |
77db0a3c | 709 | const __le16 *nvm_calib, const __le16 *regulatory, |
ce500071 | 710 | const __le16 *mac_override, const __le16 *phy_sku, |
afd5b170 | 711 | u8 tx_chains, u8 rx_chains, bool lar_fw_supported) |
b1e1adfa | 712 | { |
afd5b170 | 713 | struct device *dev = trans->dev; |
b1e1adfa | 714 | struct iwl_nvm_data *data; |
afd5b170 | 715 | bool lar_enabled; |
01a9c948 | 716 | bool no_wide_in_5ghz = iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw); |
afd5b170 | 717 | u32 sku, radio_cfg; |
d0d15197 | 718 | u16 lar_config; |
afd5b170 | 719 | const __le16 *ch_section; |
77db0a3c | 720 | |
44fd09da | 721 | if (cfg->nvm_type != IWL_NVM_EXT) |
77db0a3c EH |
722 | data = kzalloc(sizeof(*data) + |
723 | sizeof(struct ieee80211_channel) * | |
77e30e10 | 724 | IWL_NVM_NUM_CHANNELS, |
77db0a3c EH |
725 | GFP_KERNEL); |
726 | else | |
727 | data = kzalloc(sizeof(*data) + | |
728 | sizeof(struct ieee80211_channel) * | |
77e30e10 | 729 | IWL_NVM_NUM_CHANNELS_EXT, |
77db0a3c | 730 | GFP_KERNEL); |
b1e1adfa JB |
731 | if (!data) |
732 | return NULL; | |
733 | ||
77db0a3c | 734 | data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); |
b1e1adfa | 735 | |
5dd9c68a | 736 | radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku); |
77db0a3c | 737 | iwl_set_radio_cfg(cfg, data, radio_cfg); |
a0544272 MH |
738 | if (data->valid_tx_ant) |
739 | tx_chains &= data->valid_tx_ant; | |
740 | if (data->valid_rx_ant) | |
741 | rx_chains &= data->valid_rx_ant; | |
b1e1adfa | 742 | |
5dd9c68a | 743 | sku = iwl_get_sku(cfg, nvm_sw, phy_sku); |
b1e1adfa JB |
744 | data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; |
745 | data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; | |
746 | data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; | |
747 | if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) | |
748 | data->sku_cap_11n_enable = false; | |
2926f958 EP |
749 | data->sku_cap_11ac_enable = data->sku_cap_11n_enable && |
750 | (sku & NVM_SKU_CAP_11AC_ENABLE); | |
5f0d98f2 | 751 | data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE; |
b1e1adfa | 752 | |
5dd9c68a | 753 | data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); |
b1e1adfa | 754 | |
44fd09da | 755 | if (cfg->nvm_type != IWL_NVM_EXT) { |
77db0a3c EH |
756 | /* Checking for required sections */ |
757 | if (!nvm_calib) { | |
afd5b170 SS |
758 | IWL_ERR(trans, |
759 | "Can't parse empty Calib NVM sections\n"); | |
1270c416 | 760 | kfree(data); |
77db0a3c EH |
761 | return NULL; |
762 | } | |
44fd09da CRI |
763 | |
764 | ch_section = cfg->nvm_type == IWL_NVM_SDP ? | |
765 | ®ulatory[NVM_CHANNELS_SDP] : | |
766 | &nvm_sw[NVM_CHANNELS]; | |
767 | ||
77db0a3c EH |
768 | /* in family 8000 Xtal calibration values moved to OTP */ |
769 | data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); | |
770 | data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); | |
afd5b170 | 771 | lar_enabled = true; |
77db0a3c | 772 | } else { |
f5528631 | 773 | u16 lar_offset = data->nvm_version < 0xE39 ? |
7042678d SS |
774 | NVM_LAR_OFFSET_OLD : |
775 | NVM_LAR_OFFSET; | |
f5528631 AN |
776 | |
777 | lar_config = le16_to_cpup(regulatory + lar_offset); | |
d0d15197 | 778 | data->lar_enabled = !!(lar_config & |
7042678d | 779 | NVM_LAR_ENABLED); |
afd5b170 | 780 | lar_enabled = data->lar_enabled; |
7042678d | 781 | ch_section = ®ulatory[NVM_CHANNELS_EXTENDED]; |
77db0a3c | 782 | } |
b1e1adfa | 783 | |
17c867bf SS |
784 | /* If no valid mac address was found - bail out */ |
785 | if (iwl_set_hw_address(trans, cfg, data, nvm_hw, mac_override)) { | |
786 | kfree(data); | |
787 | return NULL; | |
788 | } | |
789 | ||
afd5b170 | 790 | iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains, |
01a9c948 | 791 | lar_fw_supported && lar_enabled, no_wide_in_5ghz); |
33b2f684 | 792 | data->calib_version = 255; |
b1e1adfa JB |
793 | |
794 | return data; | |
795 | } | |
48e29340 | 796 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); |
af45a900 AN |
797 | |
798 | static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, | |
b281c93d MG |
799 | int ch_idx, u16 nvm_flags, |
800 | const struct iwl_cfg *cfg) | |
af45a900 AN |
801 | { |
802 | u32 flags = NL80211_RRF_NO_HT40; | |
b281c93d MG |
803 | u32 last_5ghz_ht = LAST_5GHZ_HT; |
804 | ||
44fd09da | 805 | if (cfg->nvm_type == IWL_NVM_EXT) |
b281c93d | 806 | last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; |
af45a900 AN |
807 | |
808 | if (ch_idx < NUM_2GHZ_CHANNELS && | |
809 | (nvm_flags & NVM_CHANNEL_40MHZ)) { | |
810 | if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) | |
811 | flags &= ~NL80211_RRF_NO_HT40PLUS; | |
812 | if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) | |
813 | flags &= ~NL80211_RRF_NO_HT40MINUS; | |
b281c93d | 814 | } else if (nvm_chan[ch_idx] <= last_5ghz_ht && |
af45a900 AN |
815 | (nvm_flags & NVM_CHANNEL_40MHZ)) { |
816 | if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) | |
817 | flags &= ~NL80211_RRF_NO_HT40PLUS; | |
818 | else | |
819 | flags &= ~NL80211_RRF_NO_HT40MINUS; | |
820 | } | |
821 | ||
822 | if (!(nvm_flags & NVM_CHANNEL_80MHZ)) | |
823 | flags |= NL80211_RRF_NO_80MHZ; | |
824 | if (!(nvm_flags & NVM_CHANNEL_160MHZ)) | |
825 | flags |= NL80211_RRF_NO_160MHZ; | |
826 | ||
af45a900 AN |
827 | if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) |
828 | flags |= NL80211_RRF_NO_IR; | |
829 | ||
830 | if (nvm_flags & NVM_CHANNEL_RADAR) | |
831 | flags |= NL80211_RRF_DFS; | |
832 | ||
833 | if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) | |
834 | flags |= NL80211_RRF_NO_OUTDOOR; | |
835 | ||
836 | /* Set the GO concurrent flag only in case that NO_IR is set. | |
837 | * Otherwise it is meaningless | |
838 | */ | |
839 | if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && | |
840 | (flags & NL80211_RRF_NO_IR)) | |
841 | flags |= NL80211_RRF_GO_CONCURRENT; | |
842 | ||
843 | return flags; | |
844 | } | |
845 | ||
77e30e10 HD |
846 | struct regdb_ptrs { |
847 | struct ieee80211_wmm_rule *rule; | |
848 | u32 token; | |
849 | }; | |
850 | ||
af45a900 | 851 | struct ieee80211_regdomain * |
162ee3c9 | 852 | iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, |
77e30e10 HD |
853 | int num_of_ch, __le32 *channels, u16 fw_mcc, |
854 | u16 geo_info) | |
af45a900 AN |
855 | { |
856 | int ch_idx; | |
92b0f7b2 EG |
857 | u16 ch_flags; |
858 | u32 reg_rule_flags, prev_reg_rule_flags = 0; | |
44fd09da | 859 | const u8 *nvm_chan = cfg->nvm_type == IWL_NVM_EXT ? |
7042678d | 860 | iwl_ext_nvm_channels : iwl_nvm_channels; |
77e30e10 HD |
861 | struct ieee80211_regdomain *regd, *copy_rd; |
862 | int size_of_regd, regd_to_copy, wmms_to_copy; | |
863 | int size_of_wmms = 0; | |
af45a900 | 864 | struct ieee80211_reg_rule *rule; |
77e30e10 HD |
865 | struct ieee80211_wmm_rule *wmm_rule, *d_wmm, *s_wmm; |
866 | struct regdb_ptrs *regdb_ptrs; | |
57fbcce3 | 867 | enum nl80211_band band; |
af45a900 | 868 | int center_freq, prev_center_freq = 0; |
77e30e10 HD |
869 | int valid_rules = 0, n_wmms = 0; |
870 | int i; | |
af45a900 | 871 | bool new_rule; |
44fd09da | 872 | int max_num_ch = cfg->nvm_type == IWL_NVM_EXT ? |
77e30e10 | 873 | IWL_NVM_NUM_CHANNELS_EXT : IWL_NVM_NUM_CHANNELS; |
af45a900 AN |
874 | |
875 | if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES)) | |
876 | return ERR_PTR(-EINVAL); | |
877 | ||
4557eaba AN |
878 | if (WARN_ON(num_of_ch > max_num_ch)) |
879 | num_of_ch = max_num_ch; | |
880 | ||
af45a900 AN |
881 | IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n", |
882 | num_of_ch); | |
883 | ||
884 | /* build a regdomain rule for every valid channel */ | |
885 | size_of_regd = | |
886 | sizeof(struct ieee80211_regdomain) + | |
887 | num_of_ch * sizeof(struct ieee80211_reg_rule); | |
888 | ||
77e30e10 HD |
889 | if (geo_info & GEO_WMM_ETSI_5GHZ_INFO) |
890 | size_of_wmms = | |
891 | num_of_ch * sizeof(struct ieee80211_wmm_rule); | |
892 | ||
893 | regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL); | |
af45a900 AN |
894 | if (!regd) |
895 | return ERR_PTR(-ENOMEM); | |
896 | ||
77e30e10 HD |
897 | regdb_ptrs = kcalloc(num_of_ch, sizeof(*regdb_ptrs), GFP_KERNEL); |
898 | if (!regdb_ptrs) { | |
899 | copy_rd = ERR_PTR(-ENOMEM); | |
900 | goto out; | |
901 | } | |
902 | ||
903 | /* set alpha2 from FW. */ | |
904 | regd->alpha2[0] = fw_mcc >> 8; | |
905 | regd->alpha2[1] = fw_mcc & 0xff; | |
906 | ||
907 | wmm_rule = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd); | |
908 | ||
af45a900 AN |
909 | for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { |
910 | ch_flags = (u16)__le32_to_cpup(channels + ch_idx); | |
911 | band = (ch_idx < NUM_2GHZ_CHANNELS) ? | |
57fbcce3 | 912 | NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; |
af45a900 AN |
913 | center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx], |
914 | band); | |
915 | new_rule = false; | |
916 | ||
917 | if (!(ch_flags & NVM_CHANNEL_VALID)) { | |
d8c73e45 JB |
918 | iwl_nvm_print_channel_flags(dev, IWL_DL_LAR, |
919 | nvm_chan[ch_idx], ch_flags); | |
af45a900 AN |
920 | continue; |
921 | } | |
922 | ||
92b0f7b2 EG |
923 | reg_rule_flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, |
924 | ch_flags, cfg); | |
925 | ||
af45a900 | 926 | /* we can't continue the same rule */ |
92b0f7b2 | 927 | if (ch_idx == 0 || prev_reg_rule_flags != reg_rule_flags || |
af45a900 AN |
928 | center_freq - prev_center_freq > 20) { |
929 | valid_rules++; | |
930 | new_rule = true; | |
931 | } | |
932 | ||
933 | rule = ®d->reg_rules[valid_rules - 1]; | |
934 | ||
935 | if (new_rule) | |
936 | rule->freq_range.start_freq_khz = | |
937 | MHZ_TO_KHZ(center_freq - 10); | |
938 | ||
939 | rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10); | |
940 | ||
941 | /* this doesn't matter - not used by FW */ | |
942 | rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); | |
02a50495 EP |
943 | rule->power_rule.max_eirp = |
944 | DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER); | |
af45a900 | 945 | |
92b0f7b2 | 946 | rule->flags = reg_rule_flags; |
af45a900 AN |
947 | |
948 | /* rely on auto-calculation to merge BW of contiguous chans */ | |
949 | rule->flags |= NL80211_RRF_AUTO_BW; | |
950 | rule->freq_range.max_bandwidth_khz = 0; | |
951 | ||
af45a900 | 952 | prev_center_freq = center_freq; |
92b0f7b2 | 953 | prev_reg_rule_flags = reg_rule_flags; |
af45a900 | 954 | |
d8c73e45 JB |
955 | iwl_nvm_print_channel_flags(dev, IWL_DL_LAR, |
956 | nvm_chan[ch_idx], ch_flags); | |
77e30e10 HD |
957 | |
958 | if (!(geo_info & GEO_WMM_ETSI_5GHZ_INFO) || | |
959 | band == NL80211_BAND_2GHZ) | |
960 | continue; | |
961 | ||
962 | if (!reg_query_regdb_wmm(regd->alpha2, center_freq, | |
963 | ®db_ptrs[n_wmms].token, wmm_rule)) { | |
964 | /* Add only new rules */ | |
965 | for (i = 0; i < n_wmms; i++) { | |
966 | if (regdb_ptrs[i].token == | |
967 | regdb_ptrs[n_wmms].token) { | |
968 | rule->wmm_rule = regdb_ptrs[i].rule; | |
969 | break; | |
970 | } | |
971 | } | |
972 | if (i == n_wmms) { | |
973 | rule->wmm_rule = wmm_rule; | |
974 | regdb_ptrs[n_wmms++].rule = wmm_rule; | |
975 | wmm_rule++; | |
976 | } | |
977 | } | |
af45a900 AN |
978 | } |
979 | ||
980 | regd->n_reg_rules = valid_rules; | |
77e30e10 | 981 | regd->n_wmm_rules = n_wmms; |
af45a900 | 982 | |
77e30e10 HD |
983 | /* |
984 | * Narrow down regdom for unused regulatory rules to prevent hole | |
985 | * between reg rules to wmm rules. | |
986 | */ | |
987 | regd_to_copy = sizeof(struct ieee80211_regdomain) + | |
988 | valid_rules * sizeof(struct ieee80211_reg_rule); | |
989 | ||
990 | wmms_to_copy = sizeof(struct ieee80211_wmm_rule) * n_wmms; | |
991 | ||
992 | copy_rd = kzalloc(regd_to_copy + wmms_to_copy, GFP_KERNEL); | |
993 | if (!copy_rd) { | |
994 | copy_rd = ERR_PTR(-ENOMEM); | |
995 | goto out; | |
996 | } | |
997 | ||
998 | memcpy(copy_rd, regd, regd_to_copy); | |
999 | memcpy((u8 *)copy_rd + regd_to_copy, (u8 *)regd + size_of_regd, | |
1000 | wmms_to_copy); | |
1001 | ||
1002 | d_wmm = (struct ieee80211_wmm_rule *)((u8 *)copy_rd + regd_to_copy); | |
1003 | s_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd); | |
1004 | ||
1005 | for (i = 0; i < regd->n_reg_rules; i++) { | |
1006 | if (!regd->reg_rules[i].wmm_rule) | |
1007 | continue; | |
1008 | ||
1009 | copy_rd->reg_rules[i].wmm_rule = d_wmm + | |
1010 | (regd->reg_rules[i].wmm_rule - s_wmm) / | |
1011 | sizeof(struct ieee80211_wmm_rule); | |
1012 | } | |
af45a900 | 1013 | |
77e30e10 HD |
1014 | out: |
1015 | kfree(regdb_ptrs); | |
1016 | kfree(regd); | |
1017 | return copy_rd; | |
af45a900 AN |
1018 | } |
1019 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); |