xref: /openbmc/linux/drivers/net/wireless/ath/ath11k/debugfs.c (revision 9e2747c31e5a65e92173bc98a1b72f6a7c86d8e1)
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 
615fcb103SJakub Kicinski #include <linux/vmalloc.h>
715fcb103SJakub Kicinski 
8da3a9d3cSKalle Valo #include "debugfs.h"
9da3a9d3cSKalle Valo 
10da3a9d3cSKalle Valo #include "core.h"
11da3a9d3cSKalle Valo #include "debug.h"
12da3a9d3cSKalle Valo #include "wmi.h"
13da3a9d3cSKalle Valo #include "hal_rx.h"
14da3a9d3cSKalle Valo #include "dp_tx.h"
1556292162SKalle Valo #include "debugfs_htt_stats.h"
16da3a9d3cSKalle Valo #include "peer.h"
17da3a9d3cSKalle Valo 
18da3a9d3cSKalle Valo static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {
19da3a9d3cSKalle Valo 	"REO2SW1_RING",
20da3a9d3cSKalle Valo 	"REO2SW2_RING",
21da3a9d3cSKalle Valo 	"REO2SW3_RING",
22da3a9d3cSKalle Valo 	"REO2SW4_RING",
23da3a9d3cSKalle Valo 	"WBM2REO_LINK_RING",
24da3a9d3cSKalle Valo 	"REO2TCL_RING",
25da3a9d3cSKalle Valo 	"REO2FW_RING",
26da3a9d3cSKalle Valo 	"RELEASE_RING",
27da3a9d3cSKalle Valo 	"PPE_RELEASE_RING",
28da3a9d3cSKalle Valo 	"TCL2TQM_RING",
29da3a9d3cSKalle Valo 	"TQM_RELEASE_RING",
30da3a9d3cSKalle Valo 	"REO_RELEASE_RING",
31da3a9d3cSKalle Valo 	"WBM2SW0_RELEASE_RING",
32da3a9d3cSKalle Valo 	"WBM2SW1_RELEASE_RING",
33da3a9d3cSKalle Valo 	"WBM2SW2_RELEASE_RING",
34da3a9d3cSKalle Valo 	"WBM2SW3_RELEASE_RING",
35da3a9d3cSKalle Valo 	"REO_CMD_RING",
36da3a9d3cSKalle Valo 	"REO_STATUS_RING",
37da3a9d3cSKalle Valo };
38da3a9d3cSKalle Valo 
39da3a9d3cSKalle Valo static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = {
40da3a9d3cSKalle Valo 	"FW2RXDMA_BUF_RING",
41da3a9d3cSKalle Valo 	"FW2RXDMA_STATUS_RING",
42da3a9d3cSKalle Valo 	"FW2RXDMA_LINK_RING",
43da3a9d3cSKalle Valo 	"SW2RXDMA_BUF_RING",
44da3a9d3cSKalle Valo 	"WBM2RXDMA_LINK_RING",
45da3a9d3cSKalle Valo 	"RXDMA2FW_RING",
46da3a9d3cSKalle Valo 	"RXDMA2SW_RING",
47da3a9d3cSKalle Valo 	"RXDMA2RELEASE_RING",
48da3a9d3cSKalle Valo 	"RXDMA2REO_RING",
49da3a9d3cSKalle Valo 	"MONITOR_STATUS_RING",
50da3a9d3cSKalle Valo 	"MONITOR_BUF_RING",
51da3a9d3cSKalle Valo 	"MONITOR_DESC_RING",
52da3a9d3cSKalle Valo 	"MONITOR_DEST_RING",
53da3a9d3cSKalle Valo };
54da3a9d3cSKalle Valo 
55691425b4SVenkateswara Naralasetty void ath11k_debugfs_add_dbring_entry(struct ath11k *ar,
56691425b4SVenkateswara Naralasetty 				     enum wmi_direct_buffer_module id,
57691425b4SVenkateswara Naralasetty 				     enum ath11k_dbg_dbr_event event,
58691425b4SVenkateswara Naralasetty 				     struct hal_srng *srng)
59691425b4SVenkateswara Naralasetty {
60691425b4SVenkateswara Naralasetty 	struct ath11k_debug_dbr *dbr_debug;
61691425b4SVenkateswara Naralasetty 	struct ath11k_dbg_dbr_data *dbr_data;
62691425b4SVenkateswara Naralasetty 	struct ath11k_dbg_dbr_entry *entry;
63691425b4SVenkateswara Naralasetty 
64691425b4SVenkateswara Naralasetty 	if (id >= WMI_DIRECT_BUF_MAX || event >= ATH11K_DBG_DBR_EVENT_MAX)
65691425b4SVenkateswara Naralasetty 		return;
66691425b4SVenkateswara Naralasetty 
67691425b4SVenkateswara Naralasetty 	dbr_debug = ar->debug.dbr_debug[id];
68691425b4SVenkateswara Naralasetty 	if (!dbr_debug)
69691425b4SVenkateswara Naralasetty 		return;
70691425b4SVenkateswara Naralasetty 
71691425b4SVenkateswara Naralasetty 	if (!dbr_debug->dbr_debug_enabled)
72691425b4SVenkateswara Naralasetty 		return;
73691425b4SVenkateswara Naralasetty 
74691425b4SVenkateswara Naralasetty 	dbr_data = &dbr_debug->dbr_dbg_data;
75691425b4SVenkateswara Naralasetty 
76691425b4SVenkateswara Naralasetty 	spin_lock_bh(&dbr_data->lock);
77691425b4SVenkateswara Naralasetty 
78691425b4SVenkateswara Naralasetty 	if (dbr_data->entries) {
79691425b4SVenkateswara Naralasetty 		entry = &dbr_data->entries[dbr_data->dbr_debug_idx];
80691425b4SVenkateswara Naralasetty 		entry->hp = srng->u.src_ring.hp;
81691425b4SVenkateswara Naralasetty 		entry->tp = *srng->u.src_ring.tp_addr;
82691425b4SVenkateswara Naralasetty 		entry->timestamp = jiffies;
83691425b4SVenkateswara Naralasetty 		entry->event = event;
84691425b4SVenkateswara Naralasetty 
85691425b4SVenkateswara Naralasetty 		dbr_data->dbr_debug_idx++;
86691425b4SVenkateswara Naralasetty 		if (dbr_data->dbr_debug_idx ==
87691425b4SVenkateswara Naralasetty 		    dbr_data->num_ring_debug_entries)
88691425b4SVenkateswara Naralasetty 			dbr_data->dbr_debug_idx = 0;
89691425b4SVenkateswara Naralasetty 	}
90691425b4SVenkateswara Naralasetty 
91691425b4SVenkateswara Naralasetty 	spin_unlock_bh(&dbr_data->lock);
92691425b4SVenkateswara Naralasetty }
93691425b4SVenkateswara Naralasetty 
94da3a9d3cSKalle Valo static void ath11k_fw_stats_pdevs_free(struct list_head *head)
95da3a9d3cSKalle Valo {
96da3a9d3cSKalle Valo 	struct ath11k_fw_stats_pdev *i, *tmp;
97da3a9d3cSKalle Valo 
98da3a9d3cSKalle Valo 	list_for_each_entry_safe(i, tmp, head, list) {
99da3a9d3cSKalle Valo 		list_del(&i->list);
100da3a9d3cSKalle Valo 		kfree(i);
101da3a9d3cSKalle Valo 	}
102da3a9d3cSKalle Valo }
103da3a9d3cSKalle Valo 
104da3a9d3cSKalle Valo static void ath11k_fw_stats_vdevs_free(struct list_head *head)
105da3a9d3cSKalle Valo {
106da3a9d3cSKalle Valo 	struct ath11k_fw_stats_vdev *i, *tmp;
107da3a9d3cSKalle Valo 
108da3a9d3cSKalle Valo 	list_for_each_entry_safe(i, tmp, head, list) {
109da3a9d3cSKalle Valo 		list_del(&i->list);
110da3a9d3cSKalle Valo 		kfree(i);
111da3a9d3cSKalle Valo 	}
112da3a9d3cSKalle Valo }
113da3a9d3cSKalle Valo 
114da3a9d3cSKalle Valo static void ath11k_fw_stats_bcn_free(struct list_head *head)
115da3a9d3cSKalle Valo {
116da3a9d3cSKalle Valo 	struct ath11k_fw_stats_bcn *i, *tmp;
117da3a9d3cSKalle Valo 
118da3a9d3cSKalle Valo 	list_for_each_entry_safe(i, tmp, head, list) {
119da3a9d3cSKalle Valo 		list_del(&i->list);
120da3a9d3cSKalle Valo 		kfree(i);
121da3a9d3cSKalle Valo 	}
122da3a9d3cSKalle Valo }
123da3a9d3cSKalle Valo 
124cb4e57dbSKalle Valo static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar)
125da3a9d3cSKalle Valo {
126da3a9d3cSKalle Valo 	spin_lock_bh(&ar->data_lock);
127da3a9d3cSKalle Valo 	ar->debug.fw_stats_done = false;
128da3a9d3cSKalle Valo 	ath11k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
129da3a9d3cSKalle Valo 	ath11k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
130da3a9d3cSKalle Valo 	spin_unlock_bh(&ar->data_lock);
131da3a9d3cSKalle Valo }
132da3a9d3cSKalle Valo 
133cb4e57dbSKalle Valo void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb)
134da3a9d3cSKalle Valo {
135da3a9d3cSKalle Valo 	struct ath11k_fw_stats stats = {};
136da3a9d3cSKalle Valo 	struct ath11k *ar;
137da3a9d3cSKalle Valo 	struct ath11k_pdev *pdev;
138da3a9d3cSKalle Valo 	bool is_end;
139da3a9d3cSKalle Valo 	static unsigned int num_vdev, num_bcn;
140da3a9d3cSKalle Valo 	size_t total_vdevs_started = 0;
141da3a9d3cSKalle Valo 	int i, ret;
142da3a9d3cSKalle Valo 
143da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&stats.pdevs);
144da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&stats.vdevs);
145da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&stats.bcn);
146da3a9d3cSKalle Valo 
147da3a9d3cSKalle Valo 	ret = ath11k_wmi_pull_fw_stats(ab, skb, &stats);
148da3a9d3cSKalle Valo 	if (ret) {
149da3a9d3cSKalle Valo 		ath11k_warn(ab, "failed to pull fw stats: %d\n", ret);
150da3a9d3cSKalle Valo 		goto free;
151da3a9d3cSKalle Valo 	}
152da3a9d3cSKalle Valo 
153da3a9d3cSKalle Valo 	rcu_read_lock();
154da3a9d3cSKalle Valo 	ar = ath11k_mac_get_ar_by_pdev_id(ab, stats.pdev_id);
155da3a9d3cSKalle Valo 	if (!ar) {
156da3a9d3cSKalle Valo 		rcu_read_unlock();
157da3a9d3cSKalle Valo 		ath11k_warn(ab, "failed to get ar for pdev_id %d: %d\n",
158da3a9d3cSKalle Valo 			    stats.pdev_id, ret);
159da3a9d3cSKalle Valo 		goto free;
160da3a9d3cSKalle Valo 	}
161da3a9d3cSKalle Valo 
162da3a9d3cSKalle Valo 	spin_lock_bh(&ar->data_lock);
163da3a9d3cSKalle Valo 
164da3a9d3cSKalle Valo 	if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
165da3a9d3cSKalle Valo 		list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);
166da3a9d3cSKalle Valo 		ar->debug.fw_stats_done = true;
167da3a9d3cSKalle Valo 		goto complete;
168da3a9d3cSKalle Valo 	}
169da3a9d3cSKalle Valo 
170b488c766SWen Gong 	if (stats.stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {
171b488c766SWen Gong 		ar->debug.fw_stats_done = true;
172b488c766SWen Gong 		goto complete;
173b488c766SWen Gong 	}
174b488c766SWen Gong 
175da3a9d3cSKalle Valo 	if (stats.stats_id == WMI_REQUEST_VDEV_STAT) {
176da3a9d3cSKalle Valo 		if (list_empty(&stats.vdevs)) {
177da3a9d3cSKalle Valo 			ath11k_warn(ab, "empty vdev stats");
178da3a9d3cSKalle Valo 			goto complete;
179da3a9d3cSKalle Valo 		}
180da3a9d3cSKalle Valo 		/* FW sends all the active VDEV stats irrespective of PDEV,
181da3a9d3cSKalle Valo 		 * hence limit until the count of all VDEVs started
182da3a9d3cSKalle Valo 		 */
183da3a9d3cSKalle Valo 		for (i = 0; i < ab->num_radios; i++) {
184da3a9d3cSKalle Valo 			pdev = rcu_dereference(ab->pdevs_active[i]);
185da3a9d3cSKalle Valo 			if (pdev && pdev->ar)
186da3a9d3cSKalle Valo 				total_vdevs_started += ar->num_started_vdevs;
187da3a9d3cSKalle Valo 		}
188da3a9d3cSKalle Valo 
189da3a9d3cSKalle Valo 		is_end = ((++num_vdev) == total_vdevs_started);
190da3a9d3cSKalle Valo 
191da3a9d3cSKalle Valo 		list_splice_tail_init(&stats.vdevs,
192da3a9d3cSKalle Valo 				      &ar->debug.fw_stats.vdevs);
193da3a9d3cSKalle Valo 
194da3a9d3cSKalle Valo 		if (is_end) {
195da3a9d3cSKalle Valo 			ar->debug.fw_stats_done = true;
196da3a9d3cSKalle Valo 			num_vdev = 0;
197da3a9d3cSKalle Valo 		}
198da3a9d3cSKalle Valo 		goto complete;
199da3a9d3cSKalle Valo 	}
200da3a9d3cSKalle Valo 
201da3a9d3cSKalle Valo 	if (stats.stats_id == WMI_REQUEST_BCN_STAT) {
202da3a9d3cSKalle Valo 		if (list_empty(&stats.bcn)) {
203da3a9d3cSKalle Valo 			ath11k_warn(ab, "empty bcn stats");
204da3a9d3cSKalle Valo 			goto complete;
205da3a9d3cSKalle Valo 		}
206da3a9d3cSKalle Valo 		/* Mark end until we reached the count of all started VDEVs
207da3a9d3cSKalle Valo 		 * within the PDEV
208da3a9d3cSKalle Valo 		 */
209da3a9d3cSKalle Valo 		is_end = ((++num_bcn) == ar->num_started_vdevs);
210da3a9d3cSKalle Valo 
211da3a9d3cSKalle Valo 		list_splice_tail_init(&stats.bcn,
212da3a9d3cSKalle Valo 				      &ar->debug.fw_stats.bcn);
213da3a9d3cSKalle Valo 
214da3a9d3cSKalle Valo 		if (is_end) {
215da3a9d3cSKalle Valo 			ar->debug.fw_stats_done = true;
216da3a9d3cSKalle Valo 			num_bcn = 0;
217da3a9d3cSKalle Valo 		}
218da3a9d3cSKalle Valo 	}
219da3a9d3cSKalle Valo complete:
220da3a9d3cSKalle Valo 	complete(&ar->debug.fw_stats_complete);
221da3a9d3cSKalle Valo 	rcu_read_unlock();
222da3a9d3cSKalle Valo 	spin_unlock_bh(&ar->data_lock);
223da3a9d3cSKalle Valo 
224da3a9d3cSKalle Valo free:
225da3a9d3cSKalle Valo 	ath11k_fw_stats_pdevs_free(&stats.pdevs);
226da3a9d3cSKalle Valo 	ath11k_fw_stats_vdevs_free(&stats.vdevs);
227da3a9d3cSKalle Valo 	ath11k_fw_stats_bcn_free(&stats.bcn);
228da3a9d3cSKalle Valo }
229da3a9d3cSKalle Valo 
230cb4e57dbSKalle Valo static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,
231da3a9d3cSKalle Valo 					   struct stats_request_params *req_param)
232da3a9d3cSKalle Valo {
233da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
234da3a9d3cSKalle Valo 	unsigned long timeout, time_left;
235da3a9d3cSKalle Valo 	int ret;
236da3a9d3cSKalle Valo 
237da3a9d3cSKalle Valo 	lockdep_assert_held(&ar->conf_mutex);
238da3a9d3cSKalle Valo 
239da3a9d3cSKalle Valo 	/* FW stats can get split when exceeding the stats data buffer limit.
240da3a9d3cSKalle Valo 	 * In that case, since there is no end marking for the back-to-back
241da3a9d3cSKalle Valo 	 * received 'update stats' event, we keep a 3 seconds timeout in case,
242da3a9d3cSKalle Valo 	 * fw_stats_done is not marked yet
243da3a9d3cSKalle Valo 	 */
244c8f2d41bSWen Gong 	timeout = jiffies + msecs_to_jiffies(3 * 1000);
245da3a9d3cSKalle Valo 
246cb4e57dbSKalle Valo 	ath11k_debugfs_fw_stats_reset(ar);
247da3a9d3cSKalle Valo 
248da3a9d3cSKalle Valo 	reinit_completion(&ar->debug.fw_stats_complete);
249da3a9d3cSKalle Valo 
250da3a9d3cSKalle Valo 	ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
251da3a9d3cSKalle Valo 
252da3a9d3cSKalle Valo 	if (ret) {
253da3a9d3cSKalle Valo 		ath11k_warn(ab, "could not request fw stats (%d)\n",
254da3a9d3cSKalle Valo 			    ret);
255da3a9d3cSKalle Valo 		return ret;
256da3a9d3cSKalle Valo 	}
257da3a9d3cSKalle Valo 
258da3a9d3cSKalle Valo 	time_left =
259da3a9d3cSKalle Valo 	wait_for_completion_timeout(&ar->debug.fw_stats_complete,
260da3a9d3cSKalle Valo 				    1 * HZ);
261da3a9d3cSKalle Valo 	if (!time_left)
262da3a9d3cSKalle Valo 		return -ETIMEDOUT;
263da3a9d3cSKalle Valo 
264da3a9d3cSKalle Valo 	for (;;) {
265da3a9d3cSKalle Valo 		if (time_after(jiffies, timeout))
266da3a9d3cSKalle Valo 			break;
267da3a9d3cSKalle Valo 
268da3a9d3cSKalle Valo 		spin_lock_bh(&ar->data_lock);
269da3a9d3cSKalle Valo 		if (ar->debug.fw_stats_done) {
270da3a9d3cSKalle Valo 			spin_unlock_bh(&ar->data_lock);
271da3a9d3cSKalle Valo 			break;
272da3a9d3cSKalle Valo 		}
273da3a9d3cSKalle Valo 		spin_unlock_bh(&ar->data_lock);
274da3a9d3cSKalle Valo 	}
275da3a9d3cSKalle Valo 	return 0;
276da3a9d3cSKalle Valo }
277da3a9d3cSKalle Valo 
278b488c766SWen Gong int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id,
279b488c766SWen Gong 				u32 vdev_id, u32 stats_id)
280b488c766SWen Gong {
281b488c766SWen Gong 	struct ath11k_base *ab = ar->ab;
282b488c766SWen Gong 	struct stats_request_params req_param;
283b488c766SWen Gong 	int ret;
284b488c766SWen Gong 
285b488c766SWen Gong 	mutex_lock(&ar->conf_mutex);
286b488c766SWen Gong 
287b488c766SWen Gong 	if (ar->state != ATH11K_STATE_ON) {
288b488c766SWen Gong 		ret = -ENETDOWN;
289b488c766SWen Gong 		goto err_unlock;
290b488c766SWen Gong 	}
291b488c766SWen Gong 
292b488c766SWen Gong 	req_param.pdev_id = pdev_id;
293b488c766SWen Gong 	req_param.vdev_id = vdev_id;
294b488c766SWen Gong 	req_param.stats_id = stats_id;
295b488c766SWen Gong 
296b488c766SWen Gong 	ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
297b488c766SWen Gong 	if (ret)
298b488c766SWen Gong 		ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
299b488c766SWen Gong 
300b488c766SWen Gong 	ath11k_dbg(ab, ATH11K_DBG_WMI,
301b488c766SWen Gong 		   "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
302b488c766SWen Gong 		   pdev_id, vdev_id, stats_id);
303b488c766SWen Gong 
304b488c766SWen Gong err_unlock:
305b488c766SWen Gong 	mutex_unlock(&ar->conf_mutex);
306b488c766SWen Gong 
307b488c766SWen Gong 	return ret;
308b488c766SWen Gong }
309b488c766SWen Gong 
310da3a9d3cSKalle Valo static int ath11k_open_pdev_stats(struct inode *inode, struct file *file)
311da3a9d3cSKalle Valo {
312da3a9d3cSKalle Valo 	struct ath11k *ar = inode->i_private;
313da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
314da3a9d3cSKalle Valo 	struct stats_request_params req_param;
315da3a9d3cSKalle Valo 	void *buf = NULL;
316da3a9d3cSKalle Valo 	int ret;
317da3a9d3cSKalle Valo 
318da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
319da3a9d3cSKalle Valo 
320da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
321da3a9d3cSKalle Valo 		ret = -ENETDOWN;
322da3a9d3cSKalle Valo 		goto err_unlock;
323da3a9d3cSKalle Valo 	}
324da3a9d3cSKalle Valo 
325da3a9d3cSKalle Valo 	buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
326da3a9d3cSKalle Valo 	if (!buf) {
327da3a9d3cSKalle Valo 		ret = -ENOMEM;
328da3a9d3cSKalle Valo 		goto err_unlock;
329da3a9d3cSKalle Valo 	}
330da3a9d3cSKalle Valo 
331da3a9d3cSKalle Valo 	req_param.pdev_id = ar->pdev->pdev_id;
332da3a9d3cSKalle Valo 	req_param.vdev_id = 0;
333da3a9d3cSKalle Valo 	req_param.stats_id = WMI_REQUEST_PDEV_STAT;
334da3a9d3cSKalle Valo 
335cb4e57dbSKalle Valo 	ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
336da3a9d3cSKalle Valo 	if (ret) {
337da3a9d3cSKalle Valo 		ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
338da3a9d3cSKalle Valo 		goto err_free;
339da3a9d3cSKalle Valo 	}
340da3a9d3cSKalle Valo 
341da3a9d3cSKalle Valo 	ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id,
342da3a9d3cSKalle Valo 				 buf);
343da3a9d3cSKalle Valo 
344da3a9d3cSKalle Valo 	file->private_data = buf;
345da3a9d3cSKalle Valo 
346da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
347da3a9d3cSKalle Valo 	return 0;
348da3a9d3cSKalle Valo 
349da3a9d3cSKalle Valo err_free:
350da3a9d3cSKalle Valo 	vfree(buf);
351da3a9d3cSKalle Valo 
352da3a9d3cSKalle Valo err_unlock:
353da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
354da3a9d3cSKalle Valo 	return ret;
355da3a9d3cSKalle Valo }
356da3a9d3cSKalle Valo 
357da3a9d3cSKalle Valo static int ath11k_release_pdev_stats(struct inode *inode, struct file *file)
358da3a9d3cSKalle Valo {
359da3a9d3cSKalle Valo 	vfree(file->private_data);
360da3a9d3cSKalle Valo 
361da3a9d3cSKalle Valo 	return 0;
362da3a9d3cSKalle Valo }
363da3a9d3cSKalle Valo 
364da3a9d3cSKalle Valo static ssize_t ath11k_read_pdev_stats(struct file *file,
365da3a9d3cSKalle Valo 				      char __user *user_buf,
366da3a9d3cSKalle Valo 				      size_t count, loff_t *ppos)
367da3a9d3cSKalle Valo {
368da3a9d3cSKalle Valo 	const char *buf = file->private_data;
369da3a9d3cSKalle Valo 	size_t len = strlen(buf);
370da3a9d3cSKalle Valo 
371da3a9d3cSKalle Valo 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
372da3a9d3cSKalle Valo }
373da3a9d3cSKalle Valo 
374da3a9d3cSKalle Valo static const struct file_operations fops_pdev_stats = {
375da3a9d3cSKalle Valo 	.open = ath11k_open_pdev_stats,
376da3a9d3cSKalle Valo 	.release = ath11k_release_pdev_stats,
377da3a9d3cSKalle Valo 	.read = ath11k_read_pdev_stats,
378da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
379da3a9d3cSKalle Valo 	.llseek = default_llseek,
380da3a9d3cSKalle Valo };
381da3a9d3cSKalle Valo 
382da3a9d3cSKalle Valo static int ath11k_open_vdev_stats(struct inode *inode, struct file *file)
383da3a9d3cSKalle Valo {
384da3a9d3cSKalle Valo 	struct ath11k *ar = inode->i_private;
385da3a9d3cSKalle Valo 	struct stats_request_params req_param;
386da3a9d3cSKalle Valo 	void *buf = NULL;
387da3a9d3cSKalle Valo 	int ret;
388da3a9d3cSKalle Valo 
389da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
390da3a9d3cSKalle Valo 
391da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
392da3a9d3cSKalle Valo 		ret = -ENETDOWN;
393da3a9d3cSKalle Valo 		goto err_unlock;
394da3a9d3cSKalle Valo 	}
395da3a9d3cSKalle Valo 
396da3a9d3cSKalle Valo 	buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
397da3a9d3cSKalle Valo 	if (!buf) {
398da3a9d3cSKalle Valo 		ret = -ENOMEM;
399da3a9d3cSKalle Valo 		goto err_unlock;
400da3a9d3cSKalle Valo 	}
401da3a9d3cSKalle Valo 
402da3a9d3cSKalle Valo 	req_param.pdev_id = ar->pdev->pdev_id;
403da3a9d3cSKalle Valo 	/* VDEV stats is always sent for all active VDEVs from FW */
404da3a9d3cSKalle Valo 	req_param.vdev_id = 0;
405da3a9d3cSKalle Valo 	req_param.stats_id = WMI_REQUEST_VDEV_STAT;
406da3a9d3cSKalle Valo 
407cb4e57dbSKalle Valo 	ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
408da3a9d3cSKalle Valo 	if (ret) {
409da3a9d3cSKalle Valo 		ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);
410da3a9d3cSKalle Valo 		goto err_free;
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 	file->private_data = buf;
417da3a9d3cSKalle Valo 
418da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
419da3a9d3cSKalle Valo 	return 0;
420da3a9d3cSKalle Valo 
421da3a9d3cSKalle Valo err_free:
422da3a9d3cSKalle Valo 	vfree(buf);
423da3a9d3cSKalle Valo 
424da3a9d3cSKalle Valo err_unlock:
425da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
426da3a9d3cSKalle Valo 	return ret;
427da3a9d3cSKalle Valo }
428da3a9d3cSKalle Valo 
429da3a9d3cSKalle Valo static int ath11k_release_vdev_stats(struct inode *inode, struct file *file)
430da3a9d3cSKalle Valo {
431da3a9d3cSKalle Valo 	vfree(file->private_data);
432da3a9d3cSKalle Valo 
433da3a9d3cSKalle Valo 	return 0;
434da3a9d3cSKalle Valo }
435da3a9d3cSKalle Valo 
436da3a9d3cSKalle Valo static ssize_t ath11k_read_vdev_stats(struct file *file,
437da3a9d3cSKalle Valo 				      char __user *user_buf,
438da3a9d3cSKalle Valo 				      size_t count, loff_t *ppos)
439da3a9d3cSKalle Valo {
440da3a9d3cSKalle Valo 	const char *buf = file->private_data;
441da3a9d3cSKalle Valo 	size_t len = strlen(buf);
442da3a9d3cSKalle Valo 
443da3a9d3cSKalle Valo 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
444da3a9d3cSKalle Valo }
445da3a9d3cSKalle Valo 
446da3a9d3cSKalle Valo static const struct file_operations fops_vdev_stats = {
447da3a9d3cSKalle Valo 	.open = ath11k_open_vdev_stats,
448da3a9d3cSKalle Valo 	.release = ath11k_release_vdev_stats,
449da3a9d3cSKalle Valo 	.read = ath11k_read_vdev_stats,
450da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
451da3a9d3cSKalle Valo 	.llseek = default_llseek,
452da3a9d3cSKalle Valo };
453da3a9d3cSKalle Valo 
454da3a9d3cSKalle Valo static int ath11k_open_bcn_stats(struct inode *inode, struct file *file)
455da3a9d3cSKalle Valo {
456da3a9d3cSKalle Valo 	struct ath11k *ar = inode->i_private;
457da3a9d3cSKalle Valo 	struct ath11k_vif *arvif;
458da3a9d3cSKalle Valo 	struct stats_request_params req_param;
459da3a9d3cSKalle Valo 	void *buf = NULL;
460da3a9d3cSKalle Valo 	int ret;
461da3a9d3cSKalle Valo 
462da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
463da3a9d3cSKalle Valo 
464da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
465da3a9d3cSKalle Valo 		ret = -ENETDOWN;
466da3a9d3cSKalle Valo 		goto err_unlock;
467da3a9d3cSKalle Valo 	}
468da3a9d3cSKalle Valo 
469da3a9d3cSKalle Valo 	buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
470da3a9d3cSKalle Valo 	if (!buf) {
471da3a9d3cSKalle Valo 		ret = -ENOMEM;
472da3a9d3cSKalle Valo 		goto err_unlock;
473da3a9d3cSKalle Valo 	}
474da3a9d3cSKalle Valo 
475da3a9d3cSKalle Valo 	req_param.stats_id = WMI_REQUEST_BCN_STAT;
476da3a9d3cSKalle Valo 	req_param.pdev_id = ar->pdev->pdev_id;
477da3a9d3cSKalle Valo 
478da3a9d3cSKalle Valo 	/* loop all active VDEVs for bcn stats */
479da3a9d3cSKalle Valo 	list_for_each_entry(arvif, &ar->arvifs, list) {
480da3a9d3cSKalle Valo 		if (!arvif->is_up)
481da3a9d3cSKalle Valo 			continue;
482da3a9d3cSKalle Valo 
483da3a9d3cSKalle Valo 		req_param.vdev_id = arvif->vdev_id;
484cb4e57dbSKalle Valo 		ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
485da3a9d3cSKalle Valo 		if (ret) {
486da3a9d3cSKalle Valo 			ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);
487da3a9d3cSKalle Valo 			goto err_free;
488da3a9d3cSKalle Valo 		}
489da3a9d3cSKalle Valo 	}
490da3a9d3cSKalle Valo 
491da3a9d3cSKalle Valo 	ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id,
492da3a9d3cSKalle Valo 				 buf);
493da3a9d3cSKalle Valo 
494da3a9d3cSKalle Valo 	/* since beacon stats request is looped for all active VDEVs, saved fw
495da3a9d3cSKalle Valo 	 * stats is not freed for each request until done for all active VDEVs
496da3a9d3cSKalle Valo 	 */
497da3a9d3cSKalle Valo 	spin_lock_bh(&ar->data_lock);
498da3a9d3cSKalle Valo 	ath11k_fw_stats_bcn_free(&ar->debug.fw_stats.bcn);
499da3a9d3cSKalle Valo 	spin_unlock_bh(&ar->data_lock);
500da3a9d3cSKalle Valo 
501da3a9d3cSKalle Valo 	file->private_data = buf;
502da3a9d3cSKalle Valo 
503da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
504da3a9d3cSKalle Valo 	return 0;
505da3a9d3cSKalle Valo 
506da3a9d3cSKalle Valo err_free:
507da3a9d3cSKalle Valo 	vfree(buf);
508da3a9d3cSKalle Valo 
509da3a9d3cSKalle Valo err_unlock:
510da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
511da3a9d3cSKalle Valo 	return ret;
512da3a9d3cSKalle Valo }
513da3a9d3cSKalle Valo 
514da3a9d3cSKalle Valo static int ath11k_release_bcn_stats(struct inode *inode, struct file *file)
515da3a9d3cSKalle Valo {
516da3a9d3cSKalle Valo 	vfree(file->private_data);
517da3a9d3cSKalle Valo 
518da3a9d3cSKalle Valo 	return 0;
519da3a9d3cSKalle Valo }
520da3a9d3cSKalle Valo 
521da3a9d3cSKalle Valo static ssize_t ath11k_read_bcn_stats(struct file *file,
522da3a9d3cSKalle Valo 				     char __user *user_buf,
523da3a9d3cSKalle Valo 				     size_t count, loff_t *ppos)
524da3a9d3cSKalle Valo {
525da3a9d3cSKalle Valo 	const char *buf = file->private_data;
526da3a9d3cSKalle Valo 	size_t len = strlen(buf);
527da3a9d3cSKalle Valo 
528da3a9d3cSKalle Valo 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
529da3a9d3cSKalle Valo }
530da3a9d3cSKalle Valo 
531da3a9d3cSKalle Valo static const struct file_operations fops_bcn_stats = {
532da3a9d3cSKalle Valo 	.open = ath11k_open_bcn_stats,
533da3a9d3cSKalle Valo 	.release = ath11k_release_bcn_stats,
534da3a9d3cSKalle Valo 	.read = ath11k_read_bcn_stats,
535da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
536da3a9d3cSKalle Valo 	.llseek = default_llseek,
537da3a9d3cSKalle Valo };
538da3a9d3cSKalle Valo 
539da3a9d3cSKalle Valo static ssize_t ath11k_read_simulate_fw_crash(struct file *file,
540da3a9d3cSKalle Valo 					     char __user *user_buf,
541da3a9d3cSKalle Valo 					     size_t count, loff_t *ppos)
542da3a9d3cSKalle Valo {
543da3a9d3cSKalle Valo 	const char buf[] =
544da3a9d3cSKalle Valo 		"To simulate firmware crash write one of the keywords to this file:\n"
545da3a9d3cSKalle Valo 		"`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n"
546da3a9d3cSKalle Valo 		"`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
547da3a9d3cSKalle Valo 
548da3a9d3cSKalle Valo 	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
549da3a9d3cSKalle Valo }
550da3a9d3cSKalle Valo 
551da3a9d3cSKalle Valo /* Simulate firmware crash:
552da3a9d3cSKalle Valo  * 'soft': Call wmi command causing firmware hang. This firmware hang is
553da3a9d3cSKalle Valo  * recoverable by warm firmware reset.
554da3a9d3cSKalle Valo  * 'hard': Force firmware crash by setting any vdev parameter for not allowed
555da3a9d3cSKalle Valo  * vdev id. This is hard firmware crash because it is recoverable only by cold
556da3a9d3cSKalle Valo  * firmware reset.
557da3a9d3cSKalle Valo  */
558da3a9d3cSKalle Valo static ssize_t ath11k_write_simulate_fw_crash(struct file *file,
559da3a9d3cSKalle Valo 					      const char __user *user_buf,
560da3a9d3cSKalle Valo 					      size_t count, loff_t *ppos)
561da3a9d3cSKalle Valo {
562da3a9d3cSKalle Valo 	struct ath11k_base *ab = file->private_data;
563da3a9d3cSKalle Valo 	struct ath11k_pdev *pdev;
564da3a9d3cSKalle Valo 	struct ath11k *ar = ab->pdevs[0].ar;
565da3a9d3cSKalle Valo 	char buf[32] = {0};
566da3a9d3cSKalle Valo 	ssize_t rc;
567da3a9d3cSKalle Valo 	int i, ret, radioup = 0;
568da3a9d3cSKalle Valo 
569da3a9d3cSKalle Valo 	for (i = 0; i < ab->num_radios; i++) {
570da3a9d3cSKalle Valo 		pdev = &ab->pdevs[i];
571da3a9d3cSKalle Valo 		ar = pdev->ar;
572da3a9d3cSKalle Valo 		if (ar && ar->state == ATH11K_STATE_ON) {
573da3a9d3cSKalle Valo 			radioup = 1;
574da3a9d3cSKalle Valo 			break;
575da3a9d3cSKalle Valo 		}
576da3a9d3cSKalle Valo 	}
577da3a9d3cSKalle Valo 	/* filter partial writes and invalid commands */
578da3a9d3cSKalle Valo 	if (*ppos != 0 || count >= sizeof(buf) || count == 0)
579da3a9d3cSKalle Valo 		return -EINVAL;
580da3a9d3cSKalle Valo 
581da3a9d3cSKalle Valo 	rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
582da3a9d3cSKalle Valo 	if (rc < 0)
583da3a9d3cSKalle Valo 		return rc;
584da3a9d3cSKalle Valo 
585da3a9d3cSKalle Valo 	/* drop the possible '\n' from the end */
586da3a9d3cSKalle Valo 	if (buf[*ppos - 1] == '\n')
587da3a9d3cSKalle Valo 		buf[*ppos - 1] = '\0';
588da3a9d3cSKalle Valo 
589da3a9d3cSKalle Valo 	if (radioup == 0) {
590da3a9d3cSKalle Valo 		ret = -ENETDOWN;
591da3a9d3cSKalle Valo 		goto exit;
592da3a9d3cSKalle Valo 	}
593da3a9d3cSKalle Valo 
594da3a9d3cSKalle Valo 	if (!strcmp(buf, "assert")) {
595da3a9d3cSKalle Valo 		ath11k_info(ab, "simulating firmware assert crash\n");
596da3a9d3cSKalle Valo 		ret = ath11k_wmi_force_fw_hang_cmd(ar,
597da3a9d3cSKalle Valo 						   ATH11K_WMI_FW_HANG_ASSERT_TYPE,
598da3a9d3cSKalle Valo 						   ATH11K_WMI_FW_HANG_DELAY);
59978e3e609SWen Gong 	} else if (!strcmp(buf, "hw-restart")) {
60078e3e609SWen Gong 		ath11k_info(ab, "user requested hw restart\n");
60178e3e609SWen Gong 		queue_work(ab->workqueue_aux, &ab->reset_work);
60278e3e609SWen Gong 		ret = 0;
603da3a9d3cSKalle Valo 	} else {
604da3a9d3cSKalle Valo 		ret = -EINVAL;
605da3a9d3cSKalle Valo 		goto exit;
606da3a9d3cSKalle Valo 	}
607da3a9d3cSKalle Valo 
608da3a9d3cSKalle Valo 	if (ret) {
609da3a9d3cSKalle Valo 		ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret);
610da3a9d3cSKalle Valo 		goto exit;
611da3a9d3cSKalle Valo 	}
612da3a9d3cSKalle Valo 
613da3a9d3cSKalle Valo 	ret = count;
614da3a9d3cSKalle Valo 
615da3a9d3cSKalle Valo exit:
616da3a9d3cSKalle Valo 	return ret;
617da3a9d3cSKalle Valo }
618da3a9d3cSKalle Valo 
619da3a9d3cSKalle Valo static const struct file_operations fops_simulate_fw_crash = {
620da3a9d3cSKalle Valo 	.read = ath11k_read_simulate_fw_crash,
621da3a9d3cSKalle Valo 	.write = ath11k_write_simulate_fw_crash,
622da3a9d3cSKalle Valo 	.open = simple_open,
623da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
624da3a9d3cSKalle Valo 	.llseek = default_llseek,
625da3a9d3cSKalle Valo };
626da3a9d3cSKalle Valo 
627da3a9d3cSKalle Valo static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file,
628da3a9d3cSKalle Valo 						 const char __user *ubuf,
629da3a9d3cSKalle Valo 						 size_t count, loff_t *ppos)
630da3a9d3cSKalle Valo {
631da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
632da3a9d3cSKalle Valo 	u32 filter;
633da3a9d3cSKalle Valo 	int ret;
634da3a9d3cSKalle Valo 
635da3a9d3cSKalle Valo 	if (kstrtouint_from_user(ubuf, count, 0, &filter))
636da3a9d3cSKalle Valo 		return -EINVAL;
637da3a9d3cSKalle Valo 
638da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
639da3a9d3cSKalle Valo 
640da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
641da3a9d3cSKalle Valo 		ret = -ENETDOWN;
642da3a9d3cSKalle Valo 		goto out;
643da3a9d3cSKalle Valo 	}
644da3a9d3cSKalle Valo 
645da3a9d3cSKalle Valo 	if (filter == ar->debug.extd_tx_stats) {
646da3a9d3cSKalle Valo 		ret = count;
647da3a9d3cSKalle Valo 		goto out;
648da3a9d3cSKalle Valo 	}
649da3a9d3cSKalle Valo 
650da3a9d3cSKalle Valo 	ar->debug.extd_tx_stats = filter;
651da3a9d3cSKalle Valo 	ret = count;
652da3a9d3cSKalle Valo 
653da3a9d3cSKalle Valo out:
654da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
655da3a9d3cSKalle Valo 	return ret;
656da3a9d3cSKalle Valo }
657da3a9d3cSKalle Valo 
658da3a9d3cSKalle Valo static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file,
659da3a9d3cSKalle Valo 						char __user *ubuf,
660da3a9d3cSKalle Valo 						size_t count, loff_t *ppos)
661da3a9d3cSKalle Valo 
662da3a9d3cSKalle Valo {
663da3a9d3cSKalle Valo 	char buf[32] = {0};
664da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
665da3a9d3cSKalle Valo 	int len = 0;
666da3a9d3cSKalle Valo 
667da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
668da3a9d3cSKalle Valo 	len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
669da3a9d3cSKalle Valo 			ar->debug.extd_tx_stats);
670da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
671da3a9d3cSKalle Valo 
672da3a9d3cSKalle Valo 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
673da3a9d3cSKalle Valo }
674da3a9d3cSKalle Valo 
675da3a9d3cSKalle Valo static const struct file_operations fops_extd_tx_stats = {
676da3a9d3cSKalle Valo 	.read = ath11k_read_enable_extd_tx_stats,
677da3a9d3cSKalle Valo 	.write = ath11k_write_enable_extd_tx_stats,
678da3a9d3cSKalle Valo 	.open = simple_open
679da3a9d3cSKalle Valo };
680da3a9d3cSKalle Valo 
681da3a9d3cSKalle Valo static ssize_t ath11k_write_extd_rx_stats(struct file *file,
682da3a9d3cSKalle Valo 					  const char __user *ubuf,
683da3a9d3cSKalle Valo 					  size_t count, loff_t *ppos)
684da3a9d3cSKalle Valo {
685da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
686da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
687da3a9d3cSKalle Valo 	struct htt_rx_ring_tlv_filter tlv_filter = {0};
688da3a9d3cSKalle Valo 	u32 enable, rx_filter = 0, ring_id;
689da3a9d3cSKalle Valo 	int i;
690da3a9d3cSKalle Valo 	int ret;
691da3a9d3cSKalle Valo 
692da3a9d3cSKalle Valo 	if (kstrtouint_from_user(ubuf, count, 0, &enable))
693da3a9d3cSKalle Valo 		return -EINVAL;
694da3a9d3cSKalle Valo 
695da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
696da3a9d3cSKalle Valo 
697da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
698da3a9d3cSKalle Valo 		ret = -ENETDOWN;
699da3a9d3cSKalle Valo 		goto exit;
700da3a9d3cSKalle Valo 	}
701da3a9d3cSKalle Valo 
702da3a9d3cSKalle Valo 	if (enable > 1) {
703da3a9d3cSKalle Valo 		ret = -EINVAL;
704da3a9d3cSKalle Valo 		goto exit;
705da3a9d3cSKalle Valo 	}
706da3a9d3cSKalle Valo 
707da3a9d3cSKalle Valo 	if (enable == ar->debug.extd_rx_stats) {
708da3a9d3cSKalle Valo 		ret = count;
709da3a9d3cSKalle Valo 		goto exit;
710da3a9d3cSKalle Valo 	}
711da3a9d3cSKalle Valo 
71267a9d399SMiles Hu 	if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) {
71367a9d399SMiles Hu 		ar->debug.extd_rx_stats = enable;
71467a9d399SMiles Hu 		ret = count;
71567a9d399SMiles Hu 		goto exit;
71667a9d399SMiles Hu 	}
71767a9d399SMiles Hu 
718da3a9d3cSKalle Valo 	if (enable) {
719da3a9d3cSKalle Valo 		rx_filter =  HTT_RX_FILTER_TLV_FLAGS_MPDU_START;
720da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START;
721da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END;
722da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;
723da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT;
724da3a9d3cSKalle Valo 		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE;
725da3a9d3cSKalle Valo 
726da3a9d3cSKalle Valo 		tlv_filter.rx_filter = rx_filter;
727da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;
728da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;
729da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;
730da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |
731da3a9d3cSKalle Valo 			HTT_RX_FP_DATA_FILTER_FLASG3;
732da3a9d3cSKalle Valo 	} else {
733da3a9d3cSKalle Valo 		tlv_filter = ath11k_mac_mon_status_filter_default;
734da3a9d3cSKalle Valo 	}
735da3a9d3cSKalle Valo 
736da3a9d3cSKalle Valo 	ar->debug.rx_filter = tlv_filter.rx_filter;
737da3a9d3cSKalle Valo 
738da3a9d3cSKalle Valo 	for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {
739da3a9d3cSKalle Valo 		ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
740da3a9d3cSKalle Valo 		ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,
741da3a9d3cSKalle Valo 						       HAL_RXDMA_MONITOR_STATUS,
742da3a9d3cSKalle Valo 						       DP_RX_BUFFER_SIZE, &tlv_filter);
743da3a9d3cSKalle Valo 
744da3a9d3cSKalle Valo 		if (ret) {
745da3a9d3cSKalle Valo 			ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");
746da3a9d3cSKalle Valo 			goto exit;
747da3a9d3cSKalle Valo 		}
748da3a9d3cSKalle Valo 	}
749da3a9d3cSKalle Valo 
750da3a9d3cSKalle Valo 	ar->debug.extd_rx_stats = enable;
751da3a9d3cSKalle Valo 	ret = count;
752da3a9d3cSKalle Valo exit:
753da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
754da3a9d3cSKalle Valo 	return ret;
755da3a9d3cSKalle Valo }
756da3a9d3cSKalle Valo 
757da3a9d3cSKalle Valo static ssize_t ath11k_read_extd_rx_stats(struct file *file,
758da3a9d3cSKalle Valo 					 char __user *ubuf,
759da3a9d3cSKalle Valo 					 size_t count, loff_t *ppos)
760da3a9d3cSKalle Valo {
761da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
762da3a9d3cSKalle Valo 	char buf[32];
763da3a9d3cSKalle Valo 	int len = 0;
764da3a9d3cSKalle Valo 
765da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
766da3a9d3cSKalle Valo 	len = scnprintf(buf, sizeof(buf) - len, "%d\n",
767da3a9d3cSKalle Valo 			ar->debug.extd_rx_stats);
768da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
769da3a9d3cSKalle Valo 
770da3a9d3cSKalle Valo 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
771da3a9d3cSKalle Valo }
772da3a9d3cSKalle Valo 
773da3a9d3cSKalle Valo static const struct file_operations fops_extd_rx_stats = {
774da3a9d3cSKalle Valo 	.read = ath11k_read_extd_rx_stats,
775da3a9d3cSKalle Valo 	.write = ath11k_write_extd_rx_stats,
776da3a9d3cSKalle Valo 	.open = simple_open,
777da3a9d3cSKalle Valo };
778da3a9d3cSKalle Valo 
779da3a9d3cSKalle Valo static int ath11k_fill_bp_stats(struct ath11k_base *ab,
780da3a9d3cSKalle Valo 				struct ath11k_bp_stats *bp_stats,
781da3a9d3cSKalle Valo 				char *buf, int len, int size)
782da3a9d3cSKalle Valo {
783da3a9d3cSKalle Valo 	lockdep_assert_held(&ab->base_lock);
784da3a9d3cSKalle Valo 
785da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "count: %u\n",
786da3a9d3cSKalle Valo 			 bp_stats->count);
787da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "hp: %u\n",
788da3a9d3cSKalle Valo 			 bp_stats->hp);
789da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "tp: %u\n",
790da3a9d3cSKalle Valo 			 bp_stats->tp);
791da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "seen before: %ums\n\n",
792da3a9d3cSKalle Valo 			 jiffies_to_msecs(jiffies - bp_stats->jiffies));
793da3a9d3cSKalle Valo 	return len;
794da3a9d3cSKalle Valo }
795da3a9d3cSKalle Valo 
796cb4e57dbSKalle Valo static ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab,
797da3a9d3cSKalle Valo 						     char *buf, int size)
798da3a9d3cSKalle Valo {
799da3a9d3cSKalle Valo 	struct ath11k_bp_stats *bp_stats;
800da3a9d3cSKalle Valo 	bool stats_rxd = false;
801da3a9d3cSKalle Valo 	u8 i, pdev_idx;
802da3a9d3cSKalle Valo 	int len = 0;
803da3a9d3cSKalle Valo 
804da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nBackpressure Stats\n");
805da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "==================\n");
806da3a9d3cSKalle Valo 
807da3a9d3cSKalle Valo 	spin_lock_bh(&ab->base_lock);
808da3a9d3cSKalle Valo 	for (i = 0; i < HTT_SW_UMAC_RING_IDX_MAX; i++) {
809da3a9d3cSKalle Valo 		bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[i];
810da3a9d3cSKalle Valo 
811da3a9d3cSKalle Valo 		if (!bp_stats->count)
812da3a9d3cSKalle Valo 			continue;
813da3a9d3cSKalle Valo 
814da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len, "Ring: %s\n",
815da3a9d3cSKalle Valo 				 htt_bp_umac_ring[i]);
816da3a9d3cSKalle Valo 		len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
817da3a9d3cSKalle Valo 		stats_rxd = true;
818da3a9d3cSKalle Valo 	}
819da3a9d3cSKalle Valo 
820da3a9d3cSKalle Valo 	for (i = 0; i < HTT_SW_LMAC_RING_IDX_MAX; i++) {
821da3a9d3cSKalle Valo 		for (pdev_idx = 0; pdev_idx < MAX_RADIOS; pdev_idx++) {
822da3a9d3cSKalle Valo 			bp_stats =
823da3a9d3cSKalle Valo 				&ab->soc_stats.bp_stats.lmac_ring_bp_stats[i][pdev_idx];
824da3a9d3cSKalle Valo 
825da3a9d3cSKalle Valo 			if (!bp_stats->count)
826da3a9d3cSKalle Valo 				continue;
827da3a9d3cSKalle Valo 
828da3a9d3cSKalle Valo 			len += scnprintf(buf + len, size - len, "Ring: %s\n",
829da3a9d3cSKalle Valo 					 htt_bp_lmac_ring[i]);
830da3a9d3cSKalle Valo 			len += scnprintf(buf + len, size - len, "pdev: %d\n",
831da3a9d3cSKalle Valo 					 pdev_idx);
832da3a9d3cSKalle Valo 			len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
833da3a9d3cSKalle Valo 			stats_rxd = true;
834da3a9d3cSKalle Valo 		}
835da3a9d3cSKalle Valo 	}
836da3a9d3cSKalle Valo 	spin_unlock_bh(&ab->base_lock);
837da3a9d3cSKalle Valo 
838da3a9d3cSKalle Valo 	if (!stats_rxd)
839da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len,
840da3a9d3cSKalle Valo 				 "No Ring Backpressure stats received\n\n");
841da3a9d3cSKalle Valo 
842da3a9d3cSKalle Valo 	return len;
843da3a9d3cSKalle Valo }
844da3a9d3cSKalle Valo 
845cb4e57dbSKalle Valo static ssize_t ath11k_debugfs_dump_soc_dp_stats(struct file *file,
846da3a9d3cSKalle Valo 						char __user *user_buf,
847da3a9d3cSKalle Valo 						size_t count, loff_t *ppos)
848da3a9d3cSKalle Valo {
849da3a9d3cSKalle Valo 	struct ath11k_base *ab = file->private_data;
850da3a9d3cSKalle Valo 	struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;
851da3a9d3cSKalle Valo 	int len = 0, i, retval;
852da3a9d3cSKalle Valo 	const int size = 4096;
853da3a9d3cSKalle Valo 	static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = {
854da3a9d3cSKalle Valo 			"Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC",
855da3a9d3cSKalle Valo 			"Unencrypt", "MSDU len", "MSDU limit", "WiFi parse",
856da3a9d3cSKalle Valo 			"AMSDU parse", "SA timeout", "DA timeout",
857da3a9d3cSKalle Valo 			"Flow timeout", "Flush req"};
858da3a9d3cSKalle Valo 	static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = {
859da3a9d3cSKalle Valo 			"Desc addr zero", "Desc inval", "AMPDU in non BA",
860da3a9d3cSKalle Valo 			"Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump",
861da3a9d3cSKalle Valo 			"Frame OOR", "BAR OOR", "No BA session",
862da3a9d3cSKalle Valo 			"Frame SN equal SSN", "PN check fail", "2k err",
863da3a9d3cSKalle Valo 			"PN err", "Desc blocked"};
864da3a9d3cSKalle Valo 
865da3a9d3cSKalle Valo 	char *buf;
866da3a9d3cSKalle Valo 
867da3a9d3cSKalle Valo 	buf = kzalloc(size, GFP_KERNEL);
868da3a9d3cSKalle Valo 	if (!buf)
869da3a9d3cSKalle Valo 		return -ENOMEM;
870da3a9d3cSKalle Valo 
871da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n");
872da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "err ring pkts: %u\n",
873da3a9d3cSKalle Valo 			 soc_stats->err_ring_pkts);
874da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n",
875da3a9d3cSKalle Valo 			 soc_stats->invalid_rbm);
876da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "RXDMA errors:\n");
877da3a9d3cSKalle Valo 	for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)
878da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len, "%s: %u\n",
879da3a9d3cSKalle Valo 				 rxdma_err[i], soc_stats->rxdma_error[i]);
880da3a9d3cSKalle Valo 
881da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nREO errors:\n");
882da3a9d3cSKalle Valo 	for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)
883da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len, "%s: %u\n",
884da3a9d3cSKalle Valo 				 reo_err[i], soc_stats->reo_error[i]);
885da3a9d3cSKalle Valo 
886da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n");
887da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len,
888da3a9d3cSKalle Valo 			 "ring0: %u\nring1: %u\nring2: %u\nring3: %u\n",
889da3a9d3cSKalle Valo 			 soc_stats->hal_reo_error[0],
890da3a9d3cSKalle Valo 			 soc_stats->hal_reo_error[1],
891da3a9d3cSKalle Valo 			 soc_stats->hal_reo_error[2],
892da3a9d3cSKalle Valo 			 soc_stats->hal_reo_error[3]);
893da3a9d3cSKalle Valo 
894da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n");
895da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n");
896da3a9d3cSKalle Valo 
89731582373SBaochen Qiang 	for (i = 0; i < ab->hw_params.max_tx_ring; i++)
898da3a9d3cSKalle Valo 		len += scnprintf(buf + len, size - len, "ring%d: %u\n",
899da3a9d3cSKalle Valo 				 i, soc_stats->tx_err.desc_na[i]);
900da3a9d3cSKalle Valo 
901da3a9d3cSKalle Valo 	len += scnprintf(buf + len, size - len,
902da3a9d3cSKalle Valo 			 "\nMisc Transmit Failures: %d\n",
903da3a9d3cSKalle Valo 			 atomic_read(&soc_stats->tx_err.misc_fail));
904da3a9d3cSKalle Valo 
905cb4e57dbSKalle Valo 	len += ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf + len, size - len);
906da3a9d3cSKalle Valo 
907da3a9d3cSKalle Valo 	if (len > size)
908da3a9d3cSKalle Valo 		len = size;
909da3a9d3cSKalle Valo 	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
910da3a9d3cSKalle Valo 	kfree(buf);
911da3a9d3cSKalle Valo 
912da3a9d3cSKalle Valo 	return retval;
913da3a9d3cSKalle Valo }
914da3a9d3cSKalle Valo 
915da3a9d3cSKalle Valo static const struct file_operations fops_soc_dp_stats = {
916cb4e57dbSKalle Valo 	.read = ath11k_debugfs_dump_soc_dp_stats,
917da3a9d3cSKalle Valo 	.open = simple_open,
918da3a9d3cSKalle Valo 	.owner = THIS_MODULE,
919da3a9d3cSKalle Valo 	.llseek = default_llseek,
920da3a9d3cSKalle Valo };
921da3a9d3cSKalle Valo 
922f295ad91SSeevalamuthu Mariappan static ssize_t ath11k_write_fw_dbglog(struct file *file,
923f295ad91SSeevalamuthu Mariappan 				      const char __user *user_buf,
924f295ad91SSeevalamuthu Mariappan 				      size_t count, loff_t *ppos)
925f295ad91SSeevalamuthu Mariappan {
926f295ad91SSeevalamuthu Mariappan 	struct ath11k *ar = file->private_data;
927f295ad91SSeevalamuthu Mariappan 	char buf[128] = {0};
928f295ad91SSeevalamuthu Mariappan 	struct ath11k_fw_dbglog dbglog;
929f295ad91SSeevalamuthu Mariappan 	unsigned int param, mod_id_index, is_end;
930f295ad91SSeevalamuthu Mariappan 	u64 value;
931f295ad91SSeevalamuthu Mariappan 	int ret, num;
932f295ad91SSeevalamuthu Mariappan 
933f295ad91SSeevalamuthu Mariappan 	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,
934f295ad91SSeevalamuthu Mariappan 				     user_buf, count);
935f295ad91SSeevalamuthu Mariappan 	if (ret <= 0)
936f295ad91SSeevalamuthu Mariappan 		return ret;
937f295ad91SSeevalamuthu Mariappan 
938f295ad91SSeevalamuthu Mariappan 	num = sscanf(buf, "%u %llx %u %u", &param, &value, &mod_id_index, &is_end);
939f295ad91SSeevalamuthu Mariappan 
940f295ad91SSeevalamuthu Mariappan 	if (num < 2)
941f295ad91SSeevalamuthu Mariappan 		return -EINVAL;
942f295ad91SSeevalamuthu Mariappan 
943f295ad91SSeevalamuthu Mariappan 	mutex_lock(&ar->conf_mutex);
944f295ad91SSeevalamuthu Mariappan 	if (param == WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP ||
945f295ad91SSeevalamuthu Mariappan 	    param == WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP) {
946f295ad91SSeevalamuthu Mariappan 		if (num != 4 || mod_id_index > (MAX_MODULE_ID_BITMAP_WORDS - 1)) {
947f295ad91SSeevalamuthu Mariappan 			ret = -EINVAL;
948f295ad91SSeevalamuthu Mariappan 			goto out;
949f295ad91SSeevalamuthu Mariappan 		}
950f295ad91SSeevalamuthu Mariappan 		ar->debug.module_id_bitmap[mod_id_index] = upper_32_bits(value);
951f295ad91SSeevalamuthu Mariappan 		if (!is_end) {
952f295ad91SSeevalamuthu Mariappan 			ret = count;
953f295ad91SSeevalamuthu Mariappan 			goto out;
954f295ad91SSeevalamuthu Mariappan 		}
955f295ad91SSeevalamuthu Mariappan 	} else {
956f295ad91SSeevalamuthu Mariappan 		if (num != 2) {
957f295ad91SSeevalamuthu Mariappan 			ret = -EINVAL;
958f295ad91SSeevalamuthu Mariappan 			goto out;
959f295ad91SSeevalamuthu Mariappan 		}
960f295ad91SSeevalamuthu Mariappan 	}
961f295ad91SSeevalamuthu Mariappan 
962f295ad91SSeevalamuthu Mariappan 	dbglog.param = param;
963f295ad91SSeevalamuthu Mariappan 	dbglog.value = lower_32_bits(value);
964f295ad91SSeevalamuthu Mariappan 	ret = ath11k_wmi_fw_dbglog_cfg(ar, ar->debug.module_id_bitmap, &dbglog);
965f295ad91SSeevalamuthu Mariappan 	if (ret) {
966f295ad91SSeevalamuthu Mariappan 		ath11k_warn(ar->ab, "fw dbglog config failed from debugfs: %d\n",
967f295ad91SSeevalamuthu Mariappan 			    ret);
968f295ad91SSeevalamuthu Mariappan 		goto out;
969f295ad91SSeevalamuthu Mariappan 	}
970f295ad91SSeevalamuthu Mariappan 
971f295ad91SSeevalamuthu Mariappan 	ret = count;
972f295ad91SSeevalamuthu Mariappan 
973f295ad91SSeevalamuthu Mariappan out:
974f295ad91SSeevalamuthu Mariappan 	mutex_unlock(&ar->conf_mutex);
975f295ad91SSeevalamuthu Mariappan 	return ret;
976f295ad91SSeevalamuthu Mariappan }
977f295ad91SSeevalamuthu Mariappan 
978f295ad91SSeevalamuthu Mariappan static const struct file_operations fops_fw_dbglog = {
979f295ad91SSeevalamuthu Mariappan 	.write = ath11k_write_fw_dbglog,
980f295ad91SSeevalamuthu Mariappan 	.open = simple_open,
981f295ad91SSeevalamuthu Mariappan 	.owner = THIS_MODULE,
982f295ad91SSeevalamuthu Mariappan 	.llseek = default_llseek,
983f295ad91SSeevalamuthu Mariappan };
984f295ad91SSeevalamuthu Mariappan 
985cb4e57dbSKalle Valo int ath11k_debugfs_pdev_create(struct ath11k_base *ab)
986da3a9d3cSKalle Valo {
987da3a9d3cSKalle Valo 	if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
988da3a9d3cSKalle Valo 		return 0;
989da3a9d3cSKalle Valo 
990da3a9d3cSKalle Valo 	ab->debugfs_soc = debugfs_create_dir(ab->hw_params.name, ab->debugfs_ath11k);
991da3a9d3cSKalle Valo 	if (IS_ERR(ab->debugfs_soc))
992da3a9d3cSKalle Valo 		return PTR_ERR(ab->debugfs_soc);
993da3a9d3cSKalle Valo 
994da3a9d3cSKalle Valo 	debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab,
995da3a9d3cSKalle Valo 			    &fops_simulate_fw_crash);
996da3a9d3cSKalle Valo 
997da3a9d3cSKalle Valo 	debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab,
998da3a9d3cSKalle Valo 			    &fops_soc_dp_stats);
999da3a9d3cSKalle Valo 
1000da3a9d3cSKalle Valo 	return 0;
1001da3a9d3cSKalle Valo }
1002da3a9d3cSKalle Valo 
1003cb4e57dbSKalle Valo void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab)
1004da3a9d3cSKalle Valo {
1005089ba909SCarl Huang 	debugfs_remove_recursive(ab->debugfs_soc);
1006089ba909SCarl Huang 	ab->debugfs_soc = NULL;
1007da3a9d3cSKalle Valo }
1008da3a9d3cSKalle Valo 
1009cb4e57dbSKalle Valo int ath11k_debugfs_soc_create(struct ath11k_base *ab)
1010da3a9d3cSKalle Valo {
1011da3a9d3cSKalle Valo 	ab->debugfs_ath11k = debugfs_create_dir("ath11k", NULL);
1012da3a9d3cSKalle Valo 
1013476c1d3cSAlex Dewar 	return PTR_ERR_OR_ZERO(ab->debugfs_ath11k);
1014da3a9d3cSKalle Valo }
1015da3a9d3cSKalle Valo 
1016cb4e57dbSKalle Valo void ath11k_debugfs_soc_destroy(struct ath11k_base *ab)
1017da3a9d3cSKalle Valo {
1018089ba909SCarl Huang 	debugfs_remove_recursive(ab->debugfs_ath11k);
1019089ba909SCarl Huang 	ab->debugfs_ath11k = NULL;
1020da3a9d3cSKalle Valo }
102161a57e51SAnilkumar Kolli EXPORT_SYMBOL(ath11k_debugfs_soc_destroy);
1022da3a9d3cSKalle Valo 
1023cb4e57dbSKalle Valo void ath11k_debugfs_fw_stats_init(struct ath11k *ar)
1024da3a9d3cSKalle Valo {
1025da3a9d3cSKalle Valo 	struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",
1026da3a9d3cSKalle Valo 							ar->debug.debugfs_pdev);
1027da3a9d3cSKalle Valo 
1028da3a9d3cSKalle Valo 	ar->debug.fw_stats.debugfs_fwstats = fwstats_dir;
1029da3a9d3cSKalle Valo 
1030da3a9d3cSKalle Valo 	/* all stats debugfs files created are under "fw_stats" directory
1031da3a9d3cSKalle Valo 	 * created per PDEV
1032da3a9d3cSKalle Valo 	 */
1033da3a9d3cSKalle Valo 	debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar,
1034da3a9d3cSKalle Valo 			    &fops_pdev_stats);
1035da3a9d3cSKalle Valo 	debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,
1036da3a9d3cSKalle Valo 			    &fops_vdev_stats);
1037da3a9d3cSKalle Valo 	debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar,
1038da3a9d3cSKalle Valo 			    &fops_bcn_stats);
1039da3a9d3cSKalle Valo 
1040da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
1041da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
1042da3a9d3cSKalle Valo 	INIT_LIST_HEAD(&ar->debug.fw_stats.bcn);
1043da3a9d3cSKalle Valo 
1044da3a9d3cSKalle Valo 	init_completion(&ar->debug.fw_stats_complete);
1045da3a9d3cSKalle Valo }
1046da3a9d3cSKalle Valo 
1047da3a9d3cSKalle Valo static ssize_t ath11k_write_pktlog_filter(struct file *file,
1048da3a9d3cSKalle Valo 					  const char __user *ubuf,
1049da3a9d3cSKalle Valo 					  size_t count, loff_t *ppos)
1050da3a9d3cSKalle Valo {
1051da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
1052da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
1053da3a9d3cSKalle Valo 	struct htt_rx_ring_tlv_filter tlv_filter = {0};
1054da3a9d3cSKalle Valo 	u32 rx_filter = 0, ring_id, filter, mode;
1055da3a9d3cSKalle Valo 	u8 buf[128] = {0};
1056ab18e3bcSAnilkumar Kolli 	int i, ret, rx_buf_sz = 0;
1057da3a9d3cSKalle Valo 	ssize_t rc;
1058da3a9d3cSKalle Valo 
1059da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
1060da3a9d3cSKalle Valo 	if (ar->state != ATH11K_STATE_ON) {
1061da3a9d3cSKalle Valo 		ret = -ENETDOWN;
1062da3a9d3cSKalle Valo 		goto out;
1063da3a9d3cSKalle Valo 	}
1064da3a9d3cSKalle Valo 
1065da3a9d3cSKalle Valo 	rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1066da3a9d3cSKalle Valo 	if (rc < 0) {
1067da3a9d3cSKalle Valo 		ret = rc;
1068da3a9d3cSKalle Valo 		goto out;
1069da3a9d3cSKalle Valo 	}
1070da3a9d3cSKalle Valo 	buf[rc] = '\0';
1071da3a9d3cSKalle Valo 
1072da3a9d3cSKalle Valo 	ret = sscanf(buf, "0x%x %u", &filter, &mode);
1073da3a9d3cSKalle Valo 	if (ret != 2) {
1074da3a9d3cSKalle Valo 		ret = -EINVAL;
1075da3a9d3cSKalle Valo 		goto out;
1076da3a9d3cSKalle Valo 	}
1077da3a9d3cSKalle Valo 
1078da3a9d3cSKalle Valo 	if (filter) {
1079da3a9d3cSKalle Valo 		ret = ath11k_wmi_pdev_pktlog_enable(ar, filter);
1080da3a9d3cSKalle Valo 		if (ret) {
1081da3a9d3cSKalle Valo 			ath11k_warn(ar->ab,
1082da3a9d3cSKalle Valo 				    "failed to enable pktlog filter %x: %d\n",
1083da3a9d3cSKalle Valo 				    ar->debug.pktlog_filter, ret);
1084da3a9d3cSKalle Valo 			goto out;
1085da3a9d3cSKalle Valo 		}
1086da3a9d3cSKalle Valo 	} else {
1087da3a9d3cSKalle Valo 		ret = ath11k_wmi_pdev_pktlog_disable(ar);
1088da3a9d3cSKalle Valo 		if (ret) {
1089da3a9d3cSKalle Valo 			ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret);
1090da3a9d3cSKalle Valo 			goto out;
1091da3a9d3cSKalle Valo 		}
1092da3a9d3cSKalle Valo 	}
1093da3a9d3cSKalle Valo 
1094ab18e3bcSAnilkumar Kolli 	/* Clear rx filter set for monitor mode and rx status */
1095ab18e3bcSAnilkumar Kolli 	for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {
1096ab18e3bcSAnilkumar Kolli 		ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
1097ab18e3bcSAnilkumar Kolli 		ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,
1098ab18e3bcSAnilkumar Kolli 						       HAL_RXDMA_MONITOR_STATUS,
1099ab18e3bcSAnilkumar Kolli 						       rx_buf_sz, &tlv_filter);
1100ab18e3bcSAnilkumar Kolli 		if (ret) {
1101ab18e3bcSAnilkumar Kolli 			ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");
1102ab18e3bcSAnilkumar Kolli 			goto out;
1103ab18e3bcSAnilkumar Kolli 		}
1104ab18e3bcSAnilkumar Kolli 	}
1105da3a9d3cSKalle Valo #define HTT_RX_FILTER_TLV_LITE_MODE \
1106da3a9d3cSKalle Valo 			(HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \
1107da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \
1108da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \
1109da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \
1110da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \
1111da3a9d3cSKalle Valo 			HTT_RX_FILTER_TLV_FLAGS_MPDU_START)
1112da3a9d3cSKalle Valo 
1113da3a9d3cSKalle Valo 	if (mode == ATH11K_PKTLOG_MODE_FULL) {
1114da3a9d3cSKalle Valo 		rx_filter = HTT_RX_FILTER_TLV_LITE_MODE |
1115da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_MSDU_START |
1116da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_MSDU_END |
1117da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_MPDU_END |
1118da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER |
1119da3a9d3cSKalle Valo 			    HTT_RX_FILTER_TLV_FLAGS_ATTENTION;
1120ab18e3bcSAnilkumar Kolli 		rx_buf_sz = DP_RX_BUFFER_SIZE;
1121da3a9d3cSKalle Valo 	} else if (mode == ATH11K_PKTLOG_MODE_LITE) {
1122da3a9d3cSKalle Valo 		ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,
1123da3a9d3cSKalle Valo 							  HTT_PPDU_STATS_TAG_PKTLOG);
1124da3a9d3cSKalle Valo 		if (ret) {
1125da3a9d3cSKalle Valo 			ath11k_err(ar->ab, "failed to enable pktlog lite: %d\n", ret);
1126da3a9d3cSKalle Valo 			goto out;
1127da3a9d3cSKalle Valo 		}
1128da3a9d3cSKalle Valo 
1129da3a9d3cSKalle Valo 		rx_filter = HTT_RX_FILTER_TLV_LITE_MODE;
1130ab18e3bcSAnilkumar Kolli 		rx_buf_sz = DP_RX_BUFFER_SIZE_LITE;
1131da3a9d3cSKalle Valo 	} else {
1132ab18e3bcSAnilkumar Kolli 		rx_buf_sz = DP_RX_BUFFER_SIZE;
1133ab18e3bcSAnilkumar Kolli 		tlv_filter = ath11k_mac_mon_status_filter_default;
1134ab18e3bcSAnilkumar Kolli 		rx_filter = tlv_filter.rx_filter;
1135ab18e3bcSAnilkumar Kolli 
1136da3a9d3cSKalle Valo 		ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,
1137da3a9d3cSKalle Valo 							  HTT_PPDU_STATS_TAG_DEFAULT);
1138da3a9d3cSKalle Valo 		if (ret) {
1139da3a9d3cSKalle Valo 			ath11k_err(ar->ab, "failed to send htt ppdu stats req: %d\n",
1140da3a9d3cSKalle Valo 				   ret);
1141da3a9d3cSKalle Valo 			goto out;
1142da3a9d3cSKalle Valo 		}
1143da3a9d3cSKalle Valo 	}
1144da3a9d3cSKalle Valo 
1145da3a9d3cSKalle Valo 	tlv_filter.rx_filter = rx_filter;
1146da3a9d3cSKalle Valo 	if (rx_filter) {
1147da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;
1148da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;
1149da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;
1150da3a9d3cSKalle Valo 		tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |
1151da3a9d3cSKalle Valo 					       HTT_RX_FP_DATA_FILTER_FLASG3;
1152da3a9d3cSKalle Valo 	}
1153da3a9d3cSKalle Valo 
1154da3a9d3cSKalle Valo 	for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {
1155da3a9d3cSKalle Valo 		ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
1156da3a9d3cSKalle Valo 		ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id,
1157da3a9d3cSKalle Valo 						       ar->dp.mac_id + i,
1158da3a9d3cSKalle Valo 						       HAL_RXDMA_MONITOR_STATUS,
1159ab18e3bcSAnilkumar Kolli 						       rx_buf_sz, &tlv_filter);
1160da3a9d3cSKalle Valo 
1161da3a9d3cSKalle Valo 		if (ret) {
1162da3a9d3cSKalle Valo 			ath11k_warn(ab, "failed to set rx filter for monitor status ring\n");
1163da3a9d3cSKalle Valo 			goto out;
1164da3a9d3cSKalle Valo 		}
1165da3a9d3cSKalle Valo 	}
1166da3a9d3cSKalle Valo 
1167ab18e3bcSAnilkumar Kolli 	ath11k_info(ab, "pktlog mode %s\n",
1168ab18e3bcSAnilkumar Kolli 		    ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite"));
1169da3a9d3cSKalle Valo 
1170da3a9d3cSKalle Valo 	ar->debug.pktlog_filter = filter;
1171da3a9d3cSKalle Valo 	ar->debug.pktlog_mode = mode;
1172da3a9d3cSKalle Valo 	ret = count;
1173da3a9d3cSKalle Valo 
1174da3a9d3cSKalle Valo out:
1175da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
1176da3a9d3cSKalle Valo 	return ret;
1177da3a9d3cSKalle Valo }
1178da3a9d3cSKalle Valo 
1179da3a9d3cSKalle Valo static ssize_t ath11k_read_pktlog_filter(struct file *file,
1180da3a9d3cSKalle Valo 					 char __user *ubuf,
1181da3a9d3cSKalle Valo 					 size_t count, loff_t *ppos)
1182da3a9d3cSKalle Valo 
1183da3a9d3cSKalle Valo {
1184da3a9d3cSKalle Valo 	char buf[32] = {0};
1185da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
1186da3a9d3cSKalle Valo 	int len = 0;
1187da3a9d3cSKalle Valo 
1188da3a9d3cSKalle Valo 	mutex_lock(&ar->conf_mutex);
1189da3a9d3cSKalle Valo 	len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n",
1190da3a9d3cSKalle Valo 			ar->debug.pktlog_filter,
1191da3a9d3cSKalle Valo 			ar->debug.pktlog_mode);
1192da3a9d3cSKalle Valo 	mutex_unlock(&ar->conf_mutex);
1193da3a9d3cSKalle Valo 
1194da3a9d3cSKalle Valo 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
1195da3a9d3cSKalle Valo }
1196da3a9d3cSKalle Valo 
1197da3a9d3cSKalle Valo static const struct file_operations fops_pktlog_filter = {
1198da3a9d3cSKalle Valo 	.read = ath11k_read_pktlog_filter,
1199da3a9d3cSKalle Valo 	.write = ath11k_write_pktlog_filter,
1200da3a9d3cSKalle Valo 	.open = simple_open
1201da3a9d3cSKalle Valo };
1202da3a9d3cSKalle Valo 
1203da3a9d3cSKalle Valo static ssize_t ath11k_write_simulate_radar(struct file *file,
1204da3a9d3cSKalle Valo 					   const char __user *user_buf,
1205da3a9d3cSKalle Valo 					   size_t count, loff_t *ppos)
1206da3a9d3cSKalle Valo {
1207da3a9d3cSKalle Valo 	struct ath11k *ar = file->private_data;
1208da3a9d3cSKalle Valo 	int ret;
1209da3a9d3cSKalle Valo 
1210da3a9d3cSKalle Valo 	ret = ath11k_wmi_simulate_radar(ar);
1211da3a9d3cSKalle Valo 	if (ret)
1212da3a9d3cSKalle Valo 		return ret;
1213da3a9d3cSKalle Valo 
1214da3a9d3cSKalle Valo 	return count;
1215da3a9d3cSKalle Valo }
1216da3a9d3cSKalle Valo 
1217da3a9d3cSKalle Valo static const struct file_operations fops_simulate_radar = {
1218da3a9d3cSKalle Valo 	.write = ath11k_write_simulate_radar,
1219da3a9d3cSKalle Valo 	.open = simple_open
1220da3a9d3cSKalle Valo };
1221da3a9d3cSKalle Valo 
1222691425b4SVenkateswara Naralasetty static ssize_t ath11k_debug_dump_dbr_entries(struct file *file,
1223691425b4SVenkateswara Naralasetty 					     char __user *user_buf,
1224691425b4SVenkateswara Naralasetty 					     size_t count, loff_t *ppos)
1225691425b4SVenkateswara Naralasetty {
1226691425b4SVenkateswara Naralasetty 	struct ath11k_dbg_dbr_data *dbr_dbg_data = file->private_data;
1227691425b4SVenkateswara Naralasetty 	static const char * const event_id_to_string[] = {"empty", "Rx", "Replenish"};
1228691425b4SVenkateswara Naralasetty 	int size = ATH11K_DEBUG_DBR_ENTRIES_MAX * 100;
1229691425b4SVenkateswara Naralasetty 	char *buf;
1230691425b4SVenkateswara Naralasetty 	int i, ret;
1231691425b4SVenkateswara Naralasetty 	int len = 0;
1232691425b4SVenkateswara Naralasetty 
1233691425b4SVenkateswara Naralasetty 	buf = kzalloc(size, GFP_KERNEL);
1234691425b4SVenkateswara Naralasetty 	if (!buf)
1235691425b4SVenkateswara Naralasetty 		return -ENOMEM;
1236691425b4SVenkateswara Naralasetty 
1237691425b4SVenkateswara Naralasetty 	len += scnprintf(buf + len, size - len,
1238691425b4SVenkateswara Naralasetty 			 "-----------------------------------------\n");
1239691425b4SVenkateswara Naralasetty 	len += scnprintf(buf + len, size - len,
1240691425b4SVenkateswara Naralasetty 			 "| idx |  hp  |  tp  | timestamp |  event |\n");
1241691425b4SVenkateswara Naralasetty 	len += scnprintf(buf + len, size - len,
1242691425b4SVenkateswara Naralasetty 			 "-----------------------------------------\n");
1243691425b4SVenkateswara Naralasetty 
1244691425b4SVenkateswara Naralasetty 	spin_lock_bh(&dbr_dbg_data->lock);
1245691425b4SVenkateswara Naralasetty 
1246691425b4SVenkateswara Naralasetty 	for (i = 0; i < dbr_dbg_data->num_ring_debug_entries; i++) {
1247691425b4SVenkateswara Naralasetty 		len += scnprintf(buf + len, size - len,
1248691425b4SVenkateswara Naralasetty 				 "|%4u|%8u|%8u|%11llu|%8s|\n", i,
1249691425b4SVenkateswara Naralasetty 				 dbr_dbg_data->entries[i].hp,
1250691425b4SVenkateswara Naralasetty 				 dbr_dbg_data->entries[i].tp,
1251691425b4SVenkateswara Naralasetty 				 dbr_dbg_data->entries[i].timestamp,
1252691425b4SVenkateswara Naralasetty 				 event_id_to_string[dbr_dbg_data->entries[i].event]);
1253691425b4SVenkateswara Naralasetty 	}
1254691425b4SVenkateswara Naralasetty 
1255691425b4SVenkateswara Naralasetty 	spin_unlock_bh(&dbr_dbg_data->lock);
1256691425b4SVenkateswara Naralasetty 
1257691425b4SVenkateswara Naralasetty 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
1258691425b4SVenkateswara Naralasetty 	kfree(buf);
1259691425b4SVenkateswara Naralasetty 
1260691425b4SVenkateswara Naralasetty 	return ret;
1261691425b4SVenkateswara Naralasetty }
1262691425b4SVenkateswara Naralasetty 
1263691425b4SVenkateswara Naralasetty static const struct file_operations fops_debug_dump_dbr_entries = {
1264691425b4SVenkateswara Naralasetty 	.read = ath11k_debug_dump_dbr_entries,
1265691425b4SVenkateswara Naralasetty 	.open = simple_open,
1266691425b4SVenkateswara Naralasetty 	.owner = THIS_MODULE,
1267691425b4SVenkateswara Naralasetty 	.llseek = default_llseek,
1268691425b4SVenkateswara Naralasetty };
1269691425b4SVenkateswara Naralasetty 
1270691425b4SVenkateswara Naralasetty static void ath11k_debugfs_dbr_dbg_destroy(struct ath11k *ar, int dbr_id)
1271691425b4SVenkateswara Naralasetty {
1272691425b4SVenkateswara Naralasetty 	struct ath11k_debug_dbr *dbr_debug;
1273691425b4SVenkateswara Naralasetty 	struct ath11k_dbg_dbr_data *dbr_dbg_data;
1274691425b4SVenkateswara Naralasetty 
1275691425b4SVenkateswara Naralasetty 	if (!ar->debug.dbr_debug[dbr_id])
1276691425b4SVenkateswara Naralasetty 		return;
1277691425b4SVenkateswara Naralasetty 
1278691425b4SVenkateswara Naralasetty 	dbr_debug = ar->debug.dbr_debug[dbr_id];
1279691425b4SVenkateswara Naralasetty 	dbr_dbg_data = &dbr_debug->dbr_dbg_data;
1280691425b4SVenkateswara Naralasetty 
1281691425b4SVenkateswara Naralasetty 	debugfs_remove_recursive(dbr_debug->dbr_debugfs);
1282691425b4SVenkateswara Naralasetty 	kfree(dbr_dbg_data->entries);
1283691425b4SVenkateswara Naralasetty 	kfree(dbr_debug);
1284691425b4SVenkateswara Naralasetty 	ar->debug.dbr_debug[dbr_id] = NULL;
1285691425b4SVenkateswara Naralasetty }
1286691425b4SVenkateswara Naralasetty 
1287691425b4SVenkateswara Naralasetty static int ath11k_debugfs_dbr_dbg_init(struct ath11k *ar, int dbr_id)
1288691425b4SVenkateswara Naralasetty {
1289691425b4SVenkateswara Naralasetty 	struct ath11k_debug_dbr *dbr_debug;
1290691425b4SVenkateswara Naralasetty 	struct ath11k_dbg_dbr_data *dbr_dbg_data;
1291691425b4SVenkateswara Naralasetty 	static const char * const dbr_id_to_str[] = {"spectral", "CFR"};
1292691425b4SVenkateswara Naralasetty 
1293691425b4SVenkateswara Naralasetty 	if (ar->debug.dbr_debug[dbr_id])
1294691425b4SVenkateswara Naralasetty 		return 0;
1295691425b4SVenkateswara Naralasetty 
1296691425b4SVenkateswara Naralasetty 	ar->debug.dbr_debug[dbr_id] = kzalloc(sizeof(*dbr_debug),
1297691425b4SVenkateswara Naralasetty 					      GFP_KERNEL);
1298691425b4SVenkateswara Naralasetty 
1299691425b4SVenkateswara Naralasetty 	if (!ar->debug.dbr_debug[dbr_id])
1300691425b4SVenkateswara Naralasetty 		return -ENOMEM;
1301691425b4SVenkateswara Naralasetty 
1302691425b4SVenkateswara Naralasetty 	dbr_debug = ar->debug.dbr_debug[dbr_id];
1303691425b4SVenkateswara Naralasetty 	dbr_dbg_data = &dbr_debug->dbr_dbg_data;
1304691425b4SVenkateswara Naralasetty 
1305691425b4SVenkateswara Naralasetty 	if (dbr_debug->dbr_debugfs)
1306691425b4SVenkateswara Naralasetty 		return 0;
1307691425b4SVenkateswara Naralasetty 
1308691425b4SVenkateswara Naralasetty 	dbr_debug->dbr_debugfs = debugfs_create_dir(dbr_id_to_str[dbr_id],
1309691425b4SVenkateswara Naralasetty 						    ar->debug.debugfs_pdev);
1310691425b4SVenkateswara Naralasetty 	if (IS_ERR_OR_NULL(dbr_debug->dbr_debugfs)) {
1311691425b4SVenkateswara Naralasetty 		if (IS_ERR(dbr_debug->dbr_debugfs))
1312691425b4SVenkateswara Naralasetty 			return PTR_ERR(dbr_debug->dbr_debugfs);
1313691425b4SVenkateswara Naralasetty 		return -ENOMEM;
1314691425b4SVenkateswara Naralasetty 	}
1315691425b4SVenkateswara Naralasetty 
1316691425b4SVenkateswara Naralasetty 	dbr_debug->dbr_debug_enabled = true;
1317691425b4SVenkateswara Naralasetty 	dbr_dbg_data->num_ring_debug_entries = ATH11K_DEBUG_DBR_ENTRIES_MAX;
1318691425b4SVenkateswara Naralasetty 	dbr_dbg_data->dbr_debug_idx = 0;
1319691425b4SVenkateswara Naralasetty 	dbr_dbg_data->entries = kcalloc(ATH11K_DEBUG_DBR_ENTRIES_MAX,
1320691425b4SVenkateswara Naralasetty 					sizeof(struct ath11k_dbg_dbr_entry),
1321691425b4SVenkateswara Naralasetty 					GFP_KERNEL);
1322691425b4SVenkateswara Naralasetty 	if (!dbr_dbg_data->entries)
1323691425b4SVenkateswara Naralasetty 		return -ENOMEM;
1324691425b4SVenkateswara Naralasetty 
1325691425b4SVenkateswara Naralasetty 	spin_lock_init(&dbr_dbg_data->lock);
1326691425b4SVenkateswara Naralasetty 
1327691425b4SVenkateswara Naralasetty 	debugfs_create_file("dump_dbr_debug", 0444, dbr_debug->dbr_debugfs,
1328691425b4SVenkateswara Naralasetty 			    dbr_dbg_data, &fops_debug_dump_dbr_entries);
1329691425b4SVenkateswara Naralasetty 
1330691425b4SVenkateswara Naralasetty 	return 0;
1331691425b4SVenkateswara Naralasetty }
1332691425b4SVenkateswara Naralasetty 
1333691425b4SVenkateswara Naralasetty static ssize_t ath11k_debugfs_write_enable_dbr_dbg(struct file *file,
1334691425b4SVenkateswara Naralasetty 						   const char __user *ubuf,
1335691425b4SVenkateswara Naralasetty 						   size_t count, loff_t *ppos)
1336691425b4SVenkateswara Naralasetty {
1337691425b4SVenkateswara Naralasetty 	struct ath11k *ar = file->private_data;
1338691425b4SVenkateswara Naralasetty 	char buf[32] = {0};
1339691425b4SVenkateswara Naralasetty 	u32 dbr_id, enable;
1340691425b4SVenkateswara Naralasetty 	int ret;
1341691425b4SVenkateswara Naralasetty 
1342691425b4SVenkateswara Naralasetty 	mutex_lock(&ar->conf_mutex);
1343691425b4SVenkateswara Naralasetty 
1344691425b4SVenkateswara Naralasetty 	if (ar->state != ATH11K_STATE_ON) {
1345691425b4SVenkateswara Naralasetty 		ret = -ENETDOWN;
1346691425b4SVenkateswara Naralasetty 		goto out;
1347691425b4SVenkateswara Naralasetty 	}
1348691425b4SVenkateswara Naralasetty 
1349691425b4SVenkateswara Naralasetty 	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1350691425b4SVenkateswara Naralasetty 	if (ret < 0)
1351691425b4SVenkateswara Naralasetty 		goto out;
1352691425b4SVenkateswara Naralasetty 
1353691425b4SVenkateswara Naralasetty 	buf[ret] = '\0';
1354691425b4SVenkateswara Naralasetty 	ret = sscanf(buf, "%u %u", &dbr_id, &enable);
1355691425b4SVenkateswara Naralasetty 	if (ret != 2 || dbr_id > 1 || enable > 1) {
1356691425b4SVenkateswara Naralasetty 		ret = -EINVAL;
1357691425b4SVenkateswara Naralasetty 		ath11k_warn(ar->ab, "usage: echo <dbr_id> <val> dbr_id:0-Spectral 1-CFR val:0-disable 1-enable\n");
1358691425b4SVenkateswara Naralasetty 		goto out;
1359691425b4SVenkateswara Naralasetty 	}
1360691425b4SVenkateswara Naralasetty 
1361691425b4SVenkateswara Naralasetty 	if (enable) {
1362691425b4SVenkateswara Naralasetty 		ret = ath11k_debugfs_dbr_dbg_init(ar, dbr_id);
1363691425b4SVenkateswara Naralasetty 		if (ret) {
1364691425b4SVenkateswara Naralasetty 			ath11k_warn(ar->ab, "db ring module debugfs init failed: %d\n",
1365691425b4SVenkateswara Naralasetty 				    ret);
1366691425b4SVenkateswara Naralasetty 			goto out;
1367691425b4SVenkateswara Naralasetty 		}
1368691425b4SVenkateswara Naralasetty 	} else {
1369691425b4SVenkateswara Naralasetty 		ath11k_debugfs_dbr_dbg_destroy(ar, dbr_id);
1370691425b4SVenkateswara Naralasetty 	}
1371691425b4SVenkateswara Naralasetty 
1372691425b4SVenkateswara Naralasetty 	ret = count;
1373691425b4SVenkateswara Naralasetty out:
1374691425b4SVenkateswara Naralasetty 	mutex_unlock(&ar->conf_mutex);
1375691425b4SVenkateswara Naralasetty 	return ret;
1376691425b4SVenkateswara Naralasetty }
1377691425b4SVenkateswara Naralasetty 
1378691425b4SVenkateswara Naralasetty static const struct file_operations fops_dbr_debug = {
1379691425b4SVenkateswara Naralasetty 	.write = ath11k_debugfs_write_enable_dbr_dbg,
1380691425b4SVenkateswara Naralasetty 	.open = simple_open,
1381691425b4SVenkateswara Naralasetty 	.owner = THIS_MODULE,
1382691425b4SVenkateswara Naralasetty 	.llseek = default_llseek,
1383691425b4SVenkateswara Naralasetty };
1384691425b4SVenkateswara Naralasetty 
1385cb4e57dbSKalle Valo int ath11k_debugfs_register(struct ath11k *ar)
1386da3a9d3cSKalle Valo {
1387da3a9d3cSKalle Valo 	struct ath11k_base *ab = ar->ab;
1388da3a9d3cSKalle Valo 	char pdev_name[5];
1389da3a9d3cSKalle Valo 	char buf[100] = {0};
1390da3a9d3cSKalle Valo 
1391da3a9d3cSKalle Valo 	snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx);
1392da3a9d3cSKalle Valo 
1393da3a9d3cSKalle Valo 	ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc);
1394da3a9d3cSKalle Valo 	if (IS_ERR(ar->debug.debugfs_pdev))
1395da3a9d3cSKalle Valo 		return PTR_ERR(ar->debug.debugfs_pdev);
1396da3a9d3cSKalle Valo 
1397da3a9d3cSKalle Valo 	/* Create a symlink under ieee80211/phy* */
1398da3a9d3cSKalle Valo 	snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev);
1399da3a9d3cSKalle Valo 	debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf);
1400da3a9d3cSKalle Valo 
140156292162SKalle Valo 	ath11k_debugfs_htt_stats_init(ar);
1402da3a9d3cSKalle Valo 
1403cb4e57dbSKalle Valo 	ath11k_debugfs_fw_stats_init(ar);
1404da3a9d3cSKalle Valo 
1405da3a9d3cSKalle Valo 	debugfs_create_file("ext_tx_stats", 0644,
1406da3a9d3cSKalle Valo 			    ar->debug.debugfs_pdev, ar,
1407da3a9d3cSKalle Valo 			    &fops_extd_tx_stats);
1408da3a9d3cSKalle Valo 	debugfs_create_file("ext_rx_stats", 0644,
1409da3a9d3cSKalle Valo 			    ar->debug.debugfs_pdev, ar,
1410da3a9d3cSKalle Valo 			    &fops_extd_rx_stats);
1411da3a9d3cSKalle Valo 	debugfs_create_file("pktlog_filter", 0644,
1412da3a9d3cSKalle Valo 			    ar->debug.debugfs_pdev, ar,
1413da3a9d3cSKalle Valo 			    &fops_pktlog_filter);
1414f295ad91SSeevalamuthu Mariappan 	debugfs_create_file("fw_dbglog_config", 0600,
1415f295ad91SSeevalamuthu Mariappan 			    ar->debug.debugfs_pdev, ar,
1416f295ad91SSeevalamuthu Mariappan 			    &fops_fw_dbglog);
1417da3a9d3cSKalle Valo 
1418da3a9d3cSKalle Valo 	if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) {
1419da3a9d3cSKalle Valo 		debugfs_create_file("dfs_simulate_radar", 0200,
1420da3a9d3cSKalle Valo 				    ar->debug.debugfs_pdev, ar,
1421da3a9d3cSKalle Valo 				    &fops_simulate_radar);
1422da3a9d3cSKalle Valo 		debugfs_create_bool("dfs_block_radar_events", 0200,
1423da3a9d3cSKalle Valo 				    ar->debug.debugfs_pdev,
1424da3a9d3cSKalle Valo 				    &ar->dfs_block_radar_events);
1425da3a9d3cSKalle Valo 	}
1426da3a9d3cSKalle Valo 
1427691425b4SVenkateswara Naralasetty 	if (ab->hw_params.dbr_debug_support)
1428691425b4SVenkateswara Naralasetty 		debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev,
1429691425b4SVenkateswara Naralasetty 				    ar, &fops_dbr_debug);
1430691425b4SVenkateswara Naralasetty 
1431da3a9d3cSKalle Valo 	return 0;
1432da3a9d3cSKalle Valo }
1433da3a9d3cSKalle Valo 
1434cb4e57dbSKalle Valo void ath11k_debugfs_unregister(struct ath11k *ar)
1435da3a9d3cSKalle Valo {
1436691425b4SVenkateswara Naralasetty 	struct ath11k_debug_dbr *dbr_debug;
1437691425b4SVenkateswara Naralasetty 	struct ath11k_dbg_dbr_data *dbr_dbg_data;
1438691425b4SVenkateswara Naralasetty 	int i;
1439691425b4SVenkateswara Naralasetty 
1440691425b4SVenkateswara Naralasetty 	for (i = 0; i < WMI_DIRECT_BUF_MAX; i++) {
1441691425b4SVenkateswara Naralasetty 		dbr_debug = ar->debug.dbr_debug[i];
1442691425b4SVenkateswara Naralasetty 		if (!dbr_debug)
1443691425b4SVenkateswara Naralasetty 			continue;
1444691425b4SVenkateswara Naralasetty 
1445691425b4SVenkateswara Naralasetty 		dbr_dbg_data = &dbr_debug->dbr_dbg_data;
1446691425b4SVenkateswara Naralasetty 		kfree(dbr_dbg_data->entries);
1447691425b4SVenkateswara Naralasetty 		debugfs_remove_recursive(dbr_debug->dbr_debugfs);
1448691425b4SVenkateswara Naralasetty 		kfree(dbr_debug);
1449691425b4SVenkateswara Naralasetty 		ar->debug.dbr_debug[i] = NULL;
1450691425b4SVenkateswara Naralasetty 	}
1451da3a9d3cSKalle Valo }
1452fe98a613SJohn Crispin 
1453fe98a613SJohn Crispin static ssize_t ath11k_write_twt_add_dialog(struct file *file,
1454fe98a613SJohn Crispin 					   const char __user *ubuf,
1455fe98a613SJohn Crispin 					   size_t count, loff_t *ppos)
1456fe98a613SJohn Crispin {
1457fe98a613SJohn Crispin 	struct ath11k_vif *arvif = file->private_data;
1458fe98a613SJohn Crispin 	struct wmi_twt_add_dialog_params params = { 0 };
1459*9e2747c3SManikanta Pubbisetty 	struct wmi_twt_enable_params twt_params = {0};
1460*9e2747c3SManikanta Pubbisetty 	struct ath11k *ar = arvif->ar;
1461fe98a613SJohn Crispin 	u8 buf[128] = {0};
1462fe98a613SJohn Crispin 	int ret;
1463fe98a613SJohn Crispin 
1464*9e2747c3SManikanta Pubbisetty 	if (ar->twt_enabled == 0) {
1465*9e2747c3SManikanta Pubbisetty 		ath11k_err(ar->ab, "twt support is not enabled\n");
1466fe98a613SJohn Crispin 		return -EOPNOTSUPP;
1467fe98a613SJohn Crispin 	}
1468fe98a613SJohn Crispin 
1469fe98a613SJohn Crispin 	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1470fe98a613SJohn Crispin 	if (ret < 0)
1471fe98a613SJohn Crispin 		return ret;
1472fe98a613SJohn Crispin 
1473fe98a613SJohn Crispin 	buf[ret] = '\0';
1474fe98a613SJohn Crispin 	ret = sscanf(buf,
1475fe98a613SJohn Crispin 		     "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u %u %u %hhu %hhu %hhu %hhu %hhu",
1476fe98a613SJohn Crispin 		     &params.peer_macaddr[0],
1477fe98a613SJohn Crispin 		     &params.peer_macaddr[1],
1478fe98a613SJohn Crispin 		     &params.peer_macaddr[2],
1479fe98a613SJohn Crispin 		     &params.peer_macaddr[3],
1480fe98a613SJohn Crispin 		     &params.peer_macaddr[4],
1481fe98a613SJohn Crispin 		     &params.peer_macaddr[5],
1482fe98a613SJohn Crispin 		     &params.dialog_id,
1483fe98a613SJohn Crispin 		     &params.wake_intvl_us,
1484fe98a613SJohn Crispin 		     &params.wake_intvl_mantis,
1485fe98a613SJohn Crispin 		     &params.wake_dura_us,
1486fe98a613SJohn Crispin 		     &params.sp_offset_us,
1487fe98a613SJohn Crispin 		     &params.twt_cmd,
1488fe98a613SJohn Crispin 		     &params.flag_bcast,
1489fe98a613SJohn Crispin 		     &params.flag_trigger,
1490fe98a613SJohn Crispin 		     &params.flag_flow_type,
1491fe98a613SJohn Crispin 		     &params.flag_protection);
1492fe98a613SJohn Crispin 	if (ret != 16)
1493fe98a613SJohn Crispin 		return -EINVAL;
1494fe98a613SJohn Crispin 
1495*9e2747c3SManikanta Pubbisetty 	/* In the case of station vif, TWT is entirely handled by
1496*9e2747c3SManikanta Pubbisetty 	 * the firmware based on the input parameters in the TWT enable
1497*9e2747c3SManikanta Pubbisetty 	 * WMI command that is sent to the target during assoc.
1498*9e2747c3SManikanta Pubbisetty 	 * For manually testing the TWT feature, we need to first disable
1499*9e2747c3SManikanta Pubbisetty 	 * TWT and send enable command again with TWT input parameter
1500*9e2747c3SManikanta Pubbisetty 	 * sta_cong_timer_ms set to 0.
1501*9e2747c3SManikanta Pubbisetty 	 */
1502*9e2747c3SManikanta Pubbisetty 	if (arvif->vif->type == NL80211_IFTYPE_STATION) {
1503*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);
1504*9e2747c3SManikanta Pubbisetty 
1505*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_fill_default_twt_params(&twt_params);
1506*9e2747c3SManikanta Pubbisetty 		twt_params.sta_cong_timer_ms = 0;
1507*9e2747c3SManikanta Pubbisetty 
1508*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);
1509*9e2747c3SManikanta Pubbisetty 	}
1510*9e2747c3SManikanta Pubbisetty 
1511fe98a613SJohn Crispin 	params.vdev_id = arvif->vdev_id;
1512fe98a613SJohn Crispin 
1513fe98a613SJohn Crispin 	ret = ath11k_wmi_send_twt_add_dialog_cmd(arvif->ar, &params);
1514fe98a613SJohn Crispin 	if (ret)
1515*9e2747c3SManikanta Pubbisetty 		goto err_twt_add_dialog;
1516fe98a613SJohn Crispin 
1517fe98a613SJohn Crispin 	return count;
1518*9e2747c3SManikanta Pubbisetty 
1519*9e2747c3SManikanta Pubbisetty err_twt_add_dialog:
1520*9e2747c3SManikanta Pubbisetty 	if (arvif->vif->type == NL80211_IFTYPE_STATION) {
1521*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);
1522*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_fill_default_twt_params(&twt_params);
1523*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);
1524*9e2747c3SManikanta Pubbisetty 	}
1525*9e2747c3SManikanta Pubbisetty 
1526*9e2747c3SManikanta Pubbisetty 	return ret;
1527fe98a613SJohn Crispin }
1528fe98a613SJohn Crispin 
1529fe98a613SJohn Crispin static ssize_t ath11k_write_twt_del_dialog(struct file *file,
1530fe98a613SJohn Crispin 					   const char __user *ubuf,
1531fe98a613SJohn Crispin 					   size_t count, loff_t *ppos)
1532fe98a613SJohn Crispin {
1533fe98a613SJohn Crispin 	struct ath11k_vif *arvif = file->private_data;
1534fe98a613SJohn Crispin 	struct wmi_twt_del_dialog_params params = { 0 };
1535*9e2747c3SManikanta Pubbisetty 	struct wmi_twt_enable_params twt_params = {0};
1536*9e2747c3SManikanta Pubbisetty 	struct ath11k *ar = arvif->ar;
1537fe98a613SJohn Crispin 	u8 buf[64] = {0};
1538fe98a613SJohn Crispin 	int ret;
1539fe98a613SJohn Crispin 
1540*9e2747c3SManikanta Pubbisetty 	if (ar->twt_enabled == 0) {
1541*9e2747c3SManikanta Pubbisetty 		ath11k_err(ar->ab, "twt support is not enabled\n");
1542fe98a613SJohn Crispin 		return -EOPNOTSUPP;
1543fe98a613SJohn Crispin 	}
1544fe98a613SJohn Crispin 
1545fe98a613SJohn Crispin 	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1546fe98a613SJohn Crispin 	if (ret < 0)
1547fe98a613SJohn Crispin 		return ret;
1548fe98a613SJohn Crispin 
1549fe98a613SJohn Crispin 	buf[ret] = '\0';
1550fe98a613SJohn Crispin 	ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u",
1551fe98a613SJohn Crispin 		     &params.peer_macaddr[0],
1552fe98a613SJohn Crispin 		     &params.peer_macaddr[1],
1553fe98a613SJohn Crispin 		     &params.peer_macaddr[2],
1554fe98a613SJohn Crispin 		     &params.peer_macaddr[3],
1555fe98a613SJohn Crispin 		     &params.peer_macaddr[4],
1556fe98a613SJohn Crispin 		     &params.peer_macaddr[5],
1557fe98a613SJohn Crispin 		     &params.dialog_id);
1558fe98a613SJohn Crispin 	if (ret != 7)
1559fe98a613SJohn Crispin 		return -EINVAL;
1560fe98a613SJohn Crispin 
1561fe98a613SJohn Crispin 	params.vdev_id = arvif->vdev_id;
1562fe98a613SJohn Crispin 
1563fe98a613SJohn Crispin 	ret = ath11k_wmi_send_twt_del_dialog_cmd(arvif->ar, &params);
1564fe98a613SJohn Crispin 	if (ret)
1565fe98a613SJohn Crispin 		return ret;
1566fe98a613SJohn Crispin 
1567*9e2747c3SManikanta Pubbisetty 	if (arvif->vif->type == NL80211_IFTYPE_STATION) {
1568*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);
1569*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_fill_default_twt_params(&twt_params);
1570*9e2747c3SManikanta Pubbisetty 		ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);
1571*9e2747c3SManikanta Pubbisetty 	}
1572*9e2747c3SManikanta Pubbisetty 
1573fe98a613SJohn Crispin 	return count;
1574fe98a613SJohn Crispin }
1575fe98a613SJohn Crispin 
1576fe98a613SJohn Crispin static ssize_t ath11k_write_twt_pause_dialog(struct file *file,
1577fe98a613SJohn Crispin 					     const char __user *ubuf,
1578fe98a613SJohn Crispin 					     size_t count, loff_t *ppos)
1579fe98a613SJohn Crispin {
1580fe98a613SJohn Crispin 	struct ath11k_vif *arvif = file->private_data;
1581fe98a613SJohn Crispin 	struct wmi_twt_pause_dialog_params params = { 0 };
1582fe98a613SJohn Crispin 	u8 buf[64] = {0};
1583fe98a613SJohn Crispin 	int ret;
1584fe98a613SJohn Crispin 
1585fe98a613SJohn Crispin 	if (arvif->ar->twt_enabled == 0) {
1586fe98a613SJohn Crispin 		ath11k_err(arvif->ar->ab, "twt support is not enabled\n");
1587fe98a613SJohn Crispin 		return -EOPNOTSUPP;
1588fe98a613SJohn Crispin 	}
1589fe98a613SJohn Crispin 
1590fe98a613SJohn Crispin 	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1591fe98a613SJohn Crispin 	if (ret < 0)
1592fe98a613SJohn Crispin 		return ret;
1593fe98a613SJohn Crispin 
1594fe98a613SJohn Crispin 	buf[ret] = '\0';
1595fe98a613SJohn Crispin 	ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u",
1596fe98a613SJohn Crispin 		     &params.peer_macaddr[0],
1597fe98a613SJohn Crispin 		     &params.peer_macaddr[1],
1598fe98a613SJohn Crispin 		     &params.peer_macaddr[2],
1599fe98a613SJohn Crispin 		     &params.peer_macaddr[3],
1600fe98a613SJohn Crispin 		     &params.peer_macaddr[4],
1601fe98a613SJohn Crispin 		     &params.peer_macaddr[5],
1602fe98a613SJohn Crispin 		     &params.dialog_id);
1603fe98a613SJohn Crispin 	if (ret != 7)
1604fe98a613SJohn Crispin 		return -EINVAL;
1605fe98a613SJohn Crispin 
1606fe98a613SJohn Crispin 	params.vdev_id = arvif->vdev_id;
1607fe98a613SJohn Crispin 
1608fe98a613SJohn Crispin 	ret = ath11k_wmi_send_twt_pause_dialog_cmd(arvif->ar, &params);
1609fe98a613SJohn Crispin 	if (ret)
1610fe98a613SJohn Crispin 		return ret;
1611fe98a613SJohn Crispin 
1612fe98a613SJohn Crispin 	return count;
1613fe98a613SJohn Crispin }
1614fe98a613SJohn Crispin 
1615fe98a613SJohn Crispin static ssize_t ath11k_write_twt_resume_dialog(struct file *file,
1616fe98a613SJohn Crispin 					      const char __user *ubuf,
1617fe98a613SJohn Crispin 					      size_t count, loff_t *ppos)
1618fe98a613SJohn Crispin {
1619fe98a613SJohn Crispin 	struct ath11k_vif *arvif = file->private_data;
1620fe98a613SJohn Crispin 	struct wmi_twt_resume_dialog_params params = { 0 };
1621fe98a613SJohn Crispin 	u8 buf[64] = {0};
1622fe98a613SJohn Crispin 	int ret;
1623fe98a613SJohn Crispin 
1624fe98a613SJohn Crispin 	if (arvif->ar->twt_enabled == 0) {
1625fe98a613SJohn Crispin 		ath11k_err(arvif->ar->ab, "twt support is not enabled\n");
1626fe98a613SJohn Crispin 		return -EOPNOTSUPP;
1627fe98a613SJohn Crispin 	}
1628fe98a613SJohn Crispin 
1629fe98a613SJohn Crispin 	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1630fe98a613SJohn Crispin 	if (ret < 0)
1631fe98a613SJohn Crispin 		return ret;
1632fe98a613SJohn Crispin 
1633fe98a613SJohn Crispin 	buf[ret] = '\0';
1634fe98a613SJohn Crispin 	ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u",
1635fe98a613SJohn Crispin 		     &params.peer_macaddr[0],
1636fe98a613SJohn Crispin 		     &params.peer_macaddr[1],
1637fe98a613SJohn Crispin 		     &params.peer_macaddr[2],
1638fe98a613SJohn Crispin 		     &params.peer_macaddr[3],
1639fe98a613SJohn Crispin 		     &params.peer_macaddr[4],
1640fe98a613SJohn Crispin 		     &params.peer_macaddr[5],
1641fe98a613SJohn Crispin 		     &params.dialog_id,
1642fe98a613SJohn Crispin 		     &params.sp_offset_us,
1643fe98a613SJohn Crispin 		     &params.next_twt_size);
1644fe98a613SJohn Crispin 	if (ret != 9)
1645fe98a613SJohn Crispin 		return -EINVAL;
1646fe98a613SJohn Crispin 
1647fe98a613SJohn Crispin 	params.vdev_id = arvif->vdev_id;
1648fe98a613SJohn Crispin 
1649fe98a613SJohn Crispin 	ret = ath11k_wmi_send_twt_resume_dialog_cmd(arvif->ar, &params);
1650fe98a613SJohn Crispin 	if (ret)
1651fe98a613SJohn Crispin 		return ret;
1652fe98a613SJohn Crispin 
1653fe98a613SJohn Crispin 	return count;
1654fe98a613SJohn Crispin }
1655fe98a613SJohn Crispin 
1656fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_add_dialog = {
1657fe98a613SJohn Crispin 	.write = ath11k_write_twt_add_dialog,
1658fe98a613SJohn Crispin 	.open = simple_open
1659fe98a613SJohn Crispin };
1660fe98a613SJohn Crispin 
1661fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_del_dialog = {
1662fe98a613SJohn Crispin 	.write = ath11k_write_twt_del_dialog,
1663fe98a613SJohn Crispin 	.open = simple_open
1664fe98a613SJohn Crispin };
1665fe98a613SJohn Crispin 
1666fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_pause_dialog = {
1667fe98a613SJohn Crispin 	.write = ath11k_write_twt_pause_dialog,
1668fe98a613SJohn Crispin 	.open = simple_open
1669fe98a613SJohn Crispin };
1670fe98a613SJohn Crispin 
1671fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_resume_dialog = {
1672fe98a613SJohn Crispin 	.write = ath11k_write_twt_resume_dialog,
1673fe98a613SJohn Crispin 	.open = simple_open
1674fe98a613SJohn Crispin };
1675fe98a613SJohn Crispin 
1676fe98a613SJohn Crispin int ath11k_debugfs_add_interface(struct ath11k_vif *arvif)
1677fe98a613SJohn Crispin {
1678*9e2747c3SManikanta Pubbisetty 	struct ath11k_base *ab = arvif->ar->ab;
1679*9e2747c3SManikanta Pubbisetty 
1680*9e2747c3SManikanta Pubbisetty 	if (arvif->vif->type != NL80211_IFTYPE_AP &&
1681*9e2747c3SManikanta Pubbisetty 	    !(arvif->vif->type == NL80211_IFTYPE_STATION &&
1682*9e2747c3SManikanta Pubbisetty 	      test_bit(WMI_TLV_SERVICE_STA_TWT, ab->wmi_ab.svc_map)))
1683*9e2747c3SManikanta Pubbisetty 		return 0;
1684*9e2747c3SManikanta Pubbisetty 
1685*9e2747c3SManikanta Pubbisetty 	if (!arvif->debugfs_twt) {
1686fe98a613SJohn Crispin 		arvif->debugfs_twt = debugfs_create_dir("twt",
1687fe98a613SJohn Crispin 							arvif->vif->debugfs_dir);
1688fe98a613SJohn Crispin 		if (!arvif->debugfs_twt || IS_ERR(arvif->debugfs_twt)) {
1689*9e2747c3SManikanta Pubbisetty 			ath11k_warn(ab, "failed to create directory %p\n",
1690fe98a613SJohn Crispin 				    arvif->debugfs_twt);
1691fe98a613SJohn Crispin 			arvif->debugfs_twt = NULL;
1692fe98a613SJohn Crispin 			return -1;
1693fe98a613SJohn Crispin 		}
1694fe98a613SJohn Crispin 
1695fe98a613SJohn Crispin 		debugfs_create_file("add_dialog", 0200, arvif->debugfs_twt,
1696fe98a613SJohn Crispin 				    arvif, &ath11k_fops_twt_add_dialog);
1697fe98a613SJohn Crispin 
1698fe98a613SJohn Crispin 		debugfs_create_file("del_dialog", 0200, arvif->debugfs_twt,
1699fe98a613SJohn Crispin 				    arvif, &ath11k_fops_twt_del_dialog);
1700fe98a613SJohn Crispin 
1701fe98a613SJohn Crispin 		debugfs_create_file("pause_dialog", 0200, arvif->debugfs_twt,
1702fe98a613SJohn Crispin 				    arvif, &ath11k_fops_twt_pause_dialog);
1703fe98a613SJohn Crispin 
1704fe98a613SJohn Crispin 		debugfs_create_file("resume_dialog", 0200, arvif->debugfs_twt,
1705fe98a613SJohn Crispin 				    arvif, &ath11k_fops_twt_resume_dialog);
1706fe98a613SJohn Crispin 	}
1707fe98a613SJohn Crispin 	return 0;
1708fe98a613SJohn Crispin }
1709fe98a613SJohn Crispin 
1710fe98a613SJohn Crispin void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif)
1711fe98a613SJohn Crispin {
1712fe98a613SJohn Crispin 	debugfs_remove_recursive(arvif->debugfs_twt);
1713fe98a613SJohn Crispin 	arvif->debugfs_twt = NULL;
1714fe98a613SJohn Crispin }
1715