blob: 6be30c69850619f81c2468febb3634fd5cf390fd [file] [log] [blame]
Johannes Bergb1e1adf2013-01-24 14:14:22 +01001/******************************************************************************
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 *
Emmanuel Grumbach51368bf2013-12-30 13:15:54 +02008 * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
Johannes Bergb1e1adf2013-01-24 14:14:22 +01009 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
Emmanuel Grumbach410dc5a2013-02-18 09:22:28 +020025 * in the file called COPYING.
Johannes Bergb1e1adf2013-01-24 14:14:22 +010026 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
Emmanuel Grumbach51368bf2013-12-30 13:15:54 +020033 * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
Johannes Bergb1e1adf2013-01-24 14:14:22 +010034 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *****************************************************************************/
62#include <linux/types.h>
63#include <linux/slab.h>
64#include <linux/export.h>
Johannes Berg48e29342013-03-01 00:13:33 +010065#include "iwl-drv.h"
Johannes Bergb1e1adf2013-01-24 14:14:22 +010066#include "iwl-modparams.h"
67#include "iwl-nvm-parse.h"
68
69/* NVM offsets (in words) definitions */
70enum wkp_nvm_offsets {
71 /* NVM HW-Section offset (in words) definitions */
72 HW_ADDR = 0x15,
73
Eran Harary77db0a32014-02-04 14:21:38 +020074 /* NVM SW-Section offset (in words) definitions */
Johannes Bergb1e1adf2013-01-24 14:14:22 +010075 NVM_SW_SECTION = 0x1C0,
76 NVM_VERSION = 0,
77 RADIO_CFG = 1,
78 SKU = 2,
79 N_HW_ADDRS = 3,
80 NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION,
81
Eran Harary77db0a32014-02-04 14:21:38 +020082 /* NVM calibration section offset (in words) definitions */
Johannes Bergb1e1adf2013-01-24 14:14:22 +010083 NVM_CALIB_SECTION = 0x2B8,
84 XTAL_CALIB = 0x316 - NVM_CALIB_SECTION
85};
86
Eran Harary77db0a32014-02-04 14:21:38 +020087enum family_8000_nvm_offsets {
88 /* NVM HW-Section offset (in words) definitions */
89 HW_ADDR0_FAMILY_8000 = 0x12,
90 HW_ADDR1_FAMILY_8000 = 0x16,
91 MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1,
92
93 /* NVM SW-Section offset (in words) definitions */
94 NVM_SW_SECTION_FAMILY_8000 = 0x1C0,
95 NVM_VERSION_FAMILY_8000 = 0,
96 RADIO_CFG_FAMILY_8000 = 2,
97 SKU_FAMILY_8000 = 4,
98 N_HW_ADDRS_FAMILY_8000 = 5,
99
100 /* NVM REGULATORY -Section offset (in words) definitions */
101 NVM_CHANNELS_FAMILY_8000 = 0,
102
103 /* NVM calibration section offset (in words) definitions */
104 NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8,
105 XTAL_CALIB_FAMILY_8000 = 0x316 - NVM_CALIB_SECTION_FAMILY_8000
106};
107
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100108/* SKU Capabilities (actual values from NVM definition) */
109enum nvm_sku_bits {
110 NVM_SKU_CAP_BAND_24GHZ = BIT(0),
111 NVM_SKU_CAP_BAND_52GHZ = BIT(1),
112 NVM_SKU_CAP_11N_ENABLE = BIT(2),
Johannes Bergbfc824b2013-05-06 16:06:51 +0200113 NVM_SKU_CAP_11AC_ENABLE = BIT(3),
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100114};
115
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100116/*
117 * These are the channel numbers in the order that they are stored in the NVM
118 */
119static const u8 iwl_nvm_channels[] = {
120 /* 2.4 GHz */
121 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
122 /* 5 GHz */
123 36, 40, 44 , 48, 52, 56, 60, 64,
124 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
125 149, 153, 157, 161, 165
126};
127
Eran Harary77db0a32014-02-04 14:21:38 +0200128static const u8 iwl_nvm_channels_family_8000[] = {
129 /* 2.4 GHz */
130 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
131 /* 5 GHz */
132 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
133 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
134 149, 153, 157, 161, 165, 169, 173, 177, 181
135};
136
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100137#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels)
Eran Harary77db0a32014-02-04 14:21:38 +0200138#define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000)
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100139#define NUM_2GHZ_CHANNELS 14
140#define FIRST_2GHZ_HT_MINUS 5
141#define LAST_2GHZ_HT_PLUS 9
142#define LAST_5GHZ_HT 161
143
Matti Gottlieb88f2fd72013-07-09 15:25:46 +0300144#define DEFAULT_MAX_TX_POWER 16
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100145
146/* rate data (static) */
147static struct ieee80211_rate iwl_cfg80211_rates[] = {
148 { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, },
149 { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1,
150 .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
151 { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2,
152 .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
153 { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3,
154 .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
155 { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, },
156 { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, },
157 { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, },
158 { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, },
159 { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, },
160 { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, },
161 { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, },
162 { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, },
163};
164#define RATES_24_OFFS 0
165#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates)
166#define RATES_52_OFFS 4
167#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS)
168
169/**
170 * enum iwl_nvm_channel_flags - channel flags in NVM
171 * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
172 * @NVM_CHANNEL_IBSS: usable as an IBSS channel
173 * @NVM_CHANNEL_ACTIVE: active scanning allowed
174 * @NVM_CHANNEL_RADAR: radar detection required
175 * @NVM_CHANNEL_DFS: dynamic freq selection candidate
176 * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
177 * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200178 * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
179 * @NVM_CHANNEL_160MHZ: 160 MHz channel okay (?)
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100180 */
181enum iwl_nvm_channel_flags {
182 NVM_CHANNEL_VALID = BIT(0),
183 NVM_CHANNEL_IBSS = BIT(1),
184 NVM_CHANNEL_ACTIVE = BIT(3),
185 NVM_CHANNEL_RADAR = BIT(4),
186 NVM_CHANNEL_DFS = BIT(7),
187 NVM_CHANNEL_WIDE = BIT(8),
188 NVM_CHANNEL_40MHZ = BIT(9),
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200189 NVM_CHANNEL_80MHZ = BIT(10),
190 NVM_CHANNEL_160MHZ = BIT(11),
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100191};
192
193#define CHECK_AND_PRINT_I(x) \
194 ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "")
195
196static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
197 struct iwl_nvm_data *data,
198 const __le16 * const nvm_ch_flags)
199{
200 int ch_idx;
201 int n_channels = 0;
202 struct ieee80211_channel *channel;
203 u16 ch_flags;
204 bool is_5ghz;
Eran Harary77db0a32014-02-04 14:21:38 +0200205 int num_of_ch;
206 const u8 *nvm_chan;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100207
Eran Harary77db0a32014-02-04 14:21:38 +0200208 if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
209 num_of_ch = IWL_NUM_CHANNELS;
210 nvm_chan = &iwl_nvm_channels[0];
211 } else {
212 num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000;
213 nvm_chan = &iwl_nvm_channels_family_8000[0];
214 }
215
216 for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100217 ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
Emmanuel Grumbachc5128652013-12-05 22:42:55 +0200218
219 if (ch_idx >= NUM_2GHZ_CHANNELS &&
220 !data->sku_cap_band_52GHz_enable)
221 ch_flags &= ~NVM_CHANNEL_VALID;
222
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100223 if (!(ch_flags & NVM_CHANNEL_VALID)) {
224 IWL_DEBUG_EEPROM(dev,
225 "Ch. %d Flags %x [%sGHz] - No traffic\n",
Eran Harary77db0a32014-02-04 14:21:38 +0200226 nvm_chan[ch_idx],
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100227 ch_flags,
228 (ch_idx >= NUM_2GHZ_CHANNELS) ?
229 "5.2" : "2.4");
230 continue;
231 }
232
233 channel = &data->channels[n_channels];
234 n_channels++;
235
Eran Harary77db0a32014-02-04 14:21:38 +0200236 channel->hw_value = nvm_chan[ch_idx];
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100237 channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ?
238 IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
239 channel->center_freq =
240 ieee80211_channel_to_frequency(
241 channel->hw_value, channel->band);
242
243 /* TODO: Need to be dependent to the NVM */
244 channel->flags = IEEE80211_CHAN_NO_HT40;
245 if (ch_idx < NUM_2GHZ_CHANNELS &&
246 (ch_flags & NVM_CHANNEL_40MHZ)) {
Eran Harary77db0a32014-02-04 14:21:38 +0200247 if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100248 channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
Eran Harary77db0a32014-02-04 14:21:38 +0200249 if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100250 channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
Eran Harary77db0a32014-02-04 14:21:38 +0200251 } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT &&
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100252 (ch_flags & NVM_CHANNEL_40MHZ)) {
253 if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
254 channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
255 else
256 channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
257 }
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200258 if (!(ch_flags & NVM_CHANNEL_80MHZ))
259 channel->flags |= IEEE80211_CHAN_NO_80MHZ;
260 if (!(ch_flags & NVM_CHANNEL_160MHZ))
261 channel->flags |= IEEE80211_CHAN_NO_160MHZ;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100262
263 if (!(ch_flags & NVM_CHANNEL_IBSS))
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200264 channel->flags |= IEEE80211_CHAN_NO_IR;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100265
266 if (!(ch_flags & NVM_CHANNEL_ACTIVE))
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200267 channel->flags |= IEEE80211_CHAN_NO_IR;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100268
269 if (ch_flags & NVM_CHANNEL_RADAR)
270 channel->flags |= IEEE80211_CHAN_RADAR;
271
272 /* Initialize regulatory-based run-time data */
273
Matti Gottlieb88f2fd72013-07-09 15:25:46 +0300274 /*
275 * Default value - highest tx power value. max_power
276 * is not used in mvm, and is used for backwards compatibility
277 */
278 channel->max_power = DEFAULT_MAX_TX_POWER;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100279 is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
280 IWL_DEBUG_EEPROM(dev,
281 "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
282 channel->hw_value,
283 is_5ghz ? "5.2" : "2.4",
284 CHECK_AND_PRINT_I(VALID),
285 CHECK_AND_PRINT_I(IBSS),
286 CHECK_AND_PRINT_I(ACTIVE),
287 CHECK_AND_PRINT_I(RADAR),
288 CHECK_AND_PRINT_I(WIDE),
289 CHECK_AND_PRINT_I(DFS),
290 ch_flags,
291 channel->max_power,
292 ((ch_flags & NVM_CHANNEL_IBSS) &&
293 !(ch_flags & NVM_CHANNEL_RADAR))
294 ? "" : "not ");
295 }
296
297 return n_channels;
298}
299
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200300static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
301 struct iwl_nvm_data *data,
Johannes Berg6ca89f12014-02-12 21:56:26 +0100302 struct ieee80211_sta_vht_cap *vht_cap,
303 u8 tx_chains, u8 rx_chains)
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200304{
Johannes Berg6ca89f12014-02-12 21:56:26 +0100305 int num_rx_ants = num_of_ant(rx_chains);
306 int num_tx_ants = num_of_ant(tx_chains);
Eyal Shapira48e6de62013-11-03 10:04:08 +0200307
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200308 vht_cap->vht_supported = true;
309
310 vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 |
311 IEEE80211_VHT_CAP_RXSTBC_1 |
312 IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
Eyal Shapirae36b7662014-01-06 20:16:48 +0200313 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200314 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
315
Johannes Berg6ca89f12014-02-12 21:56:26 +0100316 if (num_tx_ants > 1)
Eyal Shapira5f7a6f92013-11-12 22:40:40 +0200317 vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
Johannes Berg6ca89f12014-02-12 21:56:26 +0100318 else
319 vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
Eyal Shapira5f7a6f92013-11-12 22:40:40 +0200320
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200321 if (iwlwifi_mod_params.amsdu_size_8K)
322 vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
323
324 vht_cap->vht_mcs.rx_mcs_map =
325 cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
326 IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
327 IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 |
328 IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
329 IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
330 IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
331 IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
332 IEEE80211_VHT_MCS_NOT_SUPPORTED << 14);
333
Johannes Berg6ca89f12014-02-12 21:56:26 +0100334 if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) {
335 vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN;
Eytan Lifshitz33158fe2013-02-20 11:01:13 +0200336 /* this works because NOT_SUPPORTED == 3 */
337 vht_cap->vht_mcs.rx_mcs_map |=
338 cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2);
339 }
340
341 vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
342}
343
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100344static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
Eran Harary77db0a32014-02-04 14:21:38 +0200345 struct iwl_nvm_data *data,
346 const __le16 *ch_section, bool enable_vht,
347 u8 tx_chains, u8 rx_chains)
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100348{
Eran Harary77db0a32014-02-04 14:21:38 +0200349 int n_channels;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100350 int n_used = 0;
351 struct ieee80211_supported_band *sband;
352
Eran Harary77db0a32014-02-04 14:21:38 +0200353 if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
354 n_channels = iwl_init_channel_map(
355 dev, cfg, data,
356 &ch_section[NVM_CHANNELS]);
357 else
358 n_channels = iwl_init_channel_map(
359 dev, cfg, data,
360 &ch_section[NVM_CHANNELS_FAMILY_8000]);
361
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100362 sband = &data->bands[IEEE80211_BAND_2GHZ];
363 sband->band = IEEE80211_BAND_2GHZ;
364 sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
365 sband->n_bitrates = N_RATES_24;
366 n_used += iwl_init_sband_channels(data, sband, n_channels,
367 IEEE80211_BAND_2GHZ);
Emmanuel Grumbach9ce4fa72013-05-22 13:16:23 +0300368 iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
369 tx_chains, rx_chains);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100370
371 sband = &data->bands[IEEE80211_BAND_5GHZ];
372 sband->band = IEEE80211_BAND_5GHZ;
373 sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
374 sband->n_bitrates = N_RATES_52;
375 n_used += iwl_init_sband_channels(data, sband, n_channels,
376 IEEE80211_BAND_5GHZ);
Emmanuel Grumbach9ce4fa72013-05-22 13:16:23 +0300377 iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
378 tx_chains, rx_chains);
Johannes Bergbfc824b2013-05-06 16:06:51 +0200379 if (enable_vht)
Johannes Berg6ca89f12014-02-12 21:56:26 +0100380 iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap,
381 tx_chains, rx_chains);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100382
383 if (n_channels != n_used)
384 IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
385 n_used, n_channels);
386}
387
Eran Harary77db0a32014-02-04 14:21:38 +0200388static int iwl_get_sku(const struct iwl_cfg *cfg,
389 const __le16 *nvm_sw)
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100390{
Eran Harary77db0a32014-02-04 14:21:38 +0200391 if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
392 return le16_to_cpup(nvm_sw + SKU);
393 else
394 return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000));
395}
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100396
Eran Harary77db0a32014-02-04 14:21:38 +0200397static int iwl_get_nvm_version(const struct iwl_cfg *cfg,
398 const __le16 *nvm_sw)
399{
400 if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
401 return le16_to_cpup(nvm_sw + NVM_VERSION);
402 else
403 return le32_to_cpup((__le32 *)(nvm_sw +
404 NVM_VERSION_FAMILY_8000));
405}
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100406
Eran Harary77db0a32014-02-04 14:21:38 +0200407static int iwl_get_radio_cfg(const struct iwl_cfg *cfg,
408 const __le16 *nvm_sw)
409{
410 if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
411 return le16_to_cpup(nvm_sw + RADIO_CFG);
412 else
413 return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
414}
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100415
Eran Harary77db0a32014-02-04 14:21:38 +0200416#define N_HW_ADDRS_MASK_FAMILY_8000 0xF
417static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg,
418 const __le16 *nvm_sw)
419{
420 if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
421 return le16_to_cpup(nvm_sw + N_HW_ADDRS);
422 else
423 return le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000))
424 & N_HW_ADDRS_MASK_FAMILY_8000;
425}
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100426
Eran Harary77db0a32014-02-04 14:21:38 +0200427static void iwl_set_radio_cfg(const struct iwl_cfg *cfg,
428 struct iwl_nvm_data *data,
429 u32 radio_cfg)
430{
431 if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
432 data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg);
433 data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg);
434 data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg);
435 data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg);
Eran Harary77db0a32014-02-04 14:21:38 +0200436 return;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100437 }
438
Eran Harary77db0a32014-02-04 14:21:38 +0200439 /* set the radio configuration for family 8000 */
440 data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK_FAMILY_8000(radio_cfg);
441 data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg);
442 data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg);
443 data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg);
Eran Harary77db0a32014-02-04 14:21:38 +0200444}
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100445
Eran Harary77db0a32014-02-04 14:21:38 +0200446static void iwl_set_hw_address(const struct iwl_cfg *cfg,
447 struct iwl_nvm_data *data,
448 const __le16 *nvm_sec)
449{
450 u8 hw_addr[ETH_ALEN];
451
452 if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
453 memcpy(hw_addr, nvm_sec + HW_ADDR, ETH_ALEN);
454 else
455 memcpy(hw_addr, nvm_sec + MAC_ADDRESS_OVERRIDE_FAMILY_8000,
456 ETH_ALEN);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100457
458 /* The byte order is little endian 16 bit, meaning 214365 */
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100459 data->hw_addr[0] = hw_addr[1];
460 data->hw_addr[1] = hw_addr[0];
461 data->hw_addr[2] = hw_addr[3];
462 data->hw_addr[3] = hw_addr[2];
463 data->hw_addr[4] = hw_addr[5];
464 data->hw_addr[5] = hw_addr[4];
Eran Harary77db0a32014-02-04 14:21:38 +0200465}
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100466
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100467struct iwl_nvm_data *
468iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
469 const __le16 *nvm_hw, const __le16 *nvm_sw,
Eran Harary77db0a32014-02-04 14:21:38 +0200470 const __le16 *nvm_calib, const __le16 *regulatory,
471 const __le16 *mac_override, u8 tx_chains, u8 rx_chains)
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100472{
473 struct iwl_nvm_data *data;
Eran Harary77db0a32014-02-04 14:21:38 +0200474 u32 sku;
475 u32 radio_cfg;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100476
Eran Harary77db0a32014-02-04 14:21:38 +0200477 if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
478 data = kzalloc(sizeof(*data) +
479 sizeof(struct ieee80211_channel) *
480 IWL_NUM_CHANNELS,
481 GFP_KERNEL);
482 else
483 data = kzalloc(sizeof(*data) +
484 sizeof(struct ieee80211_channel) *
485 IWL_NUM_CHANNELS_FAMILY_8000,
486 GFP_KERNEL);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100487 if (!data)
488 return NULL;
489
Eran Harary77db0a32014-02-04 14:21:38 +0200490 data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100491
Eran Harary77db0a32014-02-04 14:21:38 +0200492 radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw);
493 iwl_set_radio_cfg(cfg, data, radio_cfg);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100494
Eran Harary77db0a32014-02-04 14:21:38 +0200495 sku = iwl_get_sku(cfg, nvm_sw);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100496 data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
497 data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
498 data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
Eran Harary77db0a32014-02-04 14:21:38 +0200499 data->sku_cap_11ac_enable = sku & NVM_SKU_CAP_11AC_ENABLE;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100500 if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
501 data->sku_cap_11n_enable = false;
502
Eran Harary77db0a32014-02-04 14:21:38 +0200503 data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100504
Eran Harary77db0a32014-02-04 14:21:38 +0200505 if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
506 /* Checking for required sections */
507 if (!nvm_calib) {
508 IWL_ERR_DEV(dev,
509 "Can't parse empty Calib NVM sections\n");
Eytan Lifshitz1270c412014-02-18 15:02:29 +0200510 kfree(data);
Eran Harary77db0a32014-02-04 14:21:38 +0200511 return NULL;
512 }
513 /* in family 8000 Xtal calibration values moved to OTP */
514 data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
515 data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
516 }
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100517
Eran Harary77db0a32014-02-04 14:21:38 +0200518 if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
519 iwl_set_hw_address(cfg, data, nvm_hw);
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100520
Eran Harary77db0a32014-02-04 14:21:38 +0200521 iwl_init_sbands(dev, cfg, data, nvm_sw,
522 sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
523 rx_chains);
524 } else {
525 /* MAC address in family 8000 */
526 iwl_set_hw_address(cfg, data, mac_override);
527
528 iwl_init_sbands(dev, cfg, data, regulatory,
529 sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
530 rx_chains);
531 }
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100532
Emmanuel Grumbach33b2f682014-01-14 13:48:22 +0200533 data->calib_version = 255;
Johannes Bergb1e1adf2013-01-24 14:14:22 +0100534
535 return data;
536}
Johannes Berg48e29342013-03-01 00:13:33 +0100537IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);