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) 2018 - 2019 Intel Corporation
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program.
22 * The full GNU General Public License is included in this distribution
23 * in the file called COPYING.
25 * Contact Information:
26 * Intel Linux Wireless <linuxwifi@intel.com>
27 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
31 * Copyright (C) 2018 - 2019 Intel Corporation
32 * All rights reserved.
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
38 * * Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * * Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in
42 * the documentation and/or other materials provided with the
44 * * Neither the name Intel Corporation nor the names of its
45 * contributors may be used to endorse or promote products derived
46 * from this software without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
52 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
53 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
54 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
58 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60 *****************************************************************************/
62 #include <linux/firmware.h>
64 #include "iwl-trans.h"
65 #include "iwl-dbg-tlv.h"
67 #include "fw/runtime.h"
70 * enum iwl_dbg_tlv_type - debug TLV types
71 * @IWL_DBG_TLV_TYPE_DEBUG_INFO: debug info TLV
72 * @IWL_DBG_TLV_TYPE_BUF_ALLOC: buffer allocation TLV
73 * @IWL_DBG_TLV_TYPE_HCMD: host command TLV
74 * @IWL_DBG_TLV_TYPE_REGION: region TLV
75 * @IWL_DBG_TLV_TYPE_TRIGGER: trigger TLV
76 * @IWL_DBG_TLV_TYPE_NUM: number of debug TLVs
78 enum iwl_dbg_tlv_type
{
79 IWL_DBG_TLV_TYPE_DEBUG_INFO
=
80 IWL_UCODE_TLV_TYPE_DEBUG_INFO
- IWL_UCODE_TLV_DEBUG_BASE
,
81 IWL_DBG_TLV_TYPE_BUF_ALLOC
,
82 IWL_DBG_TLV_TYPE_HCMD
,
83 IWL_DBG_TLV_TYPE_REGION
,
84 IWL_DBG_TLV_TYPE_TRIGGER
,
89 * struct iwl_dbg_tlv_ver_data - debug TLV version struct
90 * @min_ver: min version supported
91 * @max_ver: max version supported
93 struct iwl_dbg_tlv_ver_data
{
99 * struct iwl_dbg_tlv_timer_node - timer node struct
100 * @list: list of &struct iwl_dbg_tlv_timer_node
102 * @fwrt: &struct iwl_fw_runtime
103 * @tlv: TLV attach to the timer node
105 struct iwl_dbg_tlv_timer_node
{
106 struct list_head list
;
107 struct timer_list timer
;
108 struct iwl_fw_runtime
*fwrt
;
109 struct iwl_ucode_tlv
*tlv
;
112 static const struct iwl_dbg_tlv_ver_data
113 dbg_ver_table
[IWL_DBG_TLV_TYPE_NUM
] = {
114 [IWL_DBG_TLV_TYPE_DEBUG_INFO
] = {.min_ver
= 1, .max_ver
= 1,},
115 [IWL_DBG_TLV_TYPE_BUF_ALLOC
] = {.min_ver
= 1, .max_ver
= 1,},
116 [IWL_DBG_TLV_TYPE_HCMD
] = {.min_ver
= 1, .max_ver
= 1,},
117 [IWL_DBG_TLV_TYPE_REGION
] = {.min_ver
= 1, .max_ver
= 1,},
118 [IWL_DBG_TLV_TYPE_TRIGGER
] = {.min_ver
= 1, .max_ver
= 1,},
121 static int iwl_dbg_tlv_add(struct iwl_ucode_tlv
*tlv
, struct list_head
*list
)
123 u32 len
= le32_to_cpu(tlv
->length
);
124 struct iwl_dbg_tlv_node
*node
;
126 node
= kzalloc(sizeof(*node
) + len
, GFP_KERNEL
);
130 memcpy(&node
->tlv
, tlv
, sizeof(node
->tlv
) + len
);
131 list_add_tail(&node
->list
, list
);
136 static bool iwl_dbg_tlv_ver_support(struct iwl_ucode_tlv
*tlv
)
138 struct iwl_fw_ini_header
*hdr
= (void *)&tlv
->data
[0];
139 u32 type
= le32_to_cpu(tlv
->type
);
140 u32 tlv_idx
= type
- IWL_UCODE_TLV_DEBUG_BASE
;
141 u32 ver
= le32_to_cpu(hdr
->version
);
143 if (ver
< dbg_ver_table
[tlv_idx
].min_ver
||
144 ver
> dbg_ver_table
[tlv_idx
].max_ver
)
150 static int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans
*trans
,
151 struct iwl_ucode_tlv
*tlv
)
153 struct iwl_fw_ini_debug_info_tlv
*debug_info
= (void *)tlv
->data
;
155 if (le32_to_cpu(tlv
->length
) != sizeof(*debug_info
))
158 IWL_DEBUG_FW(trans
, "WRT: Loading debug cfg: %s\n",
159 debug_info
->debug_cfg_name
);
161 return iwl_dbg_tlv_add(tlv
, &trans
->dbg
.debug_info_tlv_list
);
164 static int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans
*trans
,
165 struct iwl_ucode_tlv
*tlv
)
167 struct iwl_fw_ini_allocation_tlv
*alloc
= (void *)tlv
->data
;
168 u32 buf_location
= le32_to_cpu(alloc
->buf_location
);
169 u32 alloc_id
= le32_to_cpu(alloc
->alloc_id
);
171 if (le32_to_cpu(tlv
->length
) != sizeof(*alloc
) ||
172 (buf_location
!= IWL_FW_INI_LOCATION_SRAM_PATH
&&
173 buf_location
!= IWL_FW_INI_LOCATION_DRAM_PATH
))
176 if ((buf_location
== IWL_FW_INI_LOCATION_SRAM_PATH
&&
177 alloc_id
!= IWL_FW_INI_ALLOCATION_ID_DBGC1
) ||
178 (buf_location
== IWL_FW_INI_LOCATION_DRAM_PATH
&&
179 (alloc_id
== IWL_FW_INI_ALLOCATION_INVALID
||
180 alloc_id
>= IWL_FW_INI_ALLOCATION_NUM
))) {
182 "WRT: Invalid allocation id %u for allocation TLV\n",
187 trans
->dbg
.fw_mon_cfg
[alloc_id
] = *alloc
;
192 static int iwl_dbg_tlv_alloc_hcmd(struct iwl_trans
*trans
,
193 struct iwl_ucode_tlv
*tlv
)
195 struct iwl_fw_ini_hcmd_tlv
*hcmd
= (void *)tlv
->data
;
196 u32 tp
= le32_to_cpu(hcmd
->time_point
);
198 if (le32_to_cpu(tlv
->length
) <= sizeof(*hcmd
))
201 /* Host commands can not be sent in early time point since the FW
204 if (tp
== IWL_FW_INI_TIME_POINT_INVALID
||
205 tp
>= IWL_FW_INI_TIME_POINT_NUM
||
206 tp
== IWL_FW_INI_TIME_POINT_EARLY
) {
208 "WRT: Invalid time point %u for host command TLV\n",
213 return iwl_dbg_tlv_add(tlv
, &trans
->dbg
.time_point
[tp
].hcmd_list
);
216 static int iwl_dbg_tlv_alloc_region(struct iwl_trans
*trans
,
217 struct iwl_ucode_tlv
*tlv
)
219 struct iwl_fw_ini_region_tlv
*reg
= (void *)tlv
->data
;
220 struct iwl_ucode_tlv
**active_reg
;
221 u32 id
= le32_to_cpu(reg
->id
);
222 u32 type
= le32_to_cpu(reg
->type
);
223 u32 tlv_len
= sizeof(*tlv
) + le32_to_cpu(tlv
->length
);
225 if (le32_to_cpu(tlv
->length
) < sizeof(*reg
))
228 if (id
>= IWL_FW_INI_MAX_REGION_ID
) {
229 IWL_ERR(trans
, "WRT: Invalid region id %u\n", id
);
233 if (type
<= IWL_FW_INI_REGION_INVALID
||
234 type
>= IWL_FW_INI_REGION_NUM
) {
235 IWL_ERR(trans
, "WRT: Invalid region type %u\n", type
);
239 active_reg
= &trans
->dbg
.active_regions
[id
];
241 IWL_WARN(trans
, "WRT: Overriding region id %u\n", id
);
246 *active_reg
= kmemdup(tlv
, tlv_len
, GFP_KERNEL
);
250 IWL_DEBUG_FW(trans
, "WRT: Enabling region id %u type %u\n", id
, type
);
255 static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans
*trans
,
256 struct iwl_ucode_tlv
*tlv
)
258 struct iwl_fw_ini_trigger_tlv
*trig
= (void *)tlv
->data
;
259 u32 tp
= le32_to_cpu(trig
->time_point
);
261 if (le32_to_cpu(tlv
->length
) < sizeof(*trig
))
264 if (tp
<= IWL_FW_INI_TIME_POINT_INVALID
||
265 tp
>= IWL_FW_INI_TIME_POINT_NUM
) {
267 "WRT: Invalid time point %u for trigger TLV\n",
272 if (!le32_to_cpu(trig
->occurrences
))
273 trig
->occurrences
= cpu_to_le32(-1);
275 return iwl_dbg_tlv_add(tlv
, &trans
->dbg
.time_point
[tp
].trig_list
);
278 static int (*dbg_tlv_alloc
[])(struct iwl_trans
*trans
,
279 struct iwl_ucode_tlv
*tlv
) = {
280 [IWL_DBG_TLV_TYPE_DEBUG_INFO
] = iwl_dbg_tlv_alloc_debug_info
,
281 [IWL_DBG_TLV_TYPE_BUF_ALLOC
] = iwl_dbg_tlv_alloc_buf_alloc
,
282 [IWL_DBG_TLV_TYPE_HCMD
] = iwl_dbg_tlv_alloc_hcmd
,
283 [IWL_DBG_TLV_TYPE_REGION
] = iwl_dbg_tlv_alloc_region
,
284 [IWL_DBG_TLV_TYPE_TRIGGER
] = iwl_dbg_tlv_alloc_trigger
,
287 void iwl_dbg_tlv_alloc(struct iwl_trans
*trans
, struct iwl_ucode_tlv
*tlv
,
290 struct iwl_fw_ini_header
*hdr
= (void *)&tlv
->data
[0];
291 u32 type
= le32_to_cpu(tlv
->type
);
292 u32 tlv_idx
= type
- IWL_UCODE_TLV_DEBUG_BASE
;
293 enum iwl_ini_cfg_state
*cfg_state
= ext
?
294 &trans
->dbg
.external_ini_cfg
: &trans
->dbg
.internal_ini_cfg
;
297 if (tlv_idx
>= ARRAY_SIZE(dbg_tlv_alloc
) || !dbg_tlv_alloc
[tlv_idx
]) {
298 IWL_ERR(trans
, "WRT: Unsupported TLV type 0x%x\n", type
);
302 if (!iwl_dbg_tlv_ver_support(tlv
)) {
303 IWL_ERR(trans
, "WRT: Unsupported TLV 0x%x version %u\n", type
,
304 le32_to_cpu(hdr
->version
));
308 ret
= dbg_tlv_alloc
[tlv_idx
](trans
, tlv
);
311 "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n",
316 if (*cfg_state
== IWL_INI_CFG_STATE_NOT_LOADED
)
317 *cfg_state
= IWL_INI_CFG_STATE_LOADED
;
322 *cfg_state
= IWL_INI_CFG_STATE_CORRUPTED
;
325 void iwl_dbg_tlv_del_timers(struct iwl_trans
*trans
)
327 struct list_head
*timer_list
= &trans
->dbg
.periodic_trig_list
;
328 struct iwl_dbg_tlv_timer_node
*node
, *tmp
;
330 list_for_each_entry_safe(node
, tmp
, timer_list
, list
) {
331 del_timer(&node
->timer
);
332 list_del(&node
->list
);
336 IWL_EXPORT_SYMBOL(iwl_dbg_tlv_del_timers
);
338 static void iwl_dbg_tlv_fragments_free(struct iwl_trans
*trans
,
339 enum iwl_fw_ini_allocation_id alloc_id
)
341 struct iwl_fw_mon
*fw_mon
;
344 if (alloc_id
<= IWL_FW_INI_ALLOCATION_INVALID
||
345 alloc_id
>= IWL_FW_INI_ALLOCATION_NUM
)
348 fw_mon
= &trans
->dbg
.fw_mon_ini
[alloc_id
];
350 for (i
= 0; i
< fw_mon
->num_frags
; i
++) {
351 struct iwl_dram_data
*frag
= &fw_mon
->frags
[i
];
353 dma_free_coherent(trans
->dev
, frag
->size
, frag
->block
,
361 kfree(fw_mon
->frags
);
362 fw_mon
->frags
= NULL
;
363 fw_mon
->num_frags
= 0;
366 void iwl_dbg_tlv_free(struct iwl_trans
*trans
)
368 struct iwl_dbg_tlv_node
*tlv_node
, *tlv_node_tmp
;
371 iwl_dbg_tlv_del_timers(trans
);
373 for (i
= 0; i
< ARRAY_SIZE(trans
->dbg
.active_regions
); i
++) {
374 struct iwl_ucode_tlv
**active_reg
=
375 &trans
->dbg
.active_regions
[i
];
381 list_for_each_entry_safe(tlv_node
, tlv_node_tmp
,
382 &trans
->dbg
.debug_info_tlv_list
, list
) {
383 list_del(&tlv_node
->list
);
387 for (i
= 0; i
< ARRAY_SIZE(trans
->dbg
.time_point
); i
++) {
388 struct iwl_dbg_tlv_time_point_data
*tp
=
389 &trans
->dbg
.time_point
[i
];
391 list_for_each_entry_safe(tlv_node
, tlv_node_tmp
, &tp
->trig_list
,
393 list_del(&tlv_node
->list
);
397 list_for_each_entry_safe(tlv_node
, tlv_node_tmp
, &tp
->hcmd_list
,
399 list_del(&tlv_node
->list
);
403 list_for_each_entry_safe(tlv_node
, tlv_node_tmp
,
404 &tp
->active_trig_list
, list
) {
405 list_del(&tlv_node
->list
);
410 for (i
= 0; i
< ARRAY_SIZE(trans
->dbg
.fw_mon_ini
); i
++)
411 iwl_dbg_tlv_fragments_free(trans
, i
);
414 static int iwl_dbg_tlv_parse_bin(struct iwl_trans
*trans
, const u8
*data
,
417 struct iwl_ucode_tlv
*tlv
;
420 while (len
>= sizeof(*tlv
)) {
424 tlv_len
= le32_to_cpu(tlv
->length
);
427 IWL_ERR(trans
, "invalid TLV len: %zd/%u\n",
431 len
-= ALIGN(tlv_len
, 4);
432 data
+= sizeof(*tlv
) + ALIGN(tlv_len
, 4);
434 iwl_dbg_tlv_alloc(trans
, tlv
, true);
440 void iwl_dbg_tlv_load_bin(struct device
*dev
, struct iwl_trans
*trans
)
442 const struct firmware
*fw
;
445 if (!iwlwifi_mod_params
.enable_ini
)
448 res
= request_firmware(&fw
, "iwl-debug-yoyo.bin", dev
);
452 iwl_dbg_tlv_parse_bin(trans
, fw
->data
, fw
->size
);
454 release_firmware(fw
);
457 void iwl_dbg_tlv_init(struct iwl_trans
*trans
)
461 INIT_LIST_HEAD(&trans
->dbg
.debug_info_tlv_list
);
462 INIT_LIST_HEAD(&trans
->dbg
.periodic_trig_list
);
464 for (i
= 0; i
< ARRAY_SIZE(trans
->dbg
.time_point
); i
++) {
465 struct iwl_dbg_tlv_time_point_data
*tp
=
466 &trans
->dbg
.time_point
[i
];
468 INIT_LIST_HEAD(&tp
->trig_list
);
469 INIT_LIST_HEAD(&tp
->hcmd_list
);
470 INIT_LIST_HEAD(&tp
->active_trig_list
);
474 static int iwl_dbg_tlv_alloc_fragment(struct iwl_fw_runtime
*fwrt
,
475 struct iwl_dram_data
*frag
, u32 pages
)
480 if (!frag
|| frag
->size
|| !pages
)
484 block
= dma_alloc_coherent(fwrt
->dev
, pages
* PAGE_SIZE
,
486 GFP_KERNEL
| __GFP_NOWARN
);
490 IWL_WARN(fwrt
, "WRT: Failed to allocate fragment size %lu\n",
493 pages
= DIV_ROUND_UP(pages
, 2);
499 frag
->physical
= physical
;
501 frag
->size
= pages
* PAGE_SIZE
;
506 static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime
*fwrt
,
507 enum iwl_fw_ini_allocation_id alloc_id
)
509 struct iwl_fw_mon
*fw_mon
;
510 struct iwl_fw_ini_allocation_tlv
*fw_mon_cfg
;
511 u32 num_frags
, remain_pages
, frag_pages
;
514 if (alloc_id
< IWL_FW_INI_ALLOCATION_INVALID
||
515 alloc_id
>= IWL_FW_INI_ALLOCATION_NUM
)
518 fw_mon_cfg
= &fwrt
->trans
->dbg
.fw_mon_cfg
[alloc_id
];
519 fw_mon
= &fwrt
->trans
->dbg
.fw_mon_ini
[alloc_id
];
521 if (fw_mon
->num_frags
||
522 fw_mon_cfg
->buf_location
!=
523 cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH
))
526 num_frags
= le32_to_cpu(fw_mon_cfg
->max_frags_num
);
527 if (!fw_has_capa(&fwrt
->fw
->ucode_capa
,
528 IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP
)) {
529 if (alloc_id
!= IWL_FW_INI_ALLOCATION_ID_DBGC1
)
534 remain_pages
= DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg
->req_size
),
536 num_frags
= min_t(u32
, num_frags
, BUF_ALLOC_MAX_NUM_FRAGS
);
537 num_frags
= min_t(u32
, num_frags
, remain_pages
);
538 frag_pages
= DIV_ROUND_UP(remain_pages
, num_frags
);
540 fw_mon
->frags
= kcalloc(num_frags
, sizeof(*fw_mon
->frags
), GFP_KERNEL
);
544 for (i
= 0; i
< num_frags
; i
++) {
545 int pages
= min_t(u32
, frag_pages
, remain_pages
);
548 "WRT: Allocating DRAM buffer (alloc_id=%u, fragment=%u, size=0x%lx)\n",
549 alloc_id
, i
, pages
* PAGE_SIZE
);
551 pages
= iwl_dbg_tlv_alloc_fragment(fwrt
, &fw_mon
->frags
[i
],
554 u32 alloc_size
= le32_to_cpu(fw_mon_cfg
->req_size
) -
555 (remain_pages
* PAGE_SIZE
);
557 if (alloc_size
< le32_to_cpu(fw_mon_cfg
->min_size
)) {
558 iwl_dbg_tlv_fragments_free(fwrt
->trans
,
565 remain_pages
-= pages
;
572 static int iwl_dbg_tlv_apply_buffer(struct iwl_fw_runtime
*fwrt
,
573 enum iwl_fw_ini_allocation_id alloc_id
)
575 struct iwl_fw_mon
*fw_mon
;
576 u32 remain_frags
, num_commands
;
577 int i
, fw_mon_idx
= 0;
579 if (!fw_has_capa(&fwrt
->fw
->ucode_capa
,
580 IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP
))
583 if (alloc_id
< IWL_FW_INI_ALLOCATION_INVALID
||
584 alloc_id
>= IWL_FW_INI_ALLOCATION_NUM
)
587 if (le32_to_cpu(fwrt
->trans
->dbg
.fw_mon_cfg
[alloc_id
].buf_location
) !=
588 IWL_FW_INI_LOCATION_DRAM_PATH
)
591 fw_mon
= &fwrt
->trans
->dbg
.fw_mon_ini
[alloc_id
];
593 /* the first fragment of DBGC1 is given to the FW via register
596 if (alloc_id
== IWL_FW_INI_ALLOCATION_ID_DBGC1
)
599 remain_frags
= fw_mon
->num_frags
- fw_mon_idx
;
603 num_commands
= DIV_ROUND_UP(remain_frags
, BUF_ALLOC_MAX_NUM_FRAGS
);
605 IWL_DEBUG_FW(fwrt
, "WRT: Applying DRAM destination (alloc_id=%u)\n",
608 for (i
= 0; i
< num_commands
; i
++) {
609 u32 num_frags
= min_t(u32
, remain_frags
,
610 BUF_ALLOC_MAX_NUM_FRAGS
);
611 struct iwl_buf_alloc_cmd data
= {
612 .alloc_id
= cpu_to_le32(alloc_id
),
613 .num_frags
= cpu_to_le32(num_frags
),
615 cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH
),
617 struct iwl_host_cmd hcmd
= {
618 .id
= WIDE_ID(DEBUG_GROUP
, BUFFER_ALLOCATION
),
620 .len
[0] = sizeof(data
),
624 for (j
= 0; j
< num_frags
; j
++) {
625 struct iwl_buf_alloc_frag
*frag
= &data
.frags
[j
];
626 struct iwl_dram_data
*fw_mon_frag
=
627 &fw_mon
->frags
[fw_mon_idx
++];
629 frag
->addr
= cpu_to_le64(fw_mon_frag
->physical
);
630 frag
->size
= cpu_to_le32(fw_mon_frag
->size
);
632 ret
= iwl_trans_send_cmd(fwrt
->trans
, &hcmd
);
636 remain_frags
-= num_frags
;
642 static void iwl_dbg_tlv_apply_buffers(struct iwl_fw_runtime
*fwrt
)
646 for (i
= 0; i
< IWL_FW_INI_ALLOCATION_NUM
; i
++) {
647 ret
= iwl_dbg_tlv_apply_buffer(fwrt
, i
);
650 "WRT: Failed to apply DRAM buffer for allocation id %d, ret=%d\n",
655 static void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime
*fwrt
,
656 struct list_head
*hcmd_list
)
658 struct iwl_dbg_tlv_node
*node
;
660 list_for_each_entry(node
, hcmd_list
, list
) {
661 struct iwl_fw_ini_hcmd_tlv
*hcmd
= (void *)node
->tlv
.data
;
662 struct iwl_fw_ini_hcmd
*hcmd_data
= &hcmd
->hcmd
;
663 u32 domain
= le32_to_cpu(hcmd
->hdr
.domain
);
664 u16 hcmd_len
= le32_to_cpu(node
->tlv
.length
) - sizeof(*hcmd
);
665 struct iwl_host_cmd cmd
= {
666 .id
= WIDE_ID(hcmd_data
->group
, hcmd_data
->id
),
667 .len
= { hcmd_len
, },
668 .data
= { hcmd_data
->data
, },
671 if (domain
!= IWL_FW_INI_DOMAIN_ALWAYS_ON
&&
672 !(domain
& fwrt
->trans
->dbg
.domains_bitmap
))
675 iwl_trans_send_cmd(fwrt
->trans
, &cmd
);
679 static void iwl_dbg_tlv_periodic_trig_handler(struct timer_list
*t
)
681 struct iwl_dbg_tlv_timer_node
*timer_node
=
682 from_timer(timer_node
, t
, timer
);
683 struct iwl_fwrt_dump_data dump_data
= {
684 .trig
= (void *)timer_node
->tlv
->data
,
688 ret
= iwl_fw_dbg_ini_collect(timer_node
->fwrt
, &dump_data
);
689 if (!ret
|| ret
== -EBUSY
) {
690 u32 occur
= le32_to_cpu(dump_data
.trig
->occurrences
);
691 u32 collect_interval
= le32_to_cpu(dump_data
.trig
->data
[0]);
696 mod_timer(t
, jiffies
+ msecs_to_jiffies(collect_interval
));
700 static void iwl_dbg_tlv_set_periodic_trigs(struct iwl_fw_runtime
*fwrt
)
702 struct iwl_dbg_tlv_node
*node
;
703 struct list_head
*trig_list
=
704 &fwrt
->trans
->dbg
.time_point
[IWL_FW_INI_TIME_POINT_PERIODIC
].active_trig_list
;
706 list_for_each_entry(node
, trig_list
, list
) {
707 struct iwl_fw_ini_trigger_tlv
*trig
= (void *)node
->tlv
.data
;
708 struct iwl_dbg_tlv_timer_node
*timer_node
;
709 u32 occur
= le32_to_cpu(trig
->occurrences
), collect_interval
;
710 u32 min_interval
= 100;
715 /* make sure there is at least one dword of data for the
718 if (le32_to_cpu(node
->tlv
.length
) <
719 sizeof(*trig
) + sizeof(__le32
)) {
721 "WRT: Invalid periodic trigger data was not given\n");
725 if (le32_to_cpu(trig
->data
[0]) < min_interval
) {
727 "WRT: Override min interval from %u to %u msec\n",
728 le32_to_cpu(trig
->data
[0]), min_interval
);
729 trig
->data
[0] = cpu_to_le32(min_interval
);
732 collect_interval
= le32_to_cpu(trig
->data
[0]);
734 timer_node
= kzalloc(sizeof(*timer_node
), GFP_KERNEL
);
737 "WRT: Failed to allocate periodic trigger\n");
741 timer_node
->fwrt
= fwrt
;
742 timer_node
->tlv
= &node
->tlv
;
743 timer_setup(&timer_node
->timer
,
744 iwl_dbg_tlv_periodic_trig_handler
, 0);
746 list_add_tail(&timer_node
->list
,
747 &fwrt
->trans
->dbg
.periodic_trig_list
);
749 IWL_DEBUG_FW(fwrt
, "WRT: Enabling periodic trigger\n");
751 mod_timer(&timer_node
->timer
,
752 jiffies
+ msecs_to_jiffies(collect_interval
));
756 static bool is_trig_data_contained(struct iwl_ucode_tlv
*new,
757 struct iwl_ucode_tlv
*old
)
759 struct iwl_fw_ini_trigger_tlv
*new_trig
= (void *)new->data
;
760 struct iwl_fw_ini_trigger_tlv
*old_trig
= (void *)old
->data
;
761 __le32
*new_data
= new_trig
->data
, *old_data
= old_trig
->data
;
762 u32 new_dwords_num
= iwl_tlv_array_len(new, new_trig
, data
);
763 u32 old_dwords_num
= iwl_tlv_array_len(new, new_trig
, data
);
766 for (i
= 0; i
< new_dwords_num
; i
++) {
769 for (j
= 0; j
< old_dwords_num
; j
++) {
770 if (new_data
[i
] == old_data
[j
]) {
782 static int iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime
*fwrt
,
783 struct iwl_ucode_tlv
*trig_tlv
,
784 struct iwl_dbg_tlv_node
*node
)
786 struct iwl_ucode_tlv
*node_tlv
= &node
->tlv
;
787 struct iwl_fw_ini_trigger_tlv
*node_trig
= (void *)node_tlv
->data
;
788 struct iwl_fw_ini_trigger_tlv
*trig
= (void *)trig_tlv
->data
;
789 u32 policy
= le32_to_cpu(trig
->apply_policy
);
790 u32 size
= le32_to_cpu(trig_tlv
->length
);
791 u32 trig_data_len
= size
- sizeof(*trig
);
794 if (!(policy
& IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA
)) {
795 u32 data_len
= le32_to_cpu(node_tlv
->length
) -
799 "WRT: Appending trigger data (time point %u)\n",
800 le32_to_cpu(trig
->time_point
));
806 "WRT: Overriding trigger data (time point %u)\n",
807 le32_to_cpu(trig
->time_point
));
810 if (size
!= le32_to_cpu(node_tlv
->length
)) {
811 struct list_head
*prev
= node
->list
.prev
;
812 struct iwl_dbg_tlv_node
*tmp
;
814 list_del(&node
->list
);
816 tmp
= krealloc(node
, sizeof(*node
) + size
, GFP_KERNEL
);
819 "WRT: No memory to override trigger (time point %u)\n",
820 le32_to_cpu(trig
->time_point
));
822 list_add(&node
->list
, prev
);
827 list_add(&tmp
->list
, prev
);
828 node_tlv
= &tmp
->tlv
;
829 node_trig
= (void *)node_tlv
->data
;
832 memcpy(node_trig
->data
+ offset
, trig
->data
, trig_data_len
);
833 node_tlv
->length
= cpu_to_le32(size
);
835 if (policy
& IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG
) {
837 "WRT: Overriding trigger configuration (time point %u)\n",
838 le32_to_cpu(trig
->time_point
));
840 /* the first 11 dwords are configuration related */
841 memcpy(node_trig
, trig
, sizeof(__le32
) * 11);
844 if (policy
& IWL_FW_INI_APPLY_POLICY_OVERRIDE_REGIONS
) {
846 "WRT: Overriding trigger regions (time point %u)\n",
847 le32_to_cpu(trig
->time_point
));
849 node_trig
->regions_mask
= trig
->regions_mask
;
852 "WRT: Appending trigger regions (time point %u)\n",
853 le32_to_cpu(trig
->time_point
));
855 node_trig
->regions_mask
|= trig
->regions_mask
;
862 iwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime
*fwrt
,
863 struct list_head
*trig_list
,
864 struct iwl_ucode_tlv
*trig_tlv
)
866 struct iwl_fw_ini_trigger_tlv
*trig
= (void *)trig_tlv
->data
;
867 struct iwl_dbg_tlv_node
*node
, *match
= NULL
;
868 u32 policy
= le32_to_cpu(trig
->apply_policy
);
870 list_for_each_entry(node
, trig_list
, list
) {
871 if (!(policy
& IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT
))
874 if (!(policy
& IWL_FW_INI_APPLY_POLICY_MATCH_DATA
) ||
875 is_trig_data_contained(trig_tlv
, &node
->tlv
)) {
882 IWL_DEBUG_FW(fwrt
, "WRT: Enabling trigger (time point %u)\n",
883 le32_to_cpu(trig
->time_point
));
884 return iwl_dbg_tlv_add(trig_tlv
, trig_list
);
887 return iwl_dbg_tlv_override_trig_node(fwrt
, trig_tlv
, match
);
891 iwl_dbg_tlv_gen_active_trig_list(struct iwl_fw_runtime
*fwrt
,
892 struct iwl_dbg_tlv_time_point_data
*tp
)
894 struct iwl_dbg_tlv_node
*node
, *tmp
;
895 struct list_head
*trig_list
= &tp
->trig_list
;
896 struct list_head
*active_trig_list
= &tp
->active_trig_list
;
898 list_for_each_entry_safe(node
, tmp
, active_trig_list
, list
) {
899 list_del(&node
->list
);
903 list_for_each_entry(node
, trig_list
, list
) {
904 struct iwl_ucode_tlv
*tlv
= &node
->tlv
;
905 struct iwl_fw_ini_trigger_tlv
*trig
= (void *)tlv
->data
;
906 u32 domain
= le32_to_cpu(trig
->hdr
.domain
);
908 if (domain
!= IWL_FW_INI_DOMAIN_ALWAYS_ON
&&
909 !(domain
& fwrt
->trans
->dbg
.domains_bitmap
))
912 iwl_dbg_tlv_add_active_trigger(fwrt
, active_trig_list
, tlv
);
916 int iwl_dbg_tlv_gen_active_trigs(struct iwl_fw_runtime
*fwrt
, u32 new_domain
)
920 if (test_and_set_bit(STATUS_GEN_ACTIVE_TRIGS
, &fwrt
->status
))
923 iwl_fw_flush_dumps(fwrt
);
925 fwrt
->trans
->dbg
.domains_bitmap
= new_domain
;
928 "WRT: Generating active triggers list, domain 0x%x\n",
929 fwrt
->trans
->dbg
.domains_bitmap
);
931 for (i
= 0; i
< ARRAY_SIZE(fwrt
->trans
->dbg
.time_point
); i
++) {
932 struct iwl_dbg_tlv_time_point_data
*tp
=
933 &fwrt
->trans
->dbg
.time_point
[i
];
935 iwl_dbg_tlv_gen_active_trig_list(fwrt
, tp
);
938 clear_bit(STATUS_GEN_ACTIVE_TRIGS
, &fwrt
->status
);
943 static bool iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime
*fwrt
,
944 struct iwl_fwrt_dump_data
*dump_data
,
945 union iwl_dbg_tlv_tp_data
*tp_data
,
948 struct iwl_rx_packet
*pkt
= tp_data
->fw_pkt
;
949 struct iwl_cmd_header
*wanted_hdr
= (void *)&trig_data
;
951 if (pkt
&& ((wanted_hdr
->cmd
== 0 && wanted_hdr
->group_id
== 0) ||
952 (pkt
->hdr
.cmd
== wanted_hdr
->cmd
&&
953 pkt
->hdr
.group_id
== wanted_hdr
->group_id
))) {
954 struct iwl_rx_packet
*fw_pkt
=
956 sizeof(*pkt
) + iwl_rx_packet_payload_len(pkt
),
962 dump_data
->fw_pkt
= fw_pkt
;
971 iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime
*fwrt
,
972 struct list_head
*active_trig_list
,
973 union iwl_dbg_tlv_tp_data
*tp_data
,
974 bool (*data_check
)(struct iwl_fw_runtime
*fwrt
,
975 struct iwl_fwrt_dump_data
*dump_data
,
976 union iwl_dbg_tlv_tp_data
*tp_data
,
979 struct iwl_dbg_tlv_node
*node
;
981 list_for_each_entry(node
, active_trig_list
, list
) {
982 struct iwl_fwrt_dump_data dump_data
= {
983 .trig
= (void *)node
->tlv
.data
,
985 u32 num_data
= iwl_tlv_array_len(&node
->tlv
, dump_data
.trig
,
990 ret
= iwl_fw_dbg_ini_collect(fwrt
, &dump_data
);
995 for (i
= 0; i
< num_data
; i
++) {
997 data_check(fwrt
, &dump_data
, tp_data
,
998 le32_to_cpu(dump_data
.trig
->data
[i
]))) {
999 ret
= iwl_fw_dbg_ini_collect(fwrt
, &dump_data
);
1011 static void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime
*fwrt
)
1013 enum iwl_fw_ini_buffer_location
*ini_dest
= &fwrt
->trans
->dbg
.ini_dest
;
1016 iwl_dbg_tlv_gen_active_trigs(fwrt
, IWL_FW_DBG_DOMAIN
);
1018 *ini_dest
= IWL_FW_INI_LOCATION_INVALID
;
1019 for (i
= 0; i
< IWL_FW_INI_ALLOCATION_NUM
; i
++) {
1020 struct iwl_fw_ini_allocation_tlv
*fw_mon_cfg
=
1021 &fwrt
->trans
->dbg
.fw_mon_cfg
[i
];
1022 u32 dest
= le32_to_cpu(fw_mon_cfg
->buf_location
);
1024 if (dest
== IWL_FW_INI_LOCATION_INVALID
)
1027 if (*ini_dest
== IWL_FW_INI_LOCATION_INVALID
)
1030 if (dest
!= *ini_dest
)
1033 ret
= iwl_dbg_tlv_alloc_fragments(fwrt
, i
);
1036 "WRT: Failed to allocate DRAM buffer for allocation id %d, ret=%d\n",
1041 void iwl_dbg_tlv_time_point(struct iwl_fw_runtime
*fwrt
,
1042 enum iwl_fw_ini_time_point tp_id
,
1043 union iwl_dbg_tlv_tp_data
*tp_data
)
1045 struct list_head
*hcmd_list
, *trig_list
;
1047 if (!iwl_trans_dbg_ini_valid(fwrt
->trans
) ||
1048 tp_id
== IWL_FW_INI_TIME_POINT_INVALID
||
1049 tp_id
>= IWL_FW_INI_TIME_POINT_NUM
)
1052 hcmd_list
= &fwrt
->trans
->dbg
.time_point
[tp_id
].hcmd_list
;
1053 trig_list
= &fwrt
->trans
->dbg
.time_point
[tp_id
].active_trig_list
;
1056 case IWL_FW_INI_TIME_POINT_EARLY
:
1057 iwl_dbg_tlv_init_cfg(fwrt
);
1058 iwl_dbg_tlv_tp_trigger(fwrt
, trig_list
, tp_data
, NULL
);
1060 case IWL_FW_INI_TIME_POINT_AFTER_ALIVE
:
1061 iwl_dbg_tlv_apply_buffers(fwrt
);
1062 iwl_dbg_tlv_send_hcmds(fwrt
, hcmd_list
);
1063 iwl_dbg_tlv_tp_trigger(fwrt
, trig_list
, tp_data
, NULL
);
1065 case IWL_FW_INI_TIME_POINT_PERIODIC
:
1066 iwl_dbg_tlv_set_periodic_trigs(fwrt
);
1067 iwl_dbg_tlv_send_hcmds(fwrt
, hcmd_list
);
1069 case IWL_FW_INI_TIME_POINT_FW_RSP_OR_NOTIF
:
1070 case IWL_FW_INI_TIME_POINT_MISSED_BEACONS
:
1071 iwl_dbg_tlv_send_hcmds(fwrt
, hcmd_list
);
1072 iwl_dbg_tlv_tp_trigger(fwrt
, trig_list
, tp_data
,
1073 iwl_dbg_tlv_check_fw_pkt
);
1076 iwl_dbg_tlv_send_hcmds(fwrt
, hcmd_list
);
1077 iwl_dbg_tlv_tp_trigger(fwrt
, trig_list
, tp_data
, NULL
);
1081 IWL_EXPORT_SYMBOL(iwl_dbg_tlv_time_point
);