1 /******************************************************************************
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
8 * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
9 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
10 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of version 2 of the GNU General Public License as
14 * published by the Free Software Foundation.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program;
24 * The full GNU General Public License is included in this distribution
25 * in the file called COPYING.
27 * Contact Information:
28 * Intel Linux Wireless <linuxwifi@intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
33 * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
34 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
35 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
36 * All rights reserved.
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
42 * * Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * * Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in
46 * the documentation and/or other materials provided with the
48 * * Neither the name Intel Corporation nor the names of its
49 * contributors may be used to endorse or promote products derived
50 * from this software without specific prior written permission.
52 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
53 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
54 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
55 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
56 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
57 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
58 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
59 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
60 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
61 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
62 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 *****************************************************************************/
65 #include <linux/devcoredump.h>
74 * struct iwl_fw_dump_ptrs - set of pointers needed for the fw-error-dump
76 * @fwrt_ptr: pointer to the buffer coming from fwrt
77 * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the
79 * @trans_len: length of the valid data in trans_ptr
80 * @fwrt_len: length of the valid data in fwrt_ptr
82 struct iwl_fw_dump_ptrs
{
83 struct iwl_trans_dump_data
*trans_ptr
;
88 #define RADIO_REG_MAX_READ 0x2ad
89 static void iwl_read_radio_regs(struct iwl_fw_runtime
*fwrt
,
90 struct iwl_fw_error_dump_data
**dump_data
)
92 u8
*pos
= (void *)(*dump_data
)->data
;
96 IWL_DEBUG_INFO(fwrt
, "WRT radio registers dump\n");
98 if (!iwl_trans_grab_nic_access(fwrt
->trans
, &flags
))
101 (*dump_data
)->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG
);
102 (*dump_data
)->len
= cpu_to_le32(RADIO_REG_MAX_READ
);
104 for (i
= 0; i
< RADIO_REG_MAX_READ
; i
++) {
105 u32 rd_cmd
= RADIO_RSP_RD_CMD
;
107 rd_cmd
|= i
<< RADIO_RSP_ADDR_POS
;
108 iwl_write_prph_no_grab(fwrt
->trans
, RSP_RADIO_CMD
, rd_cmd
);
109 *pos
= (u8
)iwl_read_prph_no_grab(fwrt
->trans
, RSP_RADIO_RDDAT
);
114 *dump_data
= iwl_fw_error_next_data(*dump_data
);
116 iwl_trans_release_nic_access(fwrt
->trans
, &flags
);
119 static void iwl_fwrt_dump_rxf(struct iwl_fw_runtime
*fwrt
,
120 struct iwl_fw_error_dump_data
**dump_data
,
121 int size
, u32 offset
, int fifo_num
)
123 struct iwl_fw_error_dump_fifo
*fifo_hdr
;
128 fifo_hdr
= (void *)(*dump_data
)->data
;
129 fifo_data
= (void *)fifo_hdr
->data
;
132 /* No need to try to read the data if the length is 0 */
136 /* Add a TLV for the RXF */
137 (*dump_data
)->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_RXF
);
138 (*dump_data
)->len
= cpu_to_le32(fifo_len
+ sizeof(*fifo_hdr
));
140 fifo_hdr
->fifo_num
= cpu_to_le32(fifo_num
);
141 fifo_hdr
->available_bytes
=
142 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
143 RXF_RD_D_SPACE
+ offset
));
145 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
146 RXF_RD_WR_PTR
+ offset
));
148 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
149 RXF_RD_RD_PTR
+ offset
));
150 fifo_hdr
->fence_ptr
=
151 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
152 RXF_RD_FENCE_PTR
+ offset
));
153 fifo_hdr
->fence_mode
=
154 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
155 RXF_SET_FENCE_MODE
+ offset
));
158 iwl_trans_write_prph(fwrt
->trans
, RXF_SET_FENCE_MODE
+ offset
, 0x1);
159 /* Set fence pointer to the same place like WR pointer */
160 iwl_trans_write_prph(fwrt
->trans
, RXF_LD_WR2FENCE
+ offset
, 0x1);
161 /* Set fence offset */
162 iwl_trans_write_prph(fwrt
->trans
,
163 RXF_LD_FENCE_OFFSET_ADDR
+ offset
, 0x0);
166 fifo_len
/= sizeof(u32
); /* Size in DWORDS */
167 for (i
= 0; i
< fifo_len
; i
++)
168 fifo_data
[i
] = iwl_trans_read_prph(fwrt
->trans
,
169 RXF_FIFO_RD_FENCE_INC
+
171 *dump_data
= iwl_fw_error_next_data(*dump_data
);
174 static void iwl_fwrt_dump_txf(struct iwl_fw_runtime
*fwrt
,
175 struct iwl_fw_error_dump_data
**dump_data
,
176 int size
, u32 offset
, int fifo_num
)
178 struct iwl_fw_error_dump_fifo
*fifo_hdr
;
183 fifo_hdr
= (void *)(*dump_data
)->data
;
184 fifo_data
= (void *)fifo_hdr
->data
;
187 /* No need to try to read the data if the length is 0 */
191 /* Add a TLV for the FIFO */
192 (*dump_data
)->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_TXF
);
193 (*dump_data
)->len
= cpu_to_le32(fifo_len
+ sizeof(*fifo_hdr
));
195 fifo_hdr
->fifo_num
= cpu_to_le32(fifo_num
);
196 fifo_hdr
->available_bytes
=
197 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
198 TXF_FIFO_ITEM_CNT
+ offset
));
200 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
201 TXF_WR_PTR
+ offset
));
203 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
204 TXF_RD_PTR
+ offset
));
205 fifo_hdr
->fence_ptr
=
206 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
207 TXF_FENCE_PTR
+ offset
));
208 fifo_hdr
->fence_mode
=
209 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
210 TXF_LOCK_FENCE
+ offset
));
212 /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
213 iwl_trans_write_prph(fwrt
->trans
, TXF_READ_MODIFY_ADDR
+ offset
,
214 TXF_WR_PTR
+ offset
);
216 /* Dummy-read to advance the read pointer to the head */
217 iwl_trans_read_prph(fwrt
->trans
, TXF_READ_MODIFY_DATA
+ offset
);
220 fifo_len
/= sizeof(u32
); /* Size in DWORDS */
221 for (i
= 0; i
< fifo_len
; i
++)
222 fifo_data
[i
] = iwl_trans_read_prph(fwrt
->trans
,
223 TXF_READ_MODIFY_DATA
+
225 *dump_data
= iwl_fw_error_next_data(*dump_data
);
228 static void iwl_fw_dump_fifos(struct iwl_fw_runtime
*fwrt
,
229 struct iwl_fw_error_dump_data
**dump_data
)
231 struct iwl_fw_error_dump_fifo
*fifo_hdr
;
232 struct iwl_fwrt_shared_mem_cfg
*cfg
= &fwrt
->smem_cfg
;
238 IWL_DEBUG_INFO(fwrt
, "WRT FIFO dump\n");
240 if (!iwl_trans_grab_nic_access(fwrt
->trans
, &flags
))
244 iwl_fwrt_dump_rxf(fwrt
, dump_data
, cfg
->lmac
[0].rxfifo1_size
, 0, 0);
246 iwl_fwrt_dump_rxf(fwrt
, dump_data
, cfg
->rxfifo2_size
,
247 RXF_DIFF_FROM_PREV
, 1);
248 /* Pull LMAC2 RXF1 */
249 if (fwrt
->smem_cfg
.num_lmacs
> 1)
250 iwl_fwrt_dump_rxf(fwrt
, dump_data
, cfg
->lmac
[1].rxfifo1_size
,
251 LMAC2_PRPH_OFFSET
, 2);
253 /* Pull TXF data from LMAC1 */
254 for (i
= 0; i
< fwrt
->smem_cfg
.num_txfifo_entries
; i
++) {
255 /* Mark the number of TXF we're pulling now */
256 iwl_trans_write_prph(fwrt
->trans
, TXF_LARC_NUM
, i
);
257 iwl_fwrt_dump_txf(fwrt
, dump_data
, cfg
->lmac
[0].txfifo_size
[i
],
261 /* Pull TXF data from LMAC2 */
262 if (fwrt
->smem_cfg
.num_lmacs
> 1) {
263 for (i
= 0; i
< fwrt
->smem_cfg
.num_txfifo_entries
; i
++) {
264 /* Mark the number of TXF we're pulling now */
265 iwl_trans_write_prph(fwrt
->trans
,
266 TXF_LARC_NUM
+ LMAC2_PRPH_OFFSET
,
268 iwl_fwrt_dump_txf(fwrt
, dump_data
,
269 cfg
->lmac
[1].txfifo_size
[i
],
271 i
+ cfg
->num_txfifo_entries
);
275 if (fw_has_capa(&fwrt
->fw
->ucode_capa
,
276 IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG
)) {
277 /* Pull UMAC internal TXF data from all TXFs */
279 i
< ARRAY_SIZE(fwrt
->smem_cfg
.internal_txfifo_size
);
281 fifo_hdr
= (void *)(*dump_data
)->data
;
282 fifo_data
= (void *)fifo_hdr
->data
;
283 fifo_len
= fwrt
->smem_cfg
.internal_txfifo_size
[i
];
285 /* No need to try to read the data if the length is 0 */
289 /* Add a TLV for the internal FIFOs */
291 cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF
);
293 cpu_to_le32(fifo_len
+ sizeof(*fifo_hdr
));
295 fifo_hdr
->fifo_num
= cpu_to_le32(i
);
297 /* Mark the number of TXF we're pulling now */
298 iwl_trans_write_prph(fwrt
->trans
, TXF_CPU2_NUM
, i
+
299 fwrt
->smem_cfg
.num_txfifo_entries
);
301 fifo_hdr
->available_bytes
=
302 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
303 TXF_CPU2_FIFO_ITEM_CNT
));
305 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
308 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
310 fifo_hdr
->fence_ptr
=
311 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
312 TXF_CPU2_FENCE_PTR
));
313 fifo_hdr
->fence_mode
=
314 cpu_to_le32(iwl_trans_read_prph(fwrt
->trans
,
315 TXF_CPU2_LOCK_FENCE
));
317 /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */
318 iwl_trans_write_prph(fwrt
->trans
,
319 TXF_CPU2_READ_MODIFY_ADDR
,
322 /* Dummy-read to advance the read pointer to head */
323 iwl_trans_read_prph(fwrt
->trans
,
324 TXF_CPU2_READ_MODIFY_DATA
);
327 fifo_len
/= sizeof(u32
); /* Size in DWORDS */
328 for (j
= 0; j
< fifo_len
; j
++)
330 iwl_trans_read_prph(fwrt
->trans
,
331 TXF_CPU2_READ_MODIFY_DATA
);
332 *dump_data
= iwl_fw_error_next_data(*dump_data
);
336 iwl_trans_release_nic_access(fwrt
->trans
, &flags
);
339 #define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */
340 #define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */
342 struct iwl_prph_range
{
346 static const struct iwl_prph_range iwl_prph_dump_addr_comm
[] = {
347 { .start
= 0x00a00000, .end
= 0x00a00000 },
348 { .start
= 0x00a0000c, .end
= 0x00a00024 },
349 { .start
= 0x00a0002c, .end
= 0x00a0003c },
350 { .start
= 0x00a00410, .end
= 0x00a00418 },
351 { .start
= 0x00a00420, .end
= 0x00a00420 },
352 { .start
= 0x00a00428, .end
= 0x00a00428 },
353 { .start
= 0x00a00430, .end
= 0x00a0043c },
354 { .start
= 0x00a00444, .end
= 0x00a00444 },
355 { .start
= 0x00a004c0, .end
= 0x00a004cc },
356 { .start
= 0x00a004d8, .end
= 0x00a004d8 },
357 { .start
= 0x00a004e0, .end
= 0x00a004f0 },
358 { .start
= 0x00a00840, .end
= 0x00a00840 },
359 { .start
= 0x00a00850, .end
= 0x00a00858 },
360 { .start
= 0x00a01004, .end
= 0x00a01008 },
361 { .start
= 0x00a01010, .end
= 0x00a01010 },
362 { .start
= 0x00a01018, .end
= 0x00a01018 },
363 { .start
= 0x00a01024, .end
= 0x00a01024 },
364 { .start
= 0x00a0102c, .end
= 0x00a01034 },
365 { .start
= 0x00a0103c, .end
= 0x00a01040 },
366 { .start
= 0x00a01048, .end
= 0x00a01094 },
367 { .start
= 0x00a01c00, .end
= 0x00a01c20 },
368 { .start
= 0x00a01c58, .end
= 0x00a01c58 },
369 { .start
= 0x00a01c7c, .end
= 0x00a01c7c },
370 { .start
= 0x00a01c28, .end
= 0x00a01c54 },
371 { .start
= 0x00a01c5c, .end
= 0x00a01c5c },
372 { .start
= 0x00a01c60, .end
= 0x00a01cdc },
373 { .start
= 0x00a01ce0, .end
= 0x00a01d0c },
374 { .start
= 0x00a01d18, .end
= 0x00a01d20 },
375 { .start
= 0x00a01d2c, .end
= 0x00a01d30 },
376 { .start
= 0x00a01d40, .end
= 0x00a01d5c },
377 { .start
= 0x00a01d80, .end
= 0x00a01d80 },
378 { .start
= 0x00a01d98, .end
= 0x00a01d9c },
379 { .start
= 0x00a01da8, .end
= 0x00a01da8 },
380 { .start
= 0x00a01db8, .end
= 0x00a01df4 },
381 { .start
= 0x00a01dc0, .end
= 0x00a01dfc },
382 { .start
= 0x00a01e00, .end
= 0x00a01e2c },
383 { .start
= 0x00a01e40, .end
= 0x00a01e60 },
384 { .start
= 0x00a01e68, .end
= 0x00a01e6c },
385 { .start
= 0x00a01e74, .end
= 0x00a01e74 },
386 { .start
= 0x00a01e84, .end
= 0x00a01e90 },
387 { .start
= 0x00a01e9c, .end
= 0x00a01ec4 },
388 { .start
= 0x00a01ed0, .end
= 0x00a01ee0 },
389 { .start
= 0x00a01f00, .end
= 0x00a01f1c },
390 { .start
= 0x00a01f44, .end
= 0x00a01ffc },
391 { .start
= 0x00a02000, .end
= 0x00a02048 },
392 { .start
= 0x00a02068, .end
= 0x00a020f0 },
393 { .start
= 0x00a02100, .end
= 0x00a02118 },
394 { .start
= 0x00a02140, .end
= 0x00a0214c },
395 { .start
= 0x00a02168, .end
= 0x00a0218c },
396 { .start
= 0x00a021c0, .end
= 0x00a021c0 },
397 { .start
= 0x00a02400, .end
= 0x00a02410 },
398 { .start
= 0x00a02418, .end
= 0x00a02420 },
399 { .start
= 0x00a02428, .end
= 0x00a0242c },
400 { .start
= 0x00a02434, .end
= 0x00a02434 },
401 { .start
= 0x00a02440, .end
= 0x00a02460 },
402 { .start
= 0x00a02468, .end
= 0x00a024b0 },
403 { .start
= 0x00a024c8, .end
= 0x00a024cc },
404 { .start
= 0x00a02500, .end
= 0x00a02504 },
405 { .start
= 0x00a0250c, .end
= 0x00a02510 },
406 { .start
= 0x00a02540, .end
= 0x00a02554 },
407 { .start
= 0x00a02580, .end
= 0x00a025f4 },
408 { .start
= 0x00a02600, .end
= 0x00a0260c },
409 { .start
= 0x00a02648, .end
= 0x00a02650 },
410 { .start
= 0x00a02680, .end
= 0x00a02680 },
411 { .start
= 0x00a026c0, .end
= 0x00a026d0 },
412 { .start
= 0x00a02700, .end
= 0x00a0270c },
413 { .start
= 0x00a02804, .end
= 0x00a02804 },
414 { .start
= 0x00a02818, .end
= 0x00a0281c },
415 { .start
= 0x00a02c00, .end
= 0x00a02db4 },
416 { .start
= 0x00a02df4, .end
= 0x00a02fb0 },
417 { .start
= 0x00a03000, .end
= 0x00a03014 },
418 { .start
= 0x00a0301c, .end
= 0x00a0302c },
419 { .start
= 0x00a03034, .end
= 0x00a03038 },
420 { .start
= 0x00a03040, .end
= 0x00a03048 },
421 { .start
= 0x00a03060, .end
= 0x00a03068 },
422 { .start
= 0x00a03070, .end
= 0x00a03074 },
423 { .start
= 0x00a0307c, .end
= 0x00a0307c },
424 { .start
= 0x00a03080, .end
= 0x00a03084 },
425 { .start
= 0x00a0308c, .end
= 0x00a03090 },
426 { .start
= 0x00a03098, .end
= 0x00a03098 },
427 { .start
= 0x00a030a0, .end
= 0x00a030a0 },
428 { .start
= 0x00a030a8, .end
= 0x00a030b4 },
429 { .start
= 0x00a030bc, .end
= 0x00a030bc },
430 { .start
= 0x00a030c0, .end
= 0x00a0312c },
431 { .start
= 0x00a03c00, .end
= 0x00a03c5c },
432 { .start
= 0x00a04400, .end
= 0x00a04454 },
433 { .start
= 0x00a04460, .end
= 0x00a04474 },
434 { .start
= 0x00a044c0, .end
= 0x00a044ec },
435 { .start
= 0x00a04500, .end
= 0x00a04504 },
436 { .start
= 0x00a04510, .end
= 0x00a04538 },
437 { .start
= 0x00a04540, .end
= 0x00a04548 },
438 { .start
= 0x00a04560, .end
= 0x00a0457c },
439 { .start
= 0x00a04590, .end
= 0x00a04598 },
440 { .start
= 0x00a045c0, .end
= 0x00a045f4 },
443 static const struct iwl_prph_range iwl_prph_dump_addr_9000
[] = {
444 { .start
= 0x00a05c00, .end
= 0x00a05c18 },
445 { .start
= 0x00a05400, .end
= 0x00a056e8 },
446 { .start
= 0x00a08000, .end
= 0x00a098bc },
447 { .start
= 0x00a02400, .end
= 0x00a02758 },
450 static void _iwl_read_prph_block(struct iwl_trans
*trans
, u32 start
,
451 u32 len_bytes
, __le32
*data
)
455 for (i
= 0; i
< len_bytes
; i
+= 4)
456 *data
++ = cpu_to_le32(iwl_read_prph_no_grab(trans
, start
+ i
));
459 static bool iwl_read_prph_block(struct iwl_trans
*trans
, u32 start
,
460 u32 len_bytes
, __le32
*data
)
463 bool success
= false;
465 if (iwl_trans_grab_nic_access(trans
, &flags
)) {
467 _iwl_read_prph_block(trans
, start
, len_bytes
, data
);
468 iwl_trans_release_nic_access(trans
, &flags
);
474 static void iwl_dump_prph(struct iwl_trans
*trans
,
475 struct iwl_fw_error_dump_data
**data
,
476 const struct iwl_prph_range
*iwl_prph_dump_addr
,
479 struct iwl_fw_error_dump_prph
*prph
;
483 IWL_DEBUG_INFO(trans
, "WRT PRPH dump\n");
485 if (!iwl_trans_grab_nic_access(trans
, &flags
))
488 for (i
= 0; i
< range_len
; i
++) {
489 /* The range includes both boundaries */
490 int num_bytes_in_chunk
= iwl_prph_dump_addr
[i
].end
-
491 iwl_prph_dump_addr
[i
].start
+ 4;
493 (*data
)->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH
);
494 (*data
)->len
= cpu_to_le32(sizeof(*prph
) +
496 prph
= (void *)(*data
)->data
;
497 prph
->prph_start
= cpu_to_le32(iwl_prph_dump_addr
[i
].start
);
499 _iwl_read_prph_block(trans
, iwl_prph_dump_addr
[i
].start
,
500 /* our range is inclusive, hence + 4 */
501 iwl_prph_dump_addr
[i
].end
-
502 iwl_prph_dump_addr
[i
].start
+ 4,
505 *data
= iwl_fw_error_next_data(*data
);
508 iwl_trans_release_nic_access(trans
, &flags
);
512 * alloc_sgtable - allocates scallerlist table in the given size,
513 * fills it with pages and returns it
514 * @size: the size (in bytes) of the table
516 static struct scatterlist
*alloc_sgtable(int size
)
518 int alloc_size
, nents
, i
;
519 struct page
*new_page
;
520 struct scatterlist
*iter
;
521 struct scatterlist
*table
;
523 nents
= DIV_ROUND_UP(size
, PAGE_SIZE
);
524 table
= kcalloc(nents
, sizeof(*table
), GFP_KERNEL
);
527 sg_init_table(table
, nents
);
529 for_each_sg(table
, iter
, sg_nents(table
), i
) {
530 new_page
= alloc_page(GFP_KERNEL
);
532 /* release all previous allocated pages in the table */
534 for_each_sg(table
, iter
, sg_nents(table
), i
) {
535 new_page
= sg_page(iter
);
537 __free_page(new_page
);
541 alloc_size
= min_t(int, size
, PAGE_SIZE
);
543 sg_set_page(iter
, new_page
, alloc_size
, 0);
548 void iwl_fw_error_dump(struct iwl_fw_runtime
*fwrt
)
550 struct iwl_fw_error_dump_file
*dump_file
;
551 struct iwl_fw_error_dump_data
*dump_data
;
552 struct iwl_fw_error_dump_info
*dump_info
;
553 struct iwl_fw_error_dump_mem
*dump_mem
;
554 struct iwl_fw_error_dump_smem_cfg
*dump_smem_cfg
;
555 struct iwl_fw_error_dump_trigger_desc
*dump_trig
;
556 struct iwl_fw_dump_ptrs
*fw_error_dump
;
557 struct scatterlist
*sg_dump_data
;
558 u32 sram_len
, sram_ofs
;
559 const struct iwl_fw_dbg_mem_seg_tlv
*fw_dbg_mem
= fwrt
->fw
->dbg_mem_tlv
;
560 struct iwl_fwrt_shared_mem_cfg
*mem_cfg
= &fwrt
->smem_cfg
;
561 u32 file_len
, fifo_data_len
= 0, prph_len
= 0, radio_len
= 0;
562 u32 smem_len
= fwrt
->fw
->n_dbg_mem_tlv
? 0 : fwrt
->trans
->cfg
->smem_len
;
563 u32 sram2_len
= fwrt
->fw
->n_dbg_mem_tlv
?
564 0 : fwrt
->trans
->cfg
->dccm2_len
;
565 bool monitor_dump_only
= false;
568 IWL_DEBUG_INFO(fwrt
, "WRT dump start\n");
570 /* there's no point in fw dump if the bus is dead */
571 if (test_bit(STATUS_TRANS_DEAD
, &fwrt
->trans
->status
)) {
572 IWL_ERR(fwrt
, "Skip fw error dump since bus is dead\n");
576 if (fwrt
->dump
.trig
&&
577 fwrt
->dump
.trig
->mode
& IWL_FW_DBG_TRIGGER_MONITOR_ONLY
)
578 monitor_dump_only
= true;
580 fw_error_dump
= kzalloc(sizeof(*fw_error_dump
), GFP_KERNEL
);
584 /* SRAM - include stack CCM if driver knows the values for it */
585 if (!fwrt
->trans
->cfg
->dccm_offset
|| !fwrt
->trans
->cfg
->dccm_len
) {
586 const struct fw_img
*img
;
588 img
= &fwrt
->fw
->img
[fwrt
->cur_fw_img
];
589 sram_ofs
= img
->sec
[IWL_UCODE_SECTION_DATA
].offset
;
590 sram_len
= img
->sec
[IWL_UCODE_SECTION_DATA
].len
;
592 sram_ofs
= fwrt
->trans
->cfg
->dccm_offset
;
593 sram_len
= fwrt
->trans
->cfg
->dccm_len
;
596 /* reading RXF/TXF sizes */
597 if (test_bit(STATUS_FW_ERROR
, &fwrt
->trans
->status
)) {
600 /* Count RXF2 size */
601 if (mem_cfg
->rxfifo2_size
) {
602 /* Add header info */
603 fifo_data_len
+= mem_cfg
->rxfifo2_size
+
605 sizeof(struct iwl_fw_error_dump_fifo
);
608 /* Count RXF1 sizes */
609 for (i
= 0; i
< mem_cfg
->num_lmacs
; i
++) {
610 if (!mem_cfg
->lmac
[i
].rxfifo1_size
)
613 /* Add header info */
614 fifo_data_len
+= mem_cfg
->lmac
[i
].rxfifo1_size
+
616 sizeof(struct iwl_fw_error_dump_fifo
);
619 /* Count TXF sizes */
620 for (i
= 0; i
< mem_cfg
->num_lmacs
; i
++) {
623 for (j
= 0; j
< mem_cfg
->num_txfifo_entries
; j
++) {
624 if (!mem_cfg
->lmac
[i
].txfifo_size
[j
])
627 /* Add header info */
629 mem_cfg
->lmac
[i
].txfifo_size
[j
] +
631 sizeof(struct iwl_fw_error_dump_fifo
);
635 if (fw_has_capa(&fwrt
->fw
->ucode_capa
,
636 IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG
)) {
638 i
< ARRAY_SIZE(mem_cfg
->internal_txfifo_size
);
640 if (!mem_cfg
->internal_txfifo_size
[i
])
643 /* Add header info */
645 mem_cfg
->internal_txfifo_size
[i
] +
647 sizeof(struct iwl_fw_error_dump_fifo
);
651 /* Make room for PRPH registers */
652 if (!fwrt
->trans
->cfg
->gen2
) {
653 for (i
= 0; i
< ARRAY_SIZE(iwl_prph_dump_addr_comm
);
655 /* The range includes both boundaries */
656 int num_bytes_in_chunk
=
657 iwl_prph_dump_addr_comm
[i
].end
-
658 iwl_prph_dump_addr_comm
[i
].start
+ 4;
660 prph_len
+= sizeof(*dump_data
) +
661 sizeof(struct iwl_fw_error_dump_prph
) +
666 if (!fwrt
->trans
->cfg
->gen2
&&
667 fwrt
->trans
->cfg
->mq_rx_supported
) {
669 ARRAY_SIZE(iwl_prph_dump_addr_9000
); i
++) {
670 /* The range includes both boundaries */
671 int num_bytes_in_chunk
=
672 iwl_prph_dump_addr_9000
[i
].end
-
673 iwl_prph_dump_addr_9000
[i
].start
+ 4;
675 prph_len
+= sizeof(*dump_data
) +
676 sizeof(struct iwl_fw_error_dump_prph
) +
681 if (fwrt
->trans
->cfg
->device_family
== IWL_DEVICE_FAMILY_7000
)
682 radio_len
= sizeof(*dump_data
) + RADIO_REG_MAX_READ
;
685 file_len
= sizeof(*dump_file
) +
686 sizeof(*dump_data
) * 3 +
687 sizeof(*dump_smem_cfg
) +
693 /* Make room for the SMEM, if it exists */
695 file_len
+= sizeof(*dump_data
) + sizeof(*dump_mem
) + smem_len
;
697 /* Make room for the secondary SRAM, if it exists */
699 file_len
+= sizeof(*dump_data
) + sizeof(*dump_mem
) + sram2_len
;
701 /* Make room for MEM segments */
702 for (i
= 0; i
< fwrt
->fw
->n_dbg_mem_tlv
; i
++) {
703 file_len
+= sizeof(*dump_data
) + sizeof(*dump_mem
) +
704 le32_to_cpu(fw_dbg_mem
[i
].len
);
707 /* Make room for fw's virtual image pages, if it exists */
708 if (!fwrt
->trans
->cfg
->gen2
&&
709 fwrt
->fw
->img
[fwrt
->cur_fw_img
].paging_mem_size
&&
710 fwrt
->fw_paging_db
[0].fw_paging_block
)
711 file_len
+= fwrt
->num_of_paging_blk
*
712 (sizeof(*dump_data
) +
713 sizeof(struct iwl_fw_error_dump_paging
) +
716 /* If we only want a monitor dump, reset the file length */
717 if (monitor_dump_only
) {
718 file_len
= sizeof(*dump_file
) + sizeof(*dump_data
) * 2 +
719 sizeof(*dump_info
) + sizeof(*dump_smem_cfg
);
723 file_len
+= sizeof(*dump_data
) + sizeof(*dump_trig
) +
724 fwrt
->dump
.desc
->len
;
726 if (!fwrt
->fw
->n_dbg_mem_tlv
)
727 file_len
+= sram_len
+ sizeof(*dump_mem
);
729 dump_file
= vzalloc(file_len
);
731 kfree(fw_error_dump
);
735 fw_error_dump
->fwrt_ptr
= dump_file
;
737 dump_file
->barker
= cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER
);
738 dump_data
= (void *)dump_file
->data
;
740 dump_data
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO
);
741 dump_data
->len
= cpu_to_le32(sizeof(*dump_info
));
742 dump_info
= (void *)dump_data
->data
;
743 dump_info
->device_family
=
744 fwrt
->trans
->cfg
->device_family
== IWL_DEVICE_FAMILY_7000
?
745 cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7
) :
746 cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8
);
747 dump_info
->hw_step
= cpu_to_le32(CSR_HW_REV_STEP(fwrt
->trans
->hw_rev
));
748 memcpy(dump_info
->fw_human_readable
, fwrt
->fw
->human_readable
,
749 sizeof(dump_info
->fw_human_readable
));
750 strncpy(dump_info
->dev_human_readable
, fwrt
->trans
->cfg
->name
,
751 sizeof(dump_info
->dev_human_readable
));
752 strncpy(dump_info
->bus_human_readable
, fwrt
->dev
->bus
->name
,
753 sizeof(dump_info
->bus_human_readable
));
755 dump_data
= iwl_fw_error_next_data(dump_data
);
757 /* Dump shared memory configuration */
758 dump_data
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG
);
759 dump_data
->len
= cpu_to_le32(sizeof(*dump_smem_cfg
));
760 dump_smem_cfg
= (void *)dump_data
->data
;
761 dump_smem_cfg
->num_lmacs
= cpu_to_le32(mem_cfg
->num_lmacs
);
762 dump_smem_cfg
->num_txfifo_entries
=
763 cpu_to_le32(mem_cfg
->num_txfifo_entries
);
764 for (i
= 0; i
< MAX_NUM_LMAC
; i
++) {
767 for (j
= 0; j
< TX_FIFO_MAX_NUM
; j
++)
768 dump_smem_cfg
->lmac
[i
].txfifo_size
[j
] =
769 cpu_to_le32(mem_cfg
->lmac
[i
].txfifo_size
[j
]);
770 dump_smem_cfg
->lmac
[i
].rxfifo1_size
=
771 cpu_to_le32(mem_cfg
->lmac
[i
].rxfifo1_size
);
773 dump_smem_cfg
->rxfifo2_size
= cpu_to_le32(mem_cfg
->rxfifo2_size
);
774 dump_smem_cfg
->internal_txfifo_addr
=
775 cpu_to_le32(mem_cfg
->internal_txfifo_addr
);
776 for (i
= 0; i
< TX_FIFO_INTERNAL_MAX_NUM
; i
++) {
777 dump_smem_cfg
->internal_txfifo_size
[i
] =
778 cpu_to_le32(mem_cfg
->internal_txfifo_size
[i
]);
781 dump_data
= iwl_fw_error_next_data(dump_data
);
783 /* We only dump the FIFOs if the FW is in error state */
784 if (test_bit(STATUS_FW_ERROR
, &fwrt
->trans
->status
)) {
785 iwl_fw_dump_fifos(fwrt
, &dump_data
);
787 iwl_read_radio_regs(fwrt
, &dump_data
);
790 if (fwrt
->dump
.desc
) {
791 dump_data
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO
);
792 dump_data
->len
= cpu_to_le32(sizeof(*dump_trig
) +
793 fwrt
->dump
.desc
->len
);
794 dump_trig
= (void *)dump_data
->data
;
795 memcpy(dump_trig
, &fwrt
->dump
.desc
->trig_desc
,
796 sizeof(*dump_trig
) + fwrt
->dump
.desc
->len
);
798 dump_data
= iwl_fw_error_next_data(dump_data
);
801 /* In case we only want monitor dump, skip to dump trasport data */
802 if (monitor_dump_only
)
803 goto dump_trans_data
;
805 if (!fwrt
->fw
->n_dbg_mem_tlv
) {
806 dump_data
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_MEM
);
807 dump_data
->len
= cpu_to_le32(sram_len
+ sizeof(*dump_mem
));
808 dump_mem
= (void *)dump_data
->data
;
809 dump_mem
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM
);
810 dump_mem
->offset
= cpu_to_le32(sram_ofs
);
811 iwl_trans_read_mem_bytes(fwrt
->trans
, sram_ofs
, dump_mem
->data
,
813 dump_data
= iwl_fw_error_next_data(dump_data
);
816 for (i
= 0; i
< fwrt
->fw
->n_dbg_mem_tlv
; i
++) {
817 u32 len
= le32_to_cpu(fw_dbg_mem
[i
].len
);
818 u32 ofs
= le32_to_cpu(fw_dbg_mem
[i
].ofs
);
821 dump_data
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_MEM
);
822 dump_data
->len
= cpu_to_le32(len
+ sizeof(*dump_mem
));
823 dump_mem
= (void *)dump_data
->data
;
824 dump_mem
->type
= fw_dbg_mem
[i
].data_type
;
825 dump_mem
->offset
= cpu_to_le32(ofs
);
827 IWL_DEBUG_INFO(fwrt
, "WRT memory dump. Type=%u\n",
830 switch (dump_mem
->type
& cpu_to_le32(FW_DBG_MEM_TYPE_MASK
)) {
831 case cpu_to_le32(FW_DBG_MEM_TYPE_REGULAR
):
832 iwl_trans_read_mem_bytes(fwrt
->trans
, ofs
,
837 case cpu_to_le32(FW_DBG_MEM_TYPE_PRPH
):
838 success
= iwl_read_prph_block(fwrt
->trans
, ofs
, len
,
839 (void *)dump_mem
->data
);
843 * shouldn't get here, we ignored this kind
844 * of TLV earlier during the TLV parsing?!
851 dump_data
= iwl_fw_error_next_data(dump_data
);
855 IWL_DEBUG_INFO(fwrt
, "WRT SMEM dump\n");
856 dump_data
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_MEM
);
857 dump_data
->len
= cpu_to_le32(smem_len
+ sizeof(*dump_mem
));
858 dump_mem
= (void *)dump_data
->data
;
859 dump_mem
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM
);
860 dump_mem
->offset
= cpu_to_le32(fwrt
->trans
->cfg
->smem_offset
);
861 iwl_trans_read_mem_bytes(fwrt
->trans
,
862 fwrt
->trans
->cfg
->smem_offset
,
863 dump_mem
->data
, smem_len
);
864 dump_data
= iwl_fw_error_next_data(dump_data
);
868 IWL_DEBUG_INFO(fwrt
, "WRT SRAM dump\n");
869 dump_data
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_MEM
);
870 dump_data
->len
= cpu_to_le32(sram2_len
+ sizeof(*dump_mem
));
871 dump_mem
= (void *)dump_data
->data
;
872 dump_mem
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM
);
873 dump_mem
->offset
= cpu_to_le32(fwrt
->trans
->cfg
->dccm2_offset
);
874 iwl_trans_read_mem_bytes(fwrt
->trans
,
875 fwrt
->trans
->cfg
->dccm2_offset
,
876 dump_mem
->data
, sram2_len
);
877 dump_data
= iwl_fw_error_next_data(dump_data
);
880 /* Dump fw's virtual image */
881 if (!fwrt
->trans
->cfg
->gen2
&&
882 fwrt
->fw
->img
[fwrt
->cur_fw_img
].paging_mem_size
&&
883 fwrt
->fw_paging_db
[0].fw_paging_block
) {
884 IWL_DEBUG_INFO(fwrt
, "WRT paging dump\n");
885 for (i
= 1; i
< fwrt
->num_of_paging_blk
+ 1; i
++) {
886 struct iwl_fw_error_dump_paging
*paging
;
888 fwrt
->fw_paging_db
[i
].fw_paging_block
;
889 dma_addr_t addr
= fwrt
->fw_paging_db
[i
].fw_paging_phys
;
891 dump_data
->type
= cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING
);
892 dump_data
->len
= cpu_to_le32(sizeof(*paging
) +
894 paging
= (void *)dump_data
->data
;
895 paging
->index
= cpu_to_le32(i
);
896 dma_sync_single_for_cpu(fwrt
->trans
->dev
, addr
,
899 memcpy(paging
->data
, page_address(pages
),
901 dump_data
= iwl_fw_error_next_data(dump_data
);
906 iwl_dump_prph(fwrt
->trans
, &dump_data
,
907 iwl_prph_dump_addr_comm
,
908 ARRAY_SIZE(iwl_prph_dump_addr_comm
));
910 if (fwrt
->trans
->cfg
->mq_rx_supported
)
911 iwl_dump_prph(fwrt
->trans
, &dump_data
,
912 iwl_prph_dump_addr_9000
,
913 ARRAY_SIZE(iwl_prph_dump_addr_9000
));
917 fw_error_dump
->trans_ptr
= iwl_trans_dump_data(fwrt
->trans
,
919 fw_error_dump
->fwrt_len
= file_len
;
920 if (fw_error_dump
->trans_ptr
)
921 file_len
+= fw_error_dump
->trans_ptr
->len
;
922 dump_file
->file_len
= cpu_to_le32(file_len
);
924 sg_dump_data
= alloc_sgtable(file_len
);
926 sg_pcopy_from_buffer(sg_dump_data
,
927 sg_nents(sg_dump_data
),
928 fw_error_dump
->fwrt_ptr
,
929 fw_error_dump
->fwrt_len
, 0);
930 if (fw_error_dump
->trans_ptr
)
931 sg_pcopy_from_buffer(sg_dump_data
,
932 sg_nents(sg_dump_data
),
933 fw_error_dump
->trans_ptr
->data
,
934 fw_error_dump
->trans_ptr
->len
,
935 fw_error_dump
->fwrt_len
);
936 dev_coredumpsg(fwrt
->trans
->dev
, sg_dump_data
, file_len
,
939 vfree(fw_error_dump
->fwrt_ptr
);
940 vfree(fw_error_dump
->trans_ptr
);
941 kfree(fw_error_dump
);
944 iwl_fw_free_dump_desc(fwrt
);
945 fwrt
->dump
.trig
= NULL
;
946 clear_bit(IWL_FWRT_STATUS_DUMPING
, &fwrt
->status
);
947 IWL_DEBUG_INFO(fwrt
, "WRT dump done\n");
949 IWL_EXPORT_SYMBOL(iwl_fw_error_dump
);
951 const struct iwl_fw_dump_desc iwl_dump_desc_assert
= {
953 .type
= cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT
),
956 IWL_EXPORT_SYMBOL(iwl_dump_desc_assert
);
958 int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime
*fwrt
,
959 const struct iwl_fw_dump_desc
*desc
,
960 const struct iwl_fw_dbg_trigger_tlv
*trigger
)
962 unsigned int delay
= 0;
965 delay
= msecs_to_jiffies(le32_to_cpu(trigger
->stop_delay
));
968 * If the loading of the FW completed successfully, the next step is to
969 * get the SMEM config data. Thus, if fwrt->smem_cfg.num_lmacs is non
970 * zero, the FW was already loaded successully. If the state is "NO_FW"
971 * in such a case - WARN and exit, since FW may be dead. Otherwise, we
972 * can try to collect the data, since FW might just not be fully
973 * loaded (no "ALIVE" yet), and the debug data is accessible.
975 * Corner case: got the FW alive but crashed before getting the SMEM
976 * config. In such a case, due to HW access problems, we might
979 if (WARN((fwrt
->trans
->state
== IWL_TRANS_NO_FW
) &&
980 fwrt
->smem_cfg
.num_lmacs
,
981 "Can't collect dbg data when FW isn't alive\n"))
984 if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING
, &fwrt
->status
))
987 if (WARN_ON(fwrt
->dump
.desc
))
988 iwl_fw_free_dump_desc(fwrt
);
990 IWL_WARN(fwrt
, "Collecting data: trigger %d fired.\n",
991 le32_to_cpu(desc
->trig_desc
.type
));
993 fwrt
->dump
.desc
= desc
;
994 fwrt
->dump
.trig
= trigger
;
996 schedule_delayed_work(&fwrt
->dump
.wk
, delay
);
1000 IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_desc
);
1002 int iwl_fw_dbg_collect(struct iwl_fw_runtime
*fwrt
,
1003 enum iwl_fw_dbg_trigger trig
,
1004 const char *str
, size_t len
,
1005 const struct iwl_fw_dbg_trigger_tlv
*trigger
)
1007 struct iwl_fw_dump_desc
*desc
;
1009 desc
= kzalloc(sizeof(*desc
) + len
, GFP_ATOMIC
);
1014 desc
->trig_desc
.type
= cpu_to_le32(trig
);
1015 memcpy(desc
->trig_desc
.data
, str
, len
);
1017 return iwl_fw_dbg_collect_desc(fwrt
, desc
, trigger
);
1019 IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect
);
1021 int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime
*fwrt
,
1022 struct iwl_fw_dbg_trigger_tlv
*trigger
,
1023 const char *fmt
, ...)
1025 u16 occurrences
= le16_to_cpu(trigger
->occurrences
);
1035 buf
[sizeof(buf
) - 1] = '\0';
1038 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1041 /* check for truncation */
1042 if (WARN_ON_ONCE(buf
[sizeof(buf
) - 1]))
1043 buf
[sizeof(buf
) - 1] = '\0';
1045 len
= strlen(buf
) + 1;
1048 ret
= iwl_fw_dbg_collect(fwrt
, le32_to_cpu(trigger
->id
), buf
, len
,
1054 trigger
->occurrences
= cpu_to_le16(occurrences
- 1);
1057 IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_trig
);
1059 int iwl_fw_start_dbg_conf(struct iwl_fw_runtime
*fwrt
, u8 conf_id
)
1065 if (WARN_ONCE(conf_id
>= ARRAY_SIZE(fwrt
->fw
->dbg_conf_tlv
),
1066 "Invalid configuration %d\n", conf_id
))
1069 /* EARLY START - firmware's configuration is hard coded */
1070 if ((!fwrt
->fw
->dbg_conf_tlv
[conf_id
] ||
1071 !fwrt
->fw
->dbg_conf_tlv
[conf_id
]->num_of_hcmds
) &&
1072 conf_id
== FW_DBG_START_FROM_ALIVE
)
1075 if (!fwrt
->fw
->dbg_conf_tlv
[conf_id
])
1078 if (fwrt
->dump
.conf
!= FW_DBG_INVALID
)
1079 IWL_WARN(fwrt
, "FW already configured (%d) - re-configuring\n",
1082 /* Send all HCMDs for configuring the FW debug */
1083 ptr
= (void *)&fwrt
->fw
->dbg_conf_tlv
[conf_id
]->hcmd
;
1084 for (i
= 0; i
< fwrt
->fw
->dbg_conf_tlv
[conf_id
]->num_of_hcmds
; i
++) {
1085 struct iwl_fw_dbg_conf_hcmd
*cmd
= (void *)ptr
;
1086 struct iwl_host_cmd hcmd
= {
1088 .len
= { le16_to_cpu(cmd
->len
), },
1089 .data
= { cmd
->data
, },
1092 ret
= iwl_trans_send_cmd(fwrt
->trans
, &hcmd
);
1096 ptr
+= sizeof(*cmd
);
1097 ptr
+= le16_to_cpu(cmd
->len
);
1100 fwrt
->dump
.conf
= conf_id
;
1104 IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf
);
1106 void iwl_fw_error_dump_wk(struct work_struct
*work
)
1108 struct iwl_fw_runtime
*fwrt
=
1109 container_of(work
, struct iwl_fw_runtime
, dump
.wk
.work
);
1111 if (fwrt
->ops
&& fwrt
->ops
->dump_start
&&
1112 fwrt
->ops
->dump_start(fwrt
->ops_ctx
))
1115 if (fwrt
->trans
->cfg
->device_family
== IWL_DEVICE_FAMILY_7000
) {
1116 /* stop recording */
1117 iwl_fw_dbg_stop_recording(fwrt
);
1119 iwl_fw_error_dump(fwrt
);
1121 /* start recording again if the firmware is not crashed */
1122 if (!test_bit(STATUS_FW_ERROR
, &fwrt
->trans
->status
) &&
1123 fwrt
->fw
->dbg_dest_tlv
) {
1124 iwl_clear_bits_prph(fwrt
->trans
,
1125 MON_BUFF_SAMPLE_CTL
, 0x100);
1126 iwl_clear_bits_prph(fwrt
->trans
,
1127 MON_BUFF_SAMPLE_CTL
, 0x1);
1128 iwl_set_bits_prph(fwrt
->trans
,
1129 MON_BUFF_SAMPLE_CTL
, 0x1);
1132 u32 in_sample
= iwl_read_prph(fwrt
->trans
, DBGC_IN_SAMPLE
);
1133 u32 out_ctrl
= iwl_read_prph(fwrt
->trans
, DBGC_OUT_CTRL
);
1135 iwl_fw_dbg_stop_recording(fwrt
);
1136 /* wait before we collect the data till the DBGC stop */
1139 iwl_fw_error_dump(fwrt
);
1141 /* start recording again if the firmware is not crashed */
1142 if (!test_bit(STATUS_FW_ERROR
, &fwrt
->trans
->status
) &&
1143 fwrt
->fw
->dbg_dest_tlv
) {
1144 iwl_write_prph(fwrt
->trans
, DBGC_IN_SAMPLE
, in_sample
);
1145 iwl_write_prph(fwrt
->trans
, DBGC_OUT_CTRL
, out_ctrl
);
1149 if (fwrt
->ops
&& fwrt
->ops
->dump_end
)
1150 fwrt
->ops
->dump_end(fwrt
->ops_ctx
);