xref: /linux/drivers/net/wireless/ath/ath11k/debugfs.c (revision 4f6b838c378a52ea3ae0b15f12ca8a20849072fa)
1da3a9d3cSKalle Valo // SPDX-License-Identifier: BSD-3-Clause-Clear
2da3a9d3cSKalle Valo /*
3da3a9d3cSKalle Valo  * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
4da3a9d3cSKalle Valo  */
5da3a9d3cSKalle Valo 
6da3a9d3cSKalle Valo #include "debugfs.h"
7da3a9d3cSKalle Valo 
8da3a9d3cSKalle Valo #include "core.h"
9da3a9d3cSKalle Valo #include "debug.h"
10da3a9d3cSKalle Valo #include "wmi.h"
11da3a9d3cSKalle Valo #include "hal_rx.h"
12da3a9d3cSKalle Valo #include "dp_tx.h"
1356292162SKalle Valo #include "debugfs_htt_stats.h"
14da3a9d3cSKalle Valo #include "peer.h"
15da3a9d3cSKalle Valo 
16da3a9d3cSKalle Valo static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {
17da3a9d3cSKalle Valo 	"REO2SW1_RING",
18da3a9d3cSKalle Valo 	"REO2SW2_RING",
19da3a9d3cSKalle Valo 	"REO2SW3_RING",
20da3a9d3cSKalle Valo 	"REO2SW4_RING",
21da3a9d3cSKalle Valo 	"WBM2REO_LINK_RING",
22da3a9d3cSKalle Valo 	"REO2TCL_RING",
23da3a9d3cSKalle Valo 	"REO2FW_RING",
24da3a9d3cSKalle Valo 	"RELEASE_RING",
25da3a9d3cSKalle Valo 	"PPE_RELEASE_RING",
26da3a9d3cSKalle Valo 	"TCL2TQM_RING",
27da3a9d3cSKalle Valo 	"TQM_RELEASE_RING",
28da3a9d3cSKalle Valo 	"REO_RELEASE_RING",
29da3a9d3cSKalle Valo 	"WBM2SW0_RELEASE_RING",
30da3a9d3cSKalle Valo 	"WBM2SW1_RELEASE_RING",
31da3a9d3cSKalle Valo 	"WBM2SW2_RELEASE_RING",
32da3a9d3cSKalle Valo 	"WBM2SW3_RELEASE_RING",
33da3a9d3cSKalle Valo 	"REO_CMD_RING",
34da3a9d3cSKalle Valo 	"REO_STATUS_RING",
35da3a9d3cSKalle Valo };
36da3a9d3cSKalle Valo 
37da3a9d3cSKalle Valo static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = {
38da3a9d3cSKalle Valo 	"FW2RXDMA_BUF_RING",
39da3a9d3cSKalle Valo 	"FW2RXDMA_STATUS_RING",
40da3a9d3cSKalle Valo 	"FW2RXDMA_LINK_RING",
41da3a9d3cSKalle Valo 	"SW2RXDMA_BUF_RING",
42da3a9d3cSKalle Valo 	"WBM2RXDMA_LINK_RING",
43da3a9d3cSKalle Valo 	"RXDMA2FW_RING",
44da3a9d3cSKalle Valo 	"RXDMA2SW_RING",
45da3a9d3cSKalle Valo 	"RXDMA2RELEASE_RING",
46da3a9d3cSKalle Valo 	"RXDMA2REO_RING",
47da3a9d3cSKalle Valo 	"MONITOR_STATUS_RING",
48da3a9d3cSKalle Valo 	"MONITOR_BUF_RING",
49da3a9d3cSKalle Valo 	"MONITOR_DESC_RING",
50da3a9d3cSKalle Valo 	"MONITOR_DEST_RING",
51da3a9d3cSKalle Valo };
52da3a9d3cSKalle Valo 
53da3a9d3cSKalle Valo static void ath11k_fw_stats_pdevs_free(struct list_head *head)
54da3a9d3cSKalle Valo {
55da3a9d3cSKalle Valo 	struct ath11k_fw_stats_pdev *i, *tmp;
56da3a9d3cSKalle Valo 
57da3a9d3cSKalle Valo 	list_for_each_entry_safe(i, tmp, head, list) {
58da3a9d3cSKalle Valo 		list_del(&i->list);
59da3a9d3cSKalle Valo 		kfree(i);
60da3a9d3cSKalle Valo 	}
61da3a9d3cSKalle Valo }
62da3a9d3cSKalle Valo 
63da3a9d3cSKalle Valo static void ath11k_fw_stats_vdevs_free(struct list_head *head)
64da3a9d3cSKalle Valo {
65da3a9d3cSKalle Valo 	struct ath11k_fw_stats_vdev *i, *tmp;
66da3a9d3cSKalle Valo 
67da3a9d3cSKalle Valo 	list_for_each_entry_safe(i, tmp, head, list) {
68da3a9d3cSKalle Valo 		list_del(&i->list);
69da3a9d3cSKalle Valo 		kfree(i);
70da3a9d3cSKalle Valo 	}
71da3a9d3cSKalle Valo }
72da3a9d3cSKalle Valo 
73da3a9d3cSKalle Valo static void ath11k_fw_stats_bcn_free(struct list_head *head)
74da3a9d3cSKalle Valo {
75da3a9d3cSKalle Valo 	struct ath11k_fw_stats_bcn *i, *tmp;
76da3a9d3cSKalle Valo 
77da3a9d3cSKalle Valo 	list_for_each_entry_safe(i, tmp, head, list) {
78da3a9d3cSKalle Valo 		list_del(&i->list);
79da3a9d3cSKalle Valo 		kfree(i);
80da3a9d3cSKalle Valo 	}
81da3a9d3cSKalle Valo }
82da3a9d3cSKalle Valo 
83cb4e57dbSKalle Valo static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar)
84da3a9d3cSKalle Valo {
85da3a9d3cSKalle Valo 	spin_lock_bh(&ar->data_lock);
86da3a9d3cSKalle Valo 	ar->debug.fw_stats_done = false;
87da3a9d3cSKalle Valo 	ath11k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
88da3a9d3cSKalle Valo 	ath11k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
89da3a9d3cSKalle Valo 	spin_unlock_bh(&ar->data_lock);
90da3a9d3cSKalle Valo }
91da3a9d3cSKalle Valo 
92cb4e57dbSKalle Valo void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb)
93da3a9d3cSKalle Valo {
94da3a9d3cSKalle Valo 	struct ath11k_fw_stats stats = {};
95da3a9d3cSKalle Valo 	struct ath11k *ar;
96da3a9d3cSKalle Valo 	struct ath11k_pdev *pdev;
97da3a9d3cSKalle Valo 	bool is_end;
98da3a9d3cSKalle Valo 	static unsigned int num_vdev, num_bcn;
99da3a9d3cSKalle Valo 	size_t total_vdevs_started = 0;
100da3a9d3cSKalle Valo 	int i, ret;
101da3a9d3cSKalle Valo 
102da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&stats.pdevs);
103da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&stats.vdevs);
104da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&stats.bcn);
105da3a9d3cSKalle Valo 
106da3a9d3cSKalle Valo 	ret = ath11k_wmi_pull_fw_stats(ab, skb, &stats);
107da3a9d3cSKalle Valo 	if (ret) {
108da3a9d3cSKalle Valo 		ath11k_warn(ab, "failed to pull fw stats: %d\n", ret);
109da3a9d3cSKalle Valo 		goto free;
110da3a9d3cSKalle Valo 	}
111da3a9d3cSKalle Valo 
112da3a9d3cSKalle Valo 	rcu_read_lock();
113da3a9d3cSKalle Valo 	ar = ath11k_mac_get_ar_by_pdev_id(ab, stats.pdev_id);
114da3a9d3cSKalle Valo 	if (!ar) {
115da3a9d3cSKalle Valo 		rcu_read_unlock();
116da3a9d3cSKalle Valo 		ath11k_warn(ab, "failed to get ar for pdev_id %d: %d\n",
117da3a9d3cSKalle Valo 			    stats.pdev_id, ret);
118da3a9d3cSKalle Valo 		goto free;
119da3a9d3cSKalle Valo 	}
120da3a9d3cSKalle Valo 
121da3a9d3cSKalle Valo 	spin_lock_bh(&ar->data_lock);
122da3a9d3cSKalle Valo 
123da3a9d3cSKalle Valo 	if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
124da3a9d3cSKalle Valo 		list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);
125da3a9d3cSKalle Valo 		ar->debug.fw_stats_done = true;
126da3a9d3cSKalle Valo 		goto complete;
127da3a9d3cSKalle Valo 	}
128da3a9d3cSKalle Valo 
129da3a9d3cSKalle Valo 	if (stats.stats_id == WMI_REQUEST_VDEV_STAT) {
130da3a9d3cSKalle Valo 		if (list_empty(&stats.vdevs)) {
131da3a9d3cSKalle Valo 			ath11k_warn(ab, "empty vdev stats");
132da3a9d3cSKalle Valo 			goto complete;
133da3a9d3cSKalle Valo 		}
134da3a9d3cSKalle Valo 		/* FW sends all the active VDEV stats irrespective of PDEV,
135da3a9d3cSKalle Valo 		 * hence limit until the count of all VDEVs started
136da3a9d3cSKalle Valo 		 */
137da3a9d3cSKalle Valo 		for (i = 0; i < ab->num_radios; i++) {
138da3a9d3cSKalle Valo 			pdev = rcu_dereference(ab->pdevs_active[i]);
139da3a9d3cSKalle Valo 			if (pdev && pdev->ar)
140da3a9d3cSKalle Valo 				total_vdevs_started += ar->num_started_vdevs;
141da3a9d3cSKalle Valo 		}
142da3a9d3cSKalle Valo 
143da3a9d3cSKalle Valo 		is_end = ((++num_vdev) == total_vdevs_started);
144da3a9d3cSKalle Valo 
145da3a9d3cSKalle Valo 		list_splice_tail_init(&stats.vdevs,
146da3a9d3cSKalle Valo 				      &ar->debug.fw_stats.vdevs);
147da3a9d3cSKalle Valo 
148da3a9d3cSKalle Valo 		if (is_end) {
149da3a9d3cSKalle Valo 			ar->debug.fw_stats_done = true;
150da3a9d3cSKalle Valo 			num_vdev = 0;
151da3a9d3cSKalle Valo 		}
152da3a9d3cSKalle Valo 		goto complete;
153da3a9d3cSKalle Valo 	}
154da3a9d3cSKalle Valo 
155da3a9d3cSKalle Valo 	if (stats.stats_id == WMI_REQUEST_BCN_STAT) {
156da3a9d3cSKalle Valo 		if (list_empty(&stats.bcn)) {
157da3a9d3cSKalle Valo 			ath11k_warn(ab, "empty bcn stats");
158da3a9d3cSKalle Valo 			goto complete;
159da3a9d3cSKalle Valo 		}
160da3a9d3cSKalle Valo 		/* Mark end until we reached the count of all started VDEVs
161da3a9d3cSKalle Valo 		 * within the PDEV
162da3a9d3cSKalle Valo 		 */
163da3a9d3cSKalle Valo 		is_end = ((++num_bcn) == ar->num_started_vdevs);
164da3a9d3cSKalle Valo 
165da3a9d3cSKalle Valo 		list_splice_tail_init(&stats.bcn,
166da3a9d3cSKalle Valo 				      &ar->debug.fw_stats.bcn);
167da3a9d3cSKalle Valo 
168da3a9d3cSKalle Valo 		if (is_end) {
169da3a9d3cSKalle Valo 			ar->debug.fw_stats_done = true;
170da3a9d3cSKalle Valo 			num_bcn = 0;
171da3a9d3cSKalle Valo 		}
172da3a9d3cSKalle Valo 	}
173da3a9d3cSKalle Valo complete:
174da3a9d3cSKalle Valo 	complete(&ar->debug.fw_stats_complete);
175da3a9d3cSKalle Valo 	rcu_read_unlock();
176da3a9d3cSKalle Valo 	spin_unlock_bh(&ar->data_lock);
177da3a9d3cSKalle Valo 
178da3a9d3cSKalle Valo free:
179da3a9d3cSKalle Valo 	ath11k_fw_stats_pdevs_free(&stats.pdevs);
180da3a9d3cSKalle Valo 	ath11k_fw_stats_vdevs_free(&stats.vdevs);
181da3a9d3cSKalle Valo 	ath11k_fw_stats_bcn_free(&stats.bcn);
182da3a9d3cSKalle Valo }
183da3a9d3cSKalle Valo 
184cb4e57dbSKalle Valo static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,
185da3a9d3cSKalle Valo 					   struct stats_request_params *req_param)
186da3a9d3cSKalle Valo {
187da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
188da3a9d3cSKalle Valo 	unsigned long timeout, time_left;
189da3a9d3cSKalle Valo 	int ret;
190da3a9d3cSKalle Valo 
191da3a9d3cSKalle Valo 	lockdep_assert_held(&ar->conf_mutex);
192da3a9d3cSKalle Valo 
193da3a9d3cSKalle Valo 	/* FW stats can get split when exceeding the stats data buffer limit.
194da3a9d3cSKalle Valo 	 * In that case, since there is no end marking for the back-to-back
195da3a9d3cSKalle Valo 	 * received 'update stats' event, we keep a 3 seconds timeout in case,
196da3a9d3cSKalle Valo 	 * fw_stats_done is not marked yet
197da3a9d3cSKalle Valo 	 */
198da3a9d3cSKalle Valo 	timeout = jiffies + msecs_to_jiffies(3 * HZ);
199da3a9d3cSKalle Valo 
200cb4e57dbSKalle Valo 	ath11k_debugfs_fw_stats_reset(ar);
201da3a9d3cSKalle Valo 
202da3a9d3cSKalle Valo 	reinit_completion(&ar->debug.fw_stats_complete);
203da3a9d3cSKalle Valo 
204da3a9d3cSKalle Valo 	ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
205da3a9d3cSKalle Valo 
206da3a9d3cSKalle Valo 	if (ret) {
207da3a9d3cSKalle Valo 		ath11k_warn(ab, "could not request fw stats (%d)\n",
208da3a9d3cSKalle Valo 			    ret);
209da3a9d3cSKalle Valo 		return ret;
210da3a9d3cSKalle Valo 	}
211da3a9d3cSKalle Valo 
212da3a9d3cSKalle Valo 	time_left =
213da3a9d3cSKalle Valo 	wait_for_completion_timeout(&ar->debug.fw_stats_complete,
214da3a9d3cSKalle Valo 				    1 * HZ);
215da3a9d3cSKalle Valo 	if (!time_left)
216da3a9d3cSKalle Valo 		return -ETIMEDOUT;
217da3a9d3cSKalle Valo 
218da3a9d3cSKalle Valo 	for (;;) {
219da3a9d3cSKalle Valo 		if (time_after(jiffies, timeout))
220da3a9d3cSKalle Valo 			break;
221da3a9d3cSKalle Valo 
222da3a9d3cSKalle Valo 		spin_lock_bh(&ar->data_lock);
223da3a9d3cSKalle Valo 		if (ar->debug.fw_stats_done) {
224da3a9d3cSKalle Valo 			spin_unlock_bh(&ar->data_lock);
225da3a9d3cSKalle Valo 			break;
226da3a9d3cSKalle Valo 		}
227da3a9d3cSKalle Valo 		spin_unlock_bh(&ar->data_lock);
228da3a9d3cSKalle Valo 	}
229da3a9d3cSKalle Valo 	return 0;
230da3a9d3cSKalle Valo }
231da3a9d3cSKalle Valo 
232da3a9d3cSKalle Valo static int ath11k_open_pdev_stats(struct inode *inode, struct file *file)
233da3a9d3cSKalle Valo {
234da3a9d3cSKalle Valo 	struct ath11k *ar = inode->i_private;
235da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
236da3a9d3cSKalle Valo 	struct stats_request_params req_param;
237da3a9d3cSKalle Valo 	void *buf = NULL;
238da3a9d3cSKalle Valo 	int ret;
239da3a9d3cSKalle Valo 
240da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
241da3a9d3cSKalle Valo 
242da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
243da3a9d3cSKalle Valo 		ret = -ENETDOWN;
244da3a9d3cSKalle Valo 		goto err_unlock;
245da3a9d3cSKalle Valo 	}
246da3a9d3cSKalle Valo 
247da3a9d3cSKalle Valo 	buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
248da3a9d3cSKalle Valo 	if (!buf) {
249da3a9d3cSKalle Valo 		ret = -ENOMEM;
250da3a9d3cSKalle Valo 		goto err_unlock;
251da3a9d3cSKalle Valo 	}
252da3a9d3cSKalle Valo 
253da3a9d3cSKalle Valo 	req_param.pdev_id = ar->pdev->pdev_id;
254da3a9d3cSKalle Valo 	req_param.vdev_id = 0;
255da3a9d3cSKalle Valo 	req_param.stats_id = WMI_REQUEST_PDEV_STAT;
256da3a9d3cSKalle Valo 
257cb4e57dbSKalle Valo 	ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
258da3a9d3cSKalle Valo 	if (ret) {
259da3a9d3cSKalle Valo 		ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
260da3a9d3cSKalle Valo 		goto err_free;
261da3a9d3cSKalle Valo 	}
262da3a9d3cSKalle Valo 
263da3a9d3cSKalle Valo 	ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id,
264da3a9d3cSKalle Valo 				 buf);
265da3a9d3cSKalle Valo 
266da3a9d3cSKalle Valo 	file->private_data = buf;
267da3a9d3cSKalle Valo 
268da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
269da3a9d3cSKalle Valo 	return 0;
270da3a9d3cSKalle Valo 
271da3a9d3cSKalle Valo err_free:
272da3a9d3cSKalle Valo 	vfree(buf);
273da3a9d3cSKalle Valo 
274da3a9d3cSKalle Valo err_unlock:
275da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
276da3a9d3cSKalle Valo 	return ret;
277da3a9d3cSKalle Valo }
278da3a9d3cSKalle Valo 
279da3a9d3cSKalle Valo static int ath11k_release_pdev_stats(struct inode *inode, struct file *file)
280da3a9d3cSKalle Valo {
281da3a9d3cSKalle Valo 	vfree(file->private_data);
282da3a9d3cSKalle Valo 
283da3a9d3cSKalle Valo 	return 0;
284da3a9d3cSKalle Valo }
285da3a9d3cSKalle Valo 
286da3a9d3cSKalle Valo static ssize_t ath11k_read_pdev_stats(struct file *file,
287da3a9d3cSKalle Valo 				      char __user *user_buf,
288da3a9d3cSKalle Valo 				      size_t count, loff_t *ppos)
289da3a9d3cSKalle Valo {
290da3a9d3cSKalle Valo 	const char *buf = file->private_data;
291da3a9d3cSKalle Valo 	size_t len = strlen(buf);
292da3a9d3cSKalle Valo 
293da3a9d3cSKalle Valo 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
294da3a9d3cSKalle Valo }
295da3a9d3cSKalle Valo 
296da3a9d3cSKalle Valo static const struct file_operations fops_pdev_stats = {
297da3a9d3cSKalle Valo 	.open = ath11k_open_pdev_stats,
298da3a9d3cSKalle Valo 	.release = ath11k_release_pdev_stats,
299da3a9d3cSKalle Valo 	.read = ath11k_read_pdev_stats,
300da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
301da3a9d3cSKalle Valo 	.llseek = default_llseek,
302da3a9d3cSKalle Valo };
303da3a9d3cSKalle Valo 
304da3a9d3cSKalle Valo static int ath11k_open_vdev_stats(struct inode *inode, struct file *file)
305da3a9d3cSKalle Valo {
306da3a9d3cSKalle Valo 	struct ath11k *ar = inode->i_private;
307da3a9d3cSKalle Valo 	struct stats_request_params req_param;
308da3a9d3cSKalle Valo 	void *buf = NULL;
309da3a9d3cSKalle Valo 	int ret;
310da3a9d3cSKalle Valo 
311da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
312da3a9d3cSKalle Valo 
313da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
314da3a9d3cSKalle Valo 		ret = -ENETDOWN;
315da3a9d3cSKalle Valo 		goto err_unlock;
316da3a9d3cSKalle Valo 	}
317da3a9d3cSKalle Valo 
318da3a9d3cSKalle Valo 	buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
319da3a9d3cSKalle Valo 	if (!buf) {
320da3a9d3cSKalle Valo 		ret = -ENOMEM;
321da3a9d3cSKalle Valo 		goto err_unlock;
322da3a9d3cSKalle Valo 	}
323da3a9d3cSKalle Valo 
324da3a9d3cSKalle Valo 	req_param.pdev_id = ar->pdev->pdev_id;
325da3a9d3cSKalle Valo 	/* VDEV stats is always sent for all active VDEVs from FW */
326da3a9d3cSKalle Valo 	req_param.vdev_id = 0;
327da3a9d3cSKalle Valo 	req_param.stats_id = WMI_REQUEST_VDEV_STAT;
328da3a9d3cSKalle Valo 
329cb4e57dbSKalle Valo 	ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
330da3a9d3cSKalle Valo 	if (ret) {
331da3a9d3cSKalle Valo 		ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);
332da3a9d3cSKalle Valo 		goto err_free;
333da3a9d3cSKalle Valo 	}
334da3a9d3cSKalle Valo 
335da3a9d3cSKalle Valo 	ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id,
336da3a9d3cSKalle Valo 				 buf);
337da3a9d3cSKalle Valo 
338da3a9d3cSKalle Valo 	file->private_data = buf;
339da3a9d3cSKalle Valo 
340da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
341da3a9d3cSKalle Valo 	return 0;
342da3a9d3cSKalle Valo 
343da3a9d3cSKalle Valo err_free:
344da3a9d3cSKalle Valo 	vfree(buf);
345da3a9d3cSKalle Valo 
346da3a9d3cSKalle Valo err_unlock:
347da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
348da3a9d3cSKalle Valo 	return ret;
349da3a9d3cSKalle Valo }
350da3a9d3cSKalle Valo 
351da3a9d3cSKalle Valo static int ath11k_release_vdev_stats(struct inode *inode, struct file *file)
352da3a9d3cSKalle Valo {
353da3a9d3cSKalle Valo 	vfree(file->private_data);
354da3a9d3cSKalle Valo 
355da3a9d3cSKalle Valo 	return 0;
356da3a9d3cSKalle Valo }
357da3a9d3cSKalle Valo 
358da3a9d3cSKalle Valo static ssize_t ath11k_read_vdev_stats(struct file *file,
359da3a9d3cSKalle Valo 				      char __user *user_buf,
360da3a9d3cSKalle Valo 				      size_t count, loff_t *ppos)
361da3a9d3cSKalle Valo {
362da3a9d3cSKalle Valo 	const char *buf = file->private_data;
363da3a9d3cSKalle Valo 	size_t len = strlen(buf);
364da3a9d3cSKalle Valo 
365da3a9d3cSKalle Valo 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
366da3a9d3cSKalle Valo }
367da3a9d3cSKalle Valo 
368da3a9d3cSKalle Valo static const struct file_operations fops_vdev_stats = {
369da3a9d3cSKalle Valo 	.open = ath11k_open_vdev_stats,
370da3a9d3cSKalle Valo 	.release = ath11k_release_vdev_stats,
371da3a9d3cSKalle Valo 	.read = ath11k_read_vdev_stats,
372da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
373da3a9d3cSKalle Valo 	.llseek = default_llseek,
374da3a9d3cSKalle Valo };
375da3a9d3cSKalle Valo 
376da3a9d3cSKalle Valo static int ath11k_open_bcn_stats(struct inode *inode, struct file *file)
377da3a9d3cSKalle Valo {
378da3a9d3cSKalle Valo 	struct ath11k *ar = inode->i_private;
379da3a9d3cSKalle Valo 	struct ath11k_vif *arvif;
380da3a9d3cSKalle Valo 	struct stats_request_params req_param;
381da3a9d3cSKalle Valo 	void *buf = NULL;
382da3a9d3cSKalle Valo 	int ret;
383da3a9d3cSKalle Valo 
384da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
385da3a9d3cSKalle Valo 
386da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
387da3a9d3cSKalle Valo 		ret = -ENETDOWN;
388da3a9d3cSKalle Valo 		goto err_unlock;
389da3a9d3cSKalle Valo 	}
390da3a9d3cSKalle Valo 
391da3a9d3cSKalle Valo 	buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
392da3a9d3cSKalle Valo 	if (!buf) {
393da3a9d3cSKalle Valo 		ret = -ENOMEM;
394da3a9d3cSKalle Valo 		goto err_unlock;
395da3a9d3cSKalle Valo 	}
396da3a9d3cSKalle Valo 
397da3a9d3cSKalle Valo 	req_param.stats_id = WMI_REQUEST_BCN_STAT;
398da3a9d3cSKalle Valo 	req_param.pdev_id = ar->pdev->pdev_id;
399da3a9d3cSKalle Valo 
400da3a9d3cSKalle Valo 	/* loop all active VDEVs for bcn stats */
401da3a9d3cSKalle Valo 	list_for_each_entry(arvif, &ar->arvifs, list) {
402da3a9d3cSKalle Valo 		if (!arvif->is_up)
403da3a9d3cSKalle Valo 			continue;
404da3a9d3cSKalle Valo 
405da3a9d3cSKalle Valo 		req_param.vdev_id = arvif->vdev_id;
406cb4e57dbSKalle Valo 		ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
407da3a9d3cSKalle Valo 		if (ret) {
408da3a9d3cSKalle Valo 			ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);
409da3a9d3cSKalle Valo 			goto err_free;
410da3a9d3cSKalle Valo 		}
411da3a9d3cSKalle Valo 	}
412da3a9d3cSKalle Valo 
413da3a9d3cSKalle Valo 	ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id,
414da3a9d3cSKalle Valo 				 buf);
415da3a9d3cSKalle Valo 
416da3a9d3cSKalle Valo 	/* since beacon stats request is looped for all active VDEVs, saved fw
417da3a9d3cSKalle Valo 	 * stats is not freed for each request until done for all active VDEVs
418da3a9d3cSKalle Valo 	 */
419da3a9d3cSKalle Valo 	spin_lock_bh(&ar->data_lock);
420da3a9d3cSKalle Valo 	ath11k_fw_stats_bcn_free(&ar->debug.fw_stats.bcn);
421da3a9d3cSKalle Valo 	spin_unlock_bh(&ar->data_lock);
422da3a9d3cSKalle Valo 
423da3a9d3cSKalle Valo 	file->private_data = buf;
424da3a9d3cSKalle Valo 
425da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
426da3a9d3cSKalle Valo 	return 0;
427da3a9d3cSKalle Valo 
428da3a9d3cSKalle Valo err_free:
429da3a9d3cSKalle Valo 	vfree(buf);
430da3a9d3cSKalle Valo 
431da3a9d3cSKalle Valo err_unlock:
432da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
433da3a9d3cSKalle Valo 	return ret;
434da3a9d3cSKalle Valo }
435da3a9d3cSKalle Valo 
436da3a9d3cSKalle Valo static int ath11k_release_bcn_stats(struct inode *inode, struct file *file)
437da3a9d3cSKalle Valo {
438da3a9d3cSKalle Valo 	vfree(file->private_data);
439da3a9d3cSKalle Valo 
440da3a9d3cSKalle Valo 	return 0;
441da3a9d3cSKalle Valo }
442da3a9d3cSKalle Valo 
443da3a9d3cSKalle Valo static ssize_t ath11k_read_bcn_stats(struct file *file,
444da3a9d3cSKalle Valo 				     char __user *user_buf,
445da3a9d3cSKalle Valo 				     size_t count, loff_t *ppos)
446da3a9d3cSKalle Valo {
447da3a9d3cSKalle Valo 	const char *buf = file->private_data;
448da3a9d3cSKalle Valo 	size_t len = strlen(buf);
449da3a9d3cSKalle Valo 
450da3a9d3cSKalle Valo 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
451da3a9d3cSKalle Valo }
452da3a9d3cSKalle Valo 
453da3a9d3cSKalle Valo static const struct file_operations fops_bcn_stats = {
454da3a9d3cSKalle Valo 	.open = ath11k_open_bcn_stats,
455da3a9d3cSKalle Valo 	.release = ath11k_release_bcn_stats,
456da3a9d3cSKalle Valo 	.read = ath11k_read_bcn_stats,
457da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
458da3a9d3cSKalle Valo 	.llseek = default_llseek,
459da3a9d3cSKalle Valo };
460da3a9d3cSKalle Valo 
461da3a9d3cSKalle Valo static ssize_t ath11k_read_simulate_fw_crash(struct file *file,
462da3a9d3cSKalle Valo 					     char __user *user_buf,
463da3a9d3cSKalle Valo 					     size_t count, loff_t *ppos)
464da3a9d3cSKalle Valo {
465da3a9d3cSKalle Valo 	const char buf[] =
466da3a9d3cSKalle Valo 		"To simulate firmware crash write one of the keywords to this file:\n"
467da3a9d3cSKalle Valo 		"`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n"
468da3a9d3cSKalle Valo 		"`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
469da3a9d3cSKalle Valo 
470da3a9d3cSKalle Valo 	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
471da3a9d3cSKalle Valo }
472da3a9d3cSKalle Valo 
473da3a9d3cSKalle Valo /* Simulate firmware crash:
474da3a9d3cSKalle Valo  * 'soft': Call wmi command causing firmware hang. This firmware hang is
475da3a9d3cSKalle Valo  * recoverable by warm firmware reset.
476da3a9d3cSKalle Valo  * 'hard': Force firmware crash by setting any vdev parameter for not allowed
477da3a9d3cSKalle Valo  * vdev id. This is hard firmware crash because it is recoverable only by cold
478da3a9d3cSKalle Valo  * firmware reset.
479da3a9d3cSKalle Valo  */
480da3a9d3cSKalle Valo static ssize_t ath11k_write_simulate_fw_crash(struct file *file,
481da3a9d3cSKalle Valo 					      const char __user *user_buf,
482da3a9d3cSKalle Valo 					      size_t count, loff_t *ppos)
483da3a9d3cSKalle Valo {
484da3a9d3cSKalle Valo 	struct ath11k_base *ab = file->private_data;
485da3a9d3cSKalle Valo 	struct ath11k_pdev *pdev;
486da3a9d3cSKalle Valo 	struct ath11k *ar = ab->pdevs[0].ar;
487da3a9d3cSKalle Valo 	char buf[32] = {0};
488da3a9d3cSKalle Valo 	ssize_t rc;
489da3a9d3cSKalle Valo 	int i, ret, radioup = 0;
490da3a9d3cSKalle Valo 
491da3a9d3cSKalle Valo 	for (i = 0; i < ab->num_radios; i++) {
492da3a9d3cSKalle Valo 		pdev = &ab->pdevs[i];
493da3a9d3cSKalle Valo 		ar = pdev->ar;
494da3a9d3cSKalle Valo 		if (ar && ar->state == ATH11K_STATE_ON) {
495da3a9d3cSKalle Valo 			radioup = 1;
496da3a9d3cSKalle Valo 			break;
497da3a9d3cSKalle Valo 		}
498da3a9d3cSKalle Valo 	}
499da3a9d3cSKalle Valo 	/* filter partial writes and invalid commands */
500da3a9d3cSKalle Valo 	if (*ppos != 0 || count >= sizeof(buf) || count == 0)
501da3a9d3cSKalle Valo 		return -EINVAL;
502da3a9d3cSKalle Valo 
503da3a9d3cSKalle Valo 	rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
504da3a9d3cSKalle Valo 	if (rc < 0)
505da3a9d3cSKalle Valo 		return rc;
506da3a9d3cSKalle Valo 
507da3a9d3cSKalle Valo 	/* drop the possible '\n' from the end */
508da3a9d3cSKalle Valo 	if (buf[*ppos - 1] == '\n')
509da3a9d3cSKalle Valo 		buf[*ppos - 1] = '\0';
510da3a9d3cSKalle Valo 
511da3a9d3cSKalle Valo 	if (radioup == 0) {
512da3a9d3cSKalle Valo 		ret = -ENETDOWN;
513da3a9d3cSKalle Valo 		goto exit;
514da3a9d3cSKalle Valo 	}
515da3a9d3cSKalle Valo 
516da3a9d3cSKalle Valo 	if (!strcmp(buf, "assert")) {
517da3a9d3cSKalle Valo 		ath11k_info(ab, "simulating firmware assert crash\n");
518da3a9d3cSKalle Valo 		ret = ath11k_wmi_force_fw_hang_cmd(ar,
519da3a9d3cSKalle Valo 						   ATH11K_WMI_FW_HANG_ASSERT_TYPE,
520da3a9d3cSKalle Valo 						   ATH11K_WMI_FW_HANG_DELAY);
521da3a9d3cSKalle Valo 	} else {
522da3a9d3cSKalle Valo 		ret = -EINVAL;
523da3a9d3cSKalle Valo 		goto exit;
524da3a9d3cSKalle Valo 	}
525da3a9d3cSKalle Valo 
526da3a9d3cSKalle Valo 	if (ret) {
527da3a9d3cSKalle Valo 		ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret);
528da3a9d3cSKalle Valo 		goto exit;
529da3a9d3cSKalle Valo 	}
530da3a9d3cSKalle Valo 
531da3a9d3cSKalle Valo 	ret = count;
532da3a9d3cSKalle Valo 
533da3a9d3cSKalle Valo exit:
534da3a9d3cSKalle Valo 	return ret;
535da3a9d3cSKalle Valo }
536da3a9d3cSKalle Valo 
537da3a9d3cSKalle Valo static const struct file_operations fops_simulate_fw_crash = {
538da3a9d3cSKalle Valo 	.read = ath11k_read_simulate_fw_crash,
539da3a9d3cSKalle Valo 	.write = ath11k_write_simulate_fw_crash,
540da3a9d3cSKalle Valo 	.open = simple_open,
541da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
542da3a9d3cSKalle Valo 	.llseek = default_llseek,
543da3a9d3cSKalle Valo };
544da3a9d3cSKalle Valo 
545da3a9d3cSKalle Valo static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file,
546da3a9d3cSKalle Valo 						 const char __user *ubuf,
547da3a9d3cSKalle Valo 						 size_t count, loff_t *ppos)
548da3a9d3cSKalle Valo {
549da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
550da3a9d3cSKalle Valo 	u32 filter;
551da3a9d3cSKalle Valo 	int ret;
552da3a9d3cSKalle Valo 
553da3a9d3cSKalle Valo 	if (kstrtouint_from_user(ubuf, count, 0, &filter))
554da3a9d3cSKalle Valo 		return -EINVAL;
555da3a9d3cSKalle Valo 
556da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
557da3a9d3cSKalle Valo 
558da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
559da3a9d3cSKalle Valo 		ret = -ENETDOWN;
560da3a9d3cSKalle Valo 		goto out;
561da3a9d3cSKalle Valo 	}
562da3a9d3cSKalle Valo 
563da3a9d3cSKalle Valo 	if (filter == ar->debug.extd_tx_stats) {
564da3a9d3cSKalle Valo 		ret = count;
565da3a9d3cSKalle Valo 		goto out;
566da3a9d3cSKalle Valo 	}
567da3a9d3cSKalle Valo 
568da3a9d3cSKalle Valo 	ar->debug.extd_tx_stats = filter;
569da3a9d3cSKalle Valo 	ret = count;
570da3a9d3cSKalle Valo 
571da3a9d3cSKalle Valo out:
572da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
573da3a9d3cSKalle Valo 	return ret;
574da3a9d3cSKalle Valo }
575da3a9d3cSKalle Valo 
576da3a9d3cSKalle Valo static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file,
577da3a9d3cSKalle Valo 						char __user *ubuf,
578da3a9d3cSKalle Valo 						size_t count, loff_t *ppos)
579da3a9d3cSKalle Valo 
580da3a9d3cSKalle Valo {
581da3a9d3cSKalle Valo 	char buf[32] = {0};
582da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
583da3a9d3cSKalle Valo 	int len = 0;
584da3a9d3cSKalle Valo 
585da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
586da3a9d3cSKalle Valo 	len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
587da3a9d3cSKalle Valo 			ar->debug.extd_tx_stats);
588da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
589da3a9d3cSKalle Valo 
590da3a9d3cSKalle Valo 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
591da3a9d3cSKalle Valo }
592da3a9d3cSKalle Valo 
593da3a9d3cSKalle Valo static const struct file_operations fops_extd_tx_stats = {
594da3a9d3cSKalle Valo 	.read = ath11k_read_enable_extd_tx_stats,
595da3a9d3cSKalle Valo 	.write = ath11k_write_enable_extd_tx_stats,
596da3a9d3cSKalle Valo 	.open = simple_open
597da3a9d3cSKalle Valo };
598da3a9d3cSKalle Valo 
599da3a9d3cSKalle Valo static ssize_t ath11k_write_extd_rx_stats(struct file *file,
600da3a9d3cSKalle Valo 					  const char __user *ubuf,
601da3a9d3cSKalle Valo 					  size_t count, loff_t *ppos)
602da3a9d3cSKalle Valo {
603da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
604da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
605da3a9d3cSKalle Valo 	struct htt_rx_ring_tlv_filter tlv_filter = {0};
606da3a9d3cSKalle Valo 	u32 enable, rx_filter = 0, ring_id;
607da3a9d3cSKalle Valo 	int i;
608da3a9d3cSKalle Valo 	int ret;
609da3a9d3cSKalle Valo 
610da3a9d3cSKalle Valo 	if (kstrtouint_from_user(ubuf, count, 0, &enable))
611da3a9d3cSKalle Valo 		return -EINVAL;
612da3a9d3cSKalle Valo 
613da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
614da3a9d3cSKalle Valo 
615da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
616da3a9d3cSKalle Valo 		ret = -ENETDOWN;
617da3a9d3cSKalle Valo 		goto exit;
618da3a9d3cSKalle Valo 	}
619da3a9d3cSKalle Valo 
620da3a9d3cSKalle Valo 	if (enable > 1) {
621da3a9d3cSKalle Valo 		ret = -EINVAL;
622da3a9d3cSKalle Valo 		goto exit;
623da3a9d3cSKalle Valo 	}
624da3a9d3cSKalle Valo 
625da3a9d3cSKalle Valo 	if (enable == ar->debug.extd_rx_stats) {
626da3a9d3cSKalle Valo 		ret = count;
627da3a9d3cSKalle Valo 		goto exit;
628da3a9d3cSKalle Valo 	}
629da3a9d3cSKalle Valo 
630da3a9d3cSKalle Valo 	if (enable) {
631da3a9d3cSKalle Valo 		rx_filter =  HTT_RX_FILTER_TLV_FLAGS_MPDU_START;
632da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START;
633da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END;
634da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;
635da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT;
636da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE;
637da3a9d3cSKalle Valo 
638da3a9d3cSKalle Valo 		tlv_filter.rx_filter = rx_filter;
639da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;
640da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;
641da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;
642da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |
643da3a9d3cSKalle Valo 			HTT_RX_FP_DATA_FILTER_FLASG3;
644da3a9d3cSKalle Valo 	} else {
645da3a9d3cSKalle Valo 		tlv_filter = ath11k_mac_mon_status_filter_default;
646da3a9d3cSKalle Valo 	}
647da3a9d3cSKalle Valo 
648da3a9d3cSKalle Valo 	ar->debug.rx_filter = tlv_filter.rx_filter;
649da3a9d3cSKalle Valo 
650da3a9d3cSKalle Valo 	for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {
651da3a9d3cSKalle Valo 		ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
652da3a9d3cSKalle Valo 		ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,
653da3a9d3cSKalle Valo 						       HAL_RXDMA_MONITOR_STATUS,
654da3a9d3cSKalle Valo 						       DP_RX_BUFFER_SIZE, &tlv_filter);
655da3a9d3cSKalle Valo 
656da3a9d3cSKalle Valo 		if (ret) {
657da3a9d3cSKalle Valo 			ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");
658da3a9d3cSKalle Valo 			goto exit;
659da3a9d3cSKalle Valo 		}
660da3a9d3cSKalle Valo 	}
661da3a9d3cSKalle Valo 
662da3a9d3cSKalle Valo 	ar->debug.extd_rx_stats = enable;
663da3a9d3cSKalle Valo 	ret = count;
664da3a9d3cSKalle Valo exit:
665da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
666da3a9d3cSKalle Valo 	return ret;
667da3a9d3cSKalle Valo }
668da3a9d3cSKalle Valo 
669da3a9d3cSKalle Valo static ssize_t ath11k_read_extd_rx_stats(struct file *file,
670da3a9d3cSKalle Valo 					 char __user *ubuf,
671da3a9d3cSKalle Valo 					 size_t count, loff_t *ppos)
672da3a9d3cSKalle Valo {
673da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
674da3a9d3cSKalle Valo 	char buf[32];
675da3a9d3cSKalle Valo 	int len = 0;
676da3a9d3cSKalle Valo 
677da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
678da3a9d3cSKalle Valo 	len = scnprintf(buf, sizeof(buf) - len, "%d\n",
679da3a9d3cSKalle Valo 			ar->debug.extd_rx_stats);
680da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
681da3a9d3cSKalle Valo 
682da3a9d3cSKalle Valo 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
683da3a9d3cSKalle Valo }
684da3a9d3cSKalle Valo 
685da3a9d3cSKalle Valo static const struct file_operations fops_extd_rx_stats = {
686da3a9d3cSKalle Valo 	.read = ath11k_read_extd_rx_stats,
687da3a9d3cSKalle Valo 	.write = ath11k_write_extd_rx_stats,
688da3a9d3cSKalle Valo 	.open = simple_open,
689da3a9d3cSKalle Valo };
690da3a9d3cSKalle Valo 
691da3a9d3cSKalle Valo static int ath11k_fill_bp_stats(struct ath11k_base *ab,
692da3a9d3cSKalle Valo 				struct ath11k_bp_stats *bp_stats,
693da3a9d3cSKalle Valo 				char *buf, int len, int size)
694da3a9d3cSKalle Valo {
695da3a9d3cSKalle Valo 	lockdep_assert_held(&ab->base_lock);
696da3a9d3cSKalle Valo 
697da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "count: %u\n",
698da3a9d3cSKalle Valo 			 bp_stats->count);
699da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "hp: %u\n",
700da3a9d3cSKalle Valo 			 bp_stats->hp);
701da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "tp: %u\n",
702da3a9d3cSKalle Valo 			 bp_stats->tp);
703da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "seen before: %ums\n\n",
704da3a9d3cSKalle Valo 			 jiffies_to_msecs(jiffies - bp_stats->jiffies));
705da3a9d3cSKalle Valo 	return len;
706da3a9d3cSKalle Valo }
707da3a9d3cSKalle Valo 
708cb4e57dbSKalle Valo static ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab,
709da3a9d3cSKalle Valo 						     char *buf, int size)
710da3a9d3cSKalle Valo {
711da3a9d3cSKalle Valo 	struct ath11k_bp_stats *bp_stats;
712da3a9d3cSKalle Valo 	bool stats_rxd = false;
713da3a9d3cSKalle Valo 	u8 i, pdev_idx;
714da3a9d3cSKalle Valo 	int len = 0;
715da3a9d3cSKalle Valo 
716da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nBackpressure Stats\n");
717da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "==================\n");
718da3a9d3cSKalle Valo 
719da3a9d3cSKalle Valo 	spin_lock_bh(&ab->base_lock);
720da3a9d3cSKalle Valo 	for (i = 0; i < HTT_SW_UMAC_RING_IDX_MAX; i++) {
721da3a9d3cSKalle Valo 		bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[i];
722da3a9d3cSKalle Valo 
723da3a9d3cSKalle Valo 		if (!bp_stats->count)
724da3a9d3cSKalle Valo 			continue;
725da3a9d3cSKalle Valo 
726da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len, "Ring: %s\n",
727da3a9d3cSKalle Valo 				 htt_bp_umac_ring[i]);
728da3a9d3cSKalle Valo 		len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
729da3a9d3cSKalle Valo 		stats_rxd = true;
730da3a9d3cSKalle Valo 	}
731da3a9d3cSKalle Valo 
732da3a9d3cSKalle Valo 	for (i = 0; i < HTT_SW_LMAC_RING_IDX_MAX; i++) {
733da3a9d3cSKalle Valo 		for (pdev_idx = 0; pdev_idx < MAX_RADIOS; pdev_idx++) {
734da3a9d3cSKalle Valo 			bp_stats =
735da3a9d3cSKalle Valo 				&ab->soc_stats.bp_stats.lmac_ring_bp_stats[i][pdev_idx];
736da3a9d3cSKalle Valo 
737da3a9d3cSKalle Valo 			if (!bp_stats->count)
738da3a9d3cSKalle Valo 				continue;
739da3a9d3cSKalle Valo 
740da3a9d3cSKalle Valo 			len += scnprintf(buf + len, size - len, "Ring: %s\n",
741da3a9d3cSKalle Valo 					 htt_bp_lmac_ring[i]);
742da3a9d3cSKalle Valo 			len += scnprintf(buf + len, size - len, "pdev: %d\n",
743da3a9d3cSKalle Valo 					 pdev_idx);
744da3a9d3cSKalle Valo 			len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
745da3a9d3cSKalle Valo 			stats_rxd = true;
746da3a9d3cSKalle Valo 		}
747da3a9d3cSKalle Valo 	}
748da3a9d3cSKalle Valo 	spin_unlock_bh(&ab->base_lock);
749da3a9d3cSKalle Valo 
750da3a9d3cSKalle Valo 	if (!stats_rxd)
751da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len,
752da3a9d3cSKalle Valo 				 "No Ring Backpressure stats received\n\n");
753da3a9d3cSKalle Valo 
754da3a9d3cSKalle Valo 	return len;
755da3a9d3cSKalle Valo }
756da3a9d3cSKalle Valo 
757cb4e57dbSKalle Valo static ssize_t ath11k_debugfs_dump_soc_dp_stats(struct file *file,
758da3a9d3cSKalle Valo 						char __user *user_buf,
759da3a9d3cSKalle Valo 						size_t count, loff_t *ppos)
760da3a9d3cSKalle Valo {
761da3a9d3cSKalle Valo 	struct ath11k_base *ab = file->private_data;
762da3a9d3cSKalle Valo 	struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;
763da3a9d3cSKalle Valo 	int len = 0, i, retval;
764da3a9d3cSKalle Valo 	const int size = 4096;
765da3a9d3cSKalle Valo 	static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = {
766da3a9d3cSKalle Valo 			"Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC",
767da3a9d3cSKalle Valo 			"Unencrypt", "MSDU len", "MSDU limit", "WiFi parse",
768da3a9d3cSKalle Valo 			"AMSDU parse", "SA timeout", "DA timeout",
769da3a9d3cSKalle Valo 			"Flow timeout", "Flush req"};
770da3a9d3cSKalle Valo 	static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = {
771da3a9d3cSKalle Valo 			"Desc addr zero", "Desc inval", "AMPDU in non BA",
772da3a9d3cSKalle Valo 			"Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump",
773da3a9d3cSKalle Valo 			"Frame OOR", "BAR OOR", "No BA session",
774da3a9d3cSKalle Valo 			"Frame SN equal SSN", "PN check fail", "2k err",
775da3a9d3cSKalle Valo 			"PN err", "Desc blocked"};
776da3a9d3cSKalle Valo 
777da3a9d3cSKalle Valo 	char *buf;
778da3a9d3cSKalle Valo 
779da3a9d3cSKalle Valo 	buf = kzalloc(size, GFP_KERNEL);
780da3a9d3cSKalle Valo 	if (!buf)
781da3a9d3cSKalle Valo 		return -ENOMEM;
782da3a9d3cSKalle Valo 
783da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n");
784da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "err ring pkts: %u\n",
785da3a9d3cSKalle Valo 			 soc_stats->err_ring_pkts);
786da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n",
787da3a9d3cSKalle Valo 			 soc_stats->invalid_rbm);
788da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "RXDMA errors:\n");
789da3a9d3cSKalle Valo 	for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)
790da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len, "%s: %u\n",
791da3a9d3cSKalle Valo 				 rxdma_err[i], soc_stats->rxdma_error[i]);
792da3a9d3cSKalle Valo 
793da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nREO errors:\n");
794da3a9d3cSKalle Valo 	for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)
795da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len, "%s: %u\n",
796da3a9d3cSKalle Valo 				 reo_err[i], soc_stats->reo_error[i]);
797da3a9d3cSKalle Valo 
798da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n");
799da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len,
800da3a9d3cSKalle Valo 			 "ring0: %u\nring1: %u\nring2: %u\nring3: %u\n",
801da3a9d3cSKalle Valo 			 soc_stats->hal_reo_error[0],
802da3a9d3cSKalle Valo 			 soc_stats->hal_reo_error[1],
803da3a9d3cSKalle Valo 			 soc_stats->hal_reo_error[2],
804da3a9d3cSKalle Valo 			 soc_stats->hal_reo_error[3]);
805da3a9d3cSKalle Valo 
806da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n");
807da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n");
808da3a9d3cSKalle Valo 
809da3a9d3cSKalle Valo 	for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
810da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len, "ring%d: %u\n",
811da3a9d3cSKalle Valo 				 i, soc_stats->tx_err.desc_na[i]);
812da3a9d3cSKalle Valo 
813da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len,
814da3a9d3cSKalle Valo 			 "\nMisc Transmit Failures: %d\n",
815da3a9d3cSKalle Valo 			 atomic_read(&soc_stats->tx_err.misc_fail));
816da3a9d3cSKalle Valo 
817cb4e57dbSKalle Valo 	len += ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf + len, size - len);
818da3a9d3cSKalle Valo 
819da3a9d3cSKalle Valo 	if (len > size)
820da3a9d3cSKalle Valo 		len = size;
821da3a9d3cSKalle Valo 	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
822da3a9d3cSKalle Valo 	kfree(buf);
823da3a9d3cSKalle Valo 
824da3a9d3cSKalle Valo 	return retval;
825da3a9d3cSKalle Valo }
826da3a9d3cSKalle Valo 
827da3a9d3cSKalle Valo static const struct file_operations fops_soc_dp_stats = {
828cb4e57dbSKalle Valo 	.read = ath11k_debugfs_dump_soc_dp_stats,
829da3a9d3cSKalle Valo 	.open = simple_open,
830da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
831da3a9d3cSKalle Valo 	.llseek = default_llseek,
832da3a9d3cSKalle Valo };
833da3a9d3cSKalle Valo 
834cb4e57dbSKalle Valo int ath11k_debugfs_pdev_create(struct ath11k_base *ab)
835da3a9d3cSKalle Valo {
836da3a9d3cSKalle Valo 	if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
837da3a9d3cSKalle Valo 		return 0;
838da3a9d3cSKalle Valo 
839da3a9d3cSKalle Valo 	ab->debugfs_soc = debugfs_create_dir(ab->hw_params.name, ab->debugfs_ath11k);
840da3a9d3cSKalle Valo 	if (IS_ERR(ab->debugfs_soc))
841da3a9d3cSKalle Valo 		return PTR_ERR(ab->debugfs_soc);
842da3a9d3cSKalle Valo 
843da3a9d3cSKalle Valo 	debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab,
844da3a9d3cSKalle Valo 			    &fops_simulate_fw_crash);
845da3a9d3cSKalle Valo 
846da3a9d3cSKalle Valo 	debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab,
847da3a9d3cSKalle Valo 			    &fops_soc_dp_stats);
848da3a9d3cSKalle Valo 
849da3a9d3cSKalle Valo 	return 0;
850da3a9d3cSKalle Valo }
851da3a9d3cSKalle Valo 
852cb4e57dbSKalle Valo void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab)
853da3a9d3cSKalle Valo {
854*089ba909SCarl Huang 	debugfs_remove_recursive(ab->debugfs_soc);
855*089ba909SCarl Huang 	ab->debugfs_soc = NULL;
856da3a9d3cSKalle Valo }
857da3a9d3cSKalle Valo 
858cb4e57dbSKalle Valo int ath11k_debugfs_soc_create(struct ath11k_base *ab)
859da3a9d3cSKalle Valo {
860da3a9d3cSKalle Valo 	ab->debugfs_ath11k = debugfs_create_dir("ath11k", NULL);
861da3a9d3cSKalle Valo 
862476c1d3cSAlex Dewar 	return PTR_ERR_OR_ZERO(ab->debugfs_ath11k);
863da3a9d3cSKalle Valo }
864da3a9d3cSKalle Valo 
865cb4e57dbSKalle Valo void ath11k_debugfs_soc_destroy(struct ath11k_base *ab)
866da3a9d3cSKalle Valo {
867*089ba909SCarl Huang 	debugfs_remove_recursive(ab->debugfs_ath11k);
868*089ba909SCarl Huang 	ab->debugfs_ath11k = NULL;
869da3a9d3cSKalle Valo }
870da3a9d3cSKalle Valo 
871cb4e57dbSKalle Valo void ath11k_debugfs_fw_stats_init(struct ath11k *ar)
872da3a9d3cSKalle Valo {
873da3a9d3cSKalle Valo 	struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",
874da3a9d3cSKalle Valo 							ar->debug.debugfs_pdev);
875da3a9d3cSKalle Valo 
876da3a9d3cSKalle Valo 	ar->debug.fw_stats.debugfs_fwstats = fwstats_dir;
877da3a9d3cSKalle Valo 
878da3a9d3cSKalle Valo 	/* all stats debugfs files created are under "fw_stats" directory
879da3a9d3cSKalle Valo 	 * created per PDEV
880da3a9d3cSKalle Valo 	 */
881da3a9d3cSKalle Valo 	debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar,
882da3a9d3cSKalle Valo 			    &fops_pdev_stats);
883da3a9d3cSKalle Valo 	debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,
884da3a9d3cSKalle Valo 			    &fops_vdev_stats);
885da3a9d3cSKalle Valo 	debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar,
886da3a9d3cSKalle Valo 			    &fops_bcn_stats);
887da3a9d3cSKalle Valo 
888da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
889da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
890da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&ar->debug.fw_stats.bcn);
891da3a9d3cSKalle Valo 
892da3a9d3cSKalle Valo 	init_completion(&ar->debug.fw_stats_complete);
893da3a9d3cSKalle Valo }
894da3a9d3cSKalle Valo 
895da3a9d3cSKalle Valo static ssize_t ath11k_write_pktlog_filter(struct file *file,
896da3a9d3cSKalle Valo 					  const char __user *ubuf,
897da3a9d3cSKalle Valo 					  size_t count, loff_t *ppos)
898da3a9d3cSKalle Valo {
899da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
900da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
901da3a9d3cSKalle Valo 	struct htt_rx_ring_tlv_filter tlv_filter = {0};
902da3a9d3cSKalle Valo 	u32 rx_filter = 0, ring_id, filter, mode;
903da3a9d3cSKalle Valo 	u8 buf[128] = {0};
904da3a9d3cSKalle Valo 	int i, ret;
905da3a9d3cSKalle Valo 	ssize_t rc;
906da3a9d3cSKalle Valo 
907da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
908da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
909da3a9d3cSKalle Valo 		ret = -ENETDOWN;
910da3a9d3cSKalle Valo 		goto out;
911da3a9d3cSKalle Valo 	}
912da3a9d3cSKalle Valo 
913da3a9d3cSKalle Valo 	rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
914da3a9d3cSKalle Valo 	if (rc < 0) {
915da3a9d3cSKalle Valo 		ret = rc;
916da3a9d3cSKalle Valo 		goto out;
917da3a9d3cSKalle Valo 	}
918da3a9d3cSKalle Valo 	buf[rc] = '\0';
919da3a9d3cSKalle Valo 
920da3a9d3cSKalle Valo 	ret = sscanf(buf, "0x%x %u", &filter, &mode);
921da3a9d3cSKalle Valo 	if (ret != 2) {
922da3a9d3cSKalle Valo 		ret = -EINVAL;
923da3a9d3cSKalle Valo 		goto out;
924da3a9d3cSKalle Valo 	}
925da3a9d3cSKalle Valo 
926da3a9d3cSKalle Valo 	if (filter) {
927da3a9d3cSKalle Valo 		ret = ath11k_wmi_pdev_pktlog_enable(ar, filter);
928da3a9d3cSKalle Valo 		if (ret) {
929da3a9d3cSKalle Valo 			ath11k_warn(ar->ab,
930da3a9d3cSKalle Valo 				    "failed to enable pktlog filter %x: %d\n",
931da3a9d3cSKalle Valo 				    ar->debug.pktlog_filter, ret);
932da3a9d3cSKalle Valo 			goto out;
933da3a9d3cSKalle Valo 		}
934da3a9d3cSKalle Valo 	} else {
935da3a9d3cSKalle Valo 		ret = ath11k_wmi_pdev_pktlog_disable(ar);
936da3a9d3cSKalle Valo 		if (ret) {
937da3a9d3cSKalle Valo 			ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret);
938da3a9d3cSKalle Valo 			goto out;
939da3a9d3cSKalle Valo 		}
940da3a9d3cSKalle Valo 	}
941da3a9d3cSKalle Valo 
942da3a9d3cSKalle Valo #define HTT_RX_FILTER_TLV_LITE_MODE \
943da3a9d3cSKalle Valo 			(HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \
944da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \
945da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \
946da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \
947da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \
948da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_MPDU_START)
949da3a9d3cSKalle Valo 
950da3a9d3cSKalle Valo 	if (mode == ATH11K_PKTLOG_MODE_FULL) {
951da3a9d3cSKalle Valo 		rx_filter = HTT_RX_FILTER_TLV_LITE_MODE |
952da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_MSDU_START |
953da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_MSDU_END |
954da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_MPDU_END |
955da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER |
956da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_ATTENTION;
957da3a9d3cSKalle Valo 	} else if (mode == ATH11K_PKTLOG_MODE_LITE) {
958da3a9d3cSKalle Valo 		ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,
959da3a9d3cSKalle Valo 							  HTT_PPDU_STATS_TAG_PKTLOG);
960da3a9d3cSKalle Valo 		if (ret) {
961da3a9d3cSKalle Valo 			ath11k_err(ar->ab, "failed to enable pktlog lite: %d\n", ret);
962da3a9d3cSKalle Valo 			goto out;
963da3a9d3cSKalle Valo 		}
964da3a9d3cSKalle Valo 
965da3a9d3cSKalle Valo 		rx_filter = HTT_RX_FILTER_TLV_LITE_MODE;
966da3a9d3cSKalle Valo 	} else {
967da3a9d3cSKalle Valo 		ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,
968da3a9d3cSKalle Valo 							  HTT_PPDU_STATS_TAG_DEFAULT);
969da3a9d3cSKalle Valo 		if (ret) {
970da3a9d3cSKalle Valo 			ath11k_err(ar->ab, "failed to send htt ppdu stats req: %d\n",
971da3a9d3cSKalle Valo 				   ret);
972da3a9d3cSKalle Valo 			goto out;
973da3a9d3cSKalle Valo 		}
974da3a9d3cSKalle Valo 	}
975da3a9d3cSKalle Valo 
976da3a9d3cSKalle Valo 	tlv_filter.rx_filter = rx_filter;
977da3a9d3cSKalle Valo 	if (rx_filter) {
978da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;
979da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;
980da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;
981da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |
982da3a9d3cSKalle Valo 					       HTT_RX_FP_DATA_FILTER_FLASG3;
983da3a9d3cSKalle Valo 	}
984da3a9d3cSKalle Valo 
985da3a9d3cSKalle Valo 	for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {
986da3a9d3cSKalle Valo 		ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
987da3a9d3cSKalle Valo 		ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id,
988da3a9d3cSKalle Valo 						       ar->dp.mac_id + i,
989da3a9d3cSKalle Valo 						       HAL_RXDMA_MONITOR_STATUS,
990da3a9d3cSKalle Valo 						       DP_RX_BUFFER_SIZE, &tlv_filter);
991da3a9d3cSKalle Valo 
992da3a9d3cSKalle Valo 		if (ret) {
993da3a9d3cSKalle Valo 			ath11k_warn(ab, "failed to set rx filter for monitor status ring\n");
994da3a9d3cSKalle Valo 			goto out;
995da3a9d3cSKalle Valo 		}
996da3a9d3cSKalle Valo 	}
997da3a9d3cSKalle Valo 
998da3a9d3cSKalle Valo 	ath11k_dbg(ab, ATH11K_DBG_WMI, "pktlog filter %d mode %s\n",
999da3a9d3cSKalle Valo 		   filter, ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite"));
1000da3a9d3cSKalle Valo 
1001da3a9d3cSKalle Valo 	ar->debug.pktlog_filter = filter;
1002da3a9d3cSKalle Valo 	ar->debug.pktlog_mode = mode;
1003da3a9d3cSKalle Valo 	ret = count;
1004da3a9d3cSKalle Valo 
1005da3a9d3cSKalle Valo out:
1006da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
1007da3a9d3cSKalle Valo 	return ret;
1008da3a9d3cSKalle Valo }
1009da3a9d3cSKalle Valo 
1010da3a9d3cSKalle Valo static ssize_t ath11k_read_pktlog_filter(struct file *file,
1011da3a9d3cSKalle Valo 					 char __user *ubuf,
1012da3a9d3cSKalle Valo 					 size_t count, loff_t *ppos)
1013da3a9d3cSKalle Valo 
1014da3a9d3cSKalle Valo {
1015da3a9d3cSKalle Valo 	char buf[32] = {0};
1016da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
1017da3a9d3cSKalle Valo 	int len = 0;
1018da3a9d3cSKalle Valo 
1019da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
1020da3a9d3cSKalle Valo 	len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n",
1021da3a9d3cSKalle Valo 			ar->debug.pktlog_filter,
1022da3a9d3cSKalle Valo 			ar->debug.pktlog_mode);
1023da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
1024da3a9d3cSKalle Valo 
1025da3a9d3cSKalle Valo 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
1026da3a9d3cSKalle Valo }
1027da3a9d3cSKalle Valo 
1028da3a9d3cSKalle Valo static const struct file_operations fops_pktlog_filter = {
1029da3a9d3cSKalle Valo 	.read = ath11k_read_pktlog_filter,
1030da3a9d3cSKalle Valo 	.write = ath11k_write_pktlog_filter,
1031da3a9d3cSKalle Valo 	.open = simple_open
1032da3a9d3cSKalle Valo };
1033da3a9d3cSKalle Valo 
1034da3a9d3cSKalle Valo static ssize_t ath11k_write_simulate_radar(struct file *file,
1035da3a9d3cSKalle Valo 					   const char __user *user_buf,
1036da3a9d3cSKalle Valo 					   size_t count, loff_t *ppos)
1037da3a9d3cSKalle Valo {
1038da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
1039da3a9d3cSKalle Valo 	int ret;
1040da3a9d3cSKalle Valo 
1041da3a9d3cSKalle Valo 	ret = ath11k_wmi_simulate_radar(ar);
1042da3a9d3cSKalle Valo 	if (ret)
1043da3a9d3cSKalle Valo 		return ret;
1044da3a9d3cSKalle Valo 
1045da3a9d3cSKalle Valo 	return count;
1046da3a9d3cSKalle Valo }
1047da3a9d3cSKalle Valo 
1048da3a9d3cSKalle Valo static const struct file_operations fops_simulate_radar = {
1049da3a9d3cSKalle Valo 	.write = ath11k_write_simulate_radar,
1050da3a9d3cSKalle Valo 	.open = simple_open
1051da3a9d3cSKalle Valo };
1052da3a9d3cSKalle Valo 
1053cb4e57dbSKalle Valo int ath11k_debugfs_register(struct ath11k *ar)
1054da3a9d3cSKalle Valo {
1055da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
1056da3a9d3cSKalle Valo 	char pdev_name[5];
1057da3a9d3cSKalle Valo 	char buf[100] = {0};
1058da3a9d3cSKalle Valo 
1059da3a9d3cSKalle Valo 	snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx);
1060da3a9d3cSKalle Valo 
1061da3a9d3cSKalle Valo 	ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc);
1062da3a9d3cSKalle Valo 	if (IS_ERR(ar->debug.debugfs_pdev))
1063da3a9d3cSKalle Valo 		return PTR_ERR(ar->debug.debugfs_pdev);
1064da3a9d3cSKalle Valo 
1065da3a9d3cSKalle Valo 	/* Create a symlink under ieee80211/phy* */
1066da3a9d3cSKalle Valo 	snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev);
1067da3a9d3cSKalle Valo 	debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf);
1068da3a9d3cSKalle Valo 
106956292162SKalle Valo 	ath11k_debugfs_htt_stats_init(ar);
1070da3a9d3cSKalle Valo 
1071cb4e57dbSKalle Valo 	ath11k_debugfs_fw_stats_init(ar);
1072da3a9d3cSKalle Valo 
1073da3a9d3cSKalle Valo 	debugfs_create_file("ext_tx_stats", 0644,
1074da3a9d3cSKalle Valo 			    ar->debug.debugfs_pdev, ar,
1075da3a9d3cSKalle Valo 			    &fops_extd_tx_stats);
1076da3a9d3cSKalle Valo 	debugfs_create_file("ext_rx_stats", 0644,
1077da3a9d3cSKalle Valo 			    ar->debug.debugfs_pdev, ar,
1078da3a9d3cSKalle Valo 			    &fops_extd_rx_stats);
1079da3a9d3cSKalle Valo 	debugfs_create_file("pktlog_filter", 0644,
1080da3a9d3cSKalle Valo 			    ar->debug.debugfs_pdev, ar,
1081da3a9d3cSKalle Valo 			    &fops_pktlog_filter);
1082da3a9d3cSKalle Valo 
1083da3a9d3cSKalle Valo 	if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) {
1084da3a9d3cSKalle Valo 		debugfs_create_file("dfs_simulate_radar", 0200,
1085da3a9d3cSKalle Valo 				    ar->debug.debugfs_pdev, ar,
1086da3a9d3cSKalle Valo 				    &fops_simulate_radar);
1087da3a9d3cSKalle Valo 		debugfs_create_bool("dfs_block_radar_events", 0200,
1088da3a9d3cSKalle Valo 				    ar->debug.debugfs_pdev,
1089da3a9d3cSKalle Valo 				    &ar->dfs_block_radar_events);
1090da3a9d3cSKalle Valo 	}
1091da3a9d3cSKalle Valo 
1092da3a9d3cSKalle Valo 	return 0;
1093da3a9d3cSKalle Valo }
1094da3a9d3cSKalle Valo 
1095cb4e57dbSKalle Valo void ath11k_debugfs_unregister(struct ath11k *ar)
1096da3a9d3cSKalle Valo {
1097da3a9d3cSKalle Valo }
1098