aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/mvm/utils.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-01-24 08:25:36 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-02-01 05:27:15 -0500
commit8ca151b568b67a7b72dcfc6ee6ea7c107ddd795c (patch)
treedac0236038f3791140e9f864c5db2be873c568f0 /drivers/net/wireless/iwlwifi/mvm/utils.c
parentb1e1adfa7d30cd0e8ad9a5c6a89e8c45ebe084f4 (diff)
iwlwifi: add the MVM driver
Newer firmware revisions have a completely new firmware API. This is the new driver for this new API. I've listed the people who directly contributed code, but many others from various teams have contributed in other ways. Cc: Alexander Bondar <alexander.bondar@intel.com> Cc: Amit Beka <amit.beka@intel.com> Cc: Amnon Paz <amnonx.paz@intel.com> Cc: Assaf Krauss <assaf.krauss@intel.com> Cc: David Spinadel <david.spinadel@intel.com> Cc: Dor Shaish <dor.shaish@intel.com> Cc: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Cc: Eytan Lifshitz <eytan.lifshitz@intel.com> Cc: Ilan Peer <ilan.peer@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/utils.c')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c472
1 files changed, 472 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
new file mode 100644
index 000000000000..000e842c2edd
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -0,0 +1,472 @@
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 *
8 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
9 *
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
25 * in the file called LICENSE.GPL.
26 *
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 *
33 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
34 * 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 *****************************************************************************/
63#include <net/mac80211.h>
64
65#include "iwl-debug.h"
66#include "iwl-io.h"
67
68#include "mvm.h"
69#include "fw-api-rs.h"
70
71/*
72 * Will return 0 even if the cmd failed when RFKILL is asserted unless
73 * CMD_WANT_SKB is set in cmd->flags.
74 */
75int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
76{
77 int ret;
78
79 /*
80 * Synchronous commands from this op-mode must hold
81 * the mutex, this ensures we don't try to send two
82 * (or more) synchronous commands at a time.
83 */
84 if (!(cmd->flags & CMD_ASYNC))
85 lockdep_assert_held(&mvm->mutex);
86
87 ret = iwl_trans_send_cmd(mvm->trans, cmd);
88
89 /*
90 * If the caller wants the SKB, then don't hide any problems, the
91 * caller might access the response buffer which will be NULL if
92 * the command failed.
93 */
94 if (cmd->flags & CMD_WANT_SKB)
95 return ret;
96
97 /* Silently ignore failures if RFKILL is asserted */
98 if (!ret || ret == -ERFKILL)
99 return 0;
100 return ret;
101}
102
103int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id,
104 u32 flags, u16 len, const void *data)
105{
106 struct iwl_host_cmd cmd = {
107 .id = id,
108 .len = { len, },
109 .data = { data, },
110 .flags = flags,
111 };
112
113 return iwl_mvm_send_cmd(mvm, &cmd);
114}
115
116/*
117 * We assume that the caller set the status to the sucess value
118 */
119int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
120 u32 *status)
121{
122 struct iwl_rx_packet *pkt;
123 struct iwl_cmd_response *resp;
124 int ret, resp_len;
125
126 lockdep_assert_held(&mvm->mutex);
127
128 /*
129 * Only synchronous commands can wait for status,
130 * we use WANT_SKB so the caller can't.
131 */
132 if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB),
133 "cmd flags %x", cmd->flags))
134 return -EINVAL;
135
136 cmd->flags |= CMD_SYNC | CMD_WANT_SKB;
137
138 ret = iwl_trans_send_cmd(mvm->trans, cmd);
139 if (ret == -ERFKILL) {
140 /*
141 * The command failed because of RFKILL, don't update
142 * the status, leave it as success and return 0.
143 */
144 return 0;
145 } else if (ret) {
146 return ret;
147 }
148
149 pkt = cmd->resp_pkt;
150 /* Can happen if RFKILL is asserted */
151 if (!pkt) {
152 ret = 0;
153 goto out_free_resp;
154 }
155
156 if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
157 ret = -EIO;
158 goto out_free_resp;
159 }
160
161 resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
162 if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) {
163 ret = -EIO;
164 goto out_free_resp;
165 }
166
167 resp = (void *)pkt->data;
168 *status = le32_to_cpu(resp->status);
169 out_free_resp:
170 iwl_free_resp(cmd);
171 return ret;
172}
173
174/*
175 * We assume that the caller set the status to the sucess value
176 */
177int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id, u16 len,
178 const void *data, u32 *status)
179{
180 struct iwl_host_cmd cmd = {
181 .id = id,
182 .len = { len, },
183 .data = { data, },
184 };
185
186 return iwl_mvm_send_cmd_status(mvm, &cmd, status);
187}
188
189#define IWL_DECLARE_RATE_INFO(r) \
190 [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
191
192/*
193 * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
194 */
195static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
196 IWL_DECLARE_RATE_INFO(1),
197 IWL_DECLARE_RATE_INFO(2),
198 IWL_DECLARE_RATE_INFO(5),
199 IWL_DECLARE_RATE_INFO(11),
200 IWL_DECLARE_RATE_INFO(6),
201 IWL_DECLARE_RATE_INFO(9),
202 IWL_DECLARE_RATE_INFO(12),
203 IWL_DECLARE_RATE_INFO(18),
204 IWL_DECLARE_RATE_INFO(24),
205 IWL_DECLARE_RATE_INFO(36),
206 IWL_DECLARE_RATE_INFO(48),
207 IWL_DECLARE_RATE_INFO(54),
208};
209
210int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
211 enum ieee80211_band band)
212{
213 int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
214 int idx;
215 int band_offset = 0;
216
217 /* Legacy rate format, search for match in table */
218 if (band == IEEE80211_BAND_5GHZ)
219 band_offset = IWL_FIRST_OFDM_RATE;
220 for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
221 if (fw_rate_idx_to_plcp[idx] == rate)
222 return idx - band_offset;
223
224 return -1;
225}
226
227u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx)
228{
229 /* Get PLCP rate for tx_cmd->rate_n_flags */
230 return fw_rate_idx_to_plcp[rate_idx];
231}
232
233int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
234 struct iwl_device_cmd *cmd)
235{
236 struct iwl_rx_packet *pkt = rxb_addr(rxb);
237 struct iwl_error_resp *err_resp = (void *)pkt->data;
238
239 IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n",
240 le32_to_cpu(err_resp->error_type), err_resp->cmd_id);
241 IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n",
242 le16_to_cpu(err_resp->bad_cmd_seq_num),
243 le32_to_cpu(err_resp->error_service));
244 IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n",
245 le64_to_cpu(err_resp->timestamp));
246 return 0;
247}
248
249/*
250 * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h.
251 * The parameter should also be a combination of ANT_[ABC].
252 */
253u8 first_antenna(u8 mask)
254{
255 BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */
256 WARN_ON_ONCE(!mask); /* ffs will return 0 if mask is zeroed */
257 return (u8)(BIT(ffs(mask)));
258}
259
260/*
261 * Toggles between TX antennas to send the probe request on.
262 * Receives the bitmask of valid TX antennas and the *index* used
263 * for the last TX, and returns the next valid *index* to use.
264 * In order to set it in the tx_cmd, must do BIT(idx).
265 */
266u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
267{
268 u8 ind = last_idx;
269 int i;
270
271 for (i = 0; i < RATE_MCS_ANT_NUM; i++) {
272 ind = (ind + 1) % RATE_MCS_ANT_NUM;
273 if (valid & BIT(ind))
274 return ind;
275 }
276
277 WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid);
278 return last_idx;
279}
280
281static struct {
282 char *name;
283 u8 num;
284} advanced_lookup[] = {
285 { "NMI_INTERRUPT_WDG", 0x34 },
286 { "SYSASSERT", 0x35 },
287 { "UCODE_VERSION_MISMATCH", 0x37 },
288 { "BAD_COMMAND", 0x38 },
289 { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
290 { "FATAL_ERROR", 0x3D },
291 { "NMI_TRM_HW_ERR", 0x46 },
292 { "NMI_INTERRUPT_TRM", 0x4C },
293 { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
294 { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
295 { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
296 { "NMI_INTERRUPT_HOST", 0x66 },
297 { "NMI_INTERRUPT_ACTION_PT", 0x7C },
298 { "NMI_INTERRUPT_UNKNOWN", 0x84 },
299 { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
300 { "ADVANCED_SYSASSERT", 0 },
301};
302
303static const char *desc_lookup(u32 num)
304{
305 int i;
306
307 for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++)
308 if (advanced_lookup[i].num == num)
309 return advanced_lookup[i].name;
310
311 /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
312 return advanced_lookup[i].name;
313}
314
315/*
316 * Note: This structure is read from the device with IO accesses,
317 * and the reading already does the endian conversion. As it is
318 * read with u32-sized accesses, any members with a different size
319 * need to be ordered correctly though!
320 */
321struct iwl_error_event_table {
322 u32 valid; /* (nonzero) valid, (0) log is empty */
323 u32 error_id; /* type of error */
324 u32 pc; /* program counter */
325 u32 blink1; /* branch link */
326 u32 blink2; /* branch link */
327 u32 ilink1; /* interrupt link */
328 u32 ilink2; /* interrupt link */
329 u32 data1; /* error-specific data */
330 u32 data2; /* error-specific data */
331 u32 data3; /* error-specific data */
332 u32 bcon_time; /* beacon timer */
333 u32 tsf_low; /* network timestamp function timer */
334 u32 tsf_hi; /* network timestamp function timer */
335 u32 gp1; /* GP1 timer register */
336 u32 gp2; /* GP2 timer register */
337 u32 gp3; /* GP3 timer register */
338 u32 ucode_ver; /* uCode version */
339 u32 hw_ver; /* HW Silicon version */
340 u32 brd_ver; /* HW board version */
341 u32 log_pc; /* log program counter */
342 u32 frame_ptr; /* frame pointer */
343 u32 stack_ptr; /* stack pointer */
344 u32 hcmd; /* last host command header */
345 u32 isr0; /* isr status register LMPM_NIC_ISR0:
346 * rxtx_flag */
347 u32 isr1; /* isr status register LMPM_NIC_ISR1:
348 * host_flag */
349 u32 isr2; /* isr status register LMPM_NIC_ISR2:
350 * enc_flag */
351 u32 isr3; /* isr status register LMPM_NIC_ISR3:
352 * time_flag */
353 u32 isr4; /* isr status register LMPM_NIC_ISR4:
354 * wico interrupt */
355 u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
356 u32 wait_event; /* wait event() caller address */
357 u32 l2p_control; /* L2pControlField */
358 u32 l2p_duration; /* L2pDurationField */
359 u32 l2p_mhvalid; /* L2pMhValidBits */
360 u32 l2p_addr_match; /* L2pAddrMatchStat */
361 u32 lmpm_pmg_sel; /* indicate which clocks are turned on
362 * (LMPM_PMG_SEL) */
363 u32 u_timestamp; /* indicate when the date and time of the
364 * compilation */
365 u32 flow_handler; /* FH read/write pointers, RX credit */
366} __packed;
367
368#define ERROR_START_OFFSET (1 * sizeof(u32))
369#define ERROR_ELEM_SIZE (7 * sizeof(u32))
370
371void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
372{
373 struct iwl_trans *trans = mvm->trans;
374 struct iwl_error_event_table table;
375 u32 base;
376
377 base = mvm->error_event_table;
378 if (mvm->cur_ucode == IWL_UCODE_INIT) {
379 if (!base)
380 base = mvm->fw->init_errlog_ptr;
381 } else {
382 if (!base)
383 base = mvm->fw->inst_errlog_ptr;
384 }
385
386 if (base < 0x800000 || base >= 0x80C000) {
387 IWL_ERR(mvm,
388 "Not valid error log pointer 0x%08X for %s uCode\n",
389 base,
390 (mvm->cur_ucode == IWL_UCODE_INIT)
391 ? "Init" : "RT");
392 return;
393 }
394
395 iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
396
397 if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
398 IWL_ERR(trans, "Start IWL Error Log Dump:\n");
399 IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
400 mvm->status, table.valid);
401 }
402
403 trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
404 table.data1, table.data2, table.data3,
405 table.blink1, table.blink2, table.ilink1,
406 table.ilink2, table.bcon_time, table.gp1,
407 table.gp2, table.gp3, table.ucode_ver,
408 table.hw_ver, table.brd_ver);
409 IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
410 desc_lookup(table.error_id));
411 IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
412 IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
413 IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
414 IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
415 IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
416 IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
417 IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
418 IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
419 IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
420 IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
421 IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
422 IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
423 IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
424 IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
425 IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver);
426 IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
427 IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
428 IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
429 IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
430 IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
431 IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
432 IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
433 IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
434 IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
435 IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
436 IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
437 IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
438 IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
439 IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
440 IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
441 IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
442 IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
443}
444
445/**
446 * iwl_mvm_send_lq_cmd() - Send link quality command
447 * @init: This command is sent as part of station initialization right
448 * after station has been added.
449 *
450 * The link quality command is sent as the last step of station creation.
451 * This is the special case in which init is set and we call a callback in
452 * this case to clear the state indicating that station creation is in
453 * progress.
454 */
455int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
456 u8 flags, bool init)
457{
458 struct iwl_host_cmd cmd = {
459 .id = LQ_CMD,
460 .len = { sizeof(struct iwl_lq_cmd), },
461 .flags = flags,
462 .data = { lq, },
463 };
464
465 if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
466 return -EINVAL;
467
468 if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
469 return -EINVAL;
470
471 return iwl_mvm_send_cmd(mvm, &cmd);
472}