iwlwifi: mvm: Change FW channel info API
[linux-block.git] / drivers / net / wireless / intel / iwlwifi / iwl-io.c
CommitLineData
02a7fa00 1/******************************************************************************
cefec29e
JB
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
02a7fa00 7 *
51368bf7 8 * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
f65ebd88 9 * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
02a7fa00 10 *
02a7fa00
JB
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of version 2 of the GNU General Public License as
13 * published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
19 *
02a7fa00 20 * The full GNU General Public License is included in this distribution in the
cefec29e 21 * file called COPYING.
02a7fa00
JB
22 *
23 * Contact Information:
cb2f8277 24 * Intel Linux Wireless <linuxwifi@intel.com>
02a7fa00
JB
25 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
26 *
cefec29e
JB
27 * BSD LICENSE
28 *
29 * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
30 * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 *
37 * * Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * * Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in
41 * the documentation and/or other materials provided with the
42 * distribution.
43 * * Neither the name Intel Corporation nor the names of its
44 * contributors may be used to endorse or promote products derived
45 * from this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
50 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
53 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
57 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58 *
02a7fa00 59 *****************************************************************************/
83ed9015
EG
60#include <linux/delay.h>
61#include <linux/device.h>
cc5f7e39 62#include <linux/export.h>
02a7fa00 63
48e29340 64#include "iwl-drv.h"
02a7fa00 65#include "iwl-io.h"
6ac7d115 66#include "iwl-csr.h"
83ed9015 67#include "iwl-debug.h"
4c9706dc 68#include "iwl-prph.h"
313b0a29 69#include "iwl-fh.h"
02a7fa00 70
a73a2cea
EG
71void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
72{
73 trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val);
74 iwl_trans_write8(trans, ofs, val);
75}
76IWL_EXPORT_SYMBOL(iwl_write8);
77
78void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val)
79{
80 trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val);
81 iwl_trans_write32(trans, ofs, val);
82}
83IWL_EXPORT_SYMBOL(iwl_write32);
84
12a17458
SS
85void iwl_write64(struct iwl_trans *trans, u64 ofs, u64 val)
86{
87 trace_iwlwifi_dev_iowrite64(trans->dev, ofs, val);
bd31dd9d
JB
88 iwl_trans_write32(trans, ofs, lower_32_bits(val));
89 iwl_trans_write32(trans, ofs + 4, upper_32_bits(val));
12a17458
SS
90}
91IWL_EXPORT_SYMBOL(iwl_write64);
92
a73a2cea
EG
93u32 iwl_read32(struct iwl_trans *trans, u32 ofs)
94{
95 u32 val = iwl_trans_read32(trans, ofs);
89ced540 96
a73a2cea
EG
97 trace_iwlwifi_dev_ioread32(trans->dev, ofs, val);
98 return val;
99}
100IWL_EXPORT_SYMBOL(iwl_read32);
101
02a7fa00
JB
102#define IWL_POLL_INTERVAL 10 /* microseconds */
103
1042db2a 104int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
02a7fa00
JB
105 u32 bits, u32 mask, int timeout)
106{
107 int t = 0;
108
109 do {
1042db2a 110 if ((iwl_read32(trans, addr) & mask) == (bits & mask))
02a7fa00
JB
111 return t;
112 udelay(IWL_POLL_INTERVAL);
113 t += IWL_POLL_INTERVAL;
114 } while (t < timeout);
115
116 return -ETIMEDOUT;
117}
48e29340 118IWL_EXPORT_SYMBOL(iwl_poll_bit);
02a7fa00 119
1042db2a 120u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
02a7fa00 121{
abae2386 122 u32 value = 0x5a5a5a5a;
02a7fa00 123 unsigned long flags;
23ba9340 124 if (iwl_trans_grab_nic_access(trans, &flags)) {
abae2386 125 value = iwl_read32(trans, reg);
e56b04ef 126 iwl_trans_release_nic_access(trans, &flags);
abae2386 127 }
02a7fa00
JB
128
129 return value;
130}
48e29340 131IWL_EXPORT_SYMBOL(iwl_read_direct32);
02a7fa00 132
1042db2a 133void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
02a7fa00
JB
134{
135 unsigned long flags;
136
23ba9340 137 if (iwl_trans_grab_nic_access(trans, &flags)) {
1042db2a 138 iwl_write32(trans, reg, value);
e56b04ef 139 iwl_trans_release_nic_access(trans, &flags);
02a7fa00 140 }
02a7fa00 141}
48e29340 142IWL_EXPORT_SYMBOL(iwl_write_direct32);
02a7fa00 143
12a17458
SS
144void iwl_write_direct64(struct iwl_trans *trans, u64 reg, u64 value)
145{
146 unsigned long flags;
147
148 if (iwl_trans_grab_nic_access(trans, &flags)) {
149 iwl_write64(trans, reg, value);
150 iwl_trans_release_nic_access(trans, &flags);
151 }
152}
153IWL_EXPORT_SYMBOL(iwl_write_direct64);
154
1042db2a 155int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
02a7fa00
JB
156 int timeout)
157{
158 int t = 0;
159
160 do {
1042db2a 161 if ((iwl_read_direct32(trans, addr) & mask) == mask)
02a7fa00
JB
162 return t;
163 udelay(IWL_POLL_INTERVAL);
164 t += IWL_POLL_INTERVAL;
165 } while (t < timeout);
166
167 return -ETIMEDOUT;
168}
48e29340 169IWL_EXPORT_SYMBOL(iwl_poll_direct_bit);
02a7fa00 170
14ef1b43 171u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs)
02a7fa00 172{
6a06b6c1
EG
173 u32 val = iwl_trans_read_prph(trans, ofs);
174 trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val);
175 return val;
02a7fa00 176}
14ef1b43 177IWL_EXPORT_SYMBOL(iwl_read_prph_no_grab);
02a7fa00 178
14ef1b43 179void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val)
02a7fa00 180{
6a06b6c1
EG
181 trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val);
182 iwl_trans_write_prph(trans, ofs, val);
02a7fa00 183}
14ef1b43 184IWL_EXPORT_SYMBOL(iwl_write_prph_no_grab);
02a7fa00 185
12a17458
SS
186void iwl_write_prph64_no_grab(struct iwl_trans *trans, u64 ofs, u64 val)
187{
188 trace_iwlwifi_dev_iowrite_prph64(trans->dev, ofs, val);
189 iwl_write_prph_no_grab(trans, ofs, val & 0xffffffff);
190 iwl_write_prph_no_grab(trans, ofs + 4, val >> 32);
191}
192IWL_EXPORT_SYMBOL(iwl_write_prph64_no_grab);
193
6a06b6c1 194u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
02a7fa00
JB
195{
196 unsigned long flags;
abae2386 197 u32 val = 0x5a5a5a5a;
02a7fa00 198
23ba9340 199 if (iwl_trans_grab_nic_access(trans, &flags)) {
14ef1b43 200 val = iwl_read_prph_no_grab(trans, ofs);
e56b04ef 201 iwl_trans_release_nic_access(trans, &flags);
abae2386 202 }
02a7fa00
JB
203 return val;
204}
48e29340 205IWL_EXPORT_SYMBOL(iwl_read_prph);
02a7fa00 206
6a06b6c1 207void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
02a7fa00
JB
208{
209 unsigned long flags;
210
23ba9340 211 if (iwl_trans_grab_nic_access(trans, &flags)) {
14ef1b43 212 iwl_write_prph_no_grab(trans, ofs, val);
e56b04ef 213 iwl_trans_release_nic_access(trans, &flags);
02a7fa00 214 }
02a7fa00 215}
48e29340 216IWL_EXPORT_SYMBOL(iwl_write_prph);
02a7fa00 217
189fa2fa
EH
218int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
219 u32 bits, u32 mask, int timeout)
220{
221 int t = 0;
222
223 do {
224 if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
225 return t;
226 udelay(IWL_POLL_INTERVAL);
227 t += IWL_POLL_INTERVAL;
228 } while (t < timeout);
229
230 return -ETIMEDOUT;
231}
232
6a06b6c1 233void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
02a7fa00
JB
234{
235 unsigned long flags;
236
23ba9340 237 if (iwl_trans_grab_nic_access(trans, &flags)) {
14ef1b43
GBA
238 iwl_write_prph_no_grab(trans, ofs,
239 iwl_read_prph_no_grab(trans, ofs) |
240 mask);
e56b04ef 241 iwl_trans_release_nic_access(trans, &flags);
bfe4b80e 242 }
02a7fa00 243}
48e29340 244IWL_EXPORT_SYMBOL(iwl_set_bits_prph);
02a7fa00 245
6a06b6c1 246void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
02a7fa00
JB
247 u32 bits, u32 mask)
248{
249 unsigned long flags;
250
23ba9340 251 if (iwl_trans_grab_nic_access(trans, &flags)) {
14ef1b43
GBA
252 iwl_write_prph_no_grab(trans, ofs,
253 (iwl_read_prph_no_grab(trans, ofs) &
254 mask) | bits);
e56b04ef 255 iwl_trans_release_nic_access(trans, &flags);
bfe4b80e 256 }
02a7fa00 257}
48e29340 258IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph);
02a7fa00 259
6a06b6c1 260void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
02a7fa00
JB
261{
262 unsigned long flags;
263 u32 val;
264
23ba9340 265 if (iwl_trans_grab_nic_access(trans, &flags)) {
14ef1b43
GBA
266 val = iwl_read_prph_no_grab(trans, ofs);
267 iwl_write_prph_no_grab(trans, ofs, (val & ~mask));
e56b04ef 268 iwl_trans_release_nic_access(trans, &flags);
bfe4b80e 269 }
02a7fa00 270}
48e29340 271IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
313b0a29 272
4c9706dc
LK
273void iwl_force_nmi(struct iwl_trans *trans)
274{
f4ca70ef 275 if (trans->cfg->device_family < IWL_DEVICE_FAMILY_9000)
66396583
EG
276 iwl_write_prph(trans, DEVICE_SET_NMI_REG,
277 DEVICE_SET_NMI_VAL_DRV);
f4ca70ef 278 else
b9410b18 279 iwl_write_prph(trans, UREG_NIC_SET_NMI_DRIVER,
f4ca70ef 280 UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER_MSK);
4c9706dc
LK
281}
282IWL_EXPORT_SYMBOL(iwl_force_nmi);
283
f65ebd88 284static const char *get_rfh_string(int cmd)
313b0a29
IH
285{
286#define IWL_CMD(x) case x: return #x
f65ebd88
SS
287#define IWL_CMD_MQ(arg, reg, q) { if (arg == reg(q)) return #reg; }
288
289 int i;
290
291 for (i = 0; i < IWL_MAX_RX_HW_QUEUES; i++) {
292 IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_BA_LSB, i);
293 IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_WIDX, i);
294 IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_RIDX, i);
295 IWL_CMD_MQ(cmd, RFH_Q_URBD_STTS_WPTR_LSB, i);
c0ed8aa4 296 }
f65ebd88
SS
297
298 switch (cmd) {
299 IWL_CMD(RFH_RXF_DMA_CFG);
300 IWL_CMD(RFH_GEN_CFG);
301 IWL_CMD(RFH_GEN_STATUS);
302 IWL_CMD(FH_TSSR_TX_STATUS_REG);
303 IWL_CMD(FH_TSSR_TX_ERROR_REG);
304 default:
305 return "UNKNOWN";
306 }
307#undef IWL_CMD_MQ
308}
309
310struct reg {
311 u32 addr;
312 bool is64;
313};
314
315static int iwl_dump_rfh(struct iwl_trans *trans, char **buf)
316{
317 int i, q;
318 int num_q = trans->num_rx_queues;
319 static const u32 rfh_tbl[] = {
320 RFH_RXF_DMA_CFG,
321 RFH_GEN_CFG,
322 RFH_GEN_STATUS,
323 FH_TSSR_TX_STATUS_REG,
324 FH_TSSR_TX_ERROR_REG,
325 };
326 static const struct reg rfh_mq_tbl[] = {
327 { RFH_Q0_FRBDCB_BA_LSB, true },
328 { RFH_Q0_FRBDCB_WIDX, false },
329 { RFH_Q0_FRBDCB_RIDX, false },
330 { RFH_Q0_URBD_STTS_WPTR_LSB, true },
331 };
332
333#ifdef CONFIG_IWLWIFI_DEBUGFS
334 if (buf) {
335 int pos = 0;
336 /*
337 * Register (up to 34 for name + 8 blank/q for MQ): 40 chars
338 * Colon + space: 2 characters
339 * 0X%08x: 10 characters
340 * New line: 1 character
341 * Total of 53 characters
342 */
343 size_t bufsz = ARRAY_SIZE(rfh_tbl) * 53 +
344 ARRAY_SIZE(rfh_mq_tbl) * 53 * num_q + 40;
345
346 *buf = kmalloc(bufsz, GFP_KERNEL);
347 if (!*buf)
348 return -ENOMEM;
349
350 pos += scnprintf(*buf + pos, bufsz - pos,
351 "RFH register values:\n");
352
353 for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++)
354 pos += scnprintf(*buf + pos, bufsz - pos,
355 "%40s: 0X%08x\n",
356 get_rfh_string(rfh_tbl[i]),
357 iwl_read_prph(trans, rfh_tbl[i]));
358
359 for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++)
360 for (q = 0; q < num_q; q++) {
361 u32 addr = rfh_mq_tbl[i].addr;
362
363 addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4);
364 pos += scnprintf(*buf + pos, bufsz - pos,
365 "%34s(q %2d): 0X%08x\n",
366 get_rfh_string(addr), q,
367 iwl_read_prph(trans, addr));
368 }
369
370 return pos;
371 }
372#endif
373
374 IWL_ERR(trans, "RFH register values:\n");
375 for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++)
376 IWL_ERR(trans, " %34s: 0X%08x\n",
377 get_rfh_string(rfh_tbl[i]),
378 iwl_read_prph(trans, rfh_tbl[i]));
379
380 for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++)
381 for (q = 0; q < num_q; q++) {
382 u32 addr = rfh_mq_tbl[i].addr;
383
384 addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4);
385 IWL_ERR(trans, " %34s(q %d): 0X%08x\n",
386 get_rfh_string(addr), q,
387 iwl_read_prph(trans, addr));
388 }
389
390 return 0;
391}
392
393static const char *get_fh_string(int cmd)
394{
313b0a29
IH
395 switch (cmd) {
396 IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
397 IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
398 IWL_CMD(FH_RSCSR_CHNL0_WPTR);
399 IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
400 IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
401 IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
402 IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
403 IWL_CMD(FH_TSSR_TX_STATUS_REG);
404 IWL_CMD(FH_TSSR_TX_ERROR_REG);
405 default:
406 return "UNKNOWN";
407 }
408#undef IWL_CMD
409}
410
411int iwl_dump_fh(struct iwl_trans *trans, char **buf)
412{
413 int i;
414 static const u32 fh_tbl[] = {
415 FH_RSCSR_CHNL0_STTS_WPTR_REG,
416 FH_RSCSR_CHNL0_RBDCB_BASE_REG,
417 FH_RSCSR_CHNL0_WPTR,
418 FH_MEM_RCSR_CHNL0_CONFIG_REG,
419 FH_MEM_RSSR_SHARED_CTRL_REG,
420 FH_MEM_RSSR_RX_STATUS_REG,
421 FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
422 FH_TSSR_TX_STATUS_REG,
423 FH_TSSR_TX_ERROR_REG
424 };
425
f65ebd88
SS
426 if (trans->cfg->mq_rx_supported)
427 return iwl_dump_rfh(trans, buf);
428
313b0a29
IH
429#ifdef CONFIG_IWLWIFI_DEBUGFS
430 if (buf) {
431 int pos = 0;
432 size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
433
434 *buf = kmalloc(bufsz, GFP_KERNEL);
435 if (!*buf)
436 return -ENOMEM;
437
438 pos += scnprintf(*buf + pos, bufsz - pos,
439 "FH register values:\n");
440
441 for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
442 pos += scnprintf(*buf + pos, bufsz - pos,
443 " %34s: 0X%08x\n",
444 get_fh_string(fh_tbl[i]),
445 iwl_read_direct32(trans, fh_tbl[i]));
446
447 return pos;
448 }
449#endif
450
451 IWL_ERR(trans, "FH register values:\n");
452 for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
453 IWL_ERR(trans, " %34s: 0X%08x\n",
454 get_fh_string(fh_tbl[i]),
455 iwl_read_direct32(trans, fh_tbl[i]));
456
457 return 0;
458}