xref: /openbmc/linux/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28e99ea8dSJohannes Berg /*
387017189SEmmanuel Grumbach  * Copyright (C) 2018-2024 Intel Corporation
48e99ea8dSJohannes Berg  */
5f14cda6fSSara Sharon #include <linux/firmware.h>
600eacde4SShahar S Matityahu #include "iwl-drv.h"
7f14cda6fSSara Sharon #include "iwl-trans.h"
8f14cda6fSSara Sharon #include "iwl-dbg-tlv.h"
900eacde4SShahar S Matityahu #include "fw/dbg.h"
1000eacde4SShahar S Matityahu #include "fw/runtime.h"
11f14cda6fSSara Sharon 
12341bd290SShahar S Matityahu /**
13341bd290SShahar S Matityahu  * enum iwl_dbg_tlv_type - debug TLV types
14341bd290SShahar S Matityahu  * @IWL_DBG_TLV_TYPE_DEBUG_INFO: debug info TLV
15341bd290SShahar S Matityahu  * @IWL_DBG_TLV_TYPE_BUF_ALLOC: buffer allocation TLV
16341bd290SShahar S Matityahu  * @IWL_DBG_TLV_TYPE_HCMD: host command TLV
17341bd290SShahar S Matityahu  * @IWL_DBG_TLV_TYPE_REGION: region TLV
18341bd290SShahar S Matityahu  * @IWL_DBG_TLV_TYPE_TRIGGER: trigger TLV
19f21baf24SMukesh Sisodiya  * @IWL_DBG_TLV_TYPE_CONF_SET: conf set TLV
20341bd290SShahar S Matityahu  * @IWL_DBG_TLV_TYPE_NUM: number of debug TLVs
21341bd290SShahar S Matityahu  */
22341bd290SShahar S Matityahu enum iwl_dbg_tlv_type {
23341bd290SShahar S Matityahu 	IWL_DBG_TLV_TYPE_DEBUG_INFO =
24341bd290SShahar S Matityahu 		IWL_UCODE_TLV_TYPE_DEBUG_INFO - IWL_UCODE_TLV_DEBUG_BASE,
25341bd290SShahar S Matityahu 	IWL_DBG_TLV_TYPE_BUF_ALLOC,
26341bd290SShahar S Matityahu 	IWL_DBG_TLV_TYPE_HCMD,
27341bd290SShahar S Matityahu 	IWL_DBG_TLV_TYPE_REGION,
28341bd290SShahar S Matityahu 	IWL_DBG_TLV_TYPE_TRIGGER,
29f21baf24SMukesh Sisodiya 	IWL_DBG_TLV_TYPE_CONF_SET,
30341bd290SShahar S Matityahu 	IWL_DBG_TLV_TYPE_NUM,
31341bd290SShahar S Matityahu };
32341bd290SShahar S Matityahu 
33341bd290SShahar S Matityahu /**
34341bd290SShahar S Matityahu  * struct iwl_dbg_tlv_ver_data -  debug TLV version struct
35341bd290SShahar S Matityahu  * @min_ver: min version supported
36341bd290SShahar S Matityahu  * @max_ver: max version supported
37341bd290SShahar S Matityahu  */
38341bd290SShahar S Matityahu struct iwl_dbg_tlv_ver_data {
39341bd290SShahar S Matityahu 	int min_ver;
40341bd290SShahar S Matityahu 	int max_ver;
41341bd290SShahar S Matityahu };
42341bd290SShahar S Matityahu 
4360e8abd9SShahar S Matityahu /**
4460e8abd9SShahar S Matityahu  * struct iwl_dbg_tlv_timer_node - timer node struct
4560e8abd9SShahar S Matityahu  * @list: list of &struct iwl_dbg_tlv_timer_node
4660e8abd9SShahar S Matityahu  * @timer: timer
4760e8abd9SShahar S Matityahu  * @fwrt: &struct iwl_fw_runtime
4860e8abd9SShahar S Matityahu  * @tlv: TLV attach to the timer node
4960e8abd9SShahar S Matityahu  */
5060e8abd9SShahar S Matityahu struct iwl_dbg_tlv_timer_node {
5160e8abd9SShahar S Matityahu 	struct list_head list;
5260e8abd9SShahar S Matityahu 	struct timer_list timer;
5360e8abd9SShahar S Matityahu 	struct iwl_fw_runtime *fwrt;
5460e8abd9SShahar S Matityahu 	struct iwl_ucode_tlv *tlv;
5560e8abd9SShahar S Matityahu };
5660e8abd9SShahar S Matityahu 
57341bd290SShahar S Matityahu static const struct iwl_dbg_tlv_ver_data
58341bd290SShahar S Matityahu dbg_ver_table[IWL_DBG_TLV_TYPE_NUM] = {
59341bd290SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_DEBUG_INFO]	= {.min_ver = 1, .max_ver = 1,},
60341bd290SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_BUF_ALLOC]	= {.min_ver = 1, .max_ver = 1,},
61341bd290SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_HCMD]		= {.min_ver = 1, .max_ver = 1,},
6262ed5d90SMordechay Goodstein 	[IWL_DBG_TLV_TYPE_REGION]	= {.min_ver = 1, .max_ver = 3,},
63341bd290SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_TRIGGER]	= {.min_ver = 1, .max_ver = 1,},
64f21baf24SMukesh Sisodiya 	[IWL_DBG_TLV_TYPE_CONF_SET]	= {.min_ver = 1, .max_ver = 1,},
65341bd290SShahar S Matityahu };
66341bd290SShahar S Matityahu 
iwl_dbg_tlv_add(const struct iwl_ucode_tlv * tlv,struct list_head * list)67403ea939STakashi Iwai static int iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv,
68403ea939STakashi Iwai 			   struct list_head *list)
69a9248de4SShahar S Matityahu {
70a9248de4SShahar S Matityahu 	u32 len = le32_to_cpu(tlv->length);
71a9248de4SShahar S Matityahu 	struct iwl_dbg_tlv_node *node;
72a9248de4SShahar S Matityahu 
73a9248de4SShahar S Matityahu 	node = kzalloc(sizeof(*node) + len, GFP_KERNEL);
74a9248de4SShahar S Matityahu 	if (!node)
75a9248de4SShahar S Matityahu 		return -ENOMEM;
76a9248de4SShahar S Matityahu 
77cb0a1fb7SKees Cook 	memcpy(&node->tlv, tlv, sizeof(node->tlv));
78cb0a1fb7SKees Cook 	memcpy(node->tlv.data, tlv->data, len);
79a9248de4SShahar S Matityahu 	list_add_tail(&node->list, list);
80a9248de4SShahar S Matityahu 
81a9248de4SShahar S Matityahu 	return 0;
82a9248de4SShahar S Matityahu }
83a9248de4SShahar S Matityahu 
iwl_dbg_tlv_ver_support(const struct iwl_ucode_tlv * tlv)84403ea939STakashi Iwai static bool iwl_dbg_tlv_ver_support(const struct iwl_ucode_tlv *tlv)
85341bd290SShahar S Matityahu {
86403ea939STakashi Iwai 	const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];
87341bd290SShahar S Matityahu 	u32 type = le32_to_cpu(tlv->type);
88341bd290SShahar S Matityahu 	u32 tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;
89a9248de4SShahar S Matityahu 	u32 ver = le32_to_cpu(hdr->version);
90341bd290SShahar S Matityahu 
91341bd290SShahar S Matityahu 	if (ver < dbg_ver_table[tlv_idx].min_ver ||
92341bd290SShahar S Matityahu 	    ver > dbg_ver_table[tlv_idx].max_ver)
93341bd290SShahar S Matityahu 		return false;
94341bd290SShahar S Matityahu 
95341bd290SShahar S Matityahu 	return true;
96341bd290SShahar S Matityahu }
97341bd290SShahar S Matityahu 
iwl_dbg_tlv_alloc_debug_info(struct iwl_trans * trans,const struct iwl_ucode_tlv * tlv)98a9248de4SShahar S Matityahu static int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans *trans,
99403ea939STakashi Iwai 					const struct iwl_ucode_tlv *tlv)
100a9248de4SShahar S Matityahu {
101403ea939STakashi Iwai 	const struct iwl_fw_ini_debug_info_tlv *debug_info = (const void *)tlv->data;
102a9248de4SShahar S Matityahu 
103a9248de4SShahar S Matityahu 	if (le32_to_cpu(tlv->length) != sizeof(*debug_info))
104a9248de4SShahar S Matityahu 		return -EINVAL;
105a9248de4SShahar S Matityahu 
106*783d413fSJohannes Berg 	/* we use this as a string, ensure input was NUL terminated */
107*783d413fSJohannes Berg 	if (strnlen(debug_info->debug_cfg_name,
108*783d413fSJohannes Berg 		    sizeof(debug_info->debug_cfg_name)) ==
109*783d413fSJohannes Berg 			sizeof(debug_info->debug_cfg_name))
110*783d413fSJohannes Berg 		return -EINVAL;
111*783d413fSJohannes Berg 
112a9248de4SShahar S Matityahu 	IWL_DEBUG_FW(trans, "WRT: Loading debug cfg: %s\n",
113a9248de4SShahar S Matityahu 		     debug_info->debug_cfg_name);
114a9248de4SShahar S Matityahu 
115a9248de4SShahar S Matityahu 	return iwl_dbg_tlv_add(tlv, &trans->dbg.debug_info_tlv_list);
116a9248de4SShahar S Matityahu }
117a9248de4SShahar S Matityahu 
iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans * trans,const struct iwl_ucode_tlv * tlv)118a9248de4SShahar S Matityahu static int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans *trans,
119403ea939STakashi Iwai 				       const struct iwl_ucode_tlv *tlv)
120a9248de4SShahar S Matityahu {
121403ea939STakashi Iwai 	const struct iwl_fw_ini_allocation_tlv *alloc = (const void *)tlv->data;
1220c9e025eSMordechay Goodstein 	u32 buf_location;
1230c9e025eSMordechay Goodstein 	u32 alloc_id;
124a9248de4SShahar S Matityahu 
1250c9e025eSMordechay Goodstein 	if (le32_to_cpu(tlv->length) != sizeof(*alloc))
126a9248de4SShahar S Matityahu 		return -EINVAL;
1270c9e025eSMordechay Goodstein 
1280c9e025eSMordechay Goodstein 	buf_location = le32_to_cpu(alloc->buf_location);
1290c9e025eSMordechay Goodstein 	alloc_id = le32_to_cpu(alloc->alloc_id);
1300c9e025eSMordechay Goodstein 
1310c9e025eSMordechay Goodstein 	if (buf_location == IWL_FW_INI_LOCATION_INVALID ||
1320c9e025eSMordechay Goodstein 	    buf_location >= IWL_FW_INI_LOCATION_NUM)
1330c9e025eSMordechay Goodstein 		goto err;
1340c9e025eSMordechay Goodstein 
1350c9e025eSMordechay Goodstein 	if (alloc_id == IWL_FW_INI_ALLOCATION_INVALID ||
1360c9e025eSMordechay Goodstein 	    alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
1370c9e025eSMordechay Goodstein 		goto err;
138a9248de4SShahar S Matityahu 
13916b2afe0SMordechay Goodstein 	if (buf_location == IWL_FW_INI_LOCATION_NPK_PATH &&
1400c9e025eSMordechay Goodstein 	    alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
1410c9e025eSMordechay Goodstein 		goto err;
142a9248de4SShahar S Matityahu 
14316b2afe0SMordechay Goodstein 	if (buf_location == IWL_FW_INI_LOCATION_SRAM_PATH &&
144e75bc5f3SMukesh Sisodiya 	    alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
14516b2afe0SMordechay Goodstein 		goto err;
14616b2afe0SMordechay Goodstein 
147ba304151SDaniel Gabay 	if (buf_location == IWL_FW_INI_LOCATION_DRAM_PATH &&
148ba304151SDaniel Gabay 	    alloc->req_size == 0) {
149ba304151SDaniel Gabay 		IWL_ERR(trans, "WRT: Invalid DRAM buffer allocation requested size (0)\n");
150ba304151SDaniel Gabay 		return -EINVAL;
151ba304151SDaniel Gabay 	}
152ba304151SDaniel Gabay 
153a9248de4SShahar S Matityahu 	trans->dbg.fw_mon_cfg[alloc_id] = *alloc;
154a9248de4SShahar S Matityahu 
155a9248de4SShahar S Matityahu 	return 0;
1560c9e025eSMordechay Goodstein err:
1570c9e025eSMordechay Goodstein 	IWL_ERR(trans,
1580c9e025eSMordechay Goodstein 		"WRT: Invalid allocation id %u and/or location id %u for allocation TLV\n",
1590c9e025eSMordechay Goodstein 		alloc_id, buf_location);
1600c9e025eSMordechay Goodstein 	return -EINVAL;
161a9248de4SShahar S Matityahu }
162a9248de4SShahar S Matityahu 
iwl_dbg_tlv_alloc_hcmd(struct iwl_trans * trans,const struct iwl_ucode_tlv * tlv)163a9248de4SShahar S Matityahu static int iwl_dbg_tlv_alloc_hcmd(struct iwl_trans *trans,
164403ea939STakashi Iwai 				  const struct iwl_ucode_tlv *tlv)
165a9248de4SShahar S Matityahu {
166403ea939STakashi Iwai 	const struct iwl_fw_ini_hcmd_tlv *hcmd = (const void *)tlv->data;
167a9248de4SShahar S Matityahu 	u32 tp = le32_to_cpu(hcmd->time_point);
168a9248de4SShahar S Matityahu 
169a9248de4SShahar S Matityahu 	if (le32_to_cpu(tlv->length) <= sizeof(*hcmd))
170a9248de4SShahar S Matityahu 		return -EINVAL;
171a9248de4SShahar S Matityahu 
172a9248de4SShahar S Matityahu 	/* Host commands can not be sent in early time point since the FW
173a9248de4SShahar S Matityahu 	 * is not ready
174a9248de4SShahar S Matityahu 	 */
175a9248de4SShahar S Matityahu 	if (tp == IWL_FW_INI_TIME_POINT_INVALID ||
176a9248de4SShahar S Matityahu 	    tp >= IWL_FW_INI_TIME_POINT_NUM ||
177a9248de4SShahar S Matityahu 	    tp == IWL_FW_INI_TIME_POINT_EARLY) {
178a9248de4SShahar S Matityahu 		IWL_ERR(trans,
179a9248de4SShahar S Matityahu 			"WRT: Invalid time point %u for host command TLV\n",
180a9248de4SShahar S Matityahu 			tp);
181a9248de4SShahar S Matityahu 		return -EINVAL;
182a9248de4SShahar S Matityahu 	}
183a9248de4SShahar S Matityahu 
184a9248de4SShahar S Matityahu 	return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].hcmd_list);
185a9248de4SShahar S Matityahu }
186a9248de4SShahar S Matityahu 
iwl_dbg_tlv_alloc_region(struct iwl_trans * trans,const struct iwl_ucode_tlv * tlv)187a9248de4SShahar S Matityahu static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans,
188403ea939STakashi Iwai 				    const struct iwl_ucode_tlv *tlv)
189a9248de4SShahar S Matityahu {
190403ea939STakashi Iwai 	const struct iwl_fw_ini_region_tlv *reg = (const void *)tlv->data;
191a9248de4SShahar S Matityahu 	struct iwl_ucode_tlv **active_reg;
192a9248de4SShahar S Matityahu 	u32 id = le32_to_cpu(reg->id);
19362ed5d90SMordechay Goodstein 	u8 type = reg->type;
194a9248de4SShahar S Matityahu 	u32 tlv_len = sizeof(*tlv) + le32_to_cpu(tlv->length);
195a9248de4SShahar S Matityahu 
196a451b823SMukesh Sisodiya 	/*
1979d200eddSMukesh Sisodiya 	 * The higher part of the ID from version 2 is debug policy.
1989d200eddSMukesh Sisodiya 	 * The id will be only lsb 16 bits, so mask it out.
199a451b823SMukesh Sisodiya 	 */
20062ed5d90SMordechay Goodstein 	if (le32_to_cpu(reg->hdr.version) >= 2)
2019d200eddSMukesh Sisodiya 		id &= IWL_FW_INI_REGION_ID_MASK;
202a451b823SMukesh Sisodiya 
203a9248de4SShahar S Matityahu 	if (le32_to_cpu(tlv->length) < sizeof(*reg))
204a9248de4SShahar S Matityahu 		return -EINVAL;
205a9248de4SShahar S Matityahu 
206a451b823SMukesh Sisodiya 	/* for safe use of a string from FW, limit it to IWL_FW_INI_MAX_NAME */
207a451b823SMukesh Sisodiya 	IWL_DEBUG_FW(trans, "WRT: parsing region: %.*s\n",
208a451b823SMukesh Sisodiya 		     IWL_FW_INI_MAX_NAME, reg->name);
209a451b823SMukesh Sisodiya 
210a9248de4SShahar S Matityahu 	if (id >= IWL_FW_INI_MAX_REGION_ID) {
211a9248de4SShahar S Matityahu 		IWL_ERR(trans, "WRT: Invalid region id %u\n", id);
212a9248de4SShahar S Matityahu 		return -EINVAL;
213a9248de4SShahar S Matityahu 	}
214a9248de4SShahar S Matityahu 
215a9248de4SShahar S Matityahu 	if (type <= IWL_FW_INI_REGION_INVALID ||
216a9248de4SShahar S Matityahu 	    type >= IWL_FW_INI_REGION_NUM) {
217a9248de4SShahar S Matityahu 		IWL_ERR(trans, "WRT: Invalid region type %u\n", type);
218a9248de4SShahar S Matityahu 		return -EINVAL;
219a9248de4SShahar S Matityahu 	}
220a9248de4SShahar S Matityahu 
221f696a7eeSLuca Coelho 	if (type == IWL_FW_INI_REGION_PCI_IOSF_CONFIG &&
222f696a7eeSLuca Coelho 	    !trans->ops->read_config32) {
223f696a7eeSLuca Coelho 		IWL_ERR(trans, "WRT: Unsupported region type %u\n", type);
224f696a7eeSLuca Coelho 		return -EOPNOTSUPP;
225f696a7eeSLuca Coelho 	}
226f696a7eeSLuca Coelho 
227c0941aceSMukesh Sisodiya 	if (type == IWL_FW_INI_REGION_INTERNAL_BUFFER) {
228c0941aceSMukesh Sisodiya 		trans->dbg.imr_data.sram_addr =
229c0941aceSMukesh Sisodiya 			le32_to_cpu(reg->internal_buffer.base_addr);
230c0941aceSMukesh Sisodiya 		trans->dbg.imr_data.sram_size =
231c0941aceSMukesh Sisodiya 			le32_to_cpu(reg->internal_buffer.size);
232c0941aceSMukesh Sisodiya 	}
233c0941aceSMukesh Sisodiya 
234c0941aceSMukesh Sisodiya 
235a9248de4SShahar S Matityahu 	active_reg = &trans->dbg.active_regions[id];
236a9248de4SShahar S Matityahu 	if (*active_reg) {
237a9248de4SShahar S Matityahu 		IWL_WARN(trans, "WRT: Overriding region id %u\n", id);
238a9248de4SShahar S Matityahu 
239a9248de4SShahar S Matityahu 		kfree(*active_reg);
240a9248de4SShahar S Matityahu 	}
241a9248de4SShahar S Matityahu 
242a9248de4SShahar S Matityahu 	*active_reg = kmemdup(tlv, tlv_len, GFP_KERNEL);
243a9248de4SShahar S Matityahu 	if (!*active_reg)
244a9248de4SShahar S Matityahu 		return -ENOMEM;
245a9248de4SShahar S Matityahu 
246a9248de4SShahar S Matityahu 	IWL_DEBUG_FW(trans, "WRT: Enabling region id %u type %u\n", id, type);
247a9248de4SShahar S Matityahu 
248a9248de4SShahar S Matityahu 	return 0;
249a9248de4SShahar S Matityahu }
250a9248de4SShahar S Matityahu 
iwl_dbg_tlv_alloc_trigger(struct iwl_trans * trans,const struct iwl_ucode_tlv * tlv)251a9248de4SShahar S Matityahu static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans,
252403ea939STakashi Iwai 				     const struct iwl_ucode_tlv *tlv)
253a9248de4SShahar S Matityahu {
254403ea939STakashi Iwai 	const struct iwl_fw_ini_trigger_tlv *trig = (const void *)tlv->data;
255403ea939STakashi Iwai 	struct iwl_fw_ini_trigger_tlv *dup_trig;
256a9248de4SShahar S Matityahu 	u32 tp = le32_to_cpu(trig->time_point);
257ddb6b76bSMukesh Sisodiya 	u32 rf = le32_to_cpu(trig->reset_fw);
258ea0cca61SJiri Slaby 	struct iwl_ucode_tlv *dup = NULL;
259ea0cca61SJiri Slaby 	int ret;
260a9248de4SShahar S Matityahu 
261a9248de4SShahar S Matityahu 	if (le32_to_cpu(tlv->length) < sizeof(*trig))
262a9248de4SShahar S Matityahu 		return -EINVAL;
263a9248de4SShahar S Matityahu 
264a9248de4SShahar S Matityahu 	if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||
265a9248de4SShahar S Matityahu 	    tp >= IWL_FW_INI_TIME_POINT_NUM) {
266a9248de4SShahar S Matityahu 		IWL_ERR(trans,
267a9248de4SShahar S Matityahu 			"WRT: Invalid time point %u for trigger TLV\n",
268a9248de4SShahar S Matityahu 			tp);
269a9248de4SShahar S Matityahu 		return -EINVAL;
270a9248de4SShahar S Matityahu 	}
271a9248de4SShahar S Matityahu 
272ddb6b76bSMukesh Sisodiya 	IWL_DEBUG_FW(trans,
273ddb6b76bSMukesh Sisodiya 		     "WRT: time point %u for trigger TLV with reset_fw %u\n",
274ddb6b76bSMukesh Sisodiya 		     tp, rf);
275ddb6b76bSMukesh Sisodiya 	trans->dbg.last_tp_resetfw = 0xFF;
276ea0cca61SJiri Slaby 	if (!le32_to_cpu(trig->occurrences)) {
277ea0cca61SJiri Slaby 		dup = kmemdup(tlv, sizeof(*tlv) + le32_to_cpu(tlv->length),
278ea0cca61SJiri Slaby 				GFP_KERNEL);
279ea0cca61SJiri Slaby 		if (!dup)
280ea0cca61SJiri Slaby 			return -ENOMEM;
281403ea939STakashi Iwai 		dup_trig = (void *)dup->data;
282403ea939STakashi Iwai 		dup_trig->occurrences = cpu_to_le32(-1);
283ea0cca61SJiri Slaby 		tlv = dup;
284ea0cca61SJiri Slaby 	}
285a9248de4SShahar S Matityahu 
286ea0cca61SJiri Slaby 	ret = iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].trig_list);
287ea0cca61SJiri Slaby 	kfree(dup);
288ea0cca61SJiri Slaby 
289ea0cca61SJiri Slaby 	return ret;
290a9248de4SShahar S Matityahu }
291a9248de4SShahar S Matityahu 
iwl_dbg_tlv_config_set(struct iwl_trans * trans,const struct iwl_ucode_tlv * tlv)292f21baf24SMukesh Sisodiya static int iwl_dbg_tlv_config_set(struct iwl_trans *trans,
293f21baf24SMukesh Sisodiya 				  const struct iwl_ucode_tlv *tlv)
294f21baf24SMukesh Sisodiya {
29586e8e657SJohannes Berg 	const struct iwl_fw_ini_conf_set_tlv *conf_set = (const void *)tlv->data;
296f21baf24SMukesh Sisodiya 	u32 tp = le32_to_cpu(conf_set->time_point);
297f21baf24SMukesh Sisodiya 	u32 type = le32_to_cpu(conf_set->set_type);
298f21baf24SMukesh Sisodiya 
299f21baf24SMukesh Sisodiya 	if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||
300f21baf24SMukesh Sisodiya 	    tp >= IWL_FW_INI_TIME_POINT_NUM) {
301f21baf24SMukesh Sisodiya 		IWL_DEBUG_FW(trans,
302f21baf24SMukesh Sisodiya 			     "WRT: Invalid time point %u for config set TLV\n", tp);
303f21baf24SMukesh Sisodiya 		return -EINVAL;
304f21baf24SMukesh Sisodiya 	}
305f21baf24SMukesh Sisodiya 
306f21baf24SMukesh Sisodiya 	if (type <= IWL_FW_INI_CONFIG_SET_TYPE_INVALID ||
307f21baf24SMukesh Sisodiya 	    type >= IWL_FW_INI_CONFIG_SET_TYPE_MAX_NUM) {
308f21baf24SMukesh Sisodiya 		IWL_DEBUG_FW(trans,
309f21baf24SMukesh Sisodiya 			     "WRT: Invalid config set type %u for config set TLV\n", type);
310f21baf24SMukesh Sisodiya 		return -EINVAL;
311f21baf24SMukesh Sisodiya 	}
312f21baf24SMukesh Sisodiya 
313f21baf24SMukesh Sisodiya 	return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].config_list);
314f21baf24SMukesh Sisodiya }
315f21baf24SMukesh Sisodiya 
316a9248de4SShahar S Matityahu static int (*dbg_tlv_alloc[])(struct iwl_trans *trans,
317403ea939STakashi Iwai 			      const struct iwl_ucode_tlv *tlv) = {
318a9248de4SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_DEBUG_INFO]	= iwl_dbg_tlv_alloc_debug_info,
319a9248de4SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_BUF_ALLOC]	= iwl_dbg_tlv_alloc_buf_alloc,
320a9248de4SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_HCMD]		= iwl_dbg_tlv_alloc_hcmd,
321a9248de4SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_REGION]	= iwl_dbg_tlv_alloc_region,
322a9248de4SShahar S Matityahu 	[IWL_DBG_TLV_TYPE_TRIGGER]	= iwl_dbg_tlv_alloc_trigger,
323f21baf24SMukesh Sisodiya 	[IWL_DBG_TLV_TYPE_CONF_SET]	= iwl_dbg_tlv_config_set,
324a9248de4SShahar S Matityahu };
325a9248de4SShahar S Matityahu 
iwl_dbg_tlv_alloc(struct iwl_trans * trans,const struct iwl_ucode_tlv * tlv,bool ext)326403ea939STakashi Iwai void iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv,
327341bd290SShahar S Matityahu 		       bool ext)
328341bd290SShahar S Matityahu {
329341bd290SShahar S Matityahu 	enum iwl_ini_cfg_state *cfg_state = ext ?
330341bd290SShahar S Matityahu 		&trans->dbg.external_ini_cfg : &trans->dbg.internal_ini_cfg;
331ccbffd69SJohannes Berg 	const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];
332ccbffd69SJohannes Berg 	u32 type;
333ccbffd69SJohannes Berg 	u32 tlv_idx;
334ccbffd69SJohannes Berg 	u32 domain;
335a9248de4SShahar S Matityahu 	int ret;
336341bd290SShahar S Matityahu 
337ccbffd69SJohannes Berg 	if (le32_to_cpu(tlv->length) < sizeof(*hdr))
338ccbffd69SJohannes Berg 		return;
339ccbffd69SJohannes Berg 
340ccbffd69SJohannes Berg 	type = le32_to_cpu(tlv->type);
341ccbffd69SJohannes Berg 	tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;
342ccbffd69SJohannes Berg 	domain = le32_to_cpu(hdr->domain);
343ccbffd69SJohannes Berg 
344e701da0cSLuca Coelho 	if (domain != IWL_FW_INI_DOMAIN_ALWAYS_ON &&
345e701da0cSLuca Coelho 	    !(domain & trans->dbg.domains_bitmap)) {
346e701da0cSLuca Coelho 		IWL_DEBUG_FW(trans,
347e701da0cSLuca Coelho 			     "WRT: Skipping TLV with disabled domain 0x%0x (0x%0x)\n",
348e701da0cSLuca Coelho 			     domain, trans->dbg.domains_bitmap);
349e701da0cSLuca Coelho 		return;
350e701da0cSLuca Coelho 	}
351e701da0cSLuca Coelho 
352a9248de4SShahar S Matityahu 	if (tlv_idx >= ARRAY_SIZE(dbg_tlv_alloc) || !dbg_tlv_alloc[tlv_idx]) {
353a9248de4SShahar S Matityahu 		IWL_ERR(trans, "WRT: Unsupported TLV type 0x%x\n", type);
354341bd290SShahar S Matityahu 		goto out_err;
355341bd290SShahar S Matityahu 	}
356341bd290SShahar S Matityahu 
357341bd290SShahar S Matityahu 	if (!iwl_dbg_tlv_ver_support(tlv)) {
358341bd290SShahar S Matityahu 		IWL_ERR(trans, "WRT: Unsupported TLV 0x%x version %u\n", type,
359a9248de4SShahar S Matityahu 			le32_to_cpu(hdr->version));
360a9248de4SShahar S Matityahu 		goto out_err;
361a9248de4SShahar S Matityahu 	}
362a9248de4SShahar S Matityahu 
363a9248de4SShahar S Matityahu 	ret = dbg_tlv_alloc[tlv_idx](trans, tlv);
364a9248de4SShahar S Matityahu 	if (ret) {
365876882b5SGolan Ben Ami 		IWL_WARN(trans,
366a9248de4SShahar S Matityahu 			 "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n",
367a9248de4SShahar S Matityahu 			 type, ret, ext);
368341bd290SShahar S Matityahu 		goto out_err;
369341bd290SShahar S Matityahu 	}
370341bd290SShahar S Matityahu 
371341bd290SShahar S Matityahu 	if (*cfg_state == IWL_INI_CFG_STATE_NOT_LOADED)
372341bd290SShahar S Matityahu 		*cfg_state = IWL_INI_CFG_STATE_LOADED;
373341bd290SShahar S Matityahu 
374341bd290SShahar S Matityahu 	return;
375341bd290SShahar S Matityahu 
376341bd290SShahar S Matityahu out_err:
377341bd290SShahar S Matityahu 	*cfg_state = IWL_INI_CFG_STATE_CORRUPTED;
378f14cda6fSSara Sharon }
37940b7d22dSShahar S Matityahu 
iwl_dbg_tlv_del_timers(struct iwl_trans * trans)3809b1bcfccSShahar S Matityahu void iwl_dbg_tlv_del_timers(struct iwl_trans *trans)
3819b1bcfccSShahar S Matityahu {
38260e8abd9SShahar S Matityahu 	struct list_head *timer_list = &trans->dbg.periodic_trig_list;
38360e8abd9SShahar S Matityahu 	struct iwl_dbg_tlv_timer_node *node, *tmp;
38460e8abd9SShahar S Matityahu 
38560e8abd9SShahar S Matityahu 	list_for_each_entry_safe(node, tmp, timer_list, list) {
386292a089dSSteven Rostedt (Google) 		timer_shutdown_sync(&node->timer);
38760e8abd9SShahar S Matityahu 		list_del(&node->list);
38860e8abd9SShahar S Matityahu 		kfree(node);
38960e8abd9SShahar S Matityahu 	}
3909b1bcfccSShahar S Matityahu }
3919b1bcfccSShahar S Matityahu IWL_EXPORT_SYMBOL(iwl_dbg_tlv_del_timers);
3929b1bcfccSShahar S Matityahu 
iwl_dbg_tlv_fragments_free(struct iwl_trans * trans,enum iwl_fw_ini_allocation_id alloc_id)39314124b25SShahar S Matityahu static void iwl_dbg_tlv_fragments_free(struct iwl_trans *trans,
39414124b25SShahar S Matityahu 				       enum iwl_fw_ini_allocation_id alloc_id)
39514124b25SShahar S Matityahu {
39614124b25SShahar S Matityahu 	struct iwl_fw_mon *fw_mon;
39714124b25SShahar S Matityahu 	int i;
39814124b25SShahar S Matityahu 
39914124b25SShahar S Matityahu 	if (alloc_id <= IWL_FW_INI_ALLOCATION_INVALID ||
40014124b25SShahar S Matityahu 	    alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
40114124b25SShahar S Matityahu 		return;
40214124b25SShahar S Matityahu 
40314124b25SShahar S Matityahu 	fw_mon = &trans->dbg.fw_mon_ini[alloc_id];
40414124b25SShahar S Matityahu 
40514124b25SShahar S Matityahu 	for (i = 0; i < fw_mon->num_frags; i++) {
40614124b25SShahar S Matityahu 		struct iwl_dram_data *frag = &fw_mon->frags[i];
40714124b25SShahar S Matityahu 
40814124b25SShahar S Matityahu 		dma_free_coherent(trans->dev, frag->size, frag->block,
40914124b25SShahar S Matityahu 				  frag->physical);
41014124b25SShahar S Matityahu 
41114124b25SShahar S Matityahu 		frag->physical = 0;
41214124b25SShahar S Matityahu 		frag->block = NULL;
41314124b25SShahar S Matityahu 		frag->size = 0;
41414124b25SShahar S Matityahu 	}
41514124b25SShahar S Matityahu 
41614124b25SShahar S Matityahu 	kfree(fw_mon->frags);
41714124b25SShahar S Matityahu 	fw_mon->frags = NULL;
41814124b25SShahar S Matityahu 	fw_mon->num_frags = 0;
41914124b25SShahar S Matityahu }
42014124b25SShahar S Matityahu 
iwl_dbg_tlv_free(struct iwl_trans * trans)42141874d3aSShahar S Matityahu void iwl_dbg_tlv_free(struct iwl_trans *trans)
422f14cda6fSSara Sharon {
423a9248de4SShahar S Matityahu 	struct iwl_dbg_tlv_node *tlv_node, *tlv_node_tmp;
424a9248de4SShahar S Matityahu 	int i;
425a9248de4SShahar S Matityahu 
426a9248de4SShahar S Matityahu 	iwl_dbg_tlv_del_timers(trans);
427a9248de4SShahar S Matityahu 
428a9248de4SShahar S Matityahu 	for (i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) {
429a9248de4SShahar S Matityahu 		struct iwl_ucode_tlv **active_reg =
430a9248de4SShahar S Matityahu 			&trans->dbg.active_regions[i];
431a9248de4SShahar S Matityahu 
432a9248de4SShahar S Matityahu 		kfree(*active_reg);
433a9248de4SShahar S Matityahu 		*active_reg = NULL;
434a9248de4SShahar S Matityahu 	}
435a9248de4SShahar S Matityahu 
436a9248de4SShahar S Matityahu 	list_for_each_entry_safe(tlv_node, tlv_node_tmp,
437a9248de4SShahar S Matityahu 				 &trans->dbg.debug_info_tlv_list, list) {
438a9248de4SShahar S Matityahu 		list_del(&tlv_node->list);
439a9248de4SShahar S Matityahu 		kfree(tlv_node);
440a9248de4SShahar S Matityahu 	}
441a9248de4SShahar S Matityahu 
442a9248de4SShahar S Matityahu 	for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {
443a9248de4SShahar S Matityahu 		struct iwl_dbg_tlv_time_point_data *tp =
444a9248de4SShahar S Matityahu 			&trans->dbg.time_point[i];
445a9248de4SShahar S Matityahu 
446a9248de4SShahar S Matityahu 		list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->trig_list,
447a9248de4SShahar S Matityahu 					 list) {
448a9248de4SShahar S Matityahu 			list_del(&tlv_node->list);
449a9248de4SShahar S Matityahu 			kfree(tlv_node);
450a9248de4SShahar S Matityahu 		}
451a9248de4SShahar S Matityahu 
452a9248de4SShahar S Matityahu 		list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->hcmd_list,
453a9248de4SShahar S Matityahu 					 list) {
454a9248de4SShahar S Matityahu 			list_del(&tlv_node->list);
455a9248de4SShahar S Matityahu 			kfree(tlv_node);
456a9248de4SShahar S Matityahu 		}
457cf29c5b6SShahar S Matityahu 
458cf29c5b6SShahar S Matityahu 		list_for_each_entry_safe(tlv_node, tlv_node_tmp,
459cf29c5b6SShahar S Matityahu 					 &tp->active_trig_list, list) {
460cf29c5b6SShahar S Matityahu 			list_del(&tlv_node->list);
461cf29c5b6SShahar S Matityahu 			kfree(tlv_node);
462cf29c5b6SShahar S Matityahu 		}
463f21baf24SMukesh Sisodiya 
464f21baf24SMukesh Sisodiya 		list_for_each_entry_safe(tlv_node, tlv_node_tmp,
465f21baf24SMukesh Sisodiya 					 &tp->config_list, list) {
466f21baf24SMukesh Sisodiya 			list_del(&tlv_node->list);
467f21baf24SMukesh Sisodiya 			kfree(tlv_node);
468f21baf24SMukesh Sisodiya 		}
469f21baf24SMukesh Sisodiya 
470a9248de4SShahar S Matityahu 	}
47114124b25SShahar S Matityahu 
47214124b25SShahar S Matityahu 	for (i = 0; i < ARRAY_SIZE(trans->dbg.fw_mon_ini); i++)
47314124b25SShahar S Matityahu 		iwl_dbg_tlv_fragments_free(trans, i);
474f14cda6fSSara Sharon }
47568f6f492SSara Sharon 
iwl_dbg_tlv_parse_bin(struct iwl_trans * trans,const u8 * data,size_t len)47641874d3aSShahar S Matityahu static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data,
47768f6f492SSara Sharon 				 size_t len)
47868f6f492SSara Sharon {
479403ea939STakashi Iwai 	const struct iwl_ucode_tlv *tlv;
48068f6f492SSara Sharon 	u32 tlv_len;
48168f6f492SSara Sharon 
48268f6f492SSara Sharon 	while (len >= sizeof(*tlv)) {
48368f6f492SSara Sharon 		len -= sizeof(*tlv);
48473c289baSBjoern A. Zeeb 		tlv = (const void *)data;
48568f6f492SSara Sharon 
48668f6f492SSara Sharon 		tlv_len = le32_to_cpu(tlv->length);
48768f6f492SSara Sharon 
48868f6f492SSara Sharon 		if (len < tlv_len) {
48968f6f492SSara Sharon 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
49068f6f492SSara Sharon 				len, tlv_len);
49168f6f492SSara Sharon 			return -EINVAL;
49268f6f492SSara Sharon 		}
49368f6f492SSara Sharon 		len -= ALIGN(tlv_len, 4);
49468f6f492SSara Sharon 		data += sizeof(*tlv) + ALIGN(tlv_len, 4);
49568f6f492SSara Sharon 
496341bd290SShahar S Matityahu 		iwl_dbg_tlv_alloc(trans, tlv, true);
49768f6f492SSara Sharon 	}
49868f6f492SSara Sharon 
49968f6f492SSara Sharon 	return 0;
50068f6f492SSara Sharon }
50168f6f492SSara Sharon 
iwl_dbg_tlv_load_bin(struct device * dev,struct iwl_trans * trans)50241874d3aSShahar S Matityahu void iwl_dbg_tlv_load_bin(struct device *dev, struct iwl_trans *trans)
50368f6f492SSara Sharon {
50468f6f492SSara Sharon 	const struct firmware *fw;
5058fc3015dSMordechay Goodstein 	const char *yoyo_bin = "iwl-debug-yoyo.bin";
50668f6f492SSara Sharon 	int res;
50768f6f492SSara Sharon 
5087c81a025SMukesh Sisodiya 	if (!iwlwifi_mod_params.enable_ini ||
5091599a164SMukesh Sisodiya 	    trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000)
51068f6f492SSara Sharon 		return;
51168f6f492SSara Sharon 
5128fc3015dSMordechay Goodstein 	res = firmware_request_nowarn(&fw, yoyo_bin, dev);
5138fc3015dSMordechay Goodstein 	IWL_DEBUG_FW(trans, "%s %s\n", res ? "didn't load" : "loaded", yoyo_bin);
5148fc3015dSMordechay Goodstein 
51568f6f492SSara Sharon 	if (res)
51668f6f492SSara Sharon 		return;
51768f6f492SSara Sharon 
51841874d3aSShahar S Matityahu 	iwl_dbg_tlv_parse_bin(trans, fw->data, fw->size);
51968f6f492SSara Sharon 
52068f6f492SSara Sharon 	release_firmware(fw);
52168f6f492SSara Sharon }
52200eacde4SShahar S Matityahu 
iwl_dbg_tlv_init(struct iwl_trans * trans)523a9248de4SShahar S Matityahu void iwl_dbg_tlv_init(struct iwl_trans *trans)
524a9248de4SShahar S Matityahu {
525a9248de4SShahar S Matityahu 	int i;
526a9248de4SShahar S Matityahu 
527a9248de4SShahar S Matityahu 	INIT_LIST_HEAD(&trans->dbg.debug_info_tlv_list);
52860e8abd9SShahar S Matityahu 	INIT_LIST_HEAD(&trans->dbg.periodic_trig_list);
529a9248de4SShahar S Matityahu 
530a9248de4SShahar S Matityahu 	for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {
531a9248de4SShahar S Matityahu 		struct iwl_dbg_tlv_time_point_data *tp =
532a9248de4SShahar S Matityahu 			&trans->dbg.time_point[i];
533a9248de4SShahar S Matityahu 
534a9248de4SShahar S Matityahu 		INIT_LIST_HEAD(&tp->trig_list);
535a9248de4SShahar S Matityahu 		INIT_LIST_HEAD(&tp->hcmd_list);
536cf29c5b6SShahar S Matityahu 		INIT_LIST_HEAD(&tp->active_trig_list);
537f21baf24SMukesh Sisodiya 		INIT_LIST_HEAD(&tp->config_list);
538a9248de4SShahar S Matityahu 	}
539a9248de4SShahar S Matityahu }
540a9248de4SShahar S Matityahu 
iwl_dbg_tlv_alloc_fragment(struct iwl_fw_runtime * fwrt,struct iwl_dram_data * frag,u32 pages)54114124b25SShahar S Matityahu static int iwl_dbg_tlv_alloc_fragment(struct iwl_fw_runtime *fwrt,
54214124b25SShahar S Matityahu 				      struct iwl_dram_data *frag, u32 pages)
54314124b25SShahar S Matityahu {
54414124b25SShahar S Matityahu 	void *block = NULL;
54514124b25SShahar S Matityahu 	dma_addr_t physical;
54614124b25SShahar S Matityahu 
54714124b25SShahar S Matityahu 	if (!frag || frag->size || !pages)
54814124b25SShahar S Matityahu 		return -EIO;
54914124b25SShahar S Matityahu 
5504f565ee2SLuca Coelho 	/*
5514f565ee2SLuca Coelho 	 * We try to allocate as many pages as we can, starting with
5524f565ee2SLuca Coelho 	 * the requested amount and going down until we can allocate
5534f565ee2SLuca Coelho 	 * something.  Because of DIV_ROUND_UP(), pages will never go
5544f565ee2SLuca Coelho 	 * down to 0 and stop the loop, so stop when pages reaches 1,
5554f565ee2SLuca Coelho 	 * which is too small anyway.
5564f565ee2SLuca Coelho 	 */
5574f565ee2SLuca Coelho 	while (pages > 1) {
55814124b25SShahar S Matityahu 		block = dma_alloc_coherent(fwrt->dev, pages * PAGE_SIZE,
55914124b25SShahar S Matityahu 					   &physical,
56014124b25SShahar S Matityahu 					   GFP_KERNEL | __GFP_NOWARN);
56114124b25SShahar S Matityahu 		if (block)
56214124b25SShahar S Matityahu 			break;
56314124b25SShahar S Matityahu 
56414124b25SShahar S Matityahu 		IWL_WARN(fwrt, "WRT: Failed to allocate fragment size %lu\n",
56514124b25SShahar S Matityahu 			 pages * PAGE_SIZE);
56614124b25SShahar S Matityahu 
56714124b25SShahar S Matityahu 		pages = DIV_ROUND_UP(pages, 2);
56814124b25SShahar S Matityahu 	}
56914124b25SShahar S Matityahu 
57014124b25SShahar S Matityahu 	if (!block)
57114124b25SShahar S Matityahu 		return -ENOMEM;
57214124b25SShahar S Matityahu 
57314124b25SShahar S Matityahu 	frag->physical = physical;
57414124b25SShahar S Matityahu 	frag->block = block;
57514124b25SShahar S Matityahu 	frag->size = pages * PAGE_SIZE;
57614124b25SShahar S Matityahu 
57714124b25SShahar S Matityahu 	return pages;
57814124b25SShahar S Matityahu }
57914124b25SShahar S Matityahu 
iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime * fwrt,enum iwl_fw_ini_allocation_id alloc_id)58014124b25SShahar S Matityahu static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt,
58114124b25SShahar S Matityahu 				       enum iwl_fw_ini_allocation_id alloc_id)
58214124b25SShahar S Matityahu {
58314124b25SShahar S Matityahu 	struct iwl_fw_mon *fw_mon;
58414124b25SShahar S Matityahu 	struct iwl_fw_ini_allocation_tlv *fw_mon_cfg;
58514124b25SShahar S Matityahu 	u32 num_frags, remain_pages, frag_pages;
58614124b25SShahar S Matityahu 	int i;
58714124b25SShahar S Matityahu 
58814124b25SShahar S Matityahu 	if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||
58914124b25SShahar S Matityahu 	    alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
59014124b25SShahar S Matityahu 		return -EIO;
59114124b25SShahar S Matityahu 
59214124b25SShahar S Matityahu 	fw_mon_cfg = &fwrt->trans->dbg.fw_mon_cfg[alloc_id];
59314124b25SShahar S Matityahu 	fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
59414124b25SShahar S Matityahu 
59593ae8145SJohannes Berg 	if (fw_mon->num_frags) {
59693ae8145SJohannes Berg 		for (i = 0; i < fw_mon->num_frags; i++)
59793ae8145SJohannes Berg 			memset(fw_mon->frags[i].block, 0,
59893ae8145SJohannes Berg 			       fw_mon->frags[i].size);
59993ae8145SJohannes Berg 		return 0;
60093ae8145SJohannes Berg 	}
60193ae8145SJohannes Berg 
60293ae8145SJohannes Berg 	if (fw_mon_cfg->buf_location !=
60314124b25SShahar S Matityahu 	    cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH))
60414124b25SShahar S Matityahu 		return 0;
60514124b25SShahar S Matityahu 
60614124b25SShahar S Matityahu 	num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num);
607ab23da48SRotem Saado 	if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
60814124b25SShahar S Matityahu 		if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
60914124b25SShahar S Matityahu 			return -EIO;
61014124b25SShahar S Matityahu 		num_frags = 1;
6110323f194SRotem Saado 	} else if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ &&
6120323f194SRotem Saado 			   alloc_id > IWL_FW_INI_ALLOCATION_ID_DBGC3) {
6130323f194SRotem Saado 		return -EIO;
61414124b25SShahar S Matityahu 	}
61514124b25SShahar S Matityahu 
61614124b25SShahar S Matityahu 	remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size),
61714124b25SShahar S Matityahu 				    PAGE_SIZE);
61814124b25SShahar S Matityahu 	num_frags = min_t(u32, num_frags, BUF_ALLOC_MAX_NUM_FRAGS);
61914124b25SShahar S Matityahu 	num_frags = min_t(u32, num_frags, remain_pages);
62014124b25SShahar S Matityahu 	frag_pages = DIV_ROUND_UP(remain_pages, num_frags);
62114124b25SShahar S Matityahu 
62214124b25SShahar S Matityahu 	fw_mon->frags = kcalloc(num_frags, sizeof(*fw_mon->frags), GFP_KERNEL);
62314124b25SShahar S Matityahu 	if (!fw_mon->frags)
62414124b25SShahar S Matityahu 		return -ENOMEM;
62514124b25SShahar S Matityahu 
62614124b25SShahar S Matityahu 	for (i = 0; i < num_frags; i++) {
62714124b25SShahar S Matityahu 		int pages = min_t(u32, frag_pages, remain_pages);
62814124b25SShahar S Matityahu 
62914124b25SShahar S Matityahu 		IWL_DEBUG_FW(fwrt,
63014124b25SShahar S Matityahu 			     "WRT: Allocating DRAM buffer (alloc_id=%u, fragment=%u, size=0x%lx)\n",
63114124b25SShahar S Matityahu 			     alloc_id, i, pages * PAGE_SIZE);
63214124b25SShahar S Matityahu 
63314124b25SShahar S Matityahu 		pages = iwl_dbg_tlv_alloc_fragment(fwrt, &fw_mon->frags[i],
63414124b25SShahar S Matityahu 						   pages);
63514124b25SShahar S Matityahu 		if (pages < 0) {
63614124b25SShahar S Matityahu 			u32 alloc_size = le32_to_cpu(fw_mon_cfg->req_size) -
63714124b25SShahar S Matityahu 				(remain_pages * PAGE_SIZE);
63814124b25SShahar S Matityahu 
63914124b25SShahar S Matityahu 			if (alloc_size < le32_to_cpu(fw_mon_cfg->min_size)) {
64014124b25SShahar S Matityahu 				iwl_dbg_tlv_fragments_free(fwrt->trans,
64114124b25SShahar S Matityahu 							   alloc_id);
64214124b25SShahar S Matityahu 				return pages;
64314124b25SShahar S Matityahu 			}
64414124b25SShahar S Matityahu 			break;
64514124b25SShahar S Matityahu 		}
64614124b25SShahar S Matityahu 
64714124b25SShahar S Matityahu 		remain_pages -= pages;
64814124b25SShahar S Matityahu 		fw_mon->num_frags++;
64914124b25SShahar S Matityahu 	}
65014124b25SShahar S Matityahu 
65114124b25SShahar S Matityahu 	return 0;
65214124b25SShahar S Matityahu }
65314124b25SShahar S Matityahu 
iwl_dbg_tlv_apply_buffer(struct iwl_fw_runtime * fwrt,enum iwl_fw_ini_allocation_id alloc_id)65414124b25SShahar S Matityahu static int iwl_dbg_tlv_apply_buffer(struct iwl_fw_runtime *fwrt,
65514124b25SShahar S Matityahu 				    enum iwl_fw_ini_allocation_id alloc_id)
65614124b25SShahar S Matityahu {
65714124b25SShahar S Matityahu 	struct iwl_fw_mon *fw_mon;
65814124b25SShahar S Matityahu 	u32 remain_frags, num_commands;
65914124b25SShahar S Matityahu 	int i, fw_mon_idx = 0;
66014124b25SShahar S Matityahu 
66114124b25SShahar S Matityahu 	if (!fw_has_capa(&fwrt->fw->ucode_capa,
66214124b25SShahar S Matityahu 			 IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP))
66314124b25SShahar S Matityahu 		return 0;
66414124b25SShahar S Matityahu 
66514124b25SShahar S Matityahu 	if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||
66614124b25SShahar S Matityahu 	    alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
66714124b25SShahar S Matityahu 		return -EIO;
66814124b25SShahar S Matityahu 
66914124b25SShahar S Matityahu 	if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=
67014124b25SShahar S Matityahu 	    IWL_FW_INI_LOCATION_DRAM_PATH)
67114124b25SShahar S Matityahu 		return 0;
67214124b25SShahar S Matityahu 
67314124b25SShahar S Matityahu 	fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
67414124b25SShahar S Matityahu 
67514124b25SShahar S Matityahu 	/* the first fragment of DBGC1 is given to the FW via register
67614124b25SShahar S Matityahu 	 * or context info
67714124b25SShahar S Matityahu 	 */
67814124b25SShahar S Matityahu 	if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)
67914124b25SShahar S Matityahu 		fw_mon_idx++;
68014124b25SShahar S Matityahu 
68114124b25SShahar S Matityahu 	remain_frags = fw_mon->num_frags - fw_mon_idx;
68214124b25SShahar S Matityahu 	if (!remain_frags)
68314124b25SShahar S Matityahu 		return 0;
68414124b25SShahar S Matityahu 
68514124b25SShahar S Matityahu 	num_commands = DIV_ROUND_UP(remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);
68614124b25SShahar S Matityahu 
68714124b25SShahar S Matityahu 	IWL_DEBUG_FW(fwrt, "WRT: Applying DRAM destination (alloc_id=%u)\n",
68814124b25SShahar S Matityahu 		     alloc_id);
68914124b25SShahar S Matityahu 
69014124b25SShahar S Matityahu 	for (i = 0; i < num_commands; i++) {
69114124b25SShahar S Matityahu 		u32 num_frags = min_t(u32, remain_frags,
69214124b25SShahar S Matityahu 				      BUF_ALLOC_MAX_NUM_FRAGS);
69314124b25SShahar S Matityahu 		struct iwl_buf_alloc_cmd data = {
69414124b25SShahar S Matityahu 			.alloc_id = cpu_to_le32(alloc_id),
69514124b25SShahar S Matityahu 			.num_frags = cpu_to_le32(num_frags),
69614124b25SShahar S Matityahu 			.buf_location =
69714124b25SShahar S Matityahu 				cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH),
69814124b25SShahar S Matityahu 		};
69914124b25SShahar S Matityahu 		struct iwl_host_cmd hcmd = {
70014124b25SShahar S Matityahu 			.id = WIDE_ID(DEBUG_GROUP, BUFFER_ALLOCATION),
70114124b25SShahar S Matityahu 			.data[0] = &data,
70214124b25SShahar S Matityahu 			.len[0] = sizeof(data),
7037e47f416SJohannes Berg 			.flags = CMD_SEND_IN_RFKILL,
70414124b25SShahar S Matityahu 		};
70514124b25SShahar S Matityahu 		int ret, j;
70614124b25SShahar S Matityahu 
70714124b25SShahar S Matityahu 		for (j = 0; j < num_frags; j++) {
70814124b25SShahar S Matityahu 			struct iwl_buf_alloc_frag *frag = &data.frags[j];
70914124b25SShahar S Matityahu 			struct iwl_dram_data *fw_mon_frag =
71014124b25SShahar S Matityahu 				&fw_mon->frags[fw_mon_idx++];
71114124b25SShahar S Matityahu 
71214124b25SShahar S Matityahu 			frag->addr = cpu_to_le64(fw_mon_frag->physical);
71314124b25SShahar S Matityahu 			frag->size = cpu_to_le32(fw_mon_frag->size);
71414124b25SShahar S Matityahu 		}
71514124b25SShahar S Matityahu 		ret = iwl_trans_send_cmd(fwrt->trans, &hcmd);
71614124b25SShahar S Matityahu 		if (ret)
71714124b25SShahar S Matityahu 			return ret;
71814124b25SShahar S Matityahu 
71914124b25SShahar S Matityahu 		remain_frags -= num_frags;
72014124b25SShahar S Matityahu 	}
72114124b25SShahar S Matityahu 
72214124b25SShahar S Matityahu 	return 0;
72314124b25SShahar S Matityahu }
72414124b25SShahar S Matityahu 
iwl_dbg_tlv_apply_buffers(struct iwl_fw_runtime * fwrt)72514124b25SShahar S Matityahu static void iwl_dbg_tlv_apply_buffers(struct iwl_fw_runtime *fwrt)
72614124b25SShahar S Matityahu {
72714124b25SShahar S Matityahu 	int ret, i;
72814124b25SShahar S Matityahu 
729f21baf24SMukesh Sisodiya 	if (fw_has_capa(&fwrt->fw->ucode_capa,
730f21baf24SMukesh Sisodiya 			IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT))
731f21baf24SMukesh Sisodiya 		return;
732f21baf24SMukesh Sisodiya 
73314124b25SShahar S Matityahu 	for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {
73414124b25SShahar S Matityahu 		ret = iwl_dbg_tlv_apply_buffer(fwrt, i);
73514124b25SShahar S Matityahu 		if (ret)
73614124b25SShahar S Matityahu 			IWL_WARN(fwrt,
73714124b25SShahar S Matityahu 				 "WRT: Failed to apply DRAM buffer for allocation id %d, ret=%d\n",
73814124b25SShahar S Matityahu 				 i, ret);
73914124b25SShahar S Matityahu 	}
74014124b25SShahar S Matityahu }
74114124b25SShahar S Matityahu 
iwl_dbg_tlv_update_dram(struct iwl_fw_runtime * fwrt,enum iwl_fw_ini_allocation_id alloc_id,struct iwl_dram_info * dram_info)742f21baf24SMukesh Sisodiya static int iwl_dbg_tlv_update_dram(struct iwl_fw_runtime *fwrt,
743f21baf24SMukesh Sisodiya 				   enum iwl_fw_ini_allocation_id alloc_id,
744f21baf24SMukesh Sisodiya 				   struct iwl_dram_info *dram_info)
745f21baf24SMukesh Sisodiya {
746f21baf24SMukesh Sisodiya 	struct iwl_fw_mon *fw_mon;
747f21baf24SMukesh Sisodiya 	u32 remain_frags, num_frags;
748f21baf24SMukesh Sisodiya 	int j, fw_mon_idx = 0;
749f21baf24SMukesh Sisodiya 	struct iwl_buf_alloc_cmd *data;
750f21baf24SMukesh Sisodiya 
751f21baf24SMukesh Sisodiya 	if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=
752f21baf24SMukesh Sisodiya 			IWL_FW_INI_LOCATION_DRAM_PATH) {
7533278c42bSBenjamin Berg 		IWL_DEBUG_FW(fwrt, "WRT: alloc_id %u location is not in DRAM_PATH\n",
7543278c42bSBenjamin Berg 			     alloc_id);
755f21baf24SMukesh Sisodiya 		return -1;
756f21baf24SMukesh Sisodiya 	}
757f21baf24SMukesh Sisodiya 
758f21baf24SMukesh Sisodiya 	fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
759f21baf24SMukesh Sisodiya 
760f21baf24SMukesh Sisodiya 	/* the first fragment of DBGC1 is given to the FW via register
761f21baf24SMukesh Sisodiya 	 * or context info
762f21baf24SMukesh Sisodiya 	 */
763f21baf24SMukesh Sisodiya 	if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)
764f21baf24SMukesh Sisodiya 		fw_mon_idx++;
765f21baf24SMukesh Sisodiya 
766f21baf24SMukesh Sisodiya 	remain_frags = fw_mon->num_frags - fw_mon_idx;
767f21baf24SMukesh Sisodiya 	if (!remain_frags)
768f21baf24SMukesh Sisodiya 		return -1;
769f21baf24SMukesh Sisodiya 
770f21baf24SMukesh Sisodiya 	num_frags = min_t(u32, remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);
771f21baf24SMukesh Sisodiya 	data = &dram_info->dram_frags[alloc_id - 1];
772f21baf24SMukesh Sisodiya 	data->alloc_id = cpu_to_le32(alloc_id);
773f21baf24SMukesh Sisodiya 	data->num_frags = cpu_to_le32(num_frags);
774f21baf24SMukesh Sisodiya 	data->buf_location = cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH);
775f21baf24SMukesh Sisodiya 
776f21baf24SMukesh Sisodiya 	IWL_DEBUG_FW(fwrt, "WRT: DRAM buffer details alloc_id=%u, num_frags=%u\n",
777f21baf24SMukesh Sisodiya 		     cpu_to_le32(alloc_id), cpu_to_le32(num_frags));
778f21baf24SMukesh Sisodiya 
779f21baf24SMukesh Sisodiya 	for (j = 0; j < num_frags; j++) {
780f21baf24SMukesh Sisodiya 		struct iwl_buf_alloc_frag *frag = &data->frags[j];
781f21baf24SMukesh Sisodiya 		struct iwl_dram_data *fw_mon_frag = &fw_mon->frags[fw_mon_idx++];
782f21baf24SMukesh Sisodiya 
783f21baf24SMukesh Sisodiya 		frag->addr = cpu_to_le64(fw_mon_frag->physical);
784f21baf24SMukesh Sisodiya 		frag->size = cpu_to_le32(fw_mon_frag->size);
785f21baf24SMukesh Sisodiya 		IWL_DEBUG_FW(fwrt, "WRT: DRAM fragment details\n");
786f21baf24SMukesh Sisodiya 		IWL_DEBUG_FW(fwrt, "frag=%u, addr=0x%016llx, size=0x%x)\n",
787f21baf24SMukesh Sisodiya 			     j, cpu_to_le64(fw_mon_frag->physical),
788f21baf24SMukesh Sisodiya 			     cpu_to_le32(fw_mon_frag->size));
789f21baf24SMukesh Sisodiya 	}
790f21baf24SMukesh Sisodiya 	return 0;
791f21baf24SMukesh Sisodiya }
792f21baf24SMukesh Sisodiya 
iwl_dbg_tlv_update_drams(struct iwl_fw_runtime * fwrt)793f21baf24SMukesh Sisodiya static void iwl_dbg_tlv_update_drams(struct iwl_fw_runtime *fwrt)
794f21baf24SMukesh Sisodiya {
795c76c8309SJohannes Berg 	int ret, i;
796c76c8309SJohannes Berg 	bool dram_alloc = false;
797f21baf24SMukesh Sisodiya 	struct iwl_dram_data *frags =
798f21baf24SMukesh Sisodiya 		&fwrt->trans->dbg.fw_mon_ini[IWL_FW_INI_ALLOCATION_ID_DBGC1].frags[0];
799c76c8309SJohannes Berg 	struct iwl_dram_info *dram_info;
800c76c8309SJohannes Berg 
801c76c8309SJohannes Berg 	if (!frags || !frags->block)
802c76c8309SJohannes Berg 		return;
803c76c8309SJohannes Berg 
804c76c8309SJohannes Berg 	dram_info = frags->block;
805f21baf24SMukesh Sisodiya 
806f21baf24SMukesh Sisodiya 	if (!fw_has_capa(&fwrt->fw->ucode_capa,
807f21baf24SMukesh Sisodiya 			 IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT))
808f21baf24SMukesh Sisodiya 		return;
809f21baf24SMukesh Sisodiya 
810c2a2f505SJohannes Berg 	memset(dram_info, 0, sizeof(*dram_info));
811f21baf24SMukesh Sisodiya 
812f21baf24SMukesh Sisodiya 	for (i = IWL_FW_INI_ALLOCATION_ID_DBGC1;
8130323f194SRotem Saado 	     i < IWL_FW_INI_ALLOCATION_NUM; i++) {
8143278c42bSBenjamin Berg 		if (fwrt->trans->dbg.fw_mon_cfg[i].buf_location ==
8153278c42bSBenjamin Berg 				IWL_FW_INI_LOCATION_INVALID)
8163278c42bSBenjamin Berg 			continue;
8173278c42bSBenjamin Berg 
818c76c8309SJohannes Berg 		ret = iwl_dbg_tlv_update_dram(fwrt, i, dram_info);
819f21baf24SMukesh Sisodiya 		if (!ret)
820c76c8309SJohannes Berg 			dram_alloc = true;
821f21baf24SMukesh Sisodiya 		else
8229ec71b52SGolan Ben Ami 			IWL_INFO(fwrt,
823f21baf24SMukesh Sisodiya 				 "WRT: Failed to set DRAM buffer for alloc id %d, ret=%d\n",
824f21baf24SMukesh Sisodiya 				 i, ret);
825f21baf24SMukesh Sisodiya 	}
826c76c8309SJohannes Berg 
827c2a2f505SJohannes Berg 	if (dram_alloc) {
828c2a2f505SJohannes Berg 		dram_info->first_word = cpu_to_le32(DRAM_INFO_FIRST_MAGIC_WORD);
829c2a2f505SJohannes Berg 		dram_info->second_word = cpu_to_le32(DRAM_INFO_SECOND_MAGIC_WORD);
830c2a2f505SJohannes Berg 	}
831f21baf24SMukesh Sisodiya }
832f21baf24SMukesh Sisodiya 
iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime * fwrt,struct list_head * hcmd_list)833cf29c5b6SShahar S Matityahu static void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime *fwrt,
834cf29c5b6SShahar S Matityahu 				   struct list_head *hcmd_list)
835cf29c5b6SShahar S Matityahu {
836cf29c5b6SShahar S Matityahu 	struct iwl_dbg_tlv_node *node;
837cf29c5b6SShahar S Matityahu 
838cf29c5b6SShahar S Matityahu 	list_for_each_entry(node, hcmd_list, list) {
839cf29c5b6SShahar S Matityahu 		struct iwl_fw_ini_hcmd_tlv *hcmd = (void *)node->tlv.data;
840cf29c5b6SShahar S Matityahu 		struct iwl_fw_ini_hcmd *hcmd_data = &hcmd->hcmd;
841cf29c5b6SShahar S Matityahu 		u16 hcmd_len = le32_to_cpu(node->tlv.length) - sizeof(*hcmd);
842cf29c5b6SShahar S Matityahu 		struct iwl_host_cmd cmd = {
843cf29c5b6SShahar S Matityahu 			.id = WIDE_ID(hcmd_data->group, hcmd_data->id),
844cf29c5b6SShahar S Matityahu 			.len = { hcmd_len, },
845cf29c5b6SShahar S Matityahu 			.data = { hcmd_data->data, },
846cf29c5b6SShahar S Matityahu 		};
847cf29c5b6SShahar S Matityahu 
848cf29c5b6SShahar S Matityahu 		iwl_trans_send_cmd(fwrt->trans, &cmd);
849cf29c5b6SShahar S Matityahu 	}
850cf29c5b6SShahar S Matityahu }
851cf29c5b6SShahar S Matityahu 
iwl_dbg_tlv_apply_config(struct iwl_fw_runtime * fwrt,struct list_head * conf_list)852f21baf24SMukesh Sisodiya static void iwl_dbg_tlv_apply_config(struct iwl_fw_runtime *fwrt,
853d4530f63SJohannes Berg 				     struct list_head *conf_list)
854f21baf24SMukesh Sisodiya {
855f21baf24SMukesh Sisodiya 	struct iwl_dbg_tlv_node *node;
856f21baf24SMukesh Sisodiya 
857d4530f63SJohannes Berg 	list_for_each_entry(node, conf_list, list) {
858f21baf24SMukesh Sisodiya 		struct iwl_fw_ini_conf_set_tlv *config_list = (void *)node->tlv.data;
8591a5daeadSMukesh Sisodiya 		u32 count, address, value;
8601a5daeadSMukesh Sisodiya 		u32 len = (le32_to_cpu(node->tlv.length) - sizeof(*config_list)) / 8;
861f21baf24SMukesh Sisodiya 		u32 type = le32_to_cpu(config_list->set_type);
8621a5daeadSMukesh Sisodiya 		u32 offset = le32_to_cpu(config_list->addr_offset);
863f21baf24SMukesh Sisodiya 
864f21baf24SMukesh Sisodiya 		switch (type) {
8651a5daeadSMukesh Sisodiya 		case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_PERIPHERY_MAC: {
8661a5daeadSMukesh Sisodiya 			if (!iwl_trans_grab_nic_access(fwrt->trans)) {
8671a5daeadSMukesh Sisodiya 				IWL_DEBUG_FW(fwrt, "WRT: failed to get nic access\n");
8681a5daeadSMukesh Sisodiya 				IWL_DEBUG_FW(fwrt, "WRT: skipping MAC PERIPHERY config\n");
8691a5daeadSMukesh Sisodiya 				continue;
8701a5daeadSMukesh Sisodiya 			}
8711a5daeadSMukesh Sisodiya 			IWL_DEBUG_FW(fwrt, "WRT:  MAC PERIPHERY config len: len %u\n", len);
8721a5daeadSMukesh Sisodiya 			for (count = 0; count < len; count++) {
8731a5daeadSMukesh Sisodiya 				address = le32_to_cpu(config_list->addr_val[count].address);
8741a5daeadSMukesh Sisodiya 				value = le32_to_cpu(config_list->addr_val[count].value);
8751a5daeadSMukesh Sisodiya 				iwl_trans_write_prph(fwrt->trans, address + offset, value);
8761a5daeadSMukesh Sisodiya 			}
8771a5daeadSMukesh Sisodiya 			iwl_trans_release_nic_access(fwrt->trans);
8781a5daeadSMukesh Sisodiya 		break;
8791a5daeadSMukesh Sisodiya 		}
8801a5daeadSMukesh Sisodiya 		case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_MEMORY: {
8811a5daeadSMukesh Sisodiya 			for (count = 0; count < len; count++) {
8821a5daeadSMukesh Sisodiya 				address = le32_to_cpu(config_list->addr_val[count].address);
8831a5daeadSMukesh Sisodiya 				value = le32_to_cpu(config_list->addr_val[count].value);
8841a5daeadSMukesh Sisodiya 				iwl_trans_write_mem32(fwrt->trans, address + offset, value);
8851a5daeadSMukesh Sisodiya 				IWL_DEBUG_FW(fwrt, "WRT: DEV_MEM: count %u, add: %u val: %u\n",
8861a5daeadSMukesh Sisodiya 					     count, address, value);
8871a5daeadSMukesh Sisodiya 			}
8881a5daeadSMukesh Sisodiya 		break;
8891a5daeadSMukesh Sisodiya 		}
8901a5daeadSMukesh Sisodiya 		case IWL_FW_INI_CONFIG_SET_TYPE_CSR: {
8911a5daeadSMukesh Sisodiya 			for (count = 0; count < len; count++) {
8921a5daeadSMukesh Sisodiya 				address = le32_to_cpu(config_list->addr_val[count].address);
8931a5daeadSMukesh Sisodiya 				value = le32_to_cpu(config_list->addr_val[count].value);
8941a5daeadSMukesh Sisodiya 				iwl_write32(fwrt->trans, address + offset, value);
8951a5daeadSMukesh Sisodiya 				IWL_DEBUG_FW(fwrt, "WRT: CSR: count %u, add: %u val: %u\n",
8961a5daeadSMukesh Sisodiya 					     count, address, value);
8971a5daeadSMukesh Sisodiya 			}
8981a5daeadSMukesh Sisodiya 		break;
8991a5daeadSMukesh Sisodiya 		}
9001a5daeadSMukesh Sisodiya 		case IWL_FW_INI_CONFIG_SET_TYPE_DBGC_DRAM_ADDR: {
9011a5daeadSMukesh Sisodiya 			struct iwl_dbgc1_info dram_info = {};
9021a5daeadSMukesh Sisodiya 			struct iwl_dram_data *frags = &fwrt->trans->dbg.fw_mon_ini[1].frags[0];
903e2d53d10SMukesh Sisodiya 			__le64 dram_base_addr;
904e2d53d10SMukesh Sisodiya 			__le32 dram_size;
905e2d53d10SMukesh Sisodiya 			u64 dram_addr;
9061a5daeadSMukesh Sisodiya 			u32 ret;
9071a5daeadSMukesh Sisodiya 
908e2d53d10SMukesh Sisodiya 			if (!frags)
909e2d53d10SMukesh Sisodiya 				break;
910e2d53d10SMukesh Sisodiya 
911e2d53d10SMukesh Sisodiya 			dram_base_addr = cpu_to_le64(frags->physical);
912e2d53d10SMukesh Sisodiya 			dram_size = cpu_to_le32(frags->size);
913e2d53d10SMukesh Sisodiya 			dram_addr = le64_to_cpu(dram_base_addr);
914e2d53d10SMukesh Sisodiya 
9151a5daeadSMukesh Sisodiya 			IWL_DEBUG_FW(fwrt, "WRT: dram_base_addr 0x%016llx, dram_size 0x%x\n",
9161a5daeadSMukesh Sisodiya 				     dram_base_addr, dram_size);
9171a5daeadSMukesh Sisodiya 			IWL_DEBUG_FW(fwrt, "WRT: config_list->addr_offset: %u\n",
9181a5daeadSMukesh Sisodiya 				     le32_to_cpu(config_list->addr_offset));
9191a5daeadSMukesh Sisodiya 			for (count = 0; count < len; count++) {
9201a5daeadSMukesh Sisodiya 				address = le32_to_cpu(config_list->addr_val[count].address);
9211a5daeadSMukesh Sisodiya 				dram_info.dbgc1_add_lsb =
9221a5daeadSMukesh Sisodiya 					cpu_to_le32((dram_addr & 0x00000000FFFFFFFFULL) + 0x400);
9231a5daeadSMukesh Sisodiya 				dram_info.dbgc1_add_msb =
9241a5daeadSMukesh Sisodiya 					cpu_to_le32((dram_addr & 0xFFFFFFFF00000000ULL) >> 32);
9251a5daeadSMukesh Sisodiya 				dram_info.dbgc1_size = cpu_to_le32(le32_to_cpu(dram_size) - 0x400);
9261a5daeadSMukesh Sisodiya 				ret = iwl_trans_write_mem(fwrt->trans,
9271a5daeadSMukesh Sisodiya 							  address + offset, &dram_info, 4);
9281a5daeadSMukesh Sisodiya 				if (ret) {
9291a5daeadSMukesh Sisodiya 					IWL_ERR(fwrt, "Failed to write dram_info to HW_SMEM\n");
9301a5daeadSMukesh Sisodiya 					break;
9311a5daeadSMukesh Sisodiya 				}
9321a5daeadSMukesh Sisodiya 			}
9331a5daeadSMukesh Sisodiya 			break;
9341a5daeadSMukesh Sisodiya 		}
935f21baf24SMukesh Sisodiya 		case IWL_FW_INI_CONFIG_SET_TYPE_PERIPH_SCRATCH_HWM: {
936f21baf24SMukesh Sisodiya 			u32 debug_token_config =
937f21baf24SMukesh Sisodiya 				le32_to_cpu(config_list->addr_val[0].value);
938f21baf24SMukesh Sisodiya 
939f21baf24SMukesh Sisodiya 			IWL_DEBUG_FW(fwrt, "WRT: Setting HWM debug token config: %u\n",
940f21baf24SMukesh Sisodiya 				     debug_token_config);
941f21baf24SMukesh Sisodiya 			fwrt->trans->dbg.ucode_preset = debug_token_config;
942f21baf24SMukesh Sisodiya 			break;
943f21baf24SMukesh Sisodiya 		}
944f21baf24SMukesh Sisodiya 		default:
945f21baf24SMukesh Sisodiya 			break;
946f21baf24SMukesh Sisodiya 		}
947f21baf24SMukesh Sisodiya 	}
948f21baf24SMukesh Sisodiya }
949f21baf24SMukesh Sisodiya 
iwl_dbg_tlv_periodic_trig_handler(struct timer_list * t)95060e8abd9SShahar S Matityahu static void iwl_dbg_tlv_periodic_trig_handler(struct timer_list *t)
95160e8abd9SShahar S Matityahu {
95260e8abd9SShahar S Matityahu 	struct iwl_dbg_tlv_timer_node *timer_node =
95360e8abd9SShahar S Matityahu 		from_timer(timer_node, t, timer);
95460e8abd9SShahar S Matityahu 	struct iwl_fwrt_dump_data dump_data = {
95560e8abd9SShahar S Matityahu 		.trig = (void *)timer_node->tlv->data,
95660e8abd9SShahar S Matityahu 	};
95760e8abd9SShahar S Matityahu 	int ret;
95860e8abd9SShahar S Matityahu 
959b8221b0fSJohannes Berg 	ret = iwl_fw_dbg_ini_collect(timer_node->fwrt, &dump_data, false);
96060e8abd9SShahar S Matityahu 	if (!ret || ret == -EBUSY) {
96160e8abd9SShahar S Matityahu 		u32 occur = le32_to_cpu(dump_data.trig->occurrences);
96260e8abd9SShahar S Matityahu 		u32 collect_interval = le32_to_cpu(dump_data.trig->data[0]);
96360e8abd9SShahar S Matityahu 
96460e8abd9SShahar S Matityahu 		if (!occur)
96560e8abd9SShahar S Matityahu 			return;
96660e8abd9SShahar S Matityahu 
96760e8abd9SShahar S Matityahu 		mod_timer(t, jiffies + msecs_to_jiffies(collect_interval));
96860e8abd9SShahar S Matityahu 	}
96960e8abd9SShahar S Matityahu }
97060e8abd9SShahar S Matityahu 
iwl_dbg_tlv_set_periodic_trigs(struct iwl_fw_runtime * fwrt)97160e8abd9SShahar S Matityahu static void iwl_dbg_tlv_set_periodic_trigs(struct iwl_fw_runtime *fwrt)
97260e8abd9SShahar S Matityahu {
97360e8abd9SShahar S Matityahu 	struct iwl_dbg_tlv_node *node;
97460e8abd9SShahar S Matityahu 	struct list_head *trig_list =
97560e8abd9SShahar S Matityahu 		&fwrt->trans->dbg.time_point[IWL_FW_INI_TIME_POINT_PERIODIC].active_trig_list;
97660e8abd9SShahar S Matityahu 
97760e8abd9SShahar S Matityahu 	list_for_each_entry(node, trig_list, list) {
97860e8abd9SShahar S Matityahu 		struct iwl_fw_ini_trigger_tlv *trig = (void *)node->tlv.data;
97960e8abd9SShahar S Matityahu 		struct iwl_dbg_tlv_timer_node *timer_node;
98060e8abd9SShahar S Matityahu 		u32 occur = le32_to_cpu(trig->occurrences), collect_interval;
98160e8abd9SShahar S Matityahu 		u32 min_interval = 100;
98260e8abd9SShahar S Matityahu 
98360e8abd9SShahar S Matityahu 		if (!occur)
98460e8abd9SShahar S Matityahu 			continue;
98560e8abd9SShahar S Matityahu 
98660e8abd9SShahar S Matityahu 		/* make sure there is at least one dword of data for the
98760e8abd9SShahar S Matityahu 		 * interval value
98860e8abd9SShahar S Matityahu 		 */
98960e8abd9SShahar S Matityahu 		if (le32_to_cpu(node->tlv.length) <
99060e8abd9SShahar S Matityahu 		    sizeof(*trig) + sizeof(__le32)) {
99160e8abd9SShahar S Matityahu 			IWL_ERR(fwrt,
99260e8abd9SShahar S Matityahu 				"WRT: Invalid periodic trigger data was not given\n");
99360e8abd9SShahar S Matityahu 			continue;
99460e8abd9SShahar S Matityahu 		}
99560e8abd9SShahar S Matityahu 
99660e8abd9SShahar S Matityahu 		if (le32_to_cpu(trig->data[0]) < min_interval) {
99760e8abd9SShahar S Matityahu 			IWL_WARN(fwrt,
99860e8abd9SShahar S Matityahu 				 "WRT: Override min interval from %u to %u msec\n",
99960e8abd9SShahar S Matityahu 				 le32_to_cpu(trig->data[0]), min_interval);
100060e8abd9SShahar S Matityahu 			trig->data[0] = cpu_to_le32(min_interval);
100160e8abd9SShahar S Matityahu 		}
100260e8abd9SShahar S Matityahu 
100360e8abd9SShahar S Matityahu 		collect_interval = le32_to_cpu(trig->data[0]);
100460e8abd9SShahar S Matityahu 
100560e8abd9SShahar S Matityahu 		timer_node = kzalloc(sizeof(*timer_node), GFP_KERNEL);
100660e8abd9SShahar S Matityahu 		if (!timer_node) {
100760e8abd9SShahar S Matityahu 			IWL_ERR(fwrt,
100860e8abd9SShahar S Matityahu 				"WRT: Failed to allocate periodic trigger\n");
100960e8abd9SShahar S Matityahu 			continue;
101060e8abd9SShahar S Matityahu 		}
101160e8abd9SShahar S Matityahu 
101260e8abd9SShahar S Matityahu 		timer_node->fwrt = fwrt;
101360e8abd9SShahar S Matityahu 		timer_node->tlv = &node->tlv;
101460e8abd9SShahar S Matityahu 		timer_setup(&timer_node->timer,
101560e8abd9SShahar S Matityahu 			    iwl_dbg_tlv_periodic_trig_handler, 0);
101660e8abd9SShahar S Matityahu 
101760e8abd9SShahar S Matityahu 		list_add_tail(&timer_node->list,
101860e8abd9SShahar S Matityahu 			      &fwrt->trans->dbg.periodic_trig_list);
101960e8abd9SShahar S Matityahu 
102060e8abd9SShahar S Matityahu 		IWL_DEBUG_FW(fwrt, "WRT: Enabling periodic trigger\n");
102160e8abd9SShahar S Matityahu 
102260e8abd9SShahar S Matityahu 		mod_timer(&timer_node->timer,
102360e8abd9SShahar S Matityahu 			  jiffies + msecs_to_jiffies(collect_interval));
102460e8abd9SShahar S Matityahu 	}
102560e8abd9SShahar S Matityahu }
102660e8abd9SShahar S Matityahu 
is_trig_data_contained(const struct iwl_ucode_tlv * new,const struct iwl_ucode_tlv * old)1027403ea939STakashi Iwai static bool is_trig_data_contained(const struct iwl_ucode_tlv *new,
1028403ea939STakashi Iwai 				   const struct iwl_ucode_tlv *old)
1029cf29c5b6SShahar S Matityahu {
1030403ea939STakashi Iwai 	const struct iwl_fw_ini_trigger_tlv *new_trig = (const void *)new->data;
1031403ea939STakashi Iwai 	const struct iwl_fw_ini_trigger_tlv *old_trig = (const void *)old->data;
1032403ea939STakashi Iwai 	const __le32 *new_data = new_trig->data, *old_data = old_trig->data;
1033cf29c5b6SShahar S Matityahu 	u32 new_dwords_num = iwl_tlv_array_len(new, new_trig, data);
103458a1c9f9SJohannes Berg 	u32 old_dwords_num = iwl_tlv_array_len(old, old_trig, data);
1035cf29c5b6SShahar S Matityahu 	int i, j;
1036cf29c5b6SShahar S Matityahu 
1037cf29c5b6SShahar S Matityahu 	for (i = 0; i < new_dwords_num; i++) {
1038cf29c5b6SShahar S Matityahu 		bool match = false;
1039cf29c5b6SShahar S Matityahu 
1040cf29c5b6SShahar S Matityahu 		for (j = 0; j < old_dwords_num; j++) {
1041cf29c5b6SShahar S Matityahu 			if (new_data[i] == old_data[j]) {
1042cf29c5b6SShahar S Matityahu 				match = true;
1043cf29c5b6SShahar S Matityahu 				break;
1044cf29c5b6SShahar S Matityahu 			}
1045cf29c5b6SShahar S Matityahu 		}
1046cf29c5b6SShahar S Matityahu 		if (!match)
1047cf29c5b6SShahar S Matityahu 			return false;
1048cf29c5b6SShahar S Matityahu 	}
1049cf29c5b6SShahar S Matityahu 
1050cf29c5b6SShahar S Matityahu 	return true;
1051cf29c5b6SShahar S Matityahu }
1052cf29c5b6SShahar S Matityahu 
iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime * fwrt,struct iwl_ucode_tlv * trig_tlv,struct iwl_dbg_tlv_node * node)1053cf29c5b6SShahar S Matityahu static int iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime *fwrt,
1054cf29c5b6SShahar S Matityahu 					  struct iwl_ucode_tlv *trig_tlv,
1055cf29c5b6SShahar S Matityahu 					  struct iwl_dbg_tlv_node *node)
1056cf29c5b6SShahar S Matityahu {
1057cf29c5b6SShahar S Matityahu 	struct iwl_ucode_tlv *node_tlv = &node->tlv;
1058cf29c5b6SShahar S Matityahu 	struct iwl_fw_ini_trigger_tlv *node_trig = (void *)node_tlv->data;
1059cf29c5b6SShahar S Matityahu 	struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;
1060cf29c5b6SShahar S Matityahu 	u32 policy = le32_to_cpu(trig->apply_policy);
1061cf29c5b6SShahar S Matityahu 	u32 size = le32_to_cpu(trig_tlv->length);
1062cf29c5b6SShahar S Matityahu 	u32 trig_data_len = size - sizeof(*trig);
1063cf29c5b6SShahar S Matityahu 	u32 offset = 0;
1064cf29c5b6SShahar S Matityahu 
1065cf29c5b6SShahar S Matityahu 	if (!(policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA)) {
1066cf29c5b6SShahar S Matityahu 		u32 data_len = le32_to_cpu(node_tlv->length) -
1067cf29c5b6SShahar S Matityahu 			sizeof(*node_trig);
1068cf29c5b6SShahar S Matityahu 
1069cf29c5b6SShahar S Matityahu 		IWL_DEBUG_FW(fwrt,
1070cf29c5b6SShahar S Matityahu 			     "WRT: Appending trigger data (time point %u)\n",
1071cf29c5b6SShahar S Matityahu 			     le32_to_cpu(trig->time_point));
1072cf29c5b6SShahar S Matityahu 
1073cf29c5b6SShahar S Matityahu 		offset += data_len;
1074cf29c5b6SShahar S Matityahu 		size += data_len;
1075cf29c5b6SShahar S Matityahu 	} else {
1076cf29c5b6SShahar S Matityahu 		IWL_DEBUG_FW(fwrt,
1077cf29c5b6SShahar S Matityahu 			     "WRT: Overriding trigger data (time point %u)\n",
1078cf29c5b6SShahar S Matityahu 			     le32_to_cpu(trig->time_point));
1079cf29c5b6SShahar S Matityahu 	}
1080cf29c5b6SShahar S Matityahu 
1081cf29c5b6SShahar S Matityahu 	if (size != le32_to_cpu(node_tlv->length)) {
1082cf29c5b6SShahar S Matityahu 		struct list_head *prev = node->list.prev;
1083cf29c5b6SShahar S Matityahu 		struct iwl_dbg_tlv_node *tmp;
1084cf29c5b6SShahar S Matityahu 
1085cf29c5b6SShahar S Matityahu 		list_del(&node->list);
1086cf29c5b6SShahar S Matityahu 
1087cf29c5b6SShahar S Matityahu 		tmp = krealloc(node, sizeof(*node) + size, GFP_KERNEL);
1088cf29c5b6SShahar S Matityahu 		if (!tmp) {
1089cf29c5b6SShahar S Matityahu 			IWL_WARN(fwrt,
1090cf29c5b6SShahar S Matityahu 				 "WRT: No memory to override trigger (time point %u)\n",
1091cf29c5b6SShahar S Matityahu 				 le32_to_cpu(trig->time_point));
1092cf29c5b6SShahar S Matityahu 
1093cf29c5b6SShahar S Matityahu 			list_add(&node->list, prev);
1094cf29c5b6SShahar S Matityahu 
1095cf29c5b6SShahar S Matityahu 			return -ENOMEM;
1096cf29c5b6SShahar S Matityahu 		}
1097cf29c5b6SShahar S Matityahu 
1098cf29c5b6SShahar S Matityahu 		list_add(&tmp->list, prev);
1099cf29c5b6SShahar S Matityahu 		node_tlv = &tmp->tlv;
1100cf29c5b6SShahar S Matityahu 		node_trig = (void *)node_tlv->data;
1101cf29c5b6SShahar S Matityahu 	}
1102cf29c5b6SShahar S Matityahu 
110387017189SEmmanuel Grumbach 	memcpy((u8 *)node_trig->data + offset, trig->data, trig_data_len);
1104cf29c5b6SShahar S Matityahu 	node_tlv->length = cpu_to_le32(size);
1105cf29c5b6SShahar S Matityahu 
1106cf29c5b6SShahar S Matityahu 	if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG) {
1107cf29c5b6SShahar S Matityahu 		IWL_DEBUG_FW(fwrt,
1108cf29c5b6SShahar S Matityahu 			     "WRT: Overriding trigger configuration (time point %u)\n",
1109cf29c5b6SShahar S Matityahu 			     le32_to_cpu(trig->time_point));
1110cf29c5b6SShahar S Matityahu 
1111cf29c5b6SShahar S Matityahu 		/* the first 11 dwords are configuration related */
1112cf29c5b6SShahar S Matityahu 		memcpy(node_trig, trig, sizeof(__le32) * 11);
1113cf29c5b6SShahar S Matityahu 	}
1114cf29c5b6SShahar S Matityahu 
1115cf29c5b6SShahar S Matityahu 	if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_REGIONS) {
1116cf29c5b6SShahar S Matityahu 		IWL_DEBUG_FW(fwrt,
1117cf29c5b6SShahar S Matityahu 			     "WRT: Overriding trigger regions (time point %u)\n",
1118cf29c5b6SShahar S Matityahu 			     le32_to_cpu(trig->time_point));
1119cf29c5b6SShahar S Matityahu 
1120cf29c5b6SShahar S Matityahu 		node_trig->regions_mask = trig->regions_mask;
1121cf29c5b6SShahar S Matityahu 	} else {
1122cf29c5b6SShahar S Matityahu 		IWL_DEBUG_FW(fwrt,
1123cf29c5b6SShahar S Matityahu 			     "WRT: Appending trigger regions (time point %u)\n",
1124cf29c5b6SShahar S Matityahu 			     le32_to_cpu(trig->time_point));
1125cf29c5b6SShahar S Matityahu 
1126cf29c5b6SShahar S Matityahu 		node_trig->regions_mask |= trig->regions_mask;
1127cf29c5b6SShahar S Matityahu 	}
1128cf29c5b6SShahar S Matityahu 
1129cf29c5b6SShahar S Matityahu 	return 0;
1130cf29c5b6SShahar S Matityahu }
1131cf29c5b6SShahar S Matityahu 
1132cf29c5b6SShahar S Matityahu static int
iwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime * fwrt,struct list_head * trig_list,struct iwl_ucode_tlv * trig_tlv)1133cf29c5b6SShahar S Matityahu iwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime *fwrt,
1134cf29c5b6SShahar S Matityahu 			       struct list_head *trig_list,
1135cf29c5b6SShahar S Matityahu 			       struct iwl_ucode_tlv *trig_tlv)
1136cf29c5b6SShahar S Matityahu {
1137cf29c5b6SShahar S Matityahu 	struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;
1138cf29c5b6SShahar S Matityahu 	struct iwl_dbg_tlv_node *node, *match = NULL;
1139cf29c5b6SShahar S Matityahu 	u32 policy = le32_to_cpu(trig->apply_policy);
1140cf29c5b6SShahar S Matityahu 
1141cf29c5b6SShahar S Matityahu 	list_for_each_entry(node, trig_list, list) {
1142cf29c5b6SShahar S Matityahu 		if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT))
1143cf29c5b6SShahar S Matityahu 			break;
1144cf29c5b6SShahar S Matityahu 
1145cf29c5b6SShahar S Matityahu 		if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_DATA) ||
1146cf29c5b6SShahar S Matityahu 		    is_trig_data_contained(trig_tlv, &node->tlv)) {
1147cf29c5b6SShahar S Matityahu 			match = node;
1148cf29c5b6SShahar S Matityahu 			break;
1149cf29c5b6SShahar S Matityahu 		}
1150cf29c5b6SShahar S Matityahu 	}
1151cf29c5b6SShahar S Matityahu 
1152cf29c5b6SShahar S Matityahu 	if (!match) {
1153cf29c5b6SShahar S Matityahu 		IWL_DEBUG_FW(fwrt, "WRT: Enabling trigger (time point %u)\n",
1154cf29c5b6SShahar S Matityahu 			     le32_to_cpu(trig->time_point));
1155cf29c5b6SShahar S Matityahu 		return iwl_dbg_tlv_add(trig_tlv, trig_list);
1156cf29c5b6SShahar S Matityahu 	}
1157cf29c5b6SShahar S Matityahu 
1158cf29c5b6SShahar S Matityahu 	return iwl_dbg_tlv_override_trig_node(fwrt, trig_tlv, match);
1159cf29c5b6SShahar S Matityahu }
1160cf29c5b6SShahar S Matityahu 
1161cf29c5b6SShahar S Matityahu static void
iwl_dbg_tlv_gen_active_trig_list(struct iwl_fw_runtime * fwrt,struct iwl_dbg_tlv_time_point_data * tp)1162cf29c5b6SShahar S Matityahu iwl_dbg_tlv_gen_active_trig_list(struct iwl_fw_runtime *fwrt,
1163cf29c5b6SShahar S Matityahu 				 struct iwl_dbg_tlv_time_point_data *tp)
1164cf29c5b6SShahar S Matityahu {
1165058c411dSLuca Coelho 	struct iwl_dbg_tlv_node *node;
1166cf29c5b6SShahar S Matityahu 	struct list_head *trig_list = &tp->trig_list;
1167cf29c5b6SShahar S Matityahu 	struct list_head *active_trig_list = &tp->active_trig_list;
1168cf29c5b6SShahar S Matityahu 
1169cf29c5b6SShahar S Matityahu 	list_for_each_entry(node, trig_list, list) {
1170cf29c5b6SShahar S Matityahu 		struct iwl_ucode_tlv *tlv = &node->tlv;
1171cf29c5b6SShahar S Matityahu 
1172cf29c5b6SShahar S Matityahu 		iwl_dbg_tlv_add_active_trigger(fwrt, active_trig_list, tlv);
1173cf29c5b6SShahar S Matityahu 	}
1174cf29c5b6SShahar S Matityahu }
1175cf29c5b6SShahar S Matityahu 
iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime * fwrt,struct iwl_fwrt_dump_data * dump_data,union iwl_dbg_tlv_tp_data * tp_data,u32 trig_data)11763ed34fbfSShahar S Matityahu static bool iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime *fwrt,
11773ed34fbfSShahar S Matityahu 				     struct iwl_fwrt_dump_data *dump_data,
11783ed34fbfSShahar S Matityahu 				     union iwl_dbg_tlv_tp_data *tp_data,
11793ed34fbfSShahar S Matityahu 				     u32 trig_data)
11803ed34fbfSShahar S Matityahu {
11813ed34fbfSShahar S Matityahu 	struct iwl_rx_packet *pkt = tp_data->fw_pkt;
11823ed34fbfSShahar S Matityahu 	struct iwl_cmd_header *wanted_hdr = (void *)&trig_data;
11833ed34fbfSShahar S Matityahu 
1184bfdb1571SMordechay Goodstein 	if (pkt && (pkt->hdr.cmd == wanted_hdr->cmd &&
1185bfdb1571SMordechay Goodstein 		    pkt->hdr.group_id == wanted_hdr->group_id)) {
11863ed34fbfSShahar S Matityahu 		struct iwl_rx_packet *fw_pkt =
11873ed34fbfSShahar S Matityahu 			kmemdup(pkt,
11883ed34fbfSShahar S Matityahu 				sizeof(*pkt) + iwl_rx_packet_payload_len(pkt),
11893ed34fbfSShahar S Matityahu 				GFP_ATOMIC);
11903ed34fbfSShahar S Matityahu 
11913ed34fbfSShahar S Matityahu 		if (!fw_pkt)
11923ed34fbfSShahar S Matityahu 			return false;
11933ed34fbfSShahar S Matityahu 
11943ed34fbfSShahar S Matityahu 		dump_data->fw_pkt = fw_pkt;
11953ed34fbfSShahar S Matityahu 
11963ed34fbfSShahar S Matityahu 		return true;
11973ed34fbfSShahar S Matityahu 	}
11983ed34fbfSShahar S Matityahu 
11993ed34fbfSShahar S Matityahu 	return false;
12003ed34fbfSShahar S Matityahu }
12013ed34fbfSShahar S Matityahu 
1202cf29c5b6SShahar S Matityahu static int
iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime * fwrt,bool sync,struct list_head * active_trig_list,union iwl_dbg_tlv_tp_data * tp_data,bool (* data_check)(struct iwl_fw_runtime * fwrt,struct iwl_fwrt_dump_data * dump_data,union iwl_dbg_tlv_tp_data * tp_data,u32 trig_data))1203b8221b0fSJohannes Berg iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,
1204cf29c5b6SShahar S Matityahu 		       struct list_head *active_trig_list,
1205cf29c5b6SShahar S Matityahu 		       union iwl_dbg_tlv_tp_data *tp_data,
1206cf29c5b6SShahar S Matityahu 		       bool (*data_check)(struct iwl_fw_runtime *fwrt,
1207cf29c5b6SShahar S Matityahu 					  struct iwl_fwrt_dump_data *dump_data,
1208cf29c5b6SShahar S Matityahu 					  union iwl_dbg_tlv_tp_data *tp_data,
1209cf29c5b6SShahar S Matityahu 					  u32 trig_data))
1210cf29c5b6SShahar S Matityahu {
1211cf29c5b6SShahar S Matityahu 	struct iwl_dbg_tlv_node *node;
1212cf29c5b6SShahar S Matityahu 
1213cf29c5b6SShahar S Matityahu 	list_for_each_entry(node, active_trig_list, list) {
1214cf29c5b6SShahar S Matityahu 		struct iwl_fwrt_dump_data dump_data = {
1215cf29c5b6SShahar S Matityahu 			.trig = (void *)node->tlv.data,
1216cf29c5b6SShahar S Matityahu 		};
1217cf29c5b6SShahar S Matityahu 		u32 num_data = iwl_tlv_array_len(&node->tlv, dump_data.trig,
1218cf29c5b6SShahar S Matityahu 						 data);
1219cf29c5b6SShahar S Matityahu 		int ret, i;
1220ddb6b76bSMukesh Sisodiya 		u32 tp = le32_to_cpu(dump_data.trig->time_point);
1221ddb6b76bSMukesh Sisodiya 
1222cf29c5b6SShahar S Matityahu 
1223cf29c5b6SShahar S Matityahu 		if (!num_data) {
1224b8221b0fSJohannes Berg 			ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);
1225cf29c5b6SShahar S Matityahu 			if (ret)
1226cf29c5b6SShahar S Matityahu 				return ret;
1227cf29c5b6SShahar S Matityahu 		}
1228cf29c5b6SShahar S Matityahu 
1229cf29c5b6SShahar S Matityahu 		for (i = 0; i < num_data; i++) {
1230cf29c5b6SShahar S Matityahu 			if (!data_check ||
1231cf29c5b6SShahar S Matityahu 			    data_check(fwrt, &dump_data, tp_data,
1232cf29c5b6SShahar S Matityahu 				       le32_to_cpu(dump_data.trig->data[i]))) {
1233b8221b0fSJohannes Berg 				ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);
1234cf29c5b6SShahar S Matityahu 				if (ret)
1235cf29c5b6SShahar S Matityahu 					return ret;
1236cf29c5b6SShahar S Matityahu 
1237cf29c5b6SShahar S Matityahu 				break;
1238cf29c5b6SShahar S Matityahu 			}
1239cf29c5b6SShahar S Matityahu 		}
1240cf29c5b6SShahar S Matityahu 
1241ddb6b76bSMukesh Sisodiya 		fwrt->trans->dbg.restart_required = FALSE;
1242558f874eSMukesh Sisodiya 		IWL_DEBUG_FW(fwrt, "WRT: tp %d, reset_fw %d\n",
1243ddb6b76bSMukesh Sisodiya 			     tp, dump_data.trig->reset_fw);
1244558f874eSMukesh Sisodiya 		IWL_DEBUG_FW(fwrt,
1245558f874eSMukesh Sisodiya 			     "WRT: restart_required %d, last_tp_resetfw %d\n",
1246ddb6b76bSMukesh Sisodiya 			     fwrt->trans->dbg.restart_required,
1247ddb6b76bSMukesh Sisodiya 			     fwrt->trans->dbg.last_tp_resetfw);
1248ddb6b76bSMukesh Sisodiya 
1249ddb6b76bSMukesh Sisodiya 		if (fwrt->trans->trans_cfg->device_family ==
1250ddb6b76bSMukesh Sisodiya 		    IWL_DEVICE_FAMILY_9000) {
1251ddb6b76bSMukesh Sisodiya 			fwrt->trans->dbg.restart_required = TRUE;
1252ddb6b76bSMukesh Sisodiya 		} else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT &&
1253ddb6b76bSMukesh Sisodiya 			   fwrt->trans->dbg.last_tp_resetfw ==
1254ddb6b76bSMukesh Sisodiya 			   IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {
1255ddb6b76bSMukesh Sisodiya 			fwrt->trans->dbg.restart_required = FALSE;
1256ddb6b76bSMukesh Sisodiya 			fwrt->trans->dbg.last_tp_resetfw = 0xFF;
1257ddb6b76bSMukesh Sisodiya 			IWL_DEBUG_FW(fwrt, "WRT: FW_ASSERT due to reset_fw_mode-no restart\n");
1258ddb6b76bSMukesh Sisodiya 		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==
1259ddb6b76bSMukesh Sisodiya 			   IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) {
1260558f874eSMukesh Sisodiya 			IWL_DEBUG_FW(fwrt, "WRT: stop and reload firmware\n");
1261ddb6b76bSMukesh Sisodiya 			fwrt->trans->dbg.restart_required = TRUE;
1262ddb6b76bSMukesh Sisodiya 		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==
1263ddb6b76bSMukesh Sisodiya 			   IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {
1264558f874eSMukesh Sisodiya 			IWL_DEBUG_FW(fwrt,
1265558f874eSMukesh Sisodiya 				     "WRT: stop only and no reload firmware\n");
1266ddb6b76bSMukesh Sisodiya 			fwrt->trans->dbg.restart_required = FALSE;
1267ddb6b76bSMukesh Sisodiya 			fwrt->trans->dbg.last_tp_resetfw =
1268ddb6b76bSMukesh Sisodiya 				le32_to_cpu(dump_data.trig->reset_fw);
1269ddb6b76bSMukesh Sisodiya 		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==
1270ddb6b76bSMukesh Sisodiya 			   IWL_FW_INI_RESET_FW_MODE_NOTHING) {
1271558f874eSMukesh Sisodiya 			IWL_DEBUG_FW(fwrt,
1272ddb6b76bSMukesh Sisodiya 				     "WRT: nothing need to be done after debug collection\n");
1273ddb6b76bSMukesh Sisodiya 		} else {
1274ddb6b76bSMukesh Sisodiya 			IWL_ERR(fwrt, "WRT: wrong resetfw %d\n",
1275ddb6b76bSMukesh Sisodiya 				le32_to_cpu(dump_data.trig->reset_fw));
1276ddb6b76bSMukesh Sisodiya 		}
1277ddb6b76bSMukesh Sisodiya 	}
1278cf29c5b6SShahar S Matityahu 	return 0;
1279cf29c5b6SShahar S Matityahu }
1280cf29c5b6SShahar S Matityahu 
iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime * fwrt)128114124b25SShahar S Matityahu static void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime *fwrt)
128214124b25SShahar S Matityahu {
128314124b25SShahar S Matityahu 	enum iwl_fw_ini_buffer_location *ini_dest = &fwrt->trans->dbg.ini_dest;
128414124b25SShahar S Matityahu 	int ret, i;
1285beb44c0cSMordechay Goodstein 	u32 failed_alloc = 0;
128614124b25SShahar S Matityahu 
128793ae8145SJohannes Berg 	if (*ini_dest == IWL_FW_INI_LOCATION_INVALID) {
12885cbc3108SLuca Coelho 		IWL_DEBUG_FW(fwrt,
12895cbc3108SLuca Coelho 			     "WRT: Generating active triggers list, domain 0x%x\n",
12905cbc3108SLuca Coelho 			     fwrt->trans->dbg.domains_bitmap);
12915cbc3108SLuca Coelho 
12925cbc3108SLuca Coelho 		for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.time_point); i++) {
12935cbc3108SLuca Coelho 			struct iwl_dbg_tlv_time_point_data *tp =
12945cbc3108SLuca Coelho 				&fwrt->trans->dbg.time_point[i];
12955cbc3108SLuca Coelho 
12965cbc3108SLuca Coelho 			iwl_dbg_tlv_gen_active_trig_list(fwrt, tp);
12975cbc3108SLuca Coelho 		}
129893ae8145SJohannes Berg 	} else if (*ini_dest != IWL_FW_INI_LOCATION_DRAM_PATH) {
129993ae8145SJohannes Berg 		/* For DRAM, go through the loop below to clear all the buffers
130093ae8145SJohannes Berg 		 * properly on restart, otherwise garbage may be left there and
130193ae8145SJohannes Berg 		 * leak into new debug dumps.
130293ae8145SJohannes Berg 		 */
130393ae8145SJohannes Berg 		return;
130493ae8145SJohannes Berg 	}
130514124b25SShahar S Matityahu 
130614124b25SShahar S Matityahu 	*ini_dest = IWL_FW_INI_LOCATION_INVALID;
130714124b25SShahar S Matityahu 	for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {
130814124b25SShahar S Matityahu 		struct iwl_fw_ini_allocation_tlv *fw_mon_cfg =
130914124b25SShahar S Matityahu 			&fwrt->trans->dbg.fw_mon_cfg[i];
131014124b25SShahar S Matityahu 		u32 dest = le32_to_cpu(fw_mon_cfg->buf_location);
131114124b25SShahar S Matityahu 
1312d41cdbcdSRotem Saado 		if (dest == IWL_FW_INI_LOCATION_INVALID) {
1313d41cdbcdSRotem Saado 			failed_alloc |= BIT(i);
131414124b25SShahar S Matityahu 			continue;
1315d41cdbcdSRotem Saado 		}
131614124b25SShahar S Matityahu 
131714124b25SShahar S Matityahu 		if (*ini_dest == IWL_FW_INI_LOCATION_INVALID)
131814124b25SShahar S Matityahu 			*ini_dest = dest;
131914124b25SShahar S Matityahu 
132014124b25SShahar S Matityahu 		if (dest != *ini_dest)
132114124b25SShahar S Matityahu 			continue;
132214124b25SShahar S Matityahu 
132314124b25SShahar S Matityahu 		ret = iwl_dbg_tlv_alloc_fragments(fwrt, i);
1324beb44c0cSMordechay Goodstein 
1325beb44c0cSMordechay Goodstein 		if (ret) {
132614124b25SShahar S Matityahu 			IWL_WARN(fwrt,
132714124b25SShahar S Matityahu 				 "WRT: Failed to allocate DRAM buffer for allocation id %d, ret=%d\n",
132814124b25SShahar S Matityahu 				 i, ret);
1329beb44c0cSMordechay Goodstein 			failed_alloc |= BIT(i);
1330beb44c0cSMordechay Goodstein 		}
1331beb44c0cSMordechay Goodstein 	}
1332beb44c0cSMordechay Goodstein 
1333beb44c0cSMordechay Goodstein 	if (!failed_alloc)
1334beb44c0cSMordechay Goodstein 		return;
1335beb44c0cSMordechay Goodstein 
1336beb44c0cSMordechay Goodstein 	for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions) && failed_alloc; i++) {
1337beb44c0cSMordechay Goodstein 		struct iwl_fw_ini_region_tlv *reg;
1338beb44c0cSMordechay Goodstein 		struct iwl_ucode_tlv **active_reg =
1339beb44c0cSMordechay Goodstein 			&fwrt->trans->dbg.active_regions[i];
1340beb44c0cSMordechay Goodstein 		u32 reg_type;
1341beb44c0cSMordechay Goodstein 
134272c43f7dSRotem Saado 		if (!*active_reg) {
134372c43f7dSRotem Saado 			fwrt->trans->dbg.unsupported_region_msk |= BIT(i);
1344beb44c0cSMordechay Goodstein 			continue;
134572c43f7dSRotem Saado 		}
1346beb44c0cSMordechay Goodstein 
1347beb44c0cSMordechay Goodstein 		reg = (void *)(*active_reg)->data;
134862ed5d90SMordechay Goodstein 		reg_type = reg->type;
1349beb44c0cSMordechay Goodstein 
1350beb44c0cSMordechay Goodstein 		if (reg_type != IWL_FW_INI_REGION_DRAM_BUFFER ||
1351beb44c0cSMordechay Goodstein 		    !(BIT(le32_to_cpu(reg->dram_alloc_id)) & failed_alloc))
1352beb44c0cSMordechay Goodstein 			continue;
1353beb44c0cSMordechay Goodstein 
1354beb44c0cSMordechay Goodstein 		IWL_DEBUG_FW(fwrt,
1355beb44c0cSMordechay Goodstein 			     "WRT: removing allocation id %d from region id %d\n",
1356beb44c0cSMordechay Goodstein 			     le32_to_cpu(reg->dram_alloc_id), i);
1357beb44c0cSMordechay Goodstein 
1358ae5ecbb0SRotem Saado 		failed_alloc &= ~BIT(le32_to_cpu(reg->dram_alloc_id));
1359beb44c0cSMordechay Goodstein 		fwrt->trans->dbg.unsupported_region_msk |= BIT(i);
1360beb44c0cSMordechay Goodstein 
1361beb44c0cSMordechay Goodstein 		kfree(*active_reg);
1362beb44c0cSMordechay Goodstein 		*active_reg = NULL;
136314124b25SShahar S Matityahu 	}
136414124b25SShahar S Matityahu }
136514124b25SShahar S Matityahu 
_iwl_dbg_tlv_time_point(struct iwl_fw_runtime * fwrt,enum iwl_fw_ini_time_point tp_id,union iwl_dbg_tlv_tp_data * tp_data,bool sync)1366b8221b0fSJohannes Berg void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
1367b108d8c7SShahar S Matityahu 			     enum iwl_fw_ini_time_point tp_id,
1368b8221b0fSJohannes Berg 			     union iwl_dbg_tlv_tp_data *tp_data,
1369b8221b0fSJohannes Berg 			     bool sync)
137000eacde4SShahar S Matityahu {
1371f21baf24SMukesh Sisodiya 	struct list_head *hcmd_list, *trig_list, *conf_list;
1372cf29c5b6SShahar S Matityahu 
1373cf29c5b6SShahar S Matityahu 	if (!iwl_trans_dbg_ini_valid(fwrt->trans) ||
1374cf29c5b6SShahar S Matityahu 	    tp_id == IWL_FW_INI_TIME_POINT_INVALID ||
1375cf29c5b6SShahar S Matityahu 	    tp_id >= IWL_FW_INI_TIME_POINT_NUM)
1376cf29c5b6SShahar S Matityahu 		return;
1377cf29c5b6SShahar S Matityahu 
1378cf29c5b6SShahar S Matityahu 	hcmd_list = &fwrt->trans->dbg.time_point[tp_id].hcmd_list;
1379cf29c5b6SShahar S Matityahu 	trig_list = &fwrt->trans->dbg.time_point[tp_id].active_trig_list;
1380f21baf24SMukesh Sisodiya 	conf_list = &fwrt->trans->dbg.time_point[tp_id].config_list;
1381cf29c5b6SShahar S Matityahu 
1382cf29c5b6SShahar S Matityahu 	switch (tp_id) {
1383cf29c5b6SShahar S Matityahu 	case IWL_FW_INI_TIME_POINT_EARLY:
138414124b25SShahar S Matityahu 		iwl_dbg_tlv_init_cfg(fwrt);
1385f21baf24SMukesh Sisodiya 		iwl_dbg_tlv_apply_config(fwrt, conf_list);
1386f21baf24SMukesh Sisodiya 		iwl_dbg_tlv_update_drams(fwrt);
1387b8221b0fSJohannes Berg 		iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
138814124b25SShahar S Matityahu 		break;
138914124b25SShahar S Matityahu 	case IWL_FW_INI_TIME_POINT_AFTER_ALIVE:
139014124b25SShahar S Matityahu 		iwl_dbg_tlv_apply_buffers(fwrt);
139114124b25SShahar S Matityahu 		iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
13921a5daeadSMukesh Sisodiya 		iwl_dbg_tlv_apply_config(fwrt, conf_list);
1393b8221b0fSJohannes Berg 		iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
1394cf29c5b6SShahar S Matityahu 		break;
139560e8abd9SShahar S Matityahu 	case IWL_FW_INI_TIME_POINT_PERIODIC:
139660e8abd9SShahar S Matityahu 		iwl_dbg_tlv_set_periodic_trigs(fwrt);
139760e8abd9SShahar S Matityahu 		iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
139860e8abd9SShahar S Matityahu 		break;
13993ed34fbfSShahar S Matityahu 	case IWL_FW_INI_TIME_POINT_FW_RSP_OR_NOTIF:
1400eae7550bSShahar S Matityahu 	case IWL_FW_INI_TIME_POINT_MISSED_BEACONS:
14014bf3d7cfSMordechay Goodstein 	case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION:
14023ed34fbfSShahar S Matityahu 		iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
14031a5daeadSMukesh Sisodiya 		iwl_dbg_tlv_apply_config(fwrt, conf_list);
1404b8221b0fSJohannes Berg 		iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data,
14053ed34fbfSShahar S Matityahu 				       iwl_dbg_tlv_check_fw_pkt);
14063ed34fbfSShahar S Matityahu 		break;
1407cf29c5b6SShahar S Matityahu 	default:
1408cf29c5b6SShahar S Matityahu 		iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
14091a5daeadSMukesh Sisodiya 		iwl_dbg_tlv_apply_config(fwrt, conf_list);
1410b8221b0fSJohannes Berg 		iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
1411cf29c5b6SShahar S Matityahu 		break;
1412cf29c5b6SShahar S Matityahu 	}
141300eacde4SShahar S Matityahu }
1414b8221b0fSJohannes Berg IWL_EXPORT_SYMBOL(_iwl_dbg_tlv_time_point);
1415