Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/fw/dbg.c
48287 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
4
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
5
* Copyright (C) 2015-2017 Intel Deutschland GmbH
6
*/
7
#include <linux/devcoredump.h>
8
#if defined(__FreeBSD__)
9
#include <linux/delay.h>
10
#endif
11
#include "iwl-drv.h"
12
#include "runtime.h"
13
#include "dbg.h"
14
#include "debugfs.h"
15
#include "iwl-io.h"
16
#include "iwl-prph.h"
17
#include "iwl-csr.h"
18
#include "iwl-fh.h"
19
/**
20
* struct iwl_fw_dump_ptrs - set of pointers needed for the fw-error-dump
21
*
22
* @fwrt_ptr: pointer to the buffer coming from fwrt
23
* @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the
24
* transport's data.
25
* @fwrt_len: length of the valid data in fwrt_ptr
26
*/
27
struct iwl_fw_dump_ptrs {
28
struct iwl_trans_dump_data *trans_ptr;
29
void *fwrt_ptr;
30
u32 fwrt_len;
31
};
32
33
#define RADIO_REG_MAX_READ 0x2ad
34
static void iwl_read_radio_regs(struct iwl_fw_runtime *fwrt,
35
struct iwl_fw_error_dump_data **dump_data)
36
{
37
u8 *pos = (void *)(*dump_data)->data;
38
int i;
39
40
IWL_DEBUG_INFO(fwrt, "WRT radio registers dump\n");
41
42
if (!iwl_trans_grab_nic_access(fwrt->trans))
43
return;
44
45
(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG);
46
(*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ);
47
48
for (i = 0; i < RADIO_REG_MAX_READ; i++) {
49
u32 rd_cmd = RADIO_RSP_RD_CMD;
50
51
rd_cmd |= i << RADIO_RSP_ADDR_POS;
52
iwl_write_prph_no_grab(fwrt->trans, RSP_RADIO_CMD, rd_cmd);
53
*pos = (u8)iwl_read_prph_no_grab(fwrt->trans, RSP_RADIO_RDDAT);
54
55
pos++;
56
}
57
58
*dump_data = iwl_fw_error_next_data(*dump_data);
59
60
iwl_trans_release_nic_access(fwrt->trans);
61
}
62
63
static void iwl_fwrt_dump_rxf(struct iwl_fw_runtime *fwrt,
64
struct iwl_fw_error_dump_data **dump_data,
65
int size, u32 offset, int fifo_num)
66
{
67
struct iwl_fw_error_dump_fifo *fifo_hdr;
68
u32 *fifo_data;
69
u32 fifo_len;
70
int i;
71
72
fifo_hdr = (void *)(*dump_data)->data;
73
fifo_data = (void *)fifo_hdr->data;
74
fifo_len = size;
75
76
/* No need to try to read the data if the length is 0 */
77
if (fifo_len == 0)
78
return;
79
80
/* Add a TLV for the RXF */
81
(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
82
(*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
83
84
fifo_hdr->fifo_num = cpu_to_le32(fifo_num);
85
fifo_hdr->available_bytes =
86
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
87
RXF_RD_D_SPACE + offset));
88
fifo_hdr->wr_ptr =
89
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
90
RXF_RD_WR_PTR + offset));
91
fifo_hdr->rd_ptr =
92
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
93
RXF_RD_RD_PTR + offset));
94
fifo_hdr->fence_ptr =
95
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
96
RXF_RD_FENCE_PTR + offset));
97
fifo_hdr->fence_mode =
98
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
99
RXF_SET_FENCE_MODE + offset));
100
101
/* Lock fence */
102
iwl_trans_write_prph(fwrt->trans, RXF_SET_FENCE_MODE + offset, 0x1);
103
/* Set fence pointer to the same place like WR pointer */
104
iwl_trans_write_prph(fwrt->trans, RXF_LD_WR2FENCE + offset, 0x1);
105
/* Set fence offset */
106
iwl_trans_write_prph(fwrt->trans,
107
RXF_LD_FENCE_OFFSET_ADDR + offset, 0x0);
108
109
/* Read FIFO */
110
fifo_len /= sizeof(u32); /* Size in DWORDS */
111
for (i = 0; i < fifo_len; i++)
112
fifo_data[i] = iwl_trans_read_prph(fwrt->trans,
113
RXF_FIFO_RD_FENCE_INC +
114
offset);
115
*dump_data = iwl_fw_error_next_data(*dump_data);
116
}
117
118
static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt,
119
struct iwl_fw_error_dump_data **dump_data,
120
int size, u32 offset, int fifo_num)
121
{
122
struct iwl_fw_error_dump_fifo *fifo_hdr;
123
u32 *fifo_data;
124
u32 fifo_len;
125
int i;
126
127
fifo_hdr = (void *)(*dump_data)->data;
128
fifo_data = (void *)fifo_hdr->data;
129
fifo_len = size;
130
131
/* No need to try to read the data if the length is 0 */
132
if (fifo_len == 0)
133
return;
134
135
/* Add a TLV for the FIFO */
136
(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
137
(*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
138
139
fifo_hdr->fifo_num = cpu_to_le32(fifo_num);
140
fifo_hdr->available_bytes =
141
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
142
TXF_FIFO_ITEM_CNT + offset));
143
fifo_hdr->wr_ptr =
144
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
145
TXF_WR_PTR + offset));
146
fifo_hdr->rd_ptr =
147
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
148
TXF_RD_PTR + offset));
149
fifo_hdr->fence_ptr =
150
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
151
TXF_FENCE_PTR + offset));
152
fifo_hdr->fence_mode =
153
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
154
TXF_LOCK_FENCE + offset));
155
156
/* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
157
iwl_trans_write_prph(fwrt->trans, TXF_READ_MODIFY_ADDR + offset,
158
TXF_WR_PTR + offset);
159
160
/* Dummy-read to advance the read pointer to the head */
161
iwl_trans_read_prph(fwrt->trans, TXF_READ_MODIFY_DATA + offset);
162
163
/* Read FIFO */
164
for (i = 0; i < fifo_len / sizeof(u32); i++)
165
fifo_data[i] = iwl_trans_read_prph(fwrt->trans,
166
TXF_READ_MODIFY_DATA +
167
offset);
168
169
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf)
170
fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx,
171
fifo_data, fifo_len);
172
173
*dump_data = iwl_fw_error_next_data(*dump_data);
174
}
175
176
static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt,
177
struct iwl_fw_error_dump_data **dump_data)
178
{
179
struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
180
181
IWL_DEBUG_INFO(fwrt, "WRT RX FIFO dump\n");
182
183
if (!iwl_trans_grab_nic_access(fwrt->trans))
184
return;
185
186
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) {
187
/* Pull RXF1 */
188
iwl_fwrt_dump_rxf(fwrt, dump_data,
189
cfg->lmac[0].rxfifo1_size, 0, 0);
190
/* Pull RXF2 */
191
iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size,
192
RXF_DIFF_FROM_PREV +
193
fwrt->trans->mac_cfg->umac_prph_offset, 1);
194
/* Pull LMAC2 RXF1 */
195
if (fwrt->smem_cfg.num_lmacs > 1)
196
iwl_fwrt_dump_rxf(fwrt, dump_data,
197
cfg->lmac[1].rxfifo1_size,
198
LMAC2_PRPH_OFFSET, 2);
199
}
200
201
iwl_trans_release_nic_access(fwrt->trans);
202
}
203
204
static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt,
205
struct iwl_fw_error_dump_data **dump_data)
206
{
207
struct iwl_fw_error_dump_fifo *fifo_hdr;
208
struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
209
u32 *fifo_data;
210
u32 fifo_len;
211
int i, j;
212
213
IWL_DEBUG_INFO(fwrt, "WRT TX FIFO dump\n");
214
215
if (!iwl_trans_grab_nic_access(fwrt->trans))
216
return;
217
218
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) {
219
/* Pull TXF data from LMAC1 */
220
for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) {
221
/* Mark the number of TXF we're pulling now */
222
iwl_trans_write_prph(fwrt->trans, TXF_LARC_NUM, i);
223
iwl_fwrt_dump_txf(fwrt, dump_data,
224
cfg->lmac[0].txfifo_size[i], 0, i);
225
}
226
227
/* Pull TXF data from LMAC2 */
228
if (fwrt->smem_cfg.num_lmacs > 1) {
229
for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries;
230
i++) {
231
/* Mark the number of TXF we're pulling now */
232
iwl_trans_write_prph(fwrt->trans,
233
TXF_LARC_NUM +
234
LMAC2_PRPH_OFFSET, i);
235
iwl_fwrt_dump_txf(fwrt, dump_data,
236
cfg->lmac[1].txfifo_size[i],
237
LMAC2_PRPH_OFFSET,
238
i + cfg->num_txfifo_entries);
239
}
240
}
241
}
242
243
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
244
fw_has_capa(&fwrt->fw->ucode_capa,
245
IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) {
246
/* Pull UMAC internal TXF data from all TXFs */
247
for (i = 0;
248
i < ARRAY_SIZE(fwrt->smem_cfg.internal_txfifo_size);
249
i++) {
250
fifo_hdr = (void *)(*dump_data)->data;
251
fifo_data = (void *)fifo_hdr->data;
252
fifo_len = fwrt->smem_cfg.internal_txfifo_size[i];
253
254
/* No need to try to read the data if the length is 0 */
255
if (fifo_len == 0)
256
continue;
257
258
/* Add a TLV for the internal FIFOs */
259
(*dump_data)->type =
260
cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF);
261
(*dump_data)->len =
262
cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
263
264
fifo_hdr->fifo_num = cpu_to_le32(i);
265
266
/* Mark the number of TXF we're pulling now */
267
iwl_trans_write_prph(fwrt->trans, TXF_CPU2_NUM, i +
268
fwrt->smem_cfg.num_txfifo_entries);
269
270
fifo_hdr->available_bytes =
271
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
272
TXF_CPU2_FIFO_ITEM_CNT));
273
fifo_hdr->wr_ptr =
274
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
275
TXF_CPU2_WR_PTR));
276
fifo_hdr->rd_ptr =
277
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
278
TXF_CPU2_RD_PTR));
279
fifo_hdr->fence_ptr =
280
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
281
TXF_CPU2_FENCE_PTR));
282
fifo_hdr->fence_mode =
283
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
284
TXF_CPU2_LOCK_FENCE));
285
286
/* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */
287
iwl_trans_write_prph(fwrt->trans,
288
TXF_CPU2_READ_MODIFY_ADDR,
289
TXF_CPU2_WR_PTR);
290
291
/* Dummy-read to advance the read pointer to head */
292
iwl_trans_read_prph(fwrt->trans,
293
TXF_CPU2_READ_MODIFY_DATA);
294
295
/* Read FIFO */
296
fifo_len /= sizeof(u32); /* Size in DWORDS */
297
for (j = 0; j < fifo_len; j++)
298
fifo_data[j] =
299
iwl_trans_read_prph(fwrt->trans,
300
TXF_CPU2_READ_MODIFY_DATA);
301
*dump_data = iwl_fw_error_next_data(*dump_data);
302
}
303
}
304
305
iwl_trans_release_nic_access(fwrt->trans);
306
}
307
308
struct iwl_prph_range {
309
u32 start, end;
310
};
311
312
static const struct iwl_prph_range iwl_prph_dump_addr_comm[] = {
313
{ .start = 0x00a00000, .end = 0x00a00000 },
314
{ .start = 0x00a0000c, .end = 0x00a00024 },
315
{ .start = 0x00a0002c, .end = 0x00a0003c },
316
{ .start = 0x00a00410, .end = 0x00a00418 },
317
{ .start = 0x00a00420, .end = 0x00a00420 },
318
{ .start = 0x00a00428, .end = 0x00a00428 },
319
{ .start = 0x00a00430, .end = 0x00a0043c },
320
{ .start = 0x00a00444, .end = 0x00a00444 },
321
{ .start = 0x00a004c0, .end = 0x00a004cc },
322
{ .start = 0x00a004d8, .end = 0x00a004d8 },
323
{ .start = 0x00a004e0, .end = 0x00a004f0 },
324
{ .start = 0x00a00840, .end = 0x00a00840 },
325
{ .start = 0x00a00850, .end = 0x00a00858 },
326
{ .start = 0x00a01004, .end = 0x00a01008 },
327
{ .start = 0x00a01010, .end = 0x00a01010 },
328
{ .start = 0x00a01018, .end = 0x00a01018 },
329
{ .start = 0x00a01024, .end = 0x00a01024 },
330
{ .start = 0x00a0102c, .end = 0x00a01034 },
331
{ .start = 0x00a0103c, .end = 0x00a01040 },
332
{ .start = 0x00a01048, .end = 0x00a01094 },
333
{ .start = 0x00a01c00, .end = 0x00a01c20 },
334
{ .start = 0x00a01c58, .end = 0x00a01c58 },
335
{ .start = 0x00a01c7c, .end = 0x00a01c7c },
336
{ .start = 0x00a01c28, .end = 0x00a01c54 },
337
{ .start = 0x00a01c5c, .end = 0x00a01c5c },
338
{ .start = 0x00a01c60, .end = 0x00a01cdc },
339
{ .start = 0x00a01ce0, .end = 0x00a01d0c },
340
{ .start = 0x00a01d18, .end = 0x00a01d20 },
341
{ .start = 0x00a01d2c, .end = 0x00a01d30 },
342
{ .start = 0x00a01d40, .end = 0x00a01d5c },
343
{ .start = 0x00a01d80, .end = 0x00a01d80 },
344
{ .start = 0x00a01d98, .end = 0x00a01d9c },
345
{ .start = 0x00a01da8, .end = 0x00a01da8 },
346
{ .start = 0x00a01db8, .end = 0x00a01df4 },
347
{ .start = 0x00a01dc0, .end = 0x00a01dfc },
348
{ .start = 0x00a01e00, .end = 0x00a01e2c },
349
{ .start = 0x00a01e40, .end = 0x00a01e60 },
350
{ .start = 0x00a01e68, .end = 0x00a01e6c },
351
{ .start = 0x00a01e74, .end = 0x00a01e74 },
352
{ .start = 0x00a01e84, .end = 0x00a01e90 },
353
{ .start = 0x00a01e9c, .end = 0x00a01ec4 },
354
{ .start = 0x00a01ed0, .end = 0x00a01ee0 },
355
{ .start = 0x00a01f00, .end = 0x00a01f1c },
356
{ .start = 0x00a01f44, .end = 0x00a01ffc },
357
{ .start = 0x00a02000, .end = 0x00a02048 },
358
{ .start = 0x00a02068, .end = 0x00a020f0 },
359
{ .start = 0x00a02100, .end = 0x00a02118 },
360
{ .start = 0x00a02140, .end = 0x00a0214c },
361
{ .start = 0x00a02168, .end = 0x00a0218c },
362
{ .start = 0x00a021c0, .end = 0x00a021c0 },
363
{ .start = 0x00a02400, .end = 0x00a02410 },
364
{ .start = 0x00a02418, .end = 0x00a02420 },
365
{ .start = 0x00a02428, .end = 0x00a0242c },
366
{ .start = 0x00a02434, .end = 0x00a02434 },
367
{ .start = 0x00a02440, .end = 0x00a02460 },
368
{ .start = 0x00a02468, .end = 0x00a024b0 },
369
{ .start = 0x00a024c8, .end = 0x00a024cc },
370
{ .start = 0x00a02500, .end = 0x00a02504 },
371
{ .start = 0x00a0250c, .end = 0x00a02510 },
372
{ .start = 0x00a02540, .end = 0x00a02554 },
373
{ .start = 0x00a02580, .end = 0x00a025f4 },
374
{ .start = 0x00a02600, .end = 0x00a0260c },
375
{ .start = 0x00a02648, .end = 0x00a02650 },
376
{ .start = 0x00a02680, .end = 0x00a02680 },
377
{ .start = 0x00a026c0, .end = 0x00a026d0 },
378
{ .start = 0x00a02700, .end = 0x00a0270c },
379
{ .start = 0x00a02804, .end = 0x00a02804 },
380
{ .start = 0x00a02818, .end = 0x00a0281c },
381
{ .start = 0x00a02c00, .end = 0x00a02db4 },
382
{ .start = 0x00a02df4, .end = 0x00a02fb0 },
383
{ .start = 0x00a03000, .end = 0x00a03014 },
384
{ .start = 0x00a0301c, .end = 0x00a0302c },
385
{ .start = 0x00a03034, .end = 0x00a03038 },
386
{ .start = 0x00a03040, .end = 0x00a03048 },
387
{ .start = 0x00a03060, .end = 0x00a03068 },
388
{ .start = 0x00a03070, .end = 0x00a03074 },
389
{ .start = 0x00a0307c, .end = 0x00a0307c },
390
{ .start = 0x00a03080, .end = 0x00a03084 },
391
{ .start = 0x00a0308c, .end = 0x00a03090 },
392
{ .start = 0x00a03098, .end = 0x00a03098 },
393
{ .start = 0x00a030a0, .end = 0x00a030a0 },
394
{ .start = 0x00a030a8, .end = 0x00a030b4 },
395
{ .start = 0x00a030bc, .end = 0x00a030bc },
396
{ .start = 0x00a030c0, .end = 0x00a0312c },
397
{ .start = 0x00a03c00, .end = 0x00a03c5c },
398
{ .start = 0x00a04400, .end = 0x00a04454 },
399
{ .start = 0x00a04460, .end = 0x00a04474 },
400
{ .start = 0x00a044c0, .end = 0x00a044ec },
401
{ .start = 0x00a04500, .end = 0x00a04504 },
402
{ .start = 0x00a04510, .end = 0x00a04538 },
403
{ .start = 0x00a04540, .end = 0x00a04548 },
404
{ .start = 0x00a04560, .end = 0x00a0457c },
405
{ .start = 0x00a04590, .end = 0x00a04598 },
406
{ .start = 0x00a045c0, .end = 0x00a045f4 },
407
};
408
409
static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = {
410
{ .start = 0x00a05c00, .end = 0x00a05c18 },
411
{ .start = 0x00a05400, .end = 0x00a056e8 },
412
{ .start = 0x00a08000, .end = 0x00a098bc },
413
{ .start = 0x00a02400, .end = 0x00a02758 },
414
{ .start = 0x00a04764, .end = 0x00a0476c },
415
{ .start = 0x00a04770, .end = 0x00a04774 },
416
{ .start = 0x00a04620, .end = 0x00a04624 },
417
};
418
419
static const struct iwl_prph_range iwl_prph_dump_addr_22000[] = {
420
{ .start = 0x00a00000, .end = 0x00a00000 },
421
{ .start = 0x00a0000c, .end = 0x00a00024 },
422
{ .start = 0x00a0002c, .end = 0x00a00034 },
423
{ .start = 0x00a0003c, .end = 0x00a0003c },
424
{ .start = 0x00a00410, .end = 0x00a00418 },
425
{ .start = 0x00a00420, .end = 0x00a00420 },
426
{ .start = 0x00a00428, .end = 0x00a00428 },
427
{ .start = 0x00a00430, .end = 0x00a0043c },
428
{ .start = 0x00a00444, .end = 0x00a00444 },
429
{ .start = 0x00a00840, .end = 0x00a00840 },
430
{ .start = 0x00a00850, .end = 0x00a00858 },
431
{ .start = 0x00a01004, .end = 0x00a01008 },
432
{ .start = 0x00a01010, .end = 0x00a01010 },
433
{ .start = 0x00a01018, .end = 0x00a01018 },
434
{ .start = 0x00a01024, .end = 0x00a01024 },
435
{ .start = 0x00a0102c, .end = 0x00a01034 },
436
{ .start = 0x00a0103c, .end = 0x00a01040 },
437
{ .start = 0x00a01048, .end = 0x00a01050 },
438
{ .start = 0x00a01058, .end = 0x00a01058 },
439
{ .start = 0x00a01060, .end = 0x00a01070 },
440
{ .start = 0x00a0108c, .end = 0x00a0108c },
441
{ .start = 0x00a01c20, .end = 0x00a01c28 },
442
{ .start = 0x00a01d10, .end = 0x00a01d10 },
443
{ .start = 0x00a01e28, .end = 0x00a01e2c },
444
{ .start = 0x00a01e60, .end = 0x00a01e60 },
445
{ .start = 0x00a01e80, .end = 0x00a01e80 },
446
{ .start = 0x00a01ea0, .end = 0x00a01ea0 },
447
{ .start = 0x00a02000, .end = 0x00a0201c },
448
{ .start = 0x00a02024, .end = 0x00a02024 },
449
{ .start = 0x00a02040, .end = 0x00a02048 },
450
{ .start = 0x00a020c0, .end = 0x00a020e0 },
451
{ .start = 0x00a02400, .end = 0x00a02404 },
452
{ .start = 0x00a0240c, .end = 0x00a02414 },
453
{ .start = 0x00a0241c, .end = 0x00a0243c },
454
{ .start = 0x00a02448, .end = 0x00a024bc },
455
{ .start = 0x00a024c4, .end = 0x00a024cc },
456
{ .start = 0x00a02508, .end = 0x00a02508 },
457
{ .start = 0x00a02510, .end = 0x00a02514 },
458
{ .start = 0x00a0251c, .end = 0x00a0251c },
459
{ .start = 0x00a0252c, .end = 0x00a0255c },
460
{ .start = 0x00a02564, .end = 0x00a025a0 },
461
{ .start = 0x00a025a8, .end = 0x00a025b4 },
462
{ .start = 0x00a025c0, .end = 0x00a025c0 },
463
{ .start = 0x00a025e8, .end = 0x00a025f4 },
464
{ .start = 0x00a02c08, .end = 0x00a02c18 },
465
{ .start = 0x00a02c2c, .end = 0x00a02c38 },
466
{ .start = 0x00a02c68, .end = 0x00a02c78 },
467
{ .start = 0x00a03000, .end = 0x00a03000 },
468
{ .start = 0x00a03010, .end = 0x00a03014 },
469
{ .start = 0x00a0301c, .end = 0x00a0302c },
470
{ .start = 0x00a03034, .end = 0x00a03038 },
471
{ .start = 0x00a03040, .end = 0x00a03044 },
472
{ .start = 0x00a03060, .end = 0x00a03068 },
473
{ .start = 0x00a03070, .end = 0x00a03070 },
474
{ .start = 0x00a0307c, .end = 0x00a03084 },
475
{ .start = 0x00a0308c, .end = 0x00a03090 },
476
{ .start = 0x00a03098, .end = 0x00a03098 },
477
{ .start = 0x00a030a0, .end = 0x00a030a0 },
478
{ .start = 0x00a030a8, .end = 0x00a030b4 },
479
{ .start = 0x00a030bc, .end = 0x00a030c0 },
480
{ .start = 0x00a030c8, .end = 0x00a030f4 },
481
{ .start = 0x00a03100, .end = 0x00a0312c },
482
{ .start = 0x00a03c00, .end = 0x00a03c5c },
483
{ .start = 0x00a04400, .end = 0x00a04454 },
484
{ .start = 0x00a04460, .end = 0x00a04474 },
485
{ .start = 0x00a044c0, .end = 0x00a044ec },
486
{ .start = 0x00a04500, .end = 0x00a04504 },
487
{ .start = 0x00a04510, .end = 0x00a04538 },
488
{ .start = 0x00a04540, .end = 0x00a04548 },
489
{ .start = 0x00a04560, .end = 0x00a04560 },
490
{ .start = 0x00a04570, .end = 0x00a0457c },
491
{ .start = 0x00a04590, .end = 0x00a04590 },
492
{ .start = 0x00a04598, .end = 0x00a04598 },
493
{ .start = 0x00a045c0, .end = 0x00a045f4 },
494
{ .start = 0x00a05c18, .end = 0x00a05c1c },
495
{ .start = 0x00a0c000, .end = 0x00a0c018 },
496
{ .start = 0x00a0c020, .end = 0x00a0c028 },
497
{ .start = 0x00a0c038, .end = 0x00a0c094 },
498
{ .start = 0x00a0c0c0, .end = 0x00a0c104 },
499
{ .start = 0x00a0c10c, .end = 0x00a0c118 },
500
{ .start = 0x00a0c150, .end = 0x00a0c174 },
501
{ .start = 0x00a0c17c, .end = 0x00a0c188 },
502
{ .start = 0x00a0c190, .end = 0x00a0c198 },
503
{ .start = 0x00a0c1a0, .end = 0x00a0c1a8 },
504
{ .start = 0x00a0c1b0, .end = 0x00a0c1b8 },
505
};
506
507
static const struct iwl_prph_range iwl_prph_dump_addr_ax210[] = {
508
{ .start = 0x00d03c00, .end = 0x00d03c64 },
509
{ .start = 0x00d05c18, .end = 0x00d05c1c },
510
{ .start = 0x00d0c000, .end = 0x00d0c174 },
511
};
512
513
static void iwl_read_prph_block(struct iwl_trans *trans, u32 start,
514
u32 len_bytes, __le32 *data)
515
{
516
u32 i;
517
518
for (i = 0; i < len_bytes; i += 4)
519
*data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i));
520
}
521
522
static void iwl_dump_prph(struct iwl_fw_runtime *fwrt,
523
const struct iwl_prph_range *iwl_prph_dump_addr,
524
u32 range_len, void *ptr)
525
{
526
struct iwl_fw_error_dump_prph *prph;
527
struct iwl_trans *trans = fwrt->trans;
528
struct iwl_fw_error_dump_data **data =
529
(struct iwl_fw_error_dump_data **)ptr;
530
u32 i;
531
532
if (!data)
533
return;
534
535
IWL_DEBUG_INFO(trans, "WRT PRPH dump\n");
536
537
if (!iwl_trans_grab_nic_access(trans))
538
return;
539
540
for (i = 0; i < range_len; i++) {
541
/* The range includes both boundaries */
542
int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
543
iwl_prph_dump_addr[i].start + 4;
544
545
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
546
(*data)->len = cpu_to_le32(sizeof(*prph) +
547
num_bytes_in_chunk);
548
prph = (void *)(*data)->data;
549
prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
550
551
iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start,
552
/* our range is inclusive, hence + 4 */
553
iwl_prph_dump_addr[i].end -
554
iwl_prph_dump_addr[i].start + 4,
555
(void *)prph->data);
556
557
*data = iwl_fw_error_next_data(*data);
558
}
559
560
iwl_trans_release_nic_access(trans);
561
}
562
563
/*
564
* alloc_sgtable - allocates (chained) scatterlist in the given size,
565
* fills it with pages and returns it
566
* @size: the size (in bytes) of the table
567
*/
568
static struct scatterlist *alloc_sgtable(ssize_t size)
569
{
570
struct scatterlist *result = NULL, *prev;
571
int nents, i, n_prev;
572
573
nents = DIV_ROUND_UP(size, PAGE_SIZE);
574
575
#define N_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(*result))
576
/*
577
* We need an additional entry for table chaining,
578
* this ensures the loop can finish i.e. we can
579
* fit at least two entries per page (obviously,
580
* many more really fit.)
581
*/
582
BUILD_BUG_ON(N_ENTRIES_PER_PAGE < 2);
583
584
while (nents > 0) {
585
struct scatterlist *new, *iter;
586
int n_fill, n_alloc;
587
588
if (nents <= N_ENTRIES_PER_PAGE) {
589
/* last needed table */
590
n_fill = nents;
591
n_alloc = nents;
592
nents = 0;
593
} else {
594
/* fill a page with entries */
595
n_alloc = N_ENTRIES_PER_PAGE;
596
/* reserve one for chaining */
597
n_fill = n_alloc - 1;
598
nents -= n_fill;
599
}
600
601
new = kcalloc(n_alloc, sizeof(*new), GFP_KERNEL);
602
if (!new) {
603
if (result)
604
_devcd_free_sgtable(result);
605
return NULL;
606
}
607
sg_init_table(new, n_alloc);
608
609
if (!result)
610
result = new;
611
else
612
sg_chain(prev, n_prev, new);
613
prev = new;
614
n_prev = n_alloc;
615
616
for_each_sg(new, iter, n_fill, i) {
617
struct page *new_page = alloc_page(GFP_KERNEL);
618
619
if (!new_page) {
620
_devcd_free_sgtable(result);
621
return NULL;
622
}
623
624
sg_set_page(iter, new_page, PAGE_SIZE, 0);
625
}
626
}
627
628
return result;
629
}
630
631
static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt,
632
const struct iwl_prph_range *iwl_prph_dump_addr,
633
u32 range_len, void *ptr)
634
{
635
u32 *prph_len = (u32 *)ptr;
636
int i, num_bytes_in_chunk;
637
638
if (!prph_len)
639
return;
640
641
for (i = 0; i < range_len; i++) {
642
/* The range includes both boundaries */
643
num_bytes_in_chunk =
644
iwl_prph_dump_addr[i].end -
645
iwl_prph_dump_addr[i].start + 4;
646
647
*prph_len += sizeof(struct iwl_fw_error_dump_data) +
648
sizeof(struct iwl_fw_error_dump_prph) +
649
num_bytes_in_chunk;
650
}
651
}
652
653
static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr,
654
void (*handler)(struct iwl_fw_runtime *,
655
const struct iwl_prph_range *,
656
u32, void *))
657
{
658
u32 range_len;
659
660
if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
661
range_len = ARRAY_SIZE(iwl_prph_dump_addr_ax210);
662
handler(fwrt, iwl_prph_dump_addr_ax210, range_len, ptr);
663
} else if (fwrt->trans->mac_cfg->device_family >=
664
IWL_DEVICE_FAMILY_22000) {
665
range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000);
666
handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr);
667
} else {
668
range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm);
669
handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr);
670
671
if (fwrt->trans->mac_cfg->mq_rx_supported) {
672
range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000);
673
handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr);
674
}
675
}
676
}
677
678
static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt,
679
struct iwl_fw_error_dump_data **dump_data,
680
u32 len, u32 ofs, u32 type)
681
{
682
struct iwl_fw_error_dump_mem *dump_mem;
683
684
if (!len)
685
return;
686
687
(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
688
(*dump_data)->len = cpu_to_le32(len + sizeof(*dump_mem));
689
dump_mem = (void *)(*dump_data)->data;
690
dump_mem->type = cpu_to_le32(type);
691
dump_mem->offset = cpu_to_le32(ofs);
692
iwl_trans_read_mem_bytes(fwrt->trans, ofs, dump_mem->data, len);
693
*dump_data = iwl_fw_error_next_data(*dump_data);
694
695
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem)
696
fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, ofs,
697
dump_mem->data, len);
698
699
IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", dump_mem->type);
700
}
701
702
#define ADD_LEN(len, item_len, const_len) \
703
do {size_t item = item_len; len += (!!item) * const_len + item; } \
704
while (0)
705
706
static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt,
707
struct iwl_fwrt_shared_mem_cfg *mem_cfg)
708
{
709
size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) +
710
sizeof(struct iwl_fw_error_dump_fifo);
711
u32 fifo_len = 0;
712
int i;
713
714
if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF))
715
return 0;
716
717
/* Count RXF2 size */
718
ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len);
719
720
/* Count RXF1 sizes */
721
if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC))
722
mem_cfg->num_lmacs = MAX_NUM_LMAC;
723
724
for (i = 0; i < mem_cfg->num_lmacs; i++)
725
ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len);
726
727
return fifo_len;
728
}
729
730
static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt,
731
struct iwl_fwrt_shared_mem_cfg *mem_cfg)
732
{
733
size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) +
734
sizeof(struct iwl_fw_error_dump_fifo);
735
u32 fifo_len = 0;
736
int i;
737
738
if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF))
739
goto dump_internal_txf;
740
741
/* Count TXF sizes */
742
if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC))
743
mem_cfg->num_lmacs = MAX_NUM_LMAC;
744
745
for (i = 0; i < mem_cfg->num_lmacs; i++) {
746
int j;
747
748
for (j = 0; j < mem_cfg->num_txfifo_entries; j++)
749
ADD_LEN(fifo_len, mem_cfg->lmac[i].txfifo_size[j],
750
hdr_len);
751
}
752
753
dump_internal_txf:
754
if (!(iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
755
fw_has_capa(&fwrt->fw->ucode_capa,
756
IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)))
757
goto out;
758
759
for (i = 0; i < ARRAY_SIZE(mem_cfg->internal_txfifo_size); i++)
760
ADD_LEN(fifo_len, mem_cfg->internal_txfifo_size[i], hdr_len);
761
762
out:
763
return fifo_len;
764
}
765
766
static void iwl_dump_paging(struct iwl_fw_runtime *fwrt,
767
struct iwl_fw_error_dump_data **data)
768
{
769
int i;
770
771
IWL_DEBUG_INFO(fwrt, "WRT paging dump\n");
772
for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) {
773
struct iwl_fw_error_dump_paging *paging;
774
struct page *pages =
775
fwrt->fw_paging_db[i].fw_paging_block;
776
dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys;
777
778
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
779
(*data)->len = cpu_to_le32(sizeof(*paging) +
780
PAGING_BLOCK_SIZE);
781
paging = (void *)(*data)->data;
782
paging->index = cpu_to_le32(i);
783
dma_sync_single_for_cpu(fwrt->trans->dev, addr,
784
PAGING_BLOCK_SIZE,
785
DMA_BIDIRECTIONAL);
786
memcpy(paging->data, page_address(pages),
787
PAGING_BLOCK_SIZE);
788
dma_sync_single_for_device(fwrt->trans->dev, addr,
789
PAGING_BLOCK_SIZE,
790
DMA_BIDIRECTIONAL);
791
(*data) = iwl_fw_error_next_data(*data);
792
793
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem)
794
fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx,
795
fwrt->fw_paging_db[i].fw_offs,
796
paging->data,
797
PAGING_BLOCK_SIZE);
798
}
799
}
800
801
static struct iwl_fw_error_dump_file *
802
iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
803
struct iwl_fw_dump_ptrs *fw_error_dump,
804
struct iwl_fwrt_dump_data *data)
805
{
806
struct iwl_fw_error_dump_file *dump_file;
807
struct iwl_fw_error_dump_data *dump_data;
808
struct iwl_fw_error_dump_info *dump_info;
809
struct iwl_fw_error_dump_smem_cfg *dump_smem_cfg;
810
struct iwl_fw_error_dump_trigger_desc *dump_trig;
811
u32 sram_len, sram_ofs;
812
const struct iwl_fw_dbg_mem_seg_tlv *fw_mem = fwrt->fw->dbg.mem_tlv;
813
struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg;
814
u32 file_len, fifo_len = 0, prph_len = 0, radio_len = 0;
815
u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->mac_cfg->base->smem_len;
816
u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ?
817
0 : fwrt->trans->cfg->dccm2_len;
818
int i;
819
820
/* SRAM - include stack CCM if driver knows the values for it */
821
if (!fwrt->trans->cfg->dccm_offset ||
822
!fwrt->trans->cfg->dccm_len) {
823
const struct fw_img *img;
824
825
if (fwrt->cur_fw_img >= IWL_UCODE_TYPE_MAX)
826
return NULL;
827
img = &fwrt->fw->img[fwrt->cur_fw_img];
828
sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
829
sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
830
} else {
831
sram_ofs = fwrt->trans->cfg->dccm_offset;
832
sram_len = fwrt->trans->cfg->dccm_len;
833
}
834
835
/* reading RXF/TXF sizes */
836
if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) {
837
fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg);
838
fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
839
840
/* Make room for PRPH registers */
841
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH))
842
iwl_fw_prph_handler(fwrt, &prph_len,
843
iwl_fw_get_prph_len);
844
845
if (fwrt->trans->mac_cfg->device_family ==
846
IWL_DEVICE_FAMILY_7000 &&
847
iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG))
848
radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;
849
}
850
851
file_len = sizeof(*dump_file) + fifo_len + prph_len + radio_len;
852
853
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO))
854
file_len += sizeof(*dump_data) + sizeof(*dump_info);
855
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG))
856
file_len += sizeof(*dump_data) + sizeof(*dump_smem_cfg);
857
858
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) {
859
size_t hdr_len = sizeof(*dump_data) +
860
sizeof(struct iwl_fw_error_dump_mem);
861
862
/* Dump SRAM only if no mem_tlvs */
863
if (!fwrt->fw->dbg.n_mem_tlv)
864
ADD_LEN(file_len, sram_len, hdr_len);
865
866
/* Make room for all mem types that exist */
867
ADD_LEN(file_len, smem_len, hdr_len);
868
ADD_LEN(file_len, sram2_len, hdr_len);
869
870
for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++)
871
ADD_LEN(file_len, le32_to_cpu(fw_mem[i].len), hdr_len);
872
}
873
874
/* Make room for fw's virtual image pages, if it exists */
875
if (iwl_fw_dbg_is_paging_enabled(fwrt))
876
file_len += fwrt->num_of_paging_blk *
877
(sizeof(*dump_data) +
878
sizeof(struct iwl_fw_error_dump_paging) +
879
PAGING_BLOCK_SIZE);
880
881
if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) {
882
file_len += sizeof(*dump_data) +
883
fwrt->trans->mac_cfg->base->d3_debug_data_length * 2;
884
}
885
886
/* If we only want a monitor dump, reset the file length */
887
if (data->monitor_only) {
888
file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 +
889
sizeof(*dump_info) + sizeof(*dump_smem_cfg);
890
}
891
892
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) &&
893
data->desc)
894
file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
895
data->desc->len;
896
897
dump_file = vzalloc(file_len);
898
if (!dump_file)
899
return NULL;
900
901
fw_error_dump->fwrt_ptr = dump_file;
902
903
dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
904
dump_data = (void *)dump_file->data;
905
906
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) {
907
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
908
dump_data->len = cpu_to_le32(sizeof(*dump_info));
909
dump_info = (void *)dump_data->data;
910
dump_info->hw_type =
911
cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev));
912
dump_info->hw_step =
913
cpu_to_le32(fwrt->trans->info.hw_rev_step);
914
memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable,
915
sizeof(dump_info->fw_human_readable));
916
strscpy_pad(dump_info->dev_human_readable,
917
fwrt->trans->info.name,
918
sizeof(dump_info->dev_human_readable));
919
#if defined(__linux__)
920
strscpy_pad(dump_info->bus_human_readable, fwrt->dev->bus->name,
921
sizeof(dump_info->bus_human_readable));
922
#elif defined(__FreeBSD__) /* XXX TODO */
923
strscpy_pad(dump_info->bus_human_readable, "<bus>",
924
sizeof(dump_info->bus_human_readable));
925
#endif
926
dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs;
927
dump_info->lmac_err_id[0] =
928
cpu_to_le32(fwrt->dump.lmac_err_id[0]);
929
if (fwrt->smem_cfg.num_lmacs > 1)
930
dump_info->lmac_err_id[1] =
931
cpu_to_le32(fwrt->dump.lmac_err_id[1]);
932
dump_info->umac_err_id = cpu_to_le32(fwrt->dump.umac_err_id);
933
934
dump_data = iwl_fw_error_next_data(dump_data);
935
}
936
937
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) {
938
/* Dump shared memory configuration */
939
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG);
940
dump_data->len = cpu_to_le32(sizeof(*dump_smem_cfg));
941
dump_smem_cfg = (void *)dump_data->data;
942
dump_smem_cfg->num_lmacs = cpu_to_le32(mem_cfg->num_lmacs);
943
dump_smem_cfg->num_txfifo_entries =
944
cpu_to_le32(mem_cfg->num_txfifo_entries);
945
for (i = 0; i < MAX_NUM_LMAC; i++) {
946
int j;
947
u32 *txf_size = mem_cfg->lmac[i].txfifo_size;
948
949
for (j = 0; j < TX_FIFO_MAX_NUM; j++)
950
dump_smem_cfg->lmac[i].txfifo_size[j] =
951
cpu_to_le32(txf_size[j]);
952
dump_smem_cfg->lmac[i].rxfifo1_size =
953
cpu_to_le32(mem_cfg->lmac[i].rxfifo1_size);
954
}
955
dump_smem_cfg->rxfifo2_size =
956
cpu_to_le32(mem_cfg->rxfifo2_size);
957
dump_smem_cfg->internal_txfifo_addr =
958
cpu_to_le32(mem_cfg->internal_txfifo_addr);
959
for (i = 0; i < TX_FIFO_INTERNAL_MAX_NUM; i++) {
960
dump_smem_cfg->internal_txfifo_size[i] =
961
cpu_to_le32(mem_cfg->internal_txfifo_size[i]);
962
}
963
964
dump_data = iwl_fw_error_next_data(dump_data);
965
}
966
967
/* We only dump the FIFOs if the FW is in error state */
968
if (fifo_len) {
969
iwl_fw_dump_rxf(fwrt, &dump_data);
970
iwl_fw_dump_txf(fwrt, &dump_data);
971
}
972
973
if (radio_len)
974
iwl_read_radio_regs(fwrt, &dump_data);
975
976
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) &&
977
data->desc) {
978
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
979
dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
980
data->desc->len);
981
dump_trig = (void *)dump_data->data;
982
memcpy(dump_trig, &data->desc->trig_desc,
983
sizeof(*dump_trig) + data->desc->len);
984
985
dump_data = iwl_fw_error_next_data(dump_data);
986
}
987
988
/* In case we only want monitor dump, skip to dump trasport data */
989
if (data->monitor_only)
990
goto out;
991
992
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) {
993
const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem =
994
fwrt->fw->dbg.mem_tlv;
995
996
if (!fwrt->fw->dbg.n_mem_tlv)
997
iwl_fw_dump_mem(fwrt, &dump_data, sram_len, sram_ofs,
998
IWL_FW_ERROR_DUMP_MEM_SRAM);
999
1000
for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++) {
1001
u32 len = le32_to_cpu(fw_dbg_mem[i].len);
1002
u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs);
1003
1004
iwl_fw_dump_mem(fwrt, &dump_data, len, ofs,
1005
le32_to_cpu(fw_dbg_mem[i].data_type));
1006
}
1007
1008
iwl_fw_dump_mem(fwrt, &dump_data, smem_len,
1009
fwrt->trans->mac_cfg->base->smem_offset,
1010
IWL_FW_ERROR_DUMP_MEM_SMEM);
1011
1012
iwl_fw_dump_mem(fwrt, &dump_data, sram2_len,
1013
fwrt->trans->cfg->dccm2_offset,
1014
IWL_FW_ERROR_DUMP_MEM_SRAM);
1015
}
1016
1017
if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) {
1018
u32 addr = fwrt->trans->mac_cfg->base->d3_debug_data_base_addr;
1019
size_t data_size = fwrt->trans->mac_cfg->base->d3_debug_data_length;
1020
1021
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
1022
dump_data->len = cpu_to_le32(data_size * 2);
1023
1024
memcpy(dump_data->data, fwrt->dump.d3_debug_data, data_size);
1025
1026
kfree(fwrt->dump.d3_debug_data);
1027
fwrt->dump.d3_debug_data = NULL;
1028
1029
iwl_trans_read_mem_bytes(fwrt->trans, addr,
1030
dump_data->data + data_size,
1031
data_size);
1032
1033
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem)
1034
fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, addr,
1035
dump_data->data + data_size,
1036
data_size);
1037
1038
dump_data = iwl_fw_error_next_data(dump_data);
1039
}
1040
1041
/* Dump fw's virtual image */
1042
if (iwl_fw_dbg_is_paging_enabled(fwrt))
1043
iwl_dump_paging(fwrt, &dump_data);
1044
1045
if (prph_len)
1046
iwl_fw_prph_handler(fwrt, &dump_data, iwl_dump_prph);
1047
1048
out:
1049
dump_file->file_len = cpu_to_le32(file_len);
1050
return dump_file;
1051
}
1052
1053
/**
1054
* struct iwl_dump_ini_region_data - region data
1055
* @reg_tlv: region TLV
1056
* @dump_data: dump data
1057
*/
1058
struct iwl_dump_ini_region_data {
1059
struct iwl_ucode_tlv *reg_tlv;
1060
struct iwl_fwrt_dump_data *dump_data;
1061
};
1062
1063
static int iwl_dump_ini_prph_mac_iter_common(struct iwl_fw_runtime *fwrt,
1064
void *range_ptr, u32 addr,
1065
__le32 size)
1066
{
1067
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1068
__le32 *val = range->data;
1069
int i;
1070
1071
range->internal_base_addr = cpu_to_le32(addr);
1072
range->range_data_size = size;
1073
for (i = 0; i < le32_to_cpu(size); i += 4)
1074
*val++ = cpu_to_le32(iwl_read_prph(fwrt->trans, addr + i));
1075
1076
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1077
}
1078
1079
static int
1080
iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt,
1081
struct iwl_dump_ini_region_data *reg_data,
1082
void *range_ptr, u32 range_len, int idx)
1083
{
1084
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1085
u32 addr = le32_to_cpu(reg->addrs[idx]) +
1086
le32_to_cpu(reg->dev_addr.offset);
1087
1088
return iwl_dump_ini_prph_mac_iter_common(fwrt, range_ptr, addr,
1089
reg->dev_addr.size);
1090
}
1091
1092
static int
1093
iwl_dump_ini_prph_mac_block_iter(struct iwl_fw_runtime *fwrt,
1094
struct iwl_dump_ini_region_data *reg_data,
1095
void *range_ptr, u32 range_len, int idx)
1096
{
1097
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1098
struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs;
1099
u32 addr = le32_to_cpu(reg->dev_addr_range.offset) +
1100
le32_to_cpu(pairs[idx].addr);
1101
1102
return iwl_dump_ini_prph_mac_iter_common(fwrt, range_ptr, addr,
1103
pairs[idx].size);
1104
}
1105
1106
static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt,
1107
void *range_ptr, u32 addr,
1108
__le32 size, __le32 offset)
1109
{
1110
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1111
__le32 *val = range->data;
1112
u32 indirect_wr_addr = WMAL_INDRCT_RD_CMD1;
1113
u32 indirect_rd_addr = WMAL_MRSPF_1;
1114
u32 prph_val;
1115
u32 dphy_state;
1116
u32 dphy_addr;
1117
u32 prph_stts;
1118
int i;
1119
1120
range->internal_base_addr = cpu_to_le32(addr);
1121
range->range_data_size = size;
1122
1123
if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
1124
indirect_wr_addr = WMAL_INDRCT_CMD1;
1125
1126
indirect_wr_addr += le32_to_cpu(offset);
1127
indirect_rd_addr += le32_to_cpu(offset);
1128
1129
if (!iwl_trans_grab_nic_access(fwrt->trans))
1130
return -EBUSY;
1131
1132
dphy_addr = (offset) ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW;
1133
dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr);
1134
1135
for (i = 0; i < le32_to_cpu(size); i += 4) {
1136
if (dphy_state == HBUS_TIMEOUT ||
1137
(dphy_state & WFPM_PS_CTL_RW_PHYRF_PD_FSM_CURSTATE_MSK) !=
1138
WFPM_PHYRF_STATE_ON) {
1139
*val++ = cpu_to_le32(WFPM_DPHY_OFF);
1140
continue;
1141
}
1142
1143
iwl_write_prph_no_grab(fwrt->trans, indirect_wr_addr,
1144
WMAL_INDRCT_CMD(addr + i));
1145
1146
if (fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_JF1 &&
1147
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_JF2 &&
1148
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_HR1 &&
1149
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_HR2) {
1150
udelay(2);
1151
prph_stts = iwl_read_prph_no_grab(fwrt->trans,
1152
WMAL_MRSPF_STTS);
1153
1154
/* Abort dump if status is 0xA5A5A5A2 or FIFO1 empty */
1155
if (prph_stts == WMAL_TIMEOUT_VAL ||
1156
!WMAL_MRSPF_STTS_IS_FIFO1_NOT_EMPTY(prph_stts))
1157
break;
1158
}
1159
1160
prph_val = iwl_read_prph_no_grab(fwrt->trans,
1161
indirect_rd_addr);
1162
*val++ = cpu_to_le32(prph_val);
1163
}
1164
1165
iwl_trans_release_nic_access(fwrt->trans);
1166
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1167
}
1168
1169
static int
1170
iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt,
1171
struct iwl_dump_ini_region_data *reg_data,
1172
void *range_ptr, u32 range_len, int idx)
1173
{
1174
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1175
u32 addr = le32_to_cpu(reg->addrs[idx]);
1176
1177
return iwl_dump_ini_prph_phy_iter_common(fwrt, range_ptr, addr,
1178
reg->dev_addr.size,
1179
reg->dev_addr.offset);
1180
}
1181
1182
static int
1183
iwl_dump_ini_prph_phy_block_iter(struct iwl_fw_runtime *fwrt,
1184
struct iwl_dump_ini_region_data *reg_data,
1185
void *range_ptr, u32 range_len, int idx)
1186
{
1187
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1188
struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs;
1189
u32 addr = le32_to_cpu(pairs[idx].addr);
1190
1191
return iwl_dump_ini_prph_phy_iter_common(fwrt, range_ptr, addr,
1192
pairs[idx].size,
1193
reg->dev_addr_range.offset);
1194
}
1195
1196
static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt,
1197
struct iwl_dump_ini_region_data *reg_data,
1198
void *range_ptr, u32 range_len, int idx)
1199
{
1200
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1201
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1202
__le32 *val = range->data;
1203
u32 addr = le32_to_cpu(reg->addrs[idx]) +
1204
le32_to_cpu(reg->dev_addr.offset);
1205
int i;
1206
1207
range->internal_base_addr = cpu_to_le32(addr);
1208
range->range_data_size = reg->dev_addr.size;
1209
for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4)
1210
*val++ = cpu_to_le32(iwl_trans_read32(fwrt->trans, addr + i));
1211
1212
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1213
}
1214
1215
static int iwl_dump_ini_config_iter(struct iwl_fw_runtime *fwrt,
1216
struct iwl_dump_ini_region_data *reg_data,
1217
void *range_ptr, u32 range_len, int idx)
1218
{
1219
struct iwl_trans *trans = fwrt->trans;
1220
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1221
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1222
__le32 *val = range->data;
1223
u32 addr = le32_to_cpu(reg->addrs[idx]) +
1224
le32_to_cpu(reg->dev_addr.offset);
1225
int i;
1226
1227
range->internal_base_addr = cpu_to_le32(addr);
1228
range->range_data_size = reg->dev_addr.size;
1229
for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) {
1230
int ret;
1231
u32 tmp;
1232
1233
ret = iwl_trans_read_config32(trans, addr + i, &tmp);
1234
if (ret < 0)
1235
return ret;
1236
1237
*val++ = cpu_to_le32(tmp);
1238
}
1239
1240
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1241
}
1242
1243
static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt,
1244
struct iwl_dump_ini_region_data *reg_data,
1245
void *range_ptr, u32 range_len, int idx)
1246
{
1247
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1248
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1249
u32 addr = le32_to_cpu(reg->addrs[idx]) +
1250
le32_to_cpu(reg->dev_addr.offset);
1251
1252
range->internal_base_addr = cpu_to_le32(addr);
1253
range->range_data_size = reg->dev_addr.size;
1254
iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
1255
le32_to_cpu(reg->dev_addr.size));
1256
1257
if (reg->sub_type == IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_HW_SMEM &&
1258
fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf)
1259
fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx,
1260
range->data,
1261
le32_to_cpu(reg->dev_addr.size));
1262
1263
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1264
}
1265
1266
static int _iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt,
1267
void *range_ptr, u32 range_len, int idx)
1268
{
1269
struct page *page = fwrt->fw_paging_db[idx].fw_paging_block;
1270
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1271
dma_addr_t addr = fwrt->fw_paging_db[idx].fw_paging_phys;
1272
u32 page_size = fwrt->fw_paging_db[idx].fw_paging_size;
1273
1274
range->page_num = cpu_to_le32(idx);
1275
range->range_data_size = cpu_to_le32(page_size);
1276
dma_sync_single_for_cpu(fwrt->trans->dev, addr, page_size,
1277
DMA_BIDIRECTIONAL);
1278
memcpy(range->data, page_address(page), page_size);
1279
dma_sync_single_for_device(fwrt->trans->dev, addr, page_size,
1280
DMA_BIDIRECTIONAL);
1281
1282
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1283
}
1284
1285
static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt,
1286
struct iwl_dump_ini_region_data *reg_data,
1287
void *range_ptr, u32 range_len, int idx)
1288
{
1289
struct iwl_fw_ini_error_dump_range *range;
1290
u32 page_size;
1291
1292
/* all paged index start from 1 to skip CSS section */
1293
idx++;
1294
1295
if (!fwrt->trans->mac_cfg->gen2)
1296
return _iwl_dump_ini_paging_iter(fwrt, range_ptr, range_len, idx);
1297
1298
range = range_ptr;
1299
page_size = fwrt->trans->init_dram.paging[idx].size;
1300
1301
range->page_num = cpu_to_le32(idx);
1302
range->range_data_size = cpu_to_le32(page_size);
1303
memcpy(range->data, fwrt->trans->init_dram.paging[idx].block,
1304
page_size);
1305
1306
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1307
}
1308
1309
static int
1310
iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt,
1311
struct iwl_dump_ini_region_data *reg_data,
1312
void *range_ptr, u32 range_len, int idx)
1313
{
1314
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1315
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1316
struct iwl_dram_data *frag;
1317
u32 alloc_id = le32_to_cpu(reg->dram_alloc_id);
1318
1319
frag = &fwrt->trans->dbg.fw_mon_ini[alloc_id].frags[idx];
1320
1321
range->dram_base_addr = cpu_to_le64(frag->physical);
1322
range->range_data_size = cpu_to_le32(frag->size);
1323
1324
memcpy(range->data, frag->block, frag->size);
1325
1326
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1327
}
1328
1329
static int iwl_dump_ini_mon_smem_iter(struct iwl_fw_runtime *fwrt,
1330
struct iwl_dump_ini_region_data *reg_data,
1331
void *range_ptr, u32 range_len, int idx)
1332
{
1333
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1334
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1335
u32 addr = le32_to_cpu(reg->internal_buffer.base_addr);
1336
1337
range->internal_base_addr = cpu_to_le32(addr);
1338
range->range_data_size = reg->internal_buffer.size;
1339
iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
1340
le32_to_cpu(reg->internal_buffer.size));
1341
1342
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1343
}
1344
1345
static bool iwl_ini_txf_iter(struct iwl_fw_runtime *fwrt,
1346
struct iwl_dump_ini_region_data *reg_data, int idx)
1347
{
1348
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1349
struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data;
1350
struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
1351
int txf_num = cfg->num_txfifo_entries;
1352
int int_txf_num = ARRAY_SIZE(cfg->internal_txfifo_size);
1353
u32 lmac_bitmap = le32_to_cpu(reg->fifos.fid[0]);
1354
1355
if (!idx) {
1356
if (le32_to_cpu(reg->fifos.offset) && cfg->num_lmacs == 1) {
1357
IWL_ERR(fwrt, "WRT: Invalid lmac offset 0x%x\n",
1358
le32_to_cpu(reg->fifos.offset));
1359
return false;
1360
}
1361
1362
iter->internal_txf = 0;
1363
iter->fifo_size = 0;
1364
iter->fifo = -1;
1365
if (le32_to_cpu(reg->fifos.offset))
1366
iter->lmac = 1;
1367
else
1368
iter->lmac = 0;
1369
}
1370
1371
if (!iter->internal_txf) {
1372
for (iter->fifo++; iter->fifo < txf_num; iter->fifo++) {
1373
iter->fifo_size =
1374
cfg->lmac[iter->lmac].txfifo_size[iter->fifo];
1375
if (iter->fifo_size && (lmac_bitmap & BIT(iter->fifo)))
1376
return true;
1377
}
1378
iter->fifo--;
1379
}
1380
1381
iter->internal_txf = 1;
1382
1383
if (!fw_has_capa(&fwrt->fw->ucode_capa,
1384
IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG))
1385
return false;
1386
1387
for (iter->fifo++; iter->fifo < int_txf_num + txf_num; iter->fifo++) {
1388
iter->fifo_size =
1389
cfg->internal_txfifo_size[iter->fifo - txf_num];
1390
if (iter->fifo_size && (lmac_bitmap & BIT(iter->fifo)))
1391
return true;
1392
}
1393
1394
return false;
1395
}
1396
1397
static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt,
1398
struct iwl_dump_ini_region_data *reg_data,
1399
void *range_ptr, u32 range_len, int idx)
1400
{
1401
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1402
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1403
struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data;
1404
struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data;
1405
u32 offs = le32_to_cpu(reg->fifos.offset), addr;
1406
u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs);
1407
u32 registers_size = registers_num * sizeof(*reg_dump);
1408
__le32 *data;
1409
int i;
1410
1411
if (!iwl_ini_txf_iter(fwrt, reg_data, idx))
1412
return -EIO;
1413
1414
if (!iwl_trans_grab_nic_access(fwrt->trans))
1415
return -EBUSY;
1416
1417
range->fifo_hdr.fifo_num = cpu_to_le32(iter->fifo);
1418
range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num);
1419
range->range_data_size = cpu_to_le32(iter->fifo_size + registers_size);
1420
1421
iwl_write_prph_no_grab(fwrt->trans, TXF_LARC_NUM + offs, iter->fifo);
1422
1423
/*
1424
* read txf registers. for each register, write to the dump the
1425
* register address and its value
1426
*/
1427
for (i = 0; i < registers_num; i++) {
1428
addr = le32_to_cpu(reg->addrs[i]) + offs;
1429
1430
reg_dump->addr = cpu_to_le32(addr);
1431
reg_dump->data = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans,
1432
addr));
1433
1434
reg_dump++;
1435
}
1436
1437
if (reg->fifos.hdr_only) {
1438
range->range_data_size = cpu_to_le32(registers_size);
1439
goto out;
1440
}
1441
1442
/* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
1443
iwl_write_prph_no_grab(fwrt->trans, TXF_READ_MODIFY_ADDR + offs,
1444
TXF_WR_PTR + offs);
1445
1446
/* Dummy-read to advance the read pointer to the head */
1447
iwl_read_prph_no_grab(fwrt->trans, TXF_READ_MODIFY_DATA + offs);
1448
1449
/* Read FIFO */
1450
addr = TXF_READ_MODIFY_DATA + offs;
1451
data = (void *)reg_dump;
1452
for (i = 0; i < iter->fifo_size; i += sizeof(*data))
1453
*data++ = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, addr));
1454
1455
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf)
1456
fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx,
1457
reg_dump, iter->fifo_size);
1458
1459
out:
1460
iwl_trans_release_nic_access(fwrt->trans);
1461
1462
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1463
}
1464
1465
static int
1466
iwl_dump_ini_prph_snps_dphyip_iter(struct iwl_fw_runtime *fwrt,
1467
struct iwl_dump_ini_region_data *reg_data,
1468
void *range_ptr, u32 range_len, int idx)
1469
{
1470
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1471
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1472
__le32 *val = range->data;
1473
__le32 offset = reg->dev_addr.offset;
1474
u32 indirect_rd_wr_addr = DPHYIP_INDIRECT;
1475
u32 addr = le32_to_cpu(reg->addrs[idx]);
1476
u32 dphy_state, dphy_addr, prph_val;
1477
int i;
1478
1479
range->internal_base_addr = cpu_to_le32(addr);
1480
range->range_data_size = reg->dev_addr.size;
1481
1482
if (!iwl_trans_grab_nic_access(fwrt->trans))
1483
return -EBUSY;
1484
1485
indirect_rd_wr_addr += le32_to_cpu(offset);
1486
1487
dphy_addr = offset ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW;
1488
dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr);
1489
1490
for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) {
1491
if (dphy_state == HBUS_TIMEOUT ||
1492
(dphy_state & WFPM_PS_CTL_RW_PHYRF_PD_FSM_CURSTATE_MSK) !=
1493
WFPM_PHYRF_STATE_ON) {
1494
*val++ = cpu_to_le32(WFPM_DPHY_OFF);
1495
continue;
1496
}
1497
1498
iwl_write_prph_no_grab(fwrt->trans, indirect_rd_wr_addr,
1499
addr + i);
1500
/* wait a bit for value to be ready in register */
1501
udelay(1);
1502
prph_val = iwl_read_prph_no_grab(fwrt->trans,
1503
indirect_rd_wr_addr);
1504
*val++ = cpu_to_le32((prph_val & DPHYIP_INDIRECT_RD_MSK) >>
1505
DPHYIP_INDIRECT_RD_SHIFT);
1506
}
1507
1508
iwl_trans_release_nic_access(fwrt->trans);
1509
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1510
}
1511
1512
struct iwl_ini_rxf_data {
1513
u32 fifo_num;
1514
u32 size;
1515
u32 offset;
1516
};
1517
1518
static void iwl_ini_get_rxf_data(struct iwl_fw_runtime *fwrt,
1519
struct iwl_dump_ini_region_data *reg_data,
1520
struct iwl_ini_rxf_data *data)
1521
{
1522
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1523
u32 fid1 = le32_to_cpu(reg->fifos.fid[0]);
1524
u32 fid2 = le32_to_cpu(reg->fifos.fid[1]);
1525
u8 fifo_idx;
1526
1527
if (!data)
1528
return;
1529
1530
memset(data, 0, sizeof(*data));
1531
1532
/* make sure only one bit is set in only one fid */
1533
if (WARN_ONCE(hweight_long(fid1) + hweight_long(fid2) != 1,
1534
"fid1=%x, fid2=%x\n", fid1, fid2))
1535
return;
1536
1537
if (fid1) {
1538
fifo_idx = ffs(fid1) - 1;
1539
if (WARN_ONCE(fifo_idx >= MAX_NUM_LMAC, "fifo_idx=%d\n",
1540
fifo_idx))
1541
return;
1542
1543
data->size = fwrt->smem_cfg.lmac[fifo_idx].rxfifo1_size;
1544
data->fifo_num = fifo_idx;
1545
} else {
1546
u8 max_idx;
1547
1548
fifo_idx = ffs(fid2) - 1;
1549
if (iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP,
1550
SHARED_MEM_CFG_CMD, 0) <= 3)
1551
max_idx = 0;
1552
else
1553
max_idx = 1;
1554
1555
if (WARN_ONCE(fifo_idx > max_idx,
1556
"invalid umac fifo idx %d", fifo_idx))
1557
return;
1558
1559
/* use bit 31 to distinguish between umac and lmac rxf while
1560
* parsing the dump
1561
*/
1562
data->fifo_num = fifo_idx | IWL_RXF_UMAC_BIT;
1563
1564
switch (fifo_idx) {
1565
case 0:
1566
data->size = fwrt->smem_cfg.rxfifo2_size;
1567
data->offset = iwl_umac_prph(fwrt->trans,
1568
RXF_DIFF_FROM_PREV);
1569
break;
1570
case 1:
1571
data->size = fwrt->smem_cfg.rxfifo2_control_size;
1572
data->offset = iwl_umac_prph(fwrt->trans,
1573
RXF2C_DIFF_FROM_PREV);
1574
break;
1575
}
1576
}
1577
}
1578
1579
static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt,
1580
struct iwl_dump_ini_region_data *reg_data,
1581
void *range_ptr, u32 range_len, int idx)
1582
{
1583
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1584
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1585
struct iwl_ini_rxf_data rxf_data;
1586
struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data;
1587
u32 offs = le32_to_cpu(reg->fifos.offset), addr;
1588
u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs);
1589
u32 registers_size = registers_num * sizeof(*reg_dump);
1590
__le32 *data;
1591
int i;
1592
1593
#if defined(__FreeBSD__)
1594
rxf_data.size = 0;
1595
#endif
1596
iwl_ini_get_rxf_data(fwrt, reg_data, &rxf_data);
1597
if (!rxf_data.size)
1598
return -EIO;
1599
1600
if (!iwl_trans_grab_nic_access(fwrt->trans))
1601
return -EBUSY;
1602
1603
range->fifo_hdr.fifo_num = cpu_to_le32(rxf_data.fifo_num);
1604
range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num);
1605
range->range_data_size = cpu_to_le32(rxf_data.size + registers_size);
1606
1607
/*
1608
* read rxf registers. for each register, write to the dump the
1609
* register address and its value
1610
*/
1611
for (i = 0; i < registers_num; i++) {
1612
addr = le32_to_cpu(reg->addrs[i]) + offs;
1613
1614
reg_dump->addr = cpu_to_le32(addr);
1615
reg_dump->data = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans,
1616
addr));
1617
1618
reg_dump++;
1619
}
1620
1621
if (reg->fifos.hdr_only) {
1622
range->range_data_size = cpu_to_le32(registers_size);
1623
goto out;
1624
}
1625
1626
offs = rxf_data.offset;
1627
1628
/* Lock fence */
1629
iwl_write_prph_no_grab(fwrt->trans, RXF_SET_FENCE_MODE + offs, 0x1);
1630
/* Set fence pointer to the same place like WR pointer */
1631
iwl_write_prph_no_grab(fwrt->trans, RXF_LD_WR2FENCE + offs, 0x1);
1632
/* Set fence offset */
1633
iwl_write_prph_no_grab(fwrt->trans, RXF_LD_FENCE_OFFSET_ADDR + offs,
1634
0x0);
1635
1636
/* Read FIFO */
1637
addr = RXF_FIFO_RD_FENCE_INC + offs;
1638
data = (void *)reg_dump;
1639
for (i = 0; i < rxf_data.size; i += sizeof(*data))
1640
*data++ = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, addr));
1641
1642
out:
1643
iwl_trans_release_nic_access(fwrt->trans);
1644
1645
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1646
}
1647
1648
static int
1649
iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt,
1650
struct iwl_dump_ini_region_data *reg_data,
1651
void *range_ptr, u32 range_len, int idx)
1652
{
1653
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1654
struct iwl_fw_ini_region_err_table *err_table = &reg->err_table;
1655
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1656
u32 addr = le32_to_cpu(err_table->base_addr) +
1657
le32_to_cpu(err_table->offset);
1658
1659
range->internal_base_addr = cpu_to_le32(addr);
1660
range->range_data_size = err_table->size;
1661
iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
1662
le32_to_cpu(err_table->size));
1663
1664
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1665
}
1666
1667
static int
1668
iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt,
1669
struct iwl_dump_ini_region_data *reg_data,
1670
void *range_ptr, u32 range_len, int idx)
1671
{
1672
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1673
struct iwl_fw_ini_region_special_device_memory *special_mem =
1674
&reg->special_mem;
1675
1676
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1677
u32 addr = le32_to_cpu(special_mem->base_addr) +
1678
le32_to_cpu(special_mem->offset);
1679
1680
range->internal_base_addr = cpu_to_le32(addr);
1681
range->range_data_size = special_mem->size;
1682
iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
1683
le32_to_cpu(special_mem->size));
1684
1685
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1686
}
1687
1688
static int
1689
iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt,
1690
struct iwl_dump_ini_region_data *reg_data,
1691
void *range_ptr, u32 range_len, int idx)
1692
{
1693
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1694
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1695
__le32 *val = range->data;
1696
u32 prph_data;
1697
int i;
1698
1699
if (!iwl_trans_grab_nic_access(fwrt->trans))
1700
return -EBUSY;
1701
1702
range->range_data_size = reg->dev_addr.size;
1703
for (i = 0; i < (le32_to_cpu(reg->dev_addr.size) / 4); i++) {
1704
prph_data = iwl_read_prph_no_grab(fwrt->trans, (i % 2) ?
1705
DBGI_SRAM_TARGET_ACCESS_RDATA_MSB :
1706
DBGI_SRAM_TARGET_ACCESS_RDATA_LSB);
1707
if (iwl_trans_is_hw_error_value(prph_data)) {
1708
iwl_trans_release_nic_access(fwrt->trans);
1709
return -EBUSY;
1710
}
1711
*val++ = cpu_to_le32(prph_data);
1712
}
1713
iwl_trans_release_nic_access(fwrt->trans);
1714
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1715
}
1716
1717
static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt,
1718
struct iwl_dump_ini_region_data *reg_data,
1719
void *range_ptr, u32 range_len, int idx)
1720
{
1721
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1722
struct iwl_rx_packet *pkt = reg_data->dump_data->fw_pkt;
1723
u32 pkt_len;
1724
1725
if (!pkt)
1726
return -EIO;
1727
1728
pkt_len = iwl_rx_packet_payload_len(pkt);
1729
1730
memcpy(&range->fw_pkt_hdr, &pkt->hdr, sizeof(range->fw_pkt_hdr));
1731
range->range_data_size = cpu_to_le32(pkt_len);
1732
1733
memcpy(range->data, pkt->data, pkt_len);
1734
1735
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1736
}
1737
1738
static int iwl_dump_ini_imr_iter(struct iwl_fw_runtime *fwrt,
1739
struct iwl_dump_ini_region_data *reg_data,
1740
void *range_ptr, u32 range_len, int idx)
1741
{
1742
/* read the IMR memory and DMA it to SRAM */
1743
struct iwl_fw_ini_error_dump_range *range = range_ptr;
1744
u64 imr_curr_addr = fwrt->trans->dbg.imr_data.imr_curr_addr;
1745
u32 imr_rem_bytes = fwrt->trans->dbg.imr_data.imr2sram_remainbyte;
1746
u32 sram_addr = fwrt->trans->dbg.imr_data.sram_addr;
1747
u32 sram_size = fwrt->trans->dbg.imr_data.sram_size;
1748
u32 size_to_dump = (imr_rem_bytes > sram_size) ? sram_size : imr_rem_bytes;
1749
1750
range->range_data_size = cpu_to_le32(size_to_dump);
1751
if (iwl_trans_write_imr_mem(fwrt->trans, sram_addr,
1752
imr_curr_addr, size_to_dump)) {
1753
IWL_ERR(fwrt, "WRT_DEBUG: IMR Memory transfer failed\n");
1754
return -1;
1755
}
1756
1757
fwrt->trans->dbg.imr_data.imr_curr_addr = imr_curr_addr + size_to_dump;
1758
fwrt->trans->dbg.imr_data.imr2sram_remainbyte -= size_to_dump;
1759
1760
iwl_trans_read_mem_bytes(fwrt->trans, sram_addr, range->data,
1761
size_to_dump);
1762
return sizeof(*range) + le32_to_cpu(range->range_data_size);
1763
}
1764
1765
static void *
1766
iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt,
1767
struct iwl_dump_ini_region_data *reg_data,
1768
void *data, u32 data_len)
1769
{
1770
struct iwl_fw_ini_error_dump *dump = data;
1771
1772
dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
1773
1774
return dump->data;
1775
}
1776
1777
/**
1778
* mask_apply_and_normalize - applies mask on val and normalize the result
1779
*
1780
* @val: value
1781
* @mask: mask to apply and to normalize with
1782
*
1783
* The normalization is based on the first set bit in the mask
1784
*
1785
* Returns: the extracted value
1786
*/
1787
static u32 mask_apply_and_normalize(u32 val, u32 mask)
1788
{
1789
return (val & mask) >> (ffs(mask) - 1);
1790
}
1791
1792
static __le32 iwl_get_mon_reg(struct iwl_fw_runtime *fwrt, u32 alloc_id,
1793
const struct iwl_fw_mon_reg *reg_info)
1794
{
1795
u32 val, offs;
1796
1797
/* The header addresses of DBGCi is calculate as follows:
1798
* DBGC1 address + (0x100 * i)
1799
*/
1800
offs = (alloc_id - IWL_FW_INI_ALLOCATION_ID_DBGC1) * 0x100;
1801
1802
if (!reg_info || !reg_info->addr || !reg_info->mask)
1803
return 0;
1804
1805
val = iwl_read_prph_no_grab(fwrt->trans, reg_info->addr + offs);
1806
1807
return cpu_to_le32(mask_apply_and_normalize(val, reg_info->mask));
1808
}
1809
1810
static void *
1811
iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id,
1812
struct iwl_fw_ini_monitor_dump *data,
1813
const struct iwl_fw_mon_regs *addrs)
1814
{
1815
if (!iwl_trans_grab_nic_access(fwrt->trans)) {
1816
IWL_ERR(fwrt, "Failed to get monitor header\n");
1817
return NULL;
1818
}
1819
1820
data->write_ptr = iwl_get_mon_reg(fwrt, alloc_id,
1821
&addrs->write_ptr);
1822
if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
1823
u32 wrt_ptr = le32_to_cpu(data->write_ptr);
1824
1825
data->write_ptr = cpu_to_le32(wrt_ptr >> 2);
1826
}
1827
data->cycle_cnt = iwl_get_mon_reg(fwrt, alloc_id,
1828
&addrs->cycle_cnt);
1829
data->cur_frag = iwl_get_mon_reg(fwrt, alloc_id,
1830
&addrs->cur_frag);
1831
1832
iwl_trans_release_nic_access(fwrt->trans);
1833
1834
data->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
1835
1836
return data->data;
1837
}
1838
1839
static void *
1840
iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt,
1841
struct iwl_dump_ini_region_data *reg_data,
1842
void *data, u32 data_len)
1843
{
1844
struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data;
1845
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1846
u32 alloc_id = le32_to_cpu(reg->dram_alloc_id);
1847
1848
return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump,
1849
&fwrt->trans->mac_cfg->base->mon_dram_regs);
1850
}
1851
1852
static void *
1853
iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt,
1854
struct iwl_dump_ini_region_data *reg_data,
1855
void *data, u32 data_len)
1856
{
1857
struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data;
1858
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1859
u32 alloc_id = le32_to_cpu(reg->internal_buffer.alloc_id);
1860
1861
return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump,
1862
&fwrt->trans->mac_cfg->base->mon_smem_regs);
1863
}
1864
1865
static void *
1866
iwl_dump_ini_mon_dbgi_fill_header(struct iwl_fw_runtime *fwrt,
1867
struct iwl_dump_ini_region_data *reg_data,
1868
void *data, u32 data_len)
1869
{
1870
struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data;
1871
1872
return iwl_dump_ini_mon_fill_header(fwrt,
1873
/* no offset calculation later */
1874
IWL_FW_INI_ALLOCATION_ID_DBGC1,
1875
mon_dump,
1876
&fwrt->trans->mac_cfg->base->mon_dbgi_regs);
1877
}
1878
1879
static void *
1880
iwl_dump_ini_err_table_fill_header(struct iwl_fw_runtime *fwrt,
1881
struct iwl_dump_ini_region_data *reg_data,
1882
void *data, u32 data_len)
1883
{
1884
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1885
struct iwl_fw_ini_err_table_dump *dump = data;
1886
1887
dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
1888
dump->version = reg->err_table.version;
1889
1890
return dump->data;
1891
}
1892
1893
static void *
1894
iwl_dump_ini_special_mem_fill_header(struct iwl_fw_runtime *fwrt,
1895
struct iwl_dump_ini_region_data *reg_data,
1896
void *data, u32 data_len)
1897
{
1898
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1899
struct iwl_fw_ini_special_device_memory *dump = data;
1900
1901
dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
1902
dump->type = reg->special_mem.type;
1903
dump->version = reg->special_mem.version;
1904
1905
return dump->data;
1906
}
1907
1908
static void *
1909
iwl_dump_ini_imr_fill_header(struct iwl_fw_runtime *fwrt,
1910
struct iwl_dump_ini_region_data *reg_data,
1911
void *data, u32 data_len)
1912
{
1913
struct iwl_fw_ini_error_dump *dump = data;
1914
1915
dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
1916
1917
return dump->data;
1918
}
1919
1920
static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt,
1921
struct iwl_dump_ini_region_data *reg_data)
1922
{
1923
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1924
1925
return iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs);
1926
}
1927
1928
static u32
1929
iwl_dump_ini_mem_block_ranges(struct iwl_fw_runtime *fwrt,
1930
struct iwl_dump_ini_region_data *reg_data)
1931
{
1932
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1933
size_t size = sizeof(struct iwl_fw_ini_addr_size);
1934
1935
return iwl_tlv_array_len_with_size(reg_data->reg_tlv, reg, size);
1936
}
1937
1938
static u32 iwl_dump_ini_paging_ranges(struct iwl_fw_runtime *fwrt,
1939
struct iwl_dump_ini_region_data *reg_data)
1940
{
1941
if (fwrt->trans->mac_cfg->gen2) {
1942
if (fwrt->trans->init_dram.paging_cnt)
1943
return fwrt->trans->init_dram.paging_cnt - 1;
1944
else
1945
return 0;
1946
}
1947
1948
return fwrt->num_of_paging_blk;
1949
}
1950
1951
static u32
1952
iwl_dump_ini_mon_dram_ranges(struct iwl_fw_runtime *fwrt,
1953
struct iwl_dump_ini_region_data *reg_data)
1954
{
1955
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
1956
struct iwl_fw_mon *fw_mon;
1957
u32 ranges = 0, alloc_id = le32_to_cpu(reg->dram_alloc_id);
1958
int i;
1959
1960
fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
1961
1962
for (i = 0; i < fw_mon->num_frags; i++) {
1963
if (!fw_mon->frags[i].size)
1964
break;
1965
1966
ranges++;
1967
}
1968
1969
return ranges;
1970
}
1971
1972
static u32 iwl_dump_ini_txf_ranges(struct iwl_fw_runtime *fwrt,
1973
struct iwl_dump_ini_region_data *reg_data)
1974
{
1975
u32 num_of_fifos = 0;
1976
1977
while (iwl_ini_txf_iter(fwrt, reg_data, num_of_fifos))
1978
num_of_fifos++;
1979
1980
return num_of_fifos;
1981
}
1982
1983
static u32 iwl_dump_ini_single_range(struct iwl_fw_runtime *fwrt,
1984
struct iwl_dump_ini_region_data *reg_data)
1985
{
1986
return 1;
1987
}
1988
1989
static u32 iwl_dump_ini_imr_ranges(struct iwl_fw_runtime *fwrt,
1990
struct iwl_dump_ini_region_data *reg_data)
1991
{
1992
/* range is total number of pages need to copied from
1993
*IMR memory to SRAM and later from SRAM to DRAM
1994
*/
1995
u32 imr_enable = fwrt->trans->dbg.imr_data.imr_enable;
1996
u32 imr_size = fwrt->trans->dbg.imr_data.imr_size;
1997
u32 sram_size = fwrt->trans->dbg.imr_data.sram_size;
1998
1999
if (imr_enable == 0 || imr_size == 0 || sram_size == 0) {
2000
IWL_DEBUG_INFO(fwrt,
2001
"WRT: Invalid imr data enable: %d, imr_size: %d, sram_size: %d\n",
2002
imr_enable, imr_size, sram_size);
2003
return 0;
2004
}
2005
2006
return((imr_size % sram_size) ? (imr_size / sram_size + 1) : (imr_size / sram_size));
2007
}
2008
2009
static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt,
2010
struct iwl_dump_ini_region_data *reg_data)
2011
{
2012
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2013
u32 size = le32_to_cpu(reg->dev_addr.size);
2014
u32 ranges = iwl_dump_ini_mem_ranges(fwrt, reg_data);
2015
2016
if (!size || !ranges)
2017
return 0;
2018
2019
return sizeof(struct iwl_fw_ini_error_dump) + ranges *
2020
(size + sizeof(struct iwl_fw_ini_error_dump_range));
2021
}
2022
2023
static u32
2024
iwl_dump_ini_mem_block_get_size(struct iwl_fw_runtime *fwrt,
2025
struct iwl_dump_ini_region_data *reg_data)
2026
{
2027
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2028
struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs;
2029
u32 ranges = iwl_dump_ini_mem_block_ranges(fwrt, reg_data);
2030
u32 size = sizeof(struct iwl_fw_ini_error_dump);
2031
int range;
2032
2033
if (!ranges)
2034
return 0;
2035
2036
for (range = 0; range < ranges; range++)
2037
size += le32_to_cpu(pairs[range].size);
2038
2039
return size + ranges * sizeof(struct iwl_fw_ini_error_dump_range);
2040
}
2041
2042
static u32
2043
iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt,
2044
struct iwl_dump_ini_region_data *reg_data)
2045
{
2046
int i;
2047
u32 range_header_len = sizeof(struct iwl_fw_ini_error_dump_range);
2048
u32 size = sizeof(struct iwl_fw_ini_error_dump);
2049
2050
/* start from 1 to skip CSS section */
2051
for (i = 1; i <= iwl_dump_ini_paging_ranges(fwrt, reg_data); i++) {
2052
size += range_header_len;
2053
if (fwrt->trans->mac_cfg->gen2)
2054
size += fwrt->trans->init_dram.paging[i].size;
2055
else
2056
size += fwrt->fw_paging_db[i].fw_paging_size;
2057
}
2058
2059
return size;
2060
}
2061
2062
static u32
2063
iwl_dump_ini_mon_dram_get_size(struct iwl_fw_runtime *fwrt,
2064
struct iwl_dump_ini_region_data *reg_data)
2065
{
2066
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2067
struct iwl_fw_mon *fw_mon;
2068
u32 size = 0, alloc_id = le32_to_cpu(reg->dram_alloc_id);
2069
int i;
2070
2071
fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
2072
2073
for (i = 0; i < fw_mon->num_frags; i++) {
2074
struct iwl_dram_data *frag = &fw_mon->frags[i];
2075
2076
if (!frag->size)
2077
break;
2078
2079
size += sizeof(struct iwl_fw_ini_error_dump_range) + frag->size;
2080
}
2081
2082
if (size)
2083
size += sizeof(struct iwl_fw_ini_monitor_dump);
2084
2085
return size;
2086
}
2087
2088
static u32
2089
iwl_dump_ini_mon_smem_get_size(struct iwl_fw_runtime *fwrt,
2090
struct iwl_dump_ini_region_data *reg_data)
2091
{
2092
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2093
u32 size;
2094
2095
size = le32_to_cpu(reg->internal_buffer.size);
2096
if (!size)
2097
return 0;
2098
2099
size += sizeof(struct iwl_fw_ini_monitor_dump) +
2100
sizeof(struct iwl_fw_ini_error_dump_range);
2101
2102
return size;
2103
}
2104
2105
static u32 iwl_dump_ini_mon_dbgi_get_size(struct iwl_fw_runtime *fwrt,
2106
struct iwl_dump_ini_region_data *reg_data)
2107
{
2108
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2109
u32 size = le32_to_cpu(reg->dev_addr.size);
2110
u32 ranges = iwl_dump_ini_mem_ranges(fwrt, reg_data);
2111
2112
if (!size || !ranges)
2113
return 0;
2114
2115
return sizeof(struct iwl_fw_ini_monitor_dump) + ranges *
2116
(size + sizeof(struct iwl_fw_ini_error_dump_range));
2117
}
2118
2119
static u32 iwl_dump_ini_txf_get_size(struct iwl_fw_runtime *fwrt,
2120
struct iwl_dump_ini_region_data *reg_data)
2121
{
2122
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2123
struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data;
2124
u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs);
2125
u32 size = 0;
2126
u32 fifo_hdr = sizeof(struct iwl_fw_ini_error_dump_range) +
2127
registers_num *
2128
sizeof(struct iwl_fw_ini_error_dump_register);
2129
2130
while (iwl_ini_txf_iter(fwrt, reg_data, size)) {
2131
size += fifo_hdr;
2132
if (!reg->fifos.hdr_only)
2133
size += iter->fifo_size;
2134
}
2135
2136
if (!size)
2137
return 0;
2138
2139
return size + sizeof(struct iwl_fw_ini_error_dump);
2140
}
2141
2142
static u32 iwl_dump_ini_rxf_get_size(struct iwl_fw_runtime *fwrt,
2143
struct iwl_dump_ini_region_data *reg_data)
2144
{
2145
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2146
struct iwl_ini_rxf_data rx_data;
2147
u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs);
2148
u32 size = sizeof(struct iwl_fw_ini_error_dump) +
2149
sizeof(struct iwl_fw_ini_error_dump_range) +
2150
registers_num * sizeof(struct iwl_fw_ini_error_dump_register);
2151
2152
if (reg->fifos.hdr_only)
2153
return size;
2154
2155
#if defined(__FreeBSD__)
2156
rx_data.size = 0;
2157
#endif
2158
iwl_ini_get_rxf_data(fwrt, reg_data, &rx_data);
2159
size += rx_data.size;
2160
2161
return size;
2162
}
2163
2164
static u32
2165
iwl_dump_ini_err_table_get_size(struct iwl_fw_runtime *fwrt,
2166
struct iwl_dump_ini_region_data *reg_data)
2167
{
2168
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2169
u32 size = le32_to_cpu(reg->err_table.size);
2170
2171
if (size)
2172
size += sizeof(struct iwl_fw_ini_err_table_dump) +
2173
sizeof(struct iwl_fw_ini_error_dump_range);
2174
2175
return size;
2176
}
2177
2178
static u32
2179
iwl_dump_ini_special_mem_get_size(struct iwl_fw_runtime *fwrt,
2180
struct iwl_dump_ini_region_data *reg_data)
2181
{
2182
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2183
u32 size = le32_to_cpu(reg->special_mem.size);
2184
2185
if (size)
2186
size += sizeof(struct iwl_fw_ini_special_device_memory) +
2187
sizeof(struct iwl_fw_ini_error_dump_range);
2188
2189
return size;
2190
}
2191
2192
static u32
2193
iwl_dump_ini_fw_pkt_get_size(struct iwl_fw_runtime *fwrt,
2194
struct iwl_dump_ini_region_data *reg_data)
2195
{
2196
u32 size = 0;
2197
2198
if (!reg_data->dump_data->fw_pkt)
2199
return 0;
2200
2201
size += iwl_rx_packet_payload_len(reg_data->dump_data->fw_pkt);
2202
if (size)
2203
size += sizeof(struct iwl_fw_ini_error_dump) +
2204
sizeof(struct iwl_fw_ini_error_dump_range);
2205
2206
return size;
2207
}
2208
2209
static u32
2210
iwl_dump_ini_imr_get_size(struct iwl_fw_runtime *fwrt,
2211
struct iwl_dump_ini_region_data *reg_data)
2212
{
2213
u32 ranges = 0;
2214
u32 imr_enable = fwrt->trans->dbg.imr_data.imr_enable;
2215
u32 imr_size = fwrt->trans->dbg.imr_data.imr_size;
2216
u32 sram_size = fwrt->trans->dbg.imr_data.sram_size;
2217
2218
if (imr_enable == 0 || imr_size == 0 || sram_size == 0) {
2219
IWL_DEBUG_INFO(fwrt,
2220
"WRT: Invalid imr data enable: %d, imr_size: %d, sram_size: %d\n",
2221
imr_enable, imr_size, sram_size);
2222
return 0;
2223
}
2224
ranges = iwl_dump_ini_imr_ranges(fwrt, reg_data);
2225
if (!ranges) {
2226
IWL_ERR(fwrt, "WRT: ranges :=%d\n", ranges);
2227
return 0;
2228
}
2229
imr_size += sizeof(struct iwl_fw_ini_error_dump) +
2230
ranges * sizeof(struct iwl_fw_ini_error_dump_range);
2231
return imr_size;
2232
}
2233
2234
/**
2235
* struct iwl_dump_ini_mem_ops - ini memory dump operations
2236
* @get_num_of_ranges: returns the number of memory ranges in the region.
2237
* @get_size: returns the total size of the region.
2238
* @fill_mem_hdr: fills region type specific headers and returns pointer to
2239
* the first range or NULL if failed to fill headers.
2240
* @fill_range: copies a given memory range into the dump.
2241
* Returns the size of the range or negative error value otherwise.
2242
*/
2243
struct iwl_dump_ini_mem_ops {
2244
u32 (*get_num_of_ranges)(struct iwl_fw_runtime *fwrt,
2245
struct iwl_dump_ini_region_data *reg_data);
2246
u32 (*get_size)(struct iwl_fw_runtime *fwrt,
2247
struct iwl_dump_ini_region_data *reg_data);
2248
void *(*fill_mem_hdr)(struct iwl_fw_runtime *fwrt,
2249
struct iwl_dump_ini_region_data *reg_data,
2250
void *data, u32 data_len);
2251
int (*fill_range)(struct iwl_fw_runtime *fwrt,
2252
struct iwl_dump_ini_region_data *reg_data,
2253
void *range, u32 range_len, int idx);
2254
};
2255
2256
/**
2257
* iwl_dump_ini_mem - dump memory region
2258
*
2259
* @fwrt: fw runtime struct
2260
* @list: list to add the dump tlv to
2261
* @reg_data: memory region
2262
* @ops: memory dump operations
2263
*
2264
* Creates a dump tlv and copy a memory region into it.
2265
*
2266
* Returns: the size of the current dump tlv or 0 if failed
2267
*/
2268
static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list,
2269
struct iwl_dump_ini_region_data *reg_data,
2270
const struct iwl_dump_ini_mem_ops *ops)
2271
{
2272
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
2273
struct iwl_fw_ini_dump_entry *entry;
2274
struct iwl_fw_ini_error_dump_data *tlv;
2275
struct iwl_fw_ini_error_dump_header *header;
2276
u32 type = reg->type;
2277
u32 id = le32_get_bits(reg->id, IWL_FW_INI_REGION_ID_MASK);
2278
u32 num_of_ranges, i, size;
2279
u8 *range;
2280
u32 free_size;
2281
u64 header_size;
2282
u32 dump_policy = IWL_FW_INI_DUMP_VERBOSE;
2283
2284
IWL_DEBUG_FW(fwrt, "WRT: Collecting region: dump type=%d, id=%d, type=%d\n",
2285
dump_policy, id, type);
2286
2287
if (le32_to_cpu(reg->hdr.version) >= 2) {
2288
u32 dp = le32_get_bits(reg->id,
2289
IWL_FW_INI_REGION_DUMP_POLICY_MASK);
2290
2291
if (dump_policy == IWL_FW_INI_DUMP_VERBOSE &&
2292
!(dp & IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT)) {
2293
IWL_DEBUG_FW(fwrt,
2294
"WRT: no dump - type %d and policy mismatch=%d\n",
2295
dump_policy, dp);
2296
return 0;
2297
} else if (dump_policy == IWL_FW_INI_DUMP_MEDIUM &&
2298
!(dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB)) {
2299
IWL_DEBUG_FW(fwrt,
2300
"WRT: no dump - type %d and policy mismatch=%d\n",
2301
dump_policy, dp);
2302
return 0;
2303
} else if (dump_policy == IWL_FW_INI_DUMP_BRIEF &&
2304
!(dp & IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB)) {
2305
IWL_DEBUG_FW(fwrt,
2306
"WRT: no dump - type %d and policy mismatch=%d\n",
2307
dump_policy, dp);
2308
return 0;
2309
}
2310
}
2311
2312
if (!ops->get_num_of_ranges || !ops->get_size || !ops->fill_mem_hdr ||
2313
!ops->fill_range) {
2314
IWL_DEBUG_FW(fwrt, "WRT: no ops for collecting data\n");
2315
return 0;
2316
}
2317
2318
size = ops->get_size(fwrt, reg_data);
2319
2320
if (size < sizeof(*header)) {
2321
IWL_DEBUG_FW(fwrt, "WRT: size didn't include space for header\n");
2322
return 0;
2323
}
2324
2325
entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + size);
2326
if (!entry)
2327
return 0;
2328
2329
entry->size = sizeof(*tlv) + size;
2330
2331
tlv = (void *)entry->data;
2332
tlv->type = reg->type;
2333
tlv->sub_type = reg->sub_type;
2334
tlv->sub_type_ver = reg->sub_type_ver;
2335
tlv->reserved = reg->reserved;
2336
tlv->len = cpu_to_le32(size);
2337
2338
num_of_ranges = ops->get_num_of_ranges(fwrt, reg_data);
2339
2340
header = (void *)tlv->data;
2341
header->region_id = cpu_to_le32(id);
2342
header->num_of_ranges = cpu_to_le32(num_of_ranges);
2343
header->name_len = cpu_to_le32(IWL_FW_INI_MAX_NAME);
2344
memcpy(header->name, reg->name, IWL_FW_INI_MAX_NAME);
2345
2346
free_size = size;
2347
range = ops->fill_mem_hdr(fwrt, reg_data, header, free_size);
2348
if (!range) {
2349
IWL_ERR(fwrt,
2350
"WRT: Failed to fill region header: id=%d, type=%d\n",
2351
id, type);
2352
goto out_err;
2353
}
2354
2355
header_size = range - (u8 *)header;
2356
2357
if (WARN(header_size > free_size,
2358
#if defined(__linux__)
2359
"header size %llu > free_size %d",
2360
header_size, free_size)) {
2361
#elif defined(__FreeBSD__)
2362
"header size %ju > free_size %d",
2363
(uintmax_t)header_size, free_size)) {
2364
#endif
2365
IWL_ERR(fwrt,
2366
"WRT: fill_mem_hdr used more than given free_size\n");
2367
goto out_err;
2368
}
2369
2370
free_size -= header_size;
2371
2372
for (i = 0; i < num_of_ranges; i++) {
2373
int range_size = ops->fill_range(fwrt, reg_data, range,
2374
free_size, i);
2375
2376
if (range_size < 0) {
2377
IWL_ERR(fwrt,
2378
"WRT: Failed to dump region: id=%d, type=%d\n",
2379
id, type);
2380
goto out_err;
2381
}
2382
2383
if (WARN(range_size > free_size, "range_size %d > free_size %d",
2384
range_size, free_size)) {
2385
IWL_ERR(fwrt,
2386
"WRT: fill_raged used more than given free_size\n");
2387
goto out_err;
2388
}
2389
2390
free_size -= range_size;
2391
range = range + range_size;
2392
}
2393
2394
list_add_tail(&entry->list, list);
2395
2396
return entry->size;
2397
2398
out_err:
2399
vfree(entry);
2400
2401
return 0;
2402
}
2403
2404
static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt,
2405
struct iwl_fw_ini_trigger_tlv *trigger,
2406
struct list_head *list)
2407
{
2408
struct iwl_fw_ini_dump_entry *entry;
2409
struct iwl_fw_error_dump_data *tlv;
2410
struct iwl_fw_ini_dump_info *dump;
2411
struct iwl_dbg_tlv_node *node;
2412
struct iwl_fw_ini_dump_cfg_name *cfg_name;
2413
u32 size = sizeof(*tlv) + sizeof(*dump);
2414
u32 num_of_cfg_names = 0;
2415
u32 hw_type, is_cdb, is_jacket;
2416
2417
list_for_each_entry(node, &fwrt->trans->dbg.debug_info_tlv_list, list) {
2418
size += sizeof(*cfg_name);
2419
num_of_cfg_names++;
2420
}
2421
2422
entry = vzalloc(sizeof(*entry) + size);
2423
if (!entry)
2424
return 0;
2425
2426
entry->size = size;
2427
2428
tlv = (void *)entry->data;
2429
tlv->type = cpu_to_le32(IWL_INI_DUMP_INFO_TYPE);
2430
tlv->len = cpu_to_le32(size - sizeof(*tlv));
2431
2432
dump = (void *)tlv->data;
2433
2434
dump->version = cpu_to_le32(IWL_INI_DUMP_VER);
2435
dump->time_point = trigger->time_point;
2436
dump->trigger_reason = trigger->trigger_reason;
2437
dump->external_cfg_state =
2438
cpu_to_le32(fwrt->trans->dbg.external_ini_cfg);
2439
2440
dump->ver_type = cpu_to_le32(fwrt->dump.fw_ver.type);
2441
dump->ver_subtype = cpu_to_le32(fwrt->dump.fw_ver.subtype);
2442
2443
dump->hw_step = cpu_to_le32(fwrt->trans->info.hw_rev_step);
2444
2445
hw_type = CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev);
2446
2447
is_cdb = CSR_HW_RFID_IS_CDB(fwrt->trans->info.hw_rf_id);
2448
is_jacket = !!(iwl_read_umac_prph(fwrt->trans, WFPM_OTP_CFG1_ADDR) &
2449
WFPM_OTP_CFG1_IS_JACKET_BIT);
2450
2451
/* Use bits 12 and 13 to indicate jacket/CDB, respectively */
2452
hw_type |= (is_jacket | (is_cdb << 1)) << IWL_JACKET_CDB_SHIFT;
2453
2454
dump->hw_type = cpu_to_le32(hw_type);
2455
2456
dump->rf_id_flavor =
2457
cpu_to_le32(CSR_HW_RFID_FLAVOR(fwrt->trans->info.hw_rf_id));
2458
dump->rf_id_dash = cpu_to_le32(CSR_HW_RFID_DASH(fwrt->trans->info.hw_rf_id));
2459
dump->rf_id_step = cpu_to_le32(CSR_HW_RFID_STEP(fwrt->trans->info.hw_rf_id));
2460
dump->rf_id_type = cpu_to_le32(CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id));
2461
2462
dump->lmac_major = cpu_to_le32(fwrt->dump.fw_ver.lmac_major);
2463
dump->lmac_minor = cpu_to_le32(fwrt->dump.fw_ver.lmac_minor);
2464
dump->umac_major = cpu_to_le32(fwrt->dump.fw_ver.umac_major);
2465
dump->umac_minor = cpu_to_le32(fwrt->dump.fw_ver.umac_minor);
2466
2467
dump->fw_mon_mode = cpu_to_le32(fwrt->trans->dbg.ini_dest);
2468
dump->regions_mask = trigger->regions_mask &
2469
~cpu_to_le64(fwrt->trans->dbg.unsupported_region_msk);
2470
2471
dump->build_tag_len = cpu_to_le32(sizeof(dump->build_tag));
2472
memcpy(dump->build_tag, fwrt->fw->human_readable,
2473
sizeof(dump->build_tag));
2474
2475
cfg_name = dump->cfg_names;
2476
dump->num_of_cfg_names = cpu_to_le32(num_of_cfg_names);
2477
list_for_each_entry(node, &fwrt->trans->dbg.debug_info_tlv_list, list) {
2478
struct iwl_fw_ini_debug_info_tlv *debug_info =
2479
(void *)node->tlv.data;
2480
2481
BUILD_BUG_ON(sizeof(cfg_name->cfg_name) !=
2482
sizeof(debug_info->debug_cfg_name));
2483
2484
cfg_name->image_type = debug_info->image_type;
2485
cfg_name->cfg_name_len =
2486
cpu_to_le32(sizeof(cfg_name->cfg_name));
2487
memcpy(cfg_name->cfg_name, debug_info->debug_cfg_name,
2488
sizeof(cfg_name->cfg_name));
2489
cfg_name++;
2490
}
2491
2492
/* add dump info TLV to the beginning of the list since it needs to be
2493
* the first TLV in the dump
2494
*/
2495
list_add(&entry->list, list);
2496
2497
return entry->size;
2498
}
2499
2500
static u32 iwl_dump_ini_file_name_info(struct iwl_fw_runtime *fwrt,
2501
struct list_head *list)
2502
{
2503
struct iwl_fw_ini_dump_entry *entry;
2504
struct iwl_dump_file_name_info *tlv;
2505
u32 len = strnlen(fwrt->trans->dbg.dump_file_name_ext,
2506
IWL_FW_INI_MAX_NAME);
2507
2508
if (!fwrt->trans->dbg.dump_file_name_ext_valid)
2509
return 0;
2510
2511
entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + len);
2512
if (!entry)
2513
return 0;
2514
2515
entry->size = sizeof(*tlv) + len;
2516
2517
tlv = (void *)entry->data;
2518
tlv->type = cpu_to_le32(IWL_INI_DUMP_NAME_TYPE);
2519
tlv->len = cpu_to_le32(len);
2520
memcpy(tlv->data, fwrt->trans->dbg.dump_file_name_ext, len);
2521
2522
/* add the dump file name extension tlv to the list */
2523
list_add_tail(&entry->list, list);
2524
2525
fwrt->trans->dbg.dump_file_name_ext_valid = false;
2526
2527
return entry->size;
2528
}
2529
2530
static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = {
2531
[IWL_FW_INI_REGION_INVALID] = {},
2532
[IWL_FW_INI_REGION_INTERNAL_BUFFER] = {
2533
.get_num_of_ranges = iwl_dump_ini_single_range,
2534
.get_size = iwl_dump_ini_mon_smem_get_size,
2535
.fill_mem_hdr = iwl_dump_ini_mon_smem_fill_header,
2536
.fill_range = iwl_dump_ini_mon_smem_iter,
2537
},
2538
[IWL_FW_INI_REGION_DRAM_BUFFER] = {
2539
.get_num_of_ranges = iwl_dump_ini_mon_dram_ranges,
2540
.get_size = iwl_dump_ini_mon_dram_get_size,
2541
.fill_mem_hdr = iwl_dump_ini_mon_dram_fill_header,
2542
.fill_range = iwl_dump_ini_mon_dram_iter,
2543
},
2544
[IWL_FW_INI_REGION_TXF] = {
2545
.get_num_of_ranges = iwl_dump_ini_txf_ranges,
2546
.get_size = iwl_dump_ini_txf_get_size,
2547
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2548
.fill_range = iwl_dump_ini_txf_iter,
2549
},
2550
[IWL_FW_INI_REGION_RXF] = {
2551
.get_num_of_ranges = iwl_dump_ini_single_range,
2552
.get_size = iwl_dump_ini_rxf_get_size,
2553
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2554
.fill_range = iwl_dump_ini_rxf_iter,
2555
},
2556
[IWL_FW_INI_REGION_LMAC_ERROR_TABLE] = {
2557
.get_num_of_ranges = iwl_dump_ini_single_range,
2558
.get_size = iwl_dump_ini_err_table_get_size,
2559
.fill_mem_hdr = iwl_dump_ini_err_table_fill_header,
2560
.fill_range = iwl_dump_ini_err_table_iter,
2561
},
2562
[IWL_FW_INI_REGION_UMAC_ERROR_TABLE] = {
2563
.get_num_of_ranges = iwl_dump_ini_single_range,
2564
.get_size = iwl_dump_ini_err_table_get_size,
2565
.fill_mem_hdr = iwl_dump_ini_err_table_fill_header,
2566
.fill_range = iwl_dump_ini_err_table_iter,
2567
},
2568
[IWL_FW_INI_REGION_RSP_OR_NOTIF] = {
2569
.get_num_of_ranges = iwl_dump_ini_single_range,
2570
.get_size = iwl_dump_ini_fw_pkt_get_size,
2571
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2572
.fill_range = iwl_dump_ini_fw_pkt_iter,
2573
},
2574
[IWL_FW_INI_REGION_DEVICE_MEMORY] = {
2575
.get_num_of_ranges = iwl_dump_ini_mem_ranges,
2576
.get_size = iwl_dump_ini_mem_get_size,
2577
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2578
.fill_range = iwl_dump_ini_dev_mem_iter,
2579
},
2580
[IWL_FW_INI_REGION_PERIPHERY_MAC] = {
2581
.get_num_of_ranges = iwl_dump_ini_mem_ranges,
2582
.get_size = iwl_dump_ini_mem_get_size,
2583
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2584
.fill_range = iwl_dump_ini_prph_mac_iter,
2585
},
2586
[IWL_FW_INI_REGION_PERIPHERY_PHY] = {
2587
.get_num_of_ranges = iwl_dump_ini_mem_ranges,
2588
.get_size = iwl_dump_ini_mem_get_size,
2589
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2590
.fill_range = iwl_dump_ini_prph_phy_iter,
2591
},
2592
[IWL_FW_INI_REGION_PERIPHERY_MAC_RANGE] = {
2593
.get_num_of_ranges = iwl_dump_ini_mem_block_ranges,
2594
.get_size = iwl_dump_ini_mem_block_get_size,
2595
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2596
.fill_range = iwl_dump_ini_prph_mac_block_iter,
2597
},
2598
[IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE] = {
2599
.get_num_of_ranges = iwl_dump_ini_mem_block_ranges,
2600
.get_size = iwl_dump_ini_mem_block_get_size,
2601
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2602
.fill_range = iwl_dump_ini_prph_phy_block_iter,
2603
},
2604
[IWL_FW_INI_REGION_PERIPHERY_AUX] = {},
2605
[IWL_FW_INI_REGION_PAGING] = {
2606
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2607
.get_num_of_ranges = iwl_dump_ini_paging_ranges,
2608
.get_size = iwl_dump_ini_paging_get_size,
2609
.fill_range = iwl_dump_ini_paging_iter,
2610
},
2611
[IWL_FW_INI_REGION_CSR] = {
2612
.get_num_of_ranges = iwl_dump_ini_mem_ranges,
2613
.get_size = iwl_dump_ini_mem_get_size,
2614
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2615
.fill_range = iwl_dump_ini_csr_iter,
2616
},
2617
[IWL_FW_INI_REGION_DRAM_IMR] = {
2618
.get_num_of_ranges = iwl_dump_ini_imr_ranges,
2619
.get_size = iwl_dump_ini_imr_get_size,
2620
.fill_mem_hdr = iwl_dump_ini_imr_fill_header,
2621
.fill_range = iwl_dump_ini_imr_iter,
2622
},
2623
[IWL_FW_INI_REGION_PCI_IOSF_CONFIG] = {
2624
.get_num_of_ranges = iwl_dump_ini_mem_ranges,
2625
.get_size = iwl_dump_ini_mem_get_size,
2626
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2627
.fill_range = iwl_dump_ini_config_iter,
2628
},
2629
[IWL_FW_INI_REGION_SPECIAL_DEVICE_MEMORY] = {
2630
.get_num_of_ranges = iwl_dump_ini_single_range,
2631
.get_size = iwl_dump_ini_special_mem_get_size,
2632
.fill_mem_hdr = iwl_dump_ini_special_mem_fill_header,
2633
.fill_range = iwl_dump_ini_special_mem_iter,
2634
},
2635
[IWL_FW_INI_REGION_DBGI_SRAM] = {
2636
.get_num_of_ranges = iwl_dump_ini_mem_ranges,
2637
.get_size = iwl_dump_ini_mon_dbgi_get_size,
2638
.fill_mem_hdr = iwl_dump_ini_mon_dbgi_fill_header,
2639
.fill_range = iwl_dump_ini_dbgi_sram_iter,
2640
},
2641
[IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP] = {
2642
.get_num_of_ranges = iwl_dump_ini_mem_ranges,
2643
.get_size = iwl_dump_ini_mem_get_size,
2644
.fill_mem_hdr = iwl_dump_ini_mem_fill_header,
2645
.fill_range = iwl_dump_ini_prph_snps_dphyip_iter,
2646
},
2647
};
2648
2649
enum iwl_dump_ini_region_selector {
2650
IWL_INI_DUMP_ALL_REGIONS,
2651
IWL_INI_DUMP_EARLY_REGIONS,
2652
IWL_INI_DUMP_LATE_REGIONS,
2653
};
2654
2655
static bool iwl_dump_due_to_error(enum iwl_fw_ini_time_point tp_id)
2656
{
2657
return tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT ||
2658
tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR;
2659
}
2660
2661
static u32
2662
iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt,
2663
struct iwl_fwrt_dump_data *dump_data,
2664
struct list_head *list,
2665
enum iwl_fw_ini_time_point tp_id,
2666
u64 regions_mask,
2667
struct iwl_dump_ini_region_data *imr_reg_data,
2668
enum iwl_dump_ini_region_selector which)
2669
{
2670
u32 size = 0;
2671
2672
for (int i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) {
2673
struct iwl_dump_ini_region_data reg_data = {
2674
.dump_data = dump_data,
2675
};
2676
u32 reg_type, dp;
2677
struct iwl_fw_ini_region_tlv *reg;
2678
2679
if (!(BIT_ULL(i) & regions_mask))
2680
continue;
2681
2682
reg_data.reg_tlv = fwrt->trans->dbg.active_regions[i];
2683
if (!reg_data.reg_tlv) {
2684
IWL_WARN(fwrt,
2685
"WRT: Unassigned region id %d, skipping\n", i);
2686
continue;
2687
}
2688
2689
reg = (void *)reg_data.reg_tlv->data;
2690
reg_type = reg->type;
2691
if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops))
2692
continue;
2693
2694
dp = le32_get_bits(reg->id, IWL_FW_INI_REGION_DUMP_POLICY_MASK);
2695
2696
if ((reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY ||
2697
reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE ||
2698
reg_type == IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP) &&
2699
tp_id != IWL_FW_INI_TIME_POINT_FW_ASSERT) {
2700
IWL_WARN(fwrt,
2701
"WRT: trying to collect phy prph at time point: %d, skipping\n",
2702
tp_id);
2703
continue;
2704
}
2705
2706
switch (which) {
2707
case IWL_INI_DUMP_ALL_REGIONS:
2708
break;
2709
case IWL_INI_DUMP_EARLY_REGIONS:
2710
if (!(dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET))
2711
continue;
2712
break;
2713
case IWL_INI_DUMP_LATE_REGIONS:
2714
if (dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET)
2715
continue;
2716
break;
2717
}
2718
2719
/*
2720
* DRAM_IMR can be collected only for FW/HW error timepoint
2721
* when fw is not alive. In addition, it must be collected
2722
* lastly as it overwrites SRAM that can possibly contain
2723
* debug data which also need to be collected.
2724
*/
2725
if (reg_type == IWL_FW_INI_REGION_DRAM_IMR) {
2726
if (iwl_dump_due_to_error(tp_id))
2727
imr_reg_data->reg_tlv =
2728
fwrt->trans->dbg.active_regions[i];
2729
else
2730
IWL_INFO(fwrt,
2731
"WRT: trying to collect DRAM_IMR at time point: %d, skipping\n",
2732
tp_id);
2733
/* continue to next region */
2734
continue;
2735
}
2736
2737
2738
size += iwl_dump_ini_mem(fwrt, list, &reg_data,
2739
&iwl_dump_ini_region_ops[reg_type]);
2740
}
2741
2742
return size;
2743
}
2744
2745
static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
2746
struct iwl_fwrt_dump_data *dump_data,
2747
struct list_head *list)
2748
{
2749
struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig;
2750
enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point);
2751
struct iwl_dump_ini_region_data imr_reg_data = {
2752
.dump_data = dump_data,
2753
};
2754
u32 size = 0;
2755
u64 regions_mask = le64_to_cpu(trigger->regions_mask) &
2756
~(fwrt->trans->dbg.unsupported_region_msk);
2757
2758
BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask));
2759
BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) <
2760
ARRAY_SIZE(fwrt->trans->dbg.active_regions));
2761
2762
if (trigger->apply_policy &
2763
cpu_to_le32(IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET)) {
2764
size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
2765
regions_mask, &imr_reg_data,
2766
IWL_INI_DUMP_EARLY_REGIONS);
2767
iwl_trans_pcie_fw_reset_handshake(fwrt->trans);
2768
size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
2769
regions_mask, &imr_reg_data,
2770
IWL_INI_DUMP_LATE_REGIONS);
2771
} else {
2772
if (fw_has_capa(&fwrt->fw->ucode_capa,
2773
IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT) &&
2774
iwl_dump_due_to_error(tp_id))
2775
iwl_trans_pcie_fw_reset_handshake(fwrt->trans);
2776
size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
2777
regions_mask, &imr_reg_data,
2778
IWL_INI_DUMP_ALL_REGIONS);
2779
}
2780
/* collect DRAM_IMR region in the last */
2781
if (imr_reg_data.reg_tlv)
2782
size += iwl_dump_ini_mem(fwrt, list, &imr_reg_data,
2783
&iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]);
2784
2785
if (size) {
2786
size += iwl_dump_ini_file_name_info(fwrt, list);
2787
size += iwl_dump_ini_info(fwrt, trigger, list);
2788
}
2789
2790
return size;
2791
}
2792
2793
static bool iwl_fw_ini_trigger_on(struct iwl_fw_runtime *fwrt,
2794
struct iwl_fw_ini_trigger_tlv *trig)
2795
{
2796
enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trig->time_point);
2797
u32 usec = le32_to_cpu(trig->ignore_consec);
2798
2799
if (!iwl_trans_dbg_ini_valid(fwrt->trans) ||
2800
tp_id == IWL_FW_INI_TIME_POINT_INVALID ||
2801
tp_id >= IWL_FW_INI_TIME_POINT_NUM ||
2802
iwl_fw_dbg_no_trig_window(fwrt, tp_id, usec))
2803
return false;
2804
2805
return true;
2806
}
2807
2808
static u32 iwl_dump_ini_file_gen(struct iwl_fw_runtime *fwrt,
2809
struct iwl_fwrt_dump_data *dump_data,
2810
struct list_head *list)
2811
{
2812
struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig;
2813
struct iwl_fw_ini_dump_entry *entry;
2814
struct iwl_fw_ini_dump_file_hdr *hdr;
2815
u32 size;
2816
2817
if (!trigger || !iwl_fw_ini_trigger_on(fwrt, trigger) ||
2818
!le64_to_cpu(trigger->regions_mask))
2819
return 0;
2820
2821
entry = vzalloc(sizeof(*entry) + sizeof(*hdr));
2822
if (!entry)
2823
return 0;
2824
2825
entry->size = sizeof(*hdr);
2826
2827
size = iwl_dump_ini_trigger(fwrt, dump_data, list);
2828
if (!size) {
2829
vfree(entry);
2830
return 0;
2831
}
2832
2833
hdr = (void *)entry->data;
2834
hdr->barker = cpu_to_le32(IWL_FW_INI_ERROR_DUMP_BARKER);
2835
hdr->file_len = cpu_to_le32(size + entry->size);
2836
2837
list_add(&entry->list, list);
2838
2839
return le32_to_cpu(hdr->file_len);
2840
}
2841
2842
static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt,
2843
const struct iwl_fw_dump_desc *desc)
2844
{
2845
if (desc && desc != &iwl_dump_desc_assert)
2846
kfree(desc);
2847
2848
fwrt->dump.lmac_err_id[0] = 0;
2849
if (fwrt->smem_cfg.num_lmacs > 1)
2850
fwrt->dump.lmac_err_id[1] = 0;
2851
fwrt->dump.umac_err_id = 0;
2852
}
2853
2854
static void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
2855
struct iwl_fwrt_dump_data *dump_data)
2856
{
2857
struct iwl_fw_dump_ptrs fw_error_dump = {};
2858
struct iwl_fw_error_dump_file *dump_file;
2859
struct scatterlist *sg_dump_data;
2860
u32 file_len;
2861
u32 dump_mask = fwrt->fw->dbg.dump_mask;
2862
2863
dump_file = iwl_fw_error_dump_file(fwrt, &fw_error_dump, dump_data);
2864
if (!dump_file)
2865
return;
2866
2867
if (dump_data->monitor_only)
2868
dump_mask &= BIT(IWL_FW_ERROR_DUMP_FW_MONITOR);
2869
2870
fw_error_dump.trans_ptr = iwl_trans_dump_data(fwrt->trans, dump_mask,
2871
fwrt->sanitize_ops,
2872
fwrt->sanitize_ctx);
2873
file_len = le32_to_cpu(dump_file->file_len);
2874
fw_error_dump.fwrt_len = file_len;
2875
2876
if (fw_error_dump.trans_ptr) {
2877
file_len += fw_error_dump.trans_ptr->len;
2878
dump_file->file_len = cpu_to_le32(file_len);
2879
}
2880
2881
sg_dump_data = alloc_sgtable(file_len);
2882
if (sg_dump_data) {
2883
sg_pcopy_from_buffer(sg_dump_data,
2884
sg_nents(sg_dump_data),
2885
fw_error_dump.fwrt_ptr,
2886
fw_error_dump.fwrt_len, 0);
2887
if (fw_error_dump.trans_ptr)
2888
sg_pcopy_from_buffer(sg_dump_data,
2889
sg_nents(sg_dump_data),
2890
fw_error_dump.trans_ptr->data,
2891
fw_error_dump.trans_ptr->len,
2892
fw_error_dump.fwrt_len);
2893
dev_coredumpsg(fwrt->trans->dev, sg_dump_data, file_len,
2894
GFP_KERNEL);
2895
}
2896
vfree(fw_error_dump.fwrt_ptr);
2897
vfree(fw_error_dump.trans_ptr);
2898
}
2899
2900
static void iwl_dump_ini_list_free(struct list_head *list)
2901
{
2902
while (!list_empty(list)) {
2903
struct iwl_fw_ini_dump_entry *entry =
2904
list_entry(list->next, typeof(*entry), list);
2905
2906
list_del(&entry->list);
2907
vfree(entry);
2908
}
2909
}
2910
2911
static void iwl_fw_error_dump_data_free(struct iwl_fwrt_dump_data *dump_data)
2912
{
2913
dump_data->trig = NULL;
2914
kfree(dump_data->fw_pkt);
2915
dump_data->fw_pkt = NULL;
2916
}
2917
2918
static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt,
2919
struct iwl_fwrt_dump_data *dump_data)
2920
{
2921
#if defined(__linux__)
2922
LIST_HEAD(dump_list);
2923
#elif defined(__FreeBSD__)
2924
LINUX_LIST_HEAD(dump_list);
2925
#endif
2926
struct scatterlist *sg_dump_data;
2927
u32 file_len = iwl_dump_ini_file_gen(fwrt, dump_data, &dump_list);
2928
2929
if (!file_len)
2930
return;
2931
2932
sg_dump_data = alloc_sgtable(file_len);
2933
if (sg_dump_data) {
2934
struct iwl_fw_ini_dump_entry *entry;
2935
int sg_entries = sg_nents(sg_dump_data);
2936
u32 offs = 0;
2937
2938
list_for_each_entry(entry, &dump_list, list) {
2939
sg_pcopy_from_buffer(sg_dump_data, sg_entries,
2940
entry->data, entry->size, offs);
2941
offs += entry->size;
2942
}
2943
dev_coredumpsg(fwrt->trans->dev, sg_dump_data, file_len,
2944
GFP_KERNEL);
2945
}
2946
iwl_dump_ini_list_free(&dump_list);
2947
}
2948
2949
const struct iwl_fw_dump_desc iwl_dump_desc_assert = {
2950
.trig_desc = {
2951
.type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
2952
},
2953
};
2954
IWL_EXPORT_SYMBOL(iwl_dump_desc_assert);
2955
2956
int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
2957
const struct iwl_fw_dump_desc *desc,
2958
bool monitor_only,
2959
unsigned int delay)
2960
{
2961
struct iwl_fwrt_wk_data *wk_data;
2962
unsigned long idx;
2963
2964
if (iwl_trans_dbg_ini_valid(fwrt->trans)) {
2965
iwl_fw_free_dump_desc(fwrt, desc);
2966
return 0;
2967
}
2968
2969
/*
2970
* Check there is an available worker.
2971
* ffz return value is undefined if no zero exists,
2972
* so check against ~0UL first.
2973
*/
2974
if (fwrt->dump.active_wks == ~0UL)
2975
return -EBUSY;
2976
2977
idx = ffz(fwrt->dump.active_wks);
2978
2979
if (idx >= IWL_FW_RUNTIME_DUMP_WK_NUM ||
2980
test_and_set_bit(fwrt->dump.wks[idx].idx, &fwrt->dump.active_wks))
2981
return -EBUSY;
2982
2983
wk_data = &fwrt->dump.wks[idx];
2984
2985
if (WARN_ON(wk_data->dump_data.desc))
2986
iwl_fw_free_dump_desc(fwrt, wk_data->dump_data.desc);
2987
2988
wk_data->dump_data.desc = desc;
2989
wk_data->dump_data.monitor_only = monitor_only;
2990
2991
IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n",
2992
le32_to_cpu(desc->trig_desc.type));
2993
2994
queue_delayed_work(system_unbound_wq, &wk_data->wk,
2995
usecs_to_jiffies(delay));
2996
2997
return 0;
2998
}
2999
IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_desc);
3000
3001
int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt,
3002
enum iwl_fw_dbg_trigger trig_type)
3003
{
3004
if (!iwl_trans_device_enabled(fwrt->trans))
3005
return -EIO;
3006
3007
if (iwl_trans_dbg_ini_valid(fwrt->trans)) {
3008
if (trig_type != FW_DBG_TRIGGER_ALIVE_TIMEOUT &&
3009
trig_type != FW_DBG_TRIGGER_DRIVER)
3010
return -EIO;
3011
3012
iwl_dbg_tlv_time_point(fwrt,
3013
IWL_FW_INI_TIME_POINT_HOST_ALIVE_TIMEOUT,
3014
NULL);
3015
} else {
3016
struct iwl_fw_dump_desc *iwl_dump_error_desc;
3017
int ret;
3018
3019
iwl_dump_error_desc =
3020
kmalloc(sizeof(*iwl_dump_error_desc), GFP_KERNEL);
3021
3022
if (!iwl_dump_error_desc)
3023
return -ENOMEM;
3024
3025
iwl_dump_error_desc->trig_desc.type = cpu_to_le32(trig_type);
3026
iwl_dump_error_desc->len = 0;
3027
3028
ret = iwl_fw_dbg_collect_desc(fwrt, iwl_dump_error_desc,
3029
false, 0);
3030
if (ret) {
3031
kfree(iwl_dump_error_desc);
3032
return ret;
3033
}
3034
}
3035
3036
iwl_trans_sync_nmi(fwrt->trans);
3037
3038
return 0;
3039
}
3040
IWL_EXPORT_SYMBOL(iwl_fw_dbg_error_collect);
3041
3042
int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
3043
enum iwl_fw_dbg_trigger trig,
3044
const char *str, size_t len,
3045
struct iwl_fw_dbg_trigger_tlv *trigger)
3046
{
3047
struct iwl_fw_dump_desc *desc;
3048
unsigned int delay = 0;
3049
bool monitor_only = false;
3050
int ret;
3051
3052
if (trigger) {
3053
u16 occurrences = le16_to_cpu(trigger->occurrences) - 1;
3054
3055
if (!le16_to_cpu(trigger->occurrences))
3056
return 0;
3057
3058
if (trigger->flags & IWL_FW_DBG_FORCE_RESTART) {
3059
IWL_WARN(fwrt, "Force restart: trigger %d fired.\n",
3060
trig);
3061
iwl_force_nmi(fwrt->trans);
3062
return 0;
3063
}
3064
3065
trigger->occurrences = cpu_to_le16(occurrences);
3066
monitor_only = trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY;
3067
3068
/* convert msec to usec */
3069
delay = le32_to_cpu(trigger->stop_delay) * USEC_PER_MSEC;
3070
}
3071
3072
desc = kzalloc(struct_size(desc, trig_desc.data, len), GFP_ATOMIC);
3073
if (!desc)
3074
return -ENOMEM;
3075
3076
3077
desc->len = len;
3078
desc->trig_desc.type = cpu_to_le32(trig);
3079
memcpy(desc->trig_desc.data, str, len);
3080
3081
ret = iwl_fw_dbg_collect_desc(fwrt, desc, monitor_only, delay);
3082
if (ret)
3083
kfree(desc);
3084
3085
return ret;
3086
}
3087
IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect);
3088
3089
int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
3090
struct iwl_fw_dbg_trigger_tlv *trigger,
3091
const char *fmt, ...)
3092
{
3093
int len = 0;
3094
char buf[64];
3095
3096
if (iwl_trans_dbg_ini_valid(fwrt->trans))
3097
return 0;
3098
3099
if (fmt) {
3100
va_list ap;
3101
3102
buf[sizeof(buf) - 1] = '\0';
3103
3104
va_start(ap, fmt);
3105
vsnprintf(buf, sizeof(buf), fmt, ap);
3106
va_end(ap);
3107
3108
/* check for truncation */
3109
if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
3110
buf[sizeof(buf) - 1] = '\0';
3111
3112
len = strlen(buf) + 1;
3113
}
3114
3115
return iwl_fw_dbg_collect(fwrt, le32_to_cpu(trigger->id), buf, len,
3116
trigger);
3117
}
3118
IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_trig);
3119
3120
int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id)
3121
{
3122
u8 *ptr;
3123
int ret;
3124
int i;
3125
3126
if (WARN_ONCE(conf_id >= ARRAY_SIZE(fwrt->fw->dbg.conf_tlv),
3127
"Invalid configuration %d\n", conf_id))
3128
return -EINVAL;
3129
3130
/* EARLY START - firmware's configuration is hard coded */
3131
if ((!fwrt->fw->dbg.conf_tlv[conf_id] ||
3132
!fwrt->fw->dbg.conf_tlv[conf_id]->num_of_hcmds) &&
3133
conf_id == FW_DBG_START_FROM_ALIVE)
3134
return 0;
3135
3136
if (!fwrt->fw->dbg.conf_tlv[conf_id])
3137
return -EINVAL;
3138
3139
if (fwrt->dump.conf != FW_DBG_INVALID)
3140
IWL_INFO(fwrt, "FW already configured (%d) - re-configuring\n",
3141
fwrt->dump.conf);
3142
3143
/* Send all HCMDs for configuring the FW debug */
3144
ptr = (void *)&fwrt->fw->dbg.conf_tlv[conf_id]->hcmd;
3145
for (i = 0; i < fwrt->fw->dbg.conf_tlv[conf_id]->num_of_hcmds; i++) {
3146
struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
3147
struct iwl_host_cmd hcmd = {
3148
.id = cmd->id,
3149
.len = { le16_to_cpu(cmd->len), },
3150
.data = { cmd->data, },
3151
};
3152
3153
ret = iwl_trans_send_cmd(fwrt->trans, &hcmd);
3154
if (ret)
3155
return ret;
3156
3157
ptr += sizeof(*cmd);
3158
ptr += le16_to_cpu(cmd->len);
3159
}
3160
3161
fwrt->dump.conf = conf_id;
3162
3163
return 0;
3164
}
3165
IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf);
3166
3167
static void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt,
3168
u32 timepoint, u32 timepoint_data)
3169
{
3170
struct iwl_dbg_dump_complete_cmd hcmd_data;
3171
struct iwl_host_cmd hcmd = {
3172
.id = WIDE_ID(DEBUG_GROUP, FW_DUMP_COMPLETE_CMD),
3173
.data[0] = &hcmd_data,
3174
.len[0] = sizeof(hcmd_data),
3175
};
3176
3177
if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status))
3178
return;
3179
3180
if (fw_has_capa(&fwrt->fw->ucode_capa,
3181
IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT)) {
3182
hcmd_data.tp = cpu_to_le32(timepoint);
3183
hcmd_data.tp_data = cpu_to_le32(timepoint_data);
3184
iwl_trans_send_cmd(fwrt->trans, &hcmd);
3185
}
3186
}
3187
3188
/* this function assumes dump_start was called beforehand and dump_end will be
3189
* called afterwards
3190
*/
3191
static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)
3192
{
3193
struct iwl_fw_dbg_params params = {0};
3194
struct iwl_fwrt_dump_data *dump_data =
3195
&fwrt->dump.wks[wk_idx].dump_data;
3196
3197
if (!test_bit(wk_idx, &fwrt->dump.active_wks))
3198
return;
3199
3200
/* also checks 'desc' for pre-ini mode, since that shadows in union */
3201
if (!dump_data->trig) {
3202
IWL_ERR(fwrt, "dump trigger data is not set\n");
3203
goto out;
3204
}
3205
3206
if (!iwl_trans_device_enabled(fwrt->trans)) {
3207
IWL_ERR(fwrt, "Device is not enabled - cannot dump error\n");
3208
goto out;
3209
}
3210
3211
/* there's no point in fw dump if the bus is dead */
3212
if (iwl_trans_is_dead(fwrt->trans)) {
3213
IWL_ERR(fwrt, "Skip fw error dump since bus is dead\n");
3214
goto out;
3215
}
3216
3217
iwl_fw_dbg_stop_restart_recording(fwrt, &params, true);
3218
3219
IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection start\n");
3220
if (iwl_trans_dbg_ini_valid(fwrt->trans))
3221
iwl_fw_error_ini_dump(fwrt, dump_data);
3222
else
3223
iwl_fw_error_dump(fwrt, dump_data);
3224
IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection done\n");
3225
3226
iwl_fw_dbg_stop_restart_recording(fwrt, &params, false);
3227
3228
if (iwl_trans_dbg_ini_valid(fwrt->trans)) {
3229
u32 policy = le32_to_cpu(dump_data->trig->apply_policy);
3230
u32 time_point = le32_to_cpu(dump_data->trig->time_point);
3231
3232
if (policy & IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD) {
3233
IWL_DEBUG_FW_INFO(fwrt, "WRT: sending dump complete\n");
3234
iwl_send_dbg_dump_complete_cmd(fwrt, time_point, 0);
3235
}
3236
}
3237
3238
if (fwrt->trans->dbg.last_tp_resetfw == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY)
3239
iwl_force_nmi(fwrt->trans);
3240
out:
3241
if (iwl_trans_dbg_ini_valid(fwrt->trans)) {
3242
iwl_fw_error_dump_data_free(dump_data);
3243
} else {
3244
iwl_fw_free_dump_desc(fwrt, dump_data->desc);
3245
dump_data->desc = NULL;
3246
}
3247
3248
clear_bit(wk_idx, &fwrt->dump.active_wks);
3249
}
3250
3251
int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,
3252
struct iwl_fwrt_dump_data *dump_data,
3253
bool sync)
3254
{
3255
struct iwl_fw_ini_trigger_tlv *trig = dump_data->trig;
3256
enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trig->time_point);
3257
u32 occur, delay;
3258
unsigned long idx;
3259
3260
if (!iwl_fw_ini_trigger_on(fwrt, trig)) {
3261
IWL_WARN(fwrt, "WRT: Trigger %d is not active, aborting dump\n",
3262
tp_id);
3263
return -EINVAL;
3264
}
3265
3266
delay = le32_to_cpu(trig->dump_delay);
3267
occur = le32_to_cpu(trig->occurrences);
3268
if (!occur)
3269
return 0;
3270
3271
trig->occurrences = cpu_to_le32(--occur);
3272
3273
/* Check there is an available worker.
3274
* ffz return value is undefined if no zero exists,
3275
* so check against ~0UL first.
3276
*/
3277
if (fwrt->dump.active_wks == ~0UL)
3278
return -EBUSY;
3279
3280
idx = ffz(fwrt->dump.active_wks);
3281
3282
if (idx >= IWL_FW_RUNTIME_DUMP_WK_NUM ||
3283
test_and_set_bit(fwrt->dump.wks[idx].idx, &fwrt->dump.active_wks))
3284
return -EBUSY;
3285
3286
fwrt->dump.wks[idx].dump_data = *dump_data;
3287
3288
if (sync)
3289
delay = 0;
3290
3291
IWL_WARN(fwrt,
3292
"WRT: Collecting data: ini trigger %d fired (delay=%dms).\n",
3293
tp_id, (u32)(delay / USEC_PER_MSEC));
3294
3295
if (sync)
3296
iwl_fw_dbg_collect_sync(fwrt, idx);
3297
else
3298
queue_delayed_work(system_unbound_wq,
3299
&fwrt->dump.wks[idx].wk,
3300
usecs_to_jiffies(delay));
3301
3302
return 0;
3303
}
3304
3305
void iwl_fw_error_dump_wk(struct work_struct *work)
3306
{
3307
struct iwl_fwrt_wk_data *wks =
3308
container_of(work, typeof(*wks), wk.work);
3309
struct iwl_fw_runtime *fwrt =
3310
container_of(wks, typeof(*fwrt), dump.wks[wks->idx]);
3311
3312
/* assumes the op mode mutex is locked in dump_start since
3313
* iwl_fw_dbg_collect_sync can't run in parallel
3314
*/
3315
if (fwrt->ops && fwrt->ops->dump_start)
3316
fwrt->ops->dump_start(fwrt->ops_ctx);
3317
3318
iwl_fw_dbg_collect_sync(fwrt, wks->idx);
3319
3320
if (fwrt->ops && fwrt->ops->dump_end)
3321
fwrt->ops->dump_end(fwrt->ops_ctx);
3322
}
3323
3324
void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt)
3325
{
3326
const struct iwl_mac_cfg *mac_cfg = fwrt->trans->mac_cfg;
3327
3328
if (!iwl_fw_dbg_is_d3_debug_enabled(fwrt))
3329
return;
3330
3331
if (!fwrt->dump.d3_debug_data) {
3332
fwrt->dump.d3_debug_data = kmalloc(mac_cfg->base->d3_debug_data_length,
3333
GFP_KERNEL);
3334
if (!fwrt->dump.d3_debug_data) {
3335
IWL_ERR(fwrt,
3336
"failed to allocate memory for D3 debug data\n");
3337
return;
3338
}
3339
}
3340
3341
/* if the buffer holds previous debug data it is overwritten */
3342
iwl_trans_read_mem_bytes(fwrt->trans, mac_cfg->base->d3_debug_data_base_addr,
3343
fwrt->dump.d3_debug_data,
3344
mac_cfg->base->d3_debug_data_length);
3345
3346
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem)
3347
fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx,
3348
mac_cfg->base->d3_debug_data_base_addr,
3349
fwrt->dump.d3_debug_data,
3350
mac_cfg->base->d3_debug_data_length);
3351
}
3352
IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data);
3353
3354
void iwl_fw_dbg_stop_sync(struct iwl_fw_runtime *fwrt)
3355
{
3356
int i;
3357
3358
iwl_dbg_tlv_del_timers(fwrt->trans);
3359
for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++)
3360
iwl_fw_dbg_collect_sync(fwrt, i);
3361
3362
iwl_fw_dbg_stop_restart_recording(fwrt, NULL, true);
3363
}
3364
IWL_EXPORT_SYMBOL(iwl_fw_dbg_stop_sync);
3365
3366
static int iwl_fw_dbg_suspend_resume_hcmd(struct iwl_trans *trans, bool suspend)
3367
{
3368
struct iwl_dbg_suspend_resume_cmd cmd = {
3369
.operation = suspend ?
3370
cpu_to_le32(DBGC_SUSPEND_CMD) :
3371
cpu_to_le32(DBGC_RESUME_CMD),
3372
};
3373
struct iwl_host_cmd hcmd = {
3374
.id = WIDE_ID(DEBUG_GROUP, DBGC_SUSPEND_RESUME),
3375
.data[0] = &cmd,
3376
.len[0] = sizeof(cmd),
3377
};
3378
3379
return iwl_trans_send_cmd(trans, &hcmd);
3380
}
3381
3382
static void iwl_fw_dbg_stop_recording(struct iwl_trans *trans,
3383
struct iwl_fw_dbg_params *params)
3384
{
3385
if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
3386
iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100);
3387
return;
3388
}
3389
3390
if (params) {
3391
params->in_sample = iwl_read_umac_prph(trans, DBGC_IN_SAMPLE);
3392
params->out_ctrl = iwl_read_umac_prph(trans, DBGC_OUT_CTRL);
3393
}
3394
3395
iwl_write_umac_prph(trans, DBGC_IN_SAMPLE, 0);
3396
/* wait for the DBGC to finish writing the internal buffer to DRAM to
3397
* avoid halting the HW while writing
3398
*/
3399
usleep_range(700, 1000);
3400
iwl_write_umac_prph(trans, DBGC_OUT_CTRL, 0);
3401
}
3402
3403
static int iwl_fw_dbg_restart_recording(struct iwl_trans *trans,
3404
struct iwl_fw_dbg_params *params)
3405
{
3406
if (!params)
3407
return -EIO;
3408
3409
if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) {
3410
iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100);
3411
iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1);
3412
iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1);
3413
} else {
3414
iwl_write_umac_prph(trans, DBGC_IN_SAMPLE, params->in_sample);
3415
iwl_write_umac_prph(trans, DBGC_OUT_CTRL, params->out_ctrl);
3416
}
3417
3418
return 0;
3419
}
3420
3421
int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt)
3422
{
3423
struct iwl_mvm_marker marker = {
3424
.dw_len = sizeof(struct iwl_mvm_marker) / 4,
3425
.marker_id = MARKER_ID_SYNC_CLOCK,
3426
};
3427
struct iwl_host_cmd hcmd = {
3428
.flags = CMD_ASYNC,
3429
.id = WIDE_ID(LONG_GROUP, MARKER_CMD),
3430
.dataflags = {},
3431
};
3432
struct iwl_mvm_marker_rsp *resp;
3433
int cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
3434
WIDE_ID(LONG_GROUP, MARKER_CMD),
3435
IWL_FW_CMD_VER_UNKNOWN);
3436
int ret;
3437
3438
if (cmd_ver == 1) {
3439
/* the real timestamp is taken from the ftrace clock
3440
* this is for finding the match between fw and kernel logs
3441
*/
3442
marker.timestamp = cpu_to_le64(fwrt->timestamp.seq++);
3443
} else if (cmd_ver == 2) {
3444
marker.timestamp = cpu_to_le64(ktime_get_boottime_ns());
3445
} else {
3446
IWL_DEBUG_INFO(fwrt,
3447
"Invalid version of Marker CMD. Ver = %d\n",
3448
cmd_ver);
3449
return -EINVAL;
3450
}
3451
3452
hcmd.data[0] = &marker;
3453
hcmd.len[0] = sizeof(marker);
3454
3455
ret = iwl_trans_send_cmd(fwrt->trans, &hcmd);
3456
3457
if (cmd_ver > 1 && hcmd.resp_pkt) {
3458
resp = (void *)hcmd.resp_pkt->data;
3459
IWL_DEBUG_INFO(fwrt, "FW GP2 time: %u\n",
3460
le32_to_cpu(resp->gp2));
3461
}
3462
3463
return ret;
3464
}
3465
3466
void iwl_fw_dbg_stop_restart_recording(struct iwl_fw_runtime *fwrt,
3467
struct iwl_fw_dbg_params *params,
3468
bool stop)
3469
{
3470
int ret __maybe_unused = 0;
3471
3472
if (!iwl_trans_fw_running(fwrt->trans))
3473
return;
3474
3475
if (fw_has_capa(&fwrt->fw->ucode_capa,
3476
IWL_UCODE_TLV_CAPA_DBG_SUSPEND_RESUME_CMD_SUPP)) {
3477
if (stop)
3478
iwl_fw_send_timestamp_marker_cmd(fwrt);
3479
ret = iwl_fw_dbg_suspend_resume_hcmd(fwrt->trans, stop);
3480
} else if (stop) {
3481
iwl_fw_dbg_stop_recording(fwrt->trans, params);
3482
} else {
3483
ret = iwl_fw_dbg_restart_recording(fwrt->trans, params);
3484
}
3485
#ifdef CONFIG_IWLWIFI_DEBUGFS
3486
if (!ret) {
3487
if (stop)
3488
fwrt->trans->dbg.rec_on = false;
3489
else
3490
iwl_fw_set_dbg_rec_on(fwrt);
3491
}
3492
#endif
3493
}
3494
IWL_EXPORT_SYMBOL(iwl_fw_dbg_stop_restart_recording);
3495
3496
void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt)
3497
{
3498
struct iwl_fw_dbg_config_cmd cmd = {
3499
.type = cpu_to_le32(DEBUG_TOKEN_CONFIG_TYPE),
3500
.conf = cpu_to_le32(IWL_FW_DBG_CONFIG_TOKEN),
3501
};
3502
struct iwl_host_cmd hcmd = {
3503
.id = WIDE_ID(LONG_GROUP, LDBG_CONFIG_CMD),
3504
.data[0] = &cmd,
3505
.len[0] = sizeof(cmd),
3506
};
3507
u32 preset = u32_get_bits(fwrt->trans->dbg.domains_bitmap,
3508
GENMASK(31, IWL_FW_DBG_DOMAIN_POS + 1));
3509
3510
/* supported starting from 9000 devices */
3511
if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000)
3512
return;
3513
3514
if (fwrt->trans->dbg.yoyo_bin_loaded || (preset && preset != 1))
3515
return;
3516
3517
iwl_trans_send_cmd(fwrt->trans, &hcmd);
3518
}
3519
IWL_EXPORT_SYMBOL(iwl_fw_disable_dbg_asserts);
3520
3521
void iwl_fw_dbg_clear_monitor_buf(struct iwl_fw_runtime *fwrt)
3522
{
3523
struct iwl_fw_dbg_params params = {0};
3524
3525
iwl_fw_dbg_stop_sync(fwrt);
3526
3527
if (fw_has_api(&fwrt->fw->ucode_capa,
3528
IWL_UCODE_TLV_API_INT_DBG_BUF_CLEAR)) {
3529
struct iwl_host_cmd hcmd = {
3530
.id = WIDE_ID(DEBUG_GROUP, FW_CLEAR_BUFFER),
3531
};
3532
iwl_trans_send_cmd(fwrt->trans, &hcmd);
3533
}
3534
3535
iwl_dbg_tlv_init_cfg(fwrt);
3536
iwl_fw_dbg_stop_restart_recording(fwrt, &params, false);
3537
}
3538
IWL_EXPORT_SYMBOL(iwl_fw_dbg_clear_monitor_buf);
3539
3540