1da3a9d3cSKalle Valo // SPDX-License-Identifier: BSD-3-Clause-Clear
2da3a9d3cSKalle Valo /*
3da3a9d3cSKalle Valo * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
4*586c7fb1SJeff Johnson * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
5da3a9d3cSKalle Valo */
6da3a9d3cSKalle Valo
715fcb103SJakub Kicinski #include <linux/vmalloc.h>
815fcb103SJakub Kicinski
9da3a9d3cSKalle Valo #include "debugfs.h"
10da3a9d3cSKalle Valo
11da3a9d3cSKalle Valo #include "core.h"
12da3a9d3cSKalle Valo #include "debug.h"
13da3a9d3cSKalle Valo #include "wmi.h"
14da3a9d3cSKalle Valo #include "hal_rx.h"
15da3a9d3cSKalle Valo #include "dp_tx.h"
1656292162SKalle Valo #include "debugfs_htt_stats.h"
17da3a9d3cSKalle Valo #include "peer.h"
18876eb848SBaochen Qiang #include "hif.h"
19da3a9d3cSKalle Valo
20da3a9d3cSKalle Valo static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {
21da3a9d3cSKalle Valo "REO2SW1_RING",
22da3a9d3cSKalle Valo "REO2SW2_RING",
23da3a9d3cSKalle Valo "REO2SW3_RING",
24da3a9d3cSKalle Valo "REO2SW4_RING",
25da3a9d3cSKalle Valo "WBM2REO_LINK_RING",
26da3a9d3cSKalle Valo "REO2TCL_RING",
27da3a9d3cSKalle Valo "REO2FW_RING",
28da3a9d3cSKalle Valo "RELEASE_RING",
29da3a9d3cSKalle Valo "PPE_RELEASE_RING",
30da3a9d3cSKalle Valo "TCL2TQM_RING",
31da3a9d3cSKalle Valo "TQM_RELEASE_RING",
32da3a9d3cSKalle Valo "REO_RELEASE_RING",
33da3a9d3cSKalle Valo "WBM2SW0_RELEASE_RING",
34da3a9d3cSKalle Valo "WBM2SW1_RELEASE_RING",
35da3a9d3cSKalle Valo "WBM2SW2_RELEASE_RING",
36da3a9d3cSKalle Valo "WBM2SW3_RELEASE_RING",
37da3a9d3cSKalle Valo "REO_CMD_RING",
38da3a9d3cSKalle Valo "REO_STATUS_RING",
39da3a9d3cSKalle Valo };
40da3a9d3cSKalle Valo
41da3a9d3cSKalle Valo static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = {
42da3a9d3cSKalle Valo "FW2RXDMA_BUF_RING",
43da3a9d3cSKalle Valo "FW2RXDMA_STATUS_RING",
44da3a9d3cSKalle Valo "FW2RXDMA_LINK_RING",
45da3a9d3cSKalle Valo "SW2RXDMA_BUF_RING",
46da3a9d3cSKalle Valo "WBM2RXDMA_LINK_RING",
47da3a9d3cSKalle Valo "RXDMA2FW_RING",
48da3a9d3cSKalle Valo "RXDMA2SW_RING",
49da3a9d3cSKalle Valo "RXDMA2RELEASE_RING",
50da3a9d3cSKalle Valo "RXDMA2REO_RING",
51da3a9d3cSKalle Valo "MONITOR_STATUS_RING",
52da3a9d3cSKalle Valo "MONITOR_BUF_RING",
53da3a9d3cSKalle Valo "MONITOR_DESC_RING",
54da3a9d3cSKalle Valo "MONITOR_DEST_RING",
55da3a9d3cSKalle Valo };
56da3a9d3cSKalle Valo
ath11k_debugfs_add_dbring_entry(struct ath11k * ar,enum wmi_direct_buffer_module id,enum ath11k_dbg_dbr_event event,struct hal_srng * srng)57691425b4SVenkateswara Naralasetty void ath11k_debugfs_add_dbring_entry(struct ath11k *ar,
58691425b4SVenkateswara Naralasetty enum wmi_direct_buffer_module id,
59691425b4SVenkateswara Naralasetty enum ath11k_dbg_dbr_event event,
60691425b4SVenkateswara Naralasetty struct hal_srng *srng)
61691425b4SVenkateswara Naralasetty {
62691425b4SVenkateswara Naralasetty struct ath11k_debug_dbr *dbr_debug;
63691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_data;
64691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_entry *entry;
65691425b4SVenkateswara Naralasetty
66691425b4SVenkateswara Naralasetty if (id >= WMI_DIRECT_BUF_MAX || event >= ATH11K_DBG_DBR_EVENT_MAX)
67691425b4SVenkateswara Naralasetty return;
68691425b4SVenkateswara Naralasetty
69691425b4SVenkateswara Naralasetty dbr_debug = ar->debug.dbr_debug[id];
70691425b4SVenkateswara Naralasetty if (!dbr_debug)
71691425b4SVenkateswara Naralasetty return;
72691425b4SVenkateswara Naralasetty
73691425b4SVenkateswara Naralasetty if (!dbr_debug->dbr_debug_enabled)
74691425b4SVenkateswara Naralasetty return;
75691425b4SVenkateswara Naralasetty
76691425b4SVenkateswara Naralasetty dbr_data = &dbr_debug->dbr_dbg_data;
77691425b4SVenkateswara Naralasetty
78691425b4SVenkateswara Naralasetty spin_lock_bh(&dbr_data->lock);
79691425b4SVenkateswara Naralasetty
80691425b4SVenkateswara Naralasetty if (dbr_data->entries) {
81691425b4SVenkateswara Naralasetty entry = &dbr_data->entries[dbr_data->dbr_debug_idx];
82691425b4SVenkateswara Naralasetty entry->hp = srng->u.src_ring.hp;
83691425b4SVenkateswara Naralasetty entry->tp = *srng->u.src_ring.tp_addr;
84691425b4SVenkateswara Naralasetty entry->timestamp = jiffies;
85691425b4SVenkateswara Naralasetty entry->event = event;
86691425b4SVenkateswara Naralasetty
87691425b4SVenkateswara Naralasetty dbr_data->dbr_debug_idx++;
88691425b4SVenkateswara Naralasetty if (dbr_data->dbr_debug_idx ==
89691425b4SVenkateswara Naralasetty dbr_data->num_ring_debug_entries)
90691425b4SVenkateswara Naralasetty dbr_data->dbr_debug_idx = 0;
91691425b4SVenkateswara Naralasetty }
92691425b4SVenkateswara Naralasetty
93691425b4SVenkateswara Naralasetty spin_unlock_bh(&dbr_data->lock);
94691425b4SVenkateswara Naralasetty }
95691425b4SVenkateswara Naralasetty
ath11k_debugfs_fw_stats_reset(struct ath11k * ar)96cb4e57dbSKalle Valo static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar)
97da3a9d3cSKalle Valo {
98da3a9d3cSKalle Valo spin_lock_bh(&ar->data_lock);
99ec8918f9SAditya Kumar Singh ar->fw_stats_done = false;
100ec8918f9SAditya Kumar Singh ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
101ec8918f9SAditya Kumar Singh ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
102da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock);
103da3a9d3cSKalle Valo }
104da3a9d3cSKalle Valo
ath11k_debugfs_fw_stats_process(struct ath11k * ar,struct ath11k_fw_stats * stats)105ec8918f9SAditya Kumar Singh void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *stats)
106da3a9d3cSKalle Valo {
107ec8918f9SAditya Kumar Singh struct ath11k_base *ab = ar->ab;
108da3a9d3cSKalle Valo struct ath11k_pdev *pdev;
109da3a9d3cSKalle Valo bool is_end;
110da3a9d3cSKalle Valo static unsigned int num_vdev, num_bcn;
111da3a9d3cSKalle Valo size_t total_vdevs_started = 0;
112ec8918f9SAditya Kumar Singh int i;
113da3a9d3cSKalle Valo
114ec8918f9SAditya Kumar Singh /* WMI_REQUEST_PDEV_STAT request has been already processed */
115da3a9d3cSKalle Valo
116ec8918f9SAditya Kumar Singh if (stats->stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {
117ec8918f9SAditya Kumar Singh ar->fw_stats_done = true;
118ec8918f9SAditya Kumar Singh return;
119da3a9d3cSKalle Valo }
120da3a9d3cSKalle Valo
121ec8918f9SAditya Kumar Singh if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
122ec8918f9SAditya Kumar Singh if (list_empty(&stats->vdevs)) {
123da3a9d3cSKalle Valo ath11k_warn(ab, "empty vdev stats");
124ec8918f9SAditya Kumar Singh return;
125da3a9d3cSKalle Valo }
126da3a9d3cSKalle Valo /* FW sends all the active VDEV stats irrespective of PDEV,
127da3a9d3cSKalle Valo * hence limit until the count of all VDEVs started
128da3a9d3cSKalle Valo */
129da3a9d3cSKalle Valo for (i = 0; i < ab->num_radios; i++) {
130da3a9d3cSKalle Valo pdev = rcu_dereference(ab->pdevs_active[i]);
131da3a9d3cSKalle Valo if (pdev && pdev->ar)
132da3a9d3cSKalle Valo total_vdevs_started += ar->num_started_vdevs;
133da3a9d3cSKalle Valo }
134da3a9d3cSKalle Valo
135da3a9d3cSKalle Valo is_end = ((++num_vdev) == total_vdevs_started);
136da3a9d3cSKalle Valo
137ec8918f9SAditya Kumar Singh list_splice_tail_init(&stats->vdevs,
138ec8918f9SAditya Kumar Singh &ar->fw_stats.vdevs);
139da3a9d3cSKalle Valo
140da3a9d3cSKalle Valo if (is_end) {
141ec8918f9SAditya Kumar Singh ar->fw_stats_done = true;
142da3a9d3cSKalle Valo num_vdev = 0;
143da3a9d3cSKalle Valo }
144ec8918f9SAditya Kumar Singh return;
145da3a9d3cSKalle Valo }
146da3a9d3cSKalle Valo
147ec8918f9SAditya Kumar Singh if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
148ec8918f9SAditya Kumar Singh if (list_empty(&stats->bcn)) {
149da3a9d3cSKalle Valo ath11k_warn(ab, "empty bcn stats");
150ec8918f9SAditya Kumar Singh return;
151da3a9d3cSKalle Valo }
152da3a9d3cSKalle Valo /* Mark end until we reached the count of all started VDEVs
153da3a9d3cSKalle Valo * within the PDEV
154da3a9d3cSKalle Valo */
155da3a9d3cSKalle Valo is_end = ((++num_bcn) == ar->num_started_vdevs);
156da3a9d3cSKalle Valo
157ec8918f9SAditya Kumar Singh list_splice_tail_init(&stats->bcn,
158ec8918f9SAditya Kumar Singh &ar->fw_stats.bcn);
159da3a9d3cSKalle Valo
160da3a9d3cSKalle Valo if (is_end) {
161ec8918f9SAditya Kumar Singh ar->fw_stats_done = true;
162da3a9d3cSKalle Valo num_bcn = 0;
163da3a9d3cSKalle Valo }
164da3a9d3cSKalle Valo }
165da3a9d3cSKalle Valo }
166da3a9d3cSKalle Valo
ath11k_debugfs_fw_stats_request(struct ath11k * ar,struct stats_request_params * req_param)167cb4e57dbSKalle Valo static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,
168da3a9d3cSKalle Valo struct stats_request_params *req_param)
169da3a9d3cSKalle Valo {
170da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab;
171da3a9d3cSKalle Valo unsigned long timeout, time_left;
172da3a9d3cSKalle Valo int ret;
173da3a9d3cSKalle Valo
174da3a9d3cSKalle Valo lockdep_assert_held(&ar->conf_mutex);
175da3a9d3cSKalle Valo
176da3a9d3cSKalle Valo /* FW stats can get split when exceeding the stats data buffer limit.
177da3a9d3cSKalle Valo * In that case, since there is no end marking for the back-to-back
178da3a9d3cSKalle Valo * received 'update stats' event, we keep a 3 seconds timeout in case,
179da3a9d3cSKalle Valo * fw_stats_done is not marked yet
180da3a9d3cSKalle Valo */
181c8f2d41bSWen Gong timeout = jiffies + msecs_to_jiffies(3 * 1000);
182da3a9d3cSKalle Valo
183cb4e57dbSKalle Valo ath11k_debugfs_fw_stats_reset(ar);
184da3a9d3cSKalle Valo
185ec8918f9SAditya Kumar Singh reinit_completion(&ar->fw_stats_complete);
186da3a9d3cSKalle Valo
187da3a9d3cSKalle Valo ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
188da3a9d3cSKalle Valo
189da3a9d3cSKalle Valo if (ret) {
190da3a9d3cSKalle Valo ath11k_warn(ab, "could not request fw stats (%d)\n",
191da3a9d3cSKalle Valo ret);
192da3a9d3cSKalle Valo return ret;
193da3a9d3cSKalle Valo }
194da3a9d3cSKalle Valo
195ec8918f9SAditya Kumar Singh time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
196ec8918f9SAditya Kumar Singh
197da3a9d3cSKalle Valo if (!time_left)
198da3a9d3cSKalle Valo return -ETIMEDOUT;
199da3a9d3cSKalle Valo
200da3a9d3cSKalle Valo for (;;) {
201da3a9d3cSKalle Valo if (time_after(jiffies, timeout))
202da3a9d3cSKalle Valo break;
203da3a9d3cSKalle Valo
204da3a9d3cSKalle Valo spin_lock_bh(&ar->data_lock);
205ec8918f9SAditya Kumar Singh if (ar->fw_stats_done) {
206da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock);
207da3a9d3cSKalle Valo break;
208da3a9d3cSKalle Valo }
209da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock);
210da3a9d3cSKalle Valo }
211da3a9d3cSKalle Valo return 0;
212da3a9d3cSKalle Valo }
213da3a9d3cSKalle Valo
ath11k_debugfs_get_fw_stats(struct ath11k * ar,u32 pdev_id,u32 vdev_id,u32 stats_id)214b488c766SWen Gong int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id,
215b488c766SWen Gong u32 vdev_id, u32 stats_id)
216b488c766SWen Gong {
217b488c766SWen Gong struct ath11k_base *ab = ar->ab;
218b488c766SWen Gong struct stats_request_params req_param;
219b488c766SWen Gong int ret;
220b488c766SWen Gong
221b488c766SWen Gong mutex_lock(&ar->conf_mutex);
222b488c766SWen Gong
223b488c766SWen Gong if (ar->state != ATH11K_STATE_ON) {
224b488c766SWen Gong ret = -ENETDOWN;
225b488c766SWen Gong goto err_unlock;
226b488c766SWen Gong }
227b488c766SWen Gong
228b488c766SWen Gong req_param.pdev_id = pdev_id;
229b488c766SWen Gong req_param.vdev_id = vdev_id;
230b488c766SWen Gong req_param.stats_id = stats_id;
231b488c766SWen Gong
232b488c766SWen Gong ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
233b488c766SWen Gong if (ret)
234b488c766SWen Gong ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
235b488c766SWen Gong
236b488c766SWen Gong ath11k_dbg(ab, ATH11K_DBG_WMI,
237b488c766SWen Gong "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
238b488c766SWen Gong pdev_id, vdev_id, stats_id);
239b488c766SWen Gong
240b488c766SWen Gong err_unlock:
241b488c766SWen Gong mutex_unlock(&ar->conf_mutex);
242b488c766SWen Gong
243b488c766SWen Gong return ret;
244b488c766SWen Gong }
245b488c766SWen Gong
ath11k_open_pdev_stats(struct inode * inode,struct file * file)246da3a9d3cSKalle Valo static int ath11k_open_pdev_stats(struct inode *inode, struct file *file)
247da3a9d3cSKalle Valo {
248da3a9d3cSKalle Valo struct ath11k *ar = inode->i_private;
249da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab;
250da3a9d3cSKalle Valo struct stats_request_params req_param;
251da3a9d3cSKalle Valo void *buf = NULL;
252da3a9d3cSKalle Valo int ret;
253da3a9d3cSKalle Valo
254da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
255da3a9d3cSKalle Valo
256da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) {
257da3a9d3cSKalle Valo ret = -ENETDOWN;
258da3a9d3cSKalle Valo goto err_unlock;
259da3a9d3cSKalle Valo }
260da3a9d3cSKalle Valo
261da3a9d3cSKalle Valo buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
262da3a9d3cSKalle Valo if (!buf) {
263da3a9d3cSKalle Valo ret = -ENOMEM;
264da3a9d3cSKalle Valo goto err_unlock;
265da3a9d3cSKalle Valo }
266da3a9d3cSKalle Valo
267da3a9d3cSKalle Valo req_param.pdev_id = ar->pdev->pdev_id;
268da3a9d3cSKalle Valo req_param.vdev_id = 0;
269da3a9d3cSKalle Valo req_param.stats_id = WMI_REQUEST_PDEV_STAT;
270da3a9d3cSKalle Valo
271cb4e57dbSKalle Valo ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
272da3a9d3cSKalle Valo if (ret) {
273da3a9d3cSKalle Valo ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
274da3a9d3cSKalle Valo goto err_free;
275da3a9d3cSKalle Valo }
276da3a9d3cSKalle Valo
277ec8918f9SAditya Kumar Singh ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);
278da3a9d3cSKalle Valo
279da3a9d3cSKalle Valo file->private_data = buf;
280da3a9d3cSKalle Valo
281da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
282da3a9d3cSKalle Valo return 0;
283da3a9d3cSKalle Valo
284da3a9d3cSKalle Valo err_free:
285da3a9d3cSKalle Valo vfree(buf);
286da3a9d3cSKalle Valo
287da3a9d3cSKalle Valo err_unlock:
288da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
289da3a9d3cSKalle Valo return ret;
290da3a9d3cSKalle Valo }
291da3a9d3cSKalle Valo
ath11k_release_pdev_stats(struct inode * inode,struct file * file)292da3a9d3cSKalle Valo static int ath11k_release_pdev_stats(struct inode *inode, struct file *file)
293da3a9d3cSKalle Valo {
294da3a9d3cSKalle Valo vfree(file->private_data);
295da3a9d3cSKalle Valo
296da3a9d3cSKalle Valo return 0;
297da3a9d3cSKalle Valo }
298da3a9d3cSKalle Valo
ath11k_read_pdev_stats(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)299da3a9d3cSKalle Valo static ssize_t ath11k_read_pdev_stats(struct file *file,
300da3a9d3cSKalle Valo char __user *user_buf,
301da3a9d3cSKalle Valo size_t count, loff_t *ppos)
302da3a9d3cSKalle Valo {
303da3a9d3cSKalle Valo const char *buf = file->private_data;
304da3a9d3cSKalle Valo size_t len = strlen(buf);
305da3a9d3cSKalle Valo
306da3a9d3cSKalle Valo return simple_read_from_buffer(user_buf, count, ppos, buf, len);
307da3a9d3cSKalle Valo }
308da3a9d3cSKalle Valo
309da3a9d3cSKalle Valo static const struct file_operations fops_pdev_stats = {
310da3a9d3cSKalle Valo .open = ath11k_open_pdev_stats,
311da3a9d3cSKalle Valo .release = ath11k_release_pdev_stats,
312da3a9d3cSKalle Valo .read = ath11k_read_pdev_stats,
313da3a9d3cSKalle Valo .owner = THIS_MODULE,
314da3a9d3cSKalle Valo .llseek = default_llseek,
315da3a9d3cSKalle Valo };
316da3a9d3cSKalle Valo
ath11k_open_vdev_stats(struct inode * inode,struct file * file)317da3a9d3cSKalle Valo static int ath11k_open_vdev_stats(struct inode *inode, struct file *file)
318da3a9d3cSKalle Valo {
319da3a9d3cSKalle Valo struct ath11k *ar = inode->i_private;
320da3a9d3cSKalle Valo struct stats_request_params req_param;
321da3a9d3cSKalle Valo void *buf = NULL;
322da3a9d3cSKalle Valo int ret;
323da3a9d3cSKalle Valo
324da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
325da3a9d3cSKalle Valo
326da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) {
327da3a9d3cSKalle Valo ret = -ENETDOWN;
328da3a9d3cSKalle Valo goto err_unlock;
329da3a9d3cSKalle Valo }
330da3a9d3cSKalle Valo
331da3a9d3cSKalle Valo buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
332da3a9d3cSKalle Valo if (!buf) {
333da3a9d3cSKalle Valo ret = -ENOMEM;
334da3a9d3cSKalle Valo goto err_unlock;
335da3a9d3cSKalle Valo }
336da3a9d3cSKalle Valo
337da3a9d3cSKalle Valo req_param.pdev_id = ar->pdev->pdev_id;
338da3a9d3cSKalle Valo /* VDEV stats is always sent for all active VDEVs from FW */
339da3a9d3cSKalle Valo req_param.vdev_id = 0;
340da3a9d3cSKalle Valo req_param.stats_id = WMI_REQUEST_VDEV_STAT;
341da3a9d3cSKalle Valo
342cb4e57dbSKalle Valo ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
343da3a9d3cSKalle Valo if (ret) {
344da3a9d3cSKalle Valo ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);
345da3a9d3cSKalle Valo goto err_free;
346da3a9d3cSKalle Valo }
347da3a9d3cSKalle Valo
348ec8918f9SAditya Kumar Singh ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);
349da3a9d3cSKalle Valo
350da3a9d3cSKalle Valo file->private_data = buf;
351da3a9d3cSKalle Valo
352da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
353da3a9d3cSKalle Valo return 0;
354da3a9d3cSKalle Valo
355da3a9d3cSKalle Valo err_free:
356da3a9d3cSKalle Valo vfree(buf);
357da3a9d3cSKalle Valo
358da3a9d3cSKalle Valo err_unlock:
359da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
360da3a9d3cSKalle Valo return ret;
361da3a9d3cSKalle Valo }
362da3a9d3cSKalle Valo
ath11k_release_vdev_stats(struct inode * inode,struct file * file)363da3a9d3cSKalle Valo static int ath11k_release_vdev_stats(struct inode *inode, struct file *file)
364da3a9d3cSKalle Valo {
365da3a9d3cSKalle Valo vfree(file->private_data);
366da3a9d3cSKalle Valo
367da3a9d3cSKalle Valo return 0;
368da3a9d3cSKalle Valo }
369da3a9d3cSKalle Valo
ath11k_read_vdev_stats(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)370da3a9d3cSKalle Valo static ssize_t ath11k_read_vdev_stats(struct file *file,
371da3a9d3cSKalle Valo char __user *user_buf,
372da3a9d3cSKalle Valo size_t count, loff_t *ppos)
373da3a9d3cSKalle Valo {
374da3a9d3cSKalle Valo const char *buf = file->private_data;
375da3a9d3cSKalle Valo size_t len = strlen(buf);
376da3a9d3cSKalle Valo
377da3a9d3cSKalle Valo return simple_read_from_buffer(user_buf, count, ppos, buf, len);
378da3a9d3cSKalle Valo }
379da3a9d3cSKalle Valo
380da3a9d3cSKalle Valo static const struct file_operations fops_vdev_stats = {
381da3a9d3cSKalle Valo .open = ath11k_open_vdev_stats,
382da3a9d3cSKalle Valo .release = ath11k_release_vdev_stats,
383da3a9d3cSKalle Valo .read = ath11k_read_vdev_stats,
384da3a9d3cSKalle Valo .owner = THIS_MODULE,
385da3a9d3cSKalle Valo .llseek = default_llseek,
386da3a9d3cSKalle Valo };
387da3a9d3cSKalle Valo
ath11k_open_bcn_stats(struct inode * inode,struct file * file)388da3a9d3cSKalle Valo static int ath11k_open_bcn_stats(struct inode *inode, struct file *file)
389da3a9d3cSKalle Valo {
390da3a9d3cSKalle Valo struct ath11k *ar = inode->i_private;
391da3a9d3cSKalle Valo struct ath11k_vif *arvif;
392da3a9d3cSKalle Valo struct stats_request_params req_param;
393da3a9d3cSKalle Valo void *buf = NULL;
394da3a9d3cSKalle Valo int ret;
395da3a9d3cSKalle Valo
396da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
397da3a9d3cSKalle Valo
398da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) {
399da3a9d3cSKalle Valo ret = -ENETDOWN;
400da3a9d3cSKalle Valo goto err_unlock;
401da3a9d3cSKalle Valo }
402da3a9d3cSKalle Valo
403da3a9d3cSKalle Valo buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);
404da3a9d3cSKalle Valo if (!buf) {
405da3a9d3cSKalle Valo ret = -ENOMEM;
406da3a9d3cSKalle Valo goto err_unlock;
407da3a9d3cSKalle Valo }
408da3a9d3cSKalle Valo
409da3a9d3cSKalle Valo req_param.stats_id = WMI_REQUEST_BCN_STAT;
410da3a9d3cSKalle Valo req_param.pdev_id = ar->pdev->pdev_id;
411da3a9d3cSKalle Valo
412da3a9d3cSKalle Valo /* loop all active VDEVs for bcn stats */
413da3a9d3cSKalle Valo list_for_each_entry(arvif, &ar->arvifs, list) {
414da3a9d3cSKalle Valo if (!arvif->is_up)
415da3a9d3cSKalle Valo continue;
416da3a9d3cSKalle Valo
417da3a9d3cSKalle Valo req_param.vdev_id = arvif->vdev_id;
418cb4e57dbSKalle Valo ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
419da3a9d3cSKalle Valo if (ret) {
420da3a9d3cSKalle Valo ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);
421da3a9d3cSKalle Valo goto err_free;
422da3a9d3cSKalle Valo }
423da3a9d3cSKalle Valo }
424da3a9d3cSKalle Valo
425ec8918f9SAditya Kumar Singh ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);
426da3a9d3cSKalle Valo
427da3a9d3cSKalle Valo /* since beacon stats request is looped for all active VDEVs, saved fw
428da3a9d3cSKalle Valo * stats is not freed for each request until done for all active VDEVs
429da3a9d3cSKalle Valo */
430da3a9d3cSKalle Valo spin_lock_bh(&ar->data_lock);
431ec8918f9SAditya Kumar Singh ath11k_fw_stats_bcn_free(&ar->fw_stats.bcn);
432da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock);
433da3a9d3cSKalle Valo
434da3a9d3cSKalle Valo file->private_data = buf;
435da3a9d3cSKalle Valo
436da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
437da3a9d3cSKalle Valo return 0;
438da3a9d3cSKalle Valo
439da3a9d3cSKalle Valo err_free:
440da3a9d3cSKalle Valo vfree(buf);
441da3a9d3cSKalle Valo
442da3a9d3cSKalle Valo err_unlock:
443da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
444da3a9d3cSKalle Valo return ret;
445da3a9d3cSKalle Valo }
446da3a9d3cSKalle Valo
ath11k_release_bcn_stats(struct inode * inode,struct file * file)447da3a9d3cSKalle Valo static int ath11k_release_bcn_stats(struct inode *inode, struct file *file)
448da3a9d3cSKalle Valo {
449da3a9d3cSKalle Valo vfree(file->private_data);
450da3a9d3cSKalle Valo
451da3a9d3cSKalle Valo return 0;
452da3a9d3cSKalle Valo }
453da3a9d3cSKalle Valo
ath11k_read_bcn_stats(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)454da3a9d3cSKalle Valo static ssize_t ath11k_read_bcn_stats(struct file *file,
455da3a9d3cSKalle Valo char __user *user_buf,
456da3a9d3cSKalle Valo size_t count, loff_t *ppos)
457da3a9d3cSKalle Valo {
458da3a9d3cSKalle Valo const char *buf = file->private_data;
459da3a9d3cSKalle Valo size_t len = strlen(buf);
460da3a9d3cSKalle Valo
461da3a9d3cSKalle Valo return simple_read_from_buffer(user_buf, count, ppos, buf, len);
462da3a9d3cSKalle Valo }
463da3a9d3cSKalle Valo
464da3a9d3cSKalle Valo static const struct file_operations fops_bcn_stats = {
465da3a9d3cSKalle Valo .open = ath11k_open_bcn_stats,
466da3a9d3cSKalle Valo .release = ath11k_release_bcn_stats,
467da3a9d3cSKalle Valo .read = ath11k_read_bcn_stats,
468da3a9d3cSKalle Valo .owner = THIS_MODULE,
469da3a9d3cSKalle Valo .llseek = default_llseek,
470da3a9d3cSKalle Valo };
471da3a9d3cSKalle Valo
ath11k_read_simulate_fw_crash(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)472da3a9d3cSKalle Valo static ssize_t ath11k_read_simulate_fw_crash(struct file *file,
473da3a9d3cSKalle Valo char __user *user_buf,
474da3a9d3cSKalle Valo size_t count, loff_t *ppos)
475da3a9d3cSKalle Valo {
476da3a9d3cSKalle Valo const char buf[] =
477da3a9d3cSKalle Valo "To simulate firmware crash write one of the keywords to this file:\n"
478da3a9d3cSKalle Valo "`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n"
479da3a9d3cSKalle Valo "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
480da3a9d3cSKalle Valo
481da3a9d3cSKalle Valo return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
482da3a9d3cSKalle Valo }
483da3a9d3cSKalle Valo
484da3a9d3cSKalle Valo /* Simulate firmware crash:
485da3a9d3cSKalle Valo * 'soft': Call wmi command causing firmware hang. This firmware hang is
486da3a9d3cSKalle Valo * recoverable by warm firmware reset.
487da3a9d3cSKalle Valo * 'hard': Force firmware crash by setting any vdev parameter for not allowed
488da3a9d3cSKalle Valo * vdev id. This is hard firmware crash because it is recoverable only by cold
489da3a9d3cSKalle Valo * firmware reset.
490da3a9d3cSKalle Valo */
ath11k_write_simulate_fw_crash(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)491da3a9d3cSKalle Valo static ssize_t ath11k_write_simulate_fw_crash(struct file *file,
492da3a9d3cSKalle Valo const char __user *user_buf,
493da3a9d3cSKalle Valo size_t count, loff_t *ppos)
494da3a9d3cSKalle Valo {
495da3a9d3cSKalle Valo struct ath11k_base *ab = file->private_data;
496da3a9d3cSKalle Valo struct ath11k_pdev *pdev;
497da3a9d3cSKalle Valo struct ath11k *ar = ab->pdevs[0].ar;
498da3a9d3cSKalle Valo char buf[32] = {0};
499da3a9d3cSKalle Valo ssize_t rc;
500da3a9d3cSKalle Valo int i, ret, radioup = 0;
501da3a9d3cSKalle Valo
502da3a9d3cSKalle Valo for (i = 0; i < ab->num_radios; i++) {
503da3a9d3cSKalle Valo pdev = &ab->pdevs[i];
504da3a9d3cSKalle Valo ar = pdev->ar;
505da3a9d3cSKalle Valo if (ar && ar->state == ATH11K_STATE_ON) {
506da3a9d3cSKalle Valo radioup = 1;
507da3a9d3cSKalle Valo break;
508da3a9d3cSKalle Valo }
509da3a9d3cSKalle Valo }
510da3a9d3cSKalle Valo /* filter partial writes and invalid commands */
511da3a9d3cSKalle Valo if (*ppos != 0 || count >= sizeof(buf) || count == 0)
512da3a9d3cSKalle Valo return -EINVAL;
513da3a9d3cSKalle Valo
514da3a9d3cSKalle Valo rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
515da3a9d3cSKalle Valo if (rc < 0)
516da3a9d3cSKalle Valo return rc;
517da3a9d3cSKalle Valo
518da3a9d3cSKalle Valo /* drop the possible '\n' from the end */
519da3a9d3cSKalle Valo if (buf[*ppos - 1] == '\n')
520da3a9d3cSKalle Valo buf[*ppos - 1] = '\0';
521da3a9d3cSKalle Valo
522da3a9d3cSKalle Valo if (radioup == 0) {
523da3a9d3cSKalle Valo ret = -ENETDOWN;
524da3a9d3cSKalle Valo goto exit;
525da3a9d3cSKalle Valo }
526da3a9d3cSKalle Valo
527da3a9d3cSKalle Valo if (!strcmp(buf, "assert")) {
528da3a9d3cSKalle Valo ath11k_info(ab, "simulating firmware assert crash\n");
529da3a9d3cSKalle Valo ret = ath11k_wmi_force_fw_hang_cmd(ar,
530da3a9d3cSKalle Valo ATH11K_WMI_FW_HANG_ASSERT_TYPE,
531da3a9d3cSKalle Valo ATH11K_WMI_FW_HANG_DELAY);
53278e3e609SWen Gong } else if (!strcmp(buf, "hw-restart")) {
53378e3e609SWen Gong ath11k_info(ab, "user requested hw restart\n");
53478e3e609SWen Gong queue_work(ab->workqueue_aux, &ab->reset_work);
53578e3e609SWen Gong ret = 0;
536da3a9d3cSKalle Valo } else {
537da3a9d3cSKalle Valo ret = -EINVAL;
538da3a9d3cSKalle Valo goto exit;
539da3a9d3cSKalle Valo }
540da3a9d3cSKalle Valo
541da3a9d3cSKalle Valo if (ret) {
542da3a9d3cSKalle Valo ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret);
543da3a9d3cSKalle Valo goto exit;
544da3a9d3cSKalle Valo }
545da3a9d3cSKalle Valo
546da3a9d3cSKalle Valo ret = count;
547da3a9d3cSKalle Valo
548da3a9d3cSKalle Valo exit:
549da3a9d3cSKalle Valo return ret;
550da3a9d3cSKalle Valo }
551da3a9d3cSKalle Valo
552da3a9d3cSKalle Valo static const struct file_operations fops_simulate_fw_crash = {
553da3a9d3cSKalle Valo .read = ath11k_read_simulate_fw_crash,
554da3a9d3cSKalle Valo .write = ath11k_write_simulate_fw_crash,
555da3a9d3cSKalle Valo .open = simple_open,
556da3a9d3cSKalle Valo .owner = THIS_MODULE,
557da3a9d3cSKalle Valo .llseek = default_llseek,
558da3a9d3cSKalle Valo };
559da3a9d3cSKalle Valo
ath11k_write_enable_extd_tx_stats(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)560da3a9d3cSKalle Valo static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file,
561da3a9d3cSKalle Valo const char __user *ubuf,
562da3a9d3cSKalle Valo size_t count, loff_t *ppos)
563da3a9d3cSKalle Valo {
564da3a9d3cSKalle Valo struct ath11k *ar = file->private_data;
565da3a9d3cSKalle Valo u32 filter;
566da3a9d3cSKalle Valo int ret;
567da3a9d3cSKalle Valo
568da3a9d3cSKalle Valo if (kstrtouint_from_user(ubuf, count, 0, &filter))
569da3a9d3cSKalle Valo return -EINVAL;
570da3a9d3cSKalle Valo
571da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
572da3a9d3cSKalle Valo
573da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) {
574da3a9d3cSKalle Valo ret = -ENETDOWN;
575da3a9d3cSKalle Valo goto out;
576da3a9d3cSKalle Valo }
577da3a9d3cSKalle Valo
578da3a9d3cSKalle Valo if (filter == ar->debug.extd_tx_stats) {
579da3a9d3cSKalle Valo ret = count;
580da3a9d3cSKalle Valo goto out;
581da3a9d3cSKalle Valo }
582da3a9d3cSKalle Valo
583da3a9d3cSKalle Valo ar->debug.extd_tx_stats = filter;
584da3a9d3cSKalle Valo ret = count;
585da3a9d3cSKalle Valo
586da3a9d3cSKalle Valo out:
587da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
588da3a9d3cSKalle Valo return ret;
589da3a9d3cSKalle Valo }
590da3a9d3cSKalle Valo
ath11k_read_enable_extd_tx_stats(struct file * file,char __user * ubuf,size_t count,loff_t * ppos)591da3a9d3cSKalle Valo static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file,
592da3a9d3cSKalle Valo char __user *ubuf,
593da3a9d3cSKalle Valo size_t count, loff_t *ppos)
594da3a9d3cSKalle Valo
595da3a9d3cSKalle Valo {
596da3a9d3cSKalle Valo char buf[32] = {0};
597da3a9d3cSKalle Valo struct ath11k *ar = file->private_data;
598da3a9d3cSKalle Valo int len = 0;
599da3a9d3cSKalle Valo
600da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
601da3a9d3cSKalle Valo len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
602da3a9d3cSKalle Valo ar->debug.extd_tx_stats);
603da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
604da3a9d3cSKalle Valo
605da3a9d3cSKalle Valo return simple_read_from_buffer(ubuf, count, ppos, buf, len);
606da3a9d3cSKalle Valo }
607da3a9d3cSKalle Valo
608da3a9d3cSKalle Valo static const struct file_operations fops_extd_tx_stats = {
609da3a9d3cSKalle Valo .read = ath11k_read_enable_extd_tx_stats,
610da3a9d3cSKalle Valo .write = ath11k_write_enable_extd_tx_stats,
611da3a9d3cSKalle Valo .open = simple_open
612da3a9d3cSKalle Valo };
613da3a9d3cSKalle Valo
ath11k_write_extd_rx_stats(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)614da3a9d3cSKalle Valo static ssize_t ath11k_write_extd_rx_stats(struct file *file,
615da3a9d3cSKalle Valo const char __user *ubuf,
616da3a9d3cSKalle Valo size_t count, loff_t *ppos)
617da3a9d3cSKalle Valo {
618da3a9d3cSKalle Valo struct ath11k *ar = file->private_data;
619da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab;
620da3a9d3cSKalle Valo struct htt_rx_ring_tlv_filter tlv_filter = {0};
621da3a9d3cSKalle Valo u32 enable, rx_filter = 0, ring_id;
622da3a9d3cSKalle Valo int i;
623da3a9d3cSKalle Valo int ret;
624da3a9d3cSKalle Valo
625da3a9d3cSKalle Valo if (kstrtouint_from_user(ubuf, count, 0, &enable))
626da3a9d3cSKalle Valo return -EINVAL;
627da3a9d3cSKalle Valo
628da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
629da3a9d3cSKalle Valo
630da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) {
631da3a9d3cSKalle Valo ret = -ENETDOWN;
632da3a9d3cSKalle Valo goto exit;
633da3a9d3cSKalle Valo }
634da3a9d3cSKalle Valo
635da3a9d3cSKalle Valo if (enable > 1) {
636da3a9d3cSKalle Valo ret = -EINVAL;
637da3a9d3cSKalle Valo goto exit;
638da3a9d3cSKalle Valo }
639da3a9d3cSKalle Valo
640da3a9d3cSKalle Valo if (enable == ar->debug.extd_rx_stats) {
641da3a9d3cSKalle Valo ret = count;
642da3a9d3cSKalle Valo goto exit;
643da3a9d3cSKalle Valo }
644da3a9d3cSKalle Valo
64567a9d399SMiles Hu if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) {
64667a9d399SMiles Hu ar->debug.extd_rx_stats = enable;
64767a9d399SMiles Hu ret = count;
64867a9d399SMiles Hu goto exit;
64967a9d399SMiles Hu }
65067a9d399SMiles Hu
651da3a9d3cSKalle Valo if (enable) {
652da3a9d3cSKalle Valo rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START;
653da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START;
654da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END;
655da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;
656da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT;
657da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE;
658da3a9d3cSKalle Valo
659da3a9d3cSKalle Valo tlv_filter.rx_filter = rx_filter;
660da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;
661da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;
662da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;
663da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |
664da3a9d3cSKalle Valo HTT_RX_FP_DATA_FILTER_FLASG3;
665da3a9d3cSKalle Valo } else {
666da3a9d3cSKalle Valo tlv_filter = ath11k_mac_mon_status_filter_default;
667da3a9d3cSKalle Valo }
668da3a9d3cSKalle Valo
669da3a9d3cSKalle Valo ar->debug.rx_filter = tlv_filter.rx_filter;
670da3a9d3cSKalle Valo
671da3a9d3cSKalle Valo for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {
672da3a9d3cSKalle Valo ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
673da3a9d3cSKalle Valo ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,
674da3a9d3cSKalle Valo HAL_RXDMA_MONITOR_STATUS,
675da3a9d3cSKalle Valo DP_RX_BUFFER_SIZE, &tlv_filter);
676da3a9d3cSKalle Valo
677da3a9d3cSKalle Valo if (ret) {
678da3a9d3cSKalle Valo ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");
679da3a9d3cSKalle Valo goto exit;
680da3a9d3cSKalle Valo }
681da3a9d3cSKalle Valo }
682da3a9d3cSKalle Valo
683da3a9d3cSKalle Valo ar->debug.extd_rx_stats = enable;
684da3a9d3cSKalle Valo ret = count;
685da3a9d3cSKalle Valo exit:
686da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
687da3a9d3cSKalle Valo return ret;
688da3a9d3cSKalle Valo }
689da3a9d3cSKalle Valo
ath11k_read_extd_rx_stats(struct file * file,char __user * ubuf,size_t count,loff_t * ppos)690da3a9d3cSKalle Valo static ssize_t ath11k_read_extd_rx_stats(struct file *file,
691da3a9d3cSKalle Valo char __user *ubuf,
692da3a9d3cSKalle Valo size_t count, loff_t *ppos)
693da3a9d3cSKalle Valo {
694da3a9d3cSKalle Valo struct ath11k *ar = file->private_data;
695da3a9d3cSKalle Valo char buf[32];
696da3a9d3cSKalle Valo int len = 0;
697da3a9d3cSKalle Valo
698da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
699da3a9d3cSKalle Valo len = scnprintf(buf, sizeof(buf) - len, "%d\n",
700da3a9d3cSKalle Valo ar->debug.extd_rx_stats);
701da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
702da3a9d3cSKalle Valo
703da3a9d3cSKalle Valo return simple_read_from_buffer(ubuf, count, ppos, buf, len);
704da3a9d3cSKalle Valo }
705da3a9d3cSKalle Valo
706da3a9d3cSKalle Valo static const struct file_operations fops_extd_rx_stats = {
707da3a9d3cSKalle Valo .read = ath11k_read_extd_rx_stats,
708da3a9d3cSKalle Valo .write = ath11k_write_extd_rx_stats,
709da3a9d3cSKalle Valo .open = simple_open,
710da3a9d3cSKalle Valo };
711da3a9d3cSKalle Valo
ath11k_fill_bp_stats(struct ath11k_base * ab,struct ath11k_bp_stats * bp_stats,char * buf,int len,int size)712da3a9d3cSKalle Valo static int ath11k_fill_bp_stats(struct ath11k_base *ab,
713da3a9d3cSKalle Valo struct ath11k_bp_stats *bp_stats,
714da3a9d3cSKalle Valo char *buf, int len, int size)
715da3a9d3cSKalle Valo {
716da3a9d3cSKalle Valo lockdep_assert_held(&ab->base_lock);
717da3a9d3cSKalle Valo
718da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "count: %u\n",
719da3a9d3cSKalle Valo bp_stats->count);
720da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "hp: %u\n",
721da3a9d3cSKalle Valo bp_stats->hp);
722da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "tp: %u\n",
723da3a9d3cSKalle Valo bp_stats->tp);
724da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "seen before: %ums\n\n",
725da3a9d3cSKalle Valo jiffies_to_msecs(jiffies - bp_stats->jiffies));
726da3a9d3cSKalle Valo return len;
727da3a9d3cSKalle Valo }
728da3a9d3cSKalle Valo
ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base * ab,char * buf,int size)729cb4e57dbSKalle Valo static ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab,
730da3a9d3cSKalle Valo char *buf, int size)
731da3a9d3cSKalle Valo {
732da3a9d3cSKalle Valo struct ath11k_bp_stats *bp_stats;
733da3a9d3cSKalle Valo bool stats_rxd = false;
734da3a9d3cSKalle Valo u8 i, pdev_idx;
735da3a9d3cSKalle Valo int len = 0;
736da3a9d3cSKalle Valo
737da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nBackpressure Stats\n");
738da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "==================\n");
739da3a9d3cSKalle Valo
740da3a9d3cSKalle Valo spin_lock_bh(&ab->base_lock);
741da3a9d3cSKalle Valo for (i = 0; i < HTT_SW_UMAC_RING_IDX_MAX; i++) {
742da3a9d3cSKalle Valo bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[i];
743da3a9d3cSKalle Valo
744da3a9d3cSKalle Valo if (!bp_stats->count)
745da3a9d3cSKalle Valo continue;
746da3a9d3cSKalle Valo
747da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "Ring: %s\n",
748da3a9d3cSKalle Valo htt_bp_umac_ring[i]);
749da3a9d3cSKalle Valo len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
750da3a9d3cSKalle Valo stats_rxd = true;
751da3a9d3cSKalle Valo }
752da3a9d3cSKalle Valo
753da3a9d3cSKalle Valo for (i = 0; i < HTT_SW_LMAC_RING_IDX_MAX; i++) {
754da3a9d3cSKalle Valo for (pdev_idx = 0; pdev_idx < MAX_RADIOS; pdev_idx++) {
755da3a9d3cSKalle Valo bp_stats =
756da3a9d3cSKalle Valo &ab->soc_stats.bp_stats.lmac_ring_bp_stats[i][pdev_idx];
757da3a9d3cSKalle Valo
758da3a9d3cSKalle Valo if (!bp_stats->count)
759da3a9d3cSKalle Valo continue;
760da3a9d3cSKalle Valo
761da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "Ring: %s\n",
762da3a9d3cSKalle Valo htt_bp_lmac_ring[i]);
763da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "pdev: %d\n",
764da3a9d3cSKalle Valo pdev_idx);
765da3a9d3cSKalle Valo len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
766da3a9d3cSKalle Valo stats_rxd = true;
767da3a9d3cSKalle Valo }
768da3a9d3cSKalle Valo }
769da3a9d3cSKalle Valo spin_unlock_bh(&ab->base_lock);
770da3a9d3cSKalle Valo
771da3a9d3cSKalle Valo if (!stats_rxd)
772da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len,
773da3a9d3cSKalle Valo "No Ring Backpressure stats received\n\n");
774da3a9d3cSKalle Valo
775da3a9d3cSKalle Valo return len;
776da3a9d3cSKalle Valo }
777da3a9d3cSKalle Valo
ath11k_debugfs_dump_soc_dp_stats(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)778cb4e57dbSKalle Valo static ssize_t ath11k_debugfs_dump_soc_dp_stats(struct file *file,
779da3a9d3cSKalle Valo char __user *user_buf,
780da3a9d3cSKalle Valo size_t count, loff_t *ppos)
781da3a9d3cSKalle Valo {
782da3a9d3cSKalle Valo struct ath11k_base *ab = file->private_data;
783da3a9d3cSKalle Valo struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;
784da3a9d3cSKalle Valo int len = 0, i, retval;
785da3a9d3cSKalle Valo const int size = 4096;
786da3a9d3cSKalle Valo static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = {
787da3a9d3cSKalle Valo "Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC",
788da3a9d3cSKalle Valo "Unencrypt", "MSDU len", "MSDU limit", "WiFi parse",
789da3a9d3cSKalle Valo "AMSDU parse", "SA timeout", "DA timeout",
790da3a9d3cSKalle Valo "Flow timeout", "Flush req"};
791da3a9d3cSKalle Valo static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = {
792da3a9d3cSKalle Valo "Desc addr zero", "Desc inval", "AMPDU in non BA",
793da3a9d3cSKalle Valo "Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump",
794da3a9d3cSKalle Valo "Frame OOR", "BAR OOR", "No BA session",
795da3a9d3cSKalle Valo "Frame SN equal SSN", "PN check fail", "2k err",
796da3a9d3cSKalle Valo "PN err", "Desc blocked"};
797da3a9d3cSKalle Valo
798da3a9d3cSKalle Valo char *buf;
799da3a9d3cSKalle Valo
800da3a9d3cSKalle Valo buf = kzalloc(size, GFP_KERNEL);
801da3a9d3cSKalle Valo if (!buf)
802da3a9d3cSKalle Valo return -ENOMEM;
803da3a9d3cSKalle Valo
804da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n");
805da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "err ring pkts: %u\n",
806da3a9d3cSKalle Valo soc_stats->err_ring_pkts);
807da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n",
808da3a9d3cSKalle Valo soc_stats->invalid_rbm);
809da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "RXDMA errors:\n");
810da3a9d3cSKalle Valo for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)
811da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "%s: %u\n",
812da3a9d3cSKalle Valo rxdma_err[i], soc_stats->rxdma_error[i]);
813da3a9d3cSKalle Valo
814da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nREO errors:\n");
815da3a9d3cSKalle Valo for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)
816da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "%s: %u\n",
817da3a9d3cSKalle Valo reo_err[i], soc_stats->reo_error[i]);
818da3a9d3cSKalle Valo
819da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n");
820da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len,
821da3a9d3cSKalle Valo "ring0: %u\nring1: %u\nring2: %u\nring3: %u\n",
822da3a9d3cSKalle Valo soc_stats->hal_reo_error[0],
823da3a9d3cSKalle Valo soc_stats->hal_reo_error[1],
824da3a9d3cSKalle Valo soc_stats->hal_reo_error[2],
825da3a9d3cSKalle Valo soc_stats->hal_reo_error[3]);
826da3a9d3cSKalle Valo
827da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n");
828da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n");
829da3a9d3cSKalle Valo
83031582373SBaochen Qiang for (i = 0; i < ab->hw_params.max_tx_ring; i++)
831da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "ring%d: %u\n",
832da3a9d3cSKalle Valo i, soc_stats->tx_err.desc_na[i]);
833da3a9d3cSKalle Valo
834da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len,
835da3a9d3cSKalle Valo "\nMisc Transmit Failures: %d\n",
836da3a9d3cSKalle Valo atomic_read(&soc_stats->tx_err.misc_fail));
837da3a9d3cSKalle Valo
838cb4e57dbSKalle Valo len += ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf + len, size - len);
839da3a9d3cSKalle Valo
840da3a9d3cSKalle Valo if (len > size)
841da3a9d3cSKalle Valo len = size;
842da3a9d3cSKalle Valo retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
843da3a9d3cSKalle Valo kfree(buf);
844da3a9d3cSKalle Valo
845da3a9d3cSKalle Valo return retval;
846da3a9d3cSKalle Valo }
847da3a9d3cSKalle Valo
848da3a9d3cSKalle Valo static const struct file_operations fops_soc_dp_stats = {
849cb4e57dbSKalle Valo .read = ath11k_debugfs_dump_soc_dp_stats,
850da3a9d3cSKalle Valo .open = simple_open,
851da3a9d3cSKalle Valo .owner = THIS_MODULE,
852da3a9d3cSKalle Valo .llseek = default_llseek,
853da3a9d3cSKalle Valo };
854da3a9d3cSKalle Valo
ath11k_write_fw_dbglog(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)855f295ad91SSeevalamuthu Mariappan static ssize_t ath11k_write_fw_dbglog(struct file *file,
856f295ad91SSeevalamuthu Mariappan const char __user *user_buf,
857f295ad91SSeevalamuthu Mariappan size_t count, loff_t *ppos)
858f295ad91SSeevalamuthu Mariappan {
859f295ad91SSeevalamuthu Mariappan struct ath11k *ar = file->private_data;
860f295ad91SSeevalamuthu Mariappan char buf[128] = {0};
861f295ad91SSeevalamuthu Mariappan struct ath11k_fw_dbglog dbglog;
862f295ad91SSeevalamuthu Mariappan unsigned int param, mod_id_index, is_end;
863f295ad91SSeevalamuthu Mariappan u64 value;
864f295ad91SSeevalamuthu Mariappan int ret, num;
865f295ad91SSeevalamuthu Mariappan
866f295ad91SSeevalamuthu Mariappan ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,
867f295ad91SSeevalamuthu Mariappan user_buf, count);
868f295ad91SSeevalamuthu Mariappan if (ret <= 0)
869f295ad91SSeevalamuthu Mariappan return ret;
870f295ad91SSeevalamuthu Mariappan
871f295ad91SSeevalamuthu Mariappan num = sscanf(buf, "%u %llx %u %u", ¶m, &value, &mod_id_index, &is_end);
872f295ad91SSeevalamuthu Mariappan
873f295ad91SSeevalamuthu Mariappan if (num < 2)
874f295ad91SSeevalamuthu Mariappan return -EINVAL;
875f295ad91SSeevalamuthu Mariappan
876f295ad91SSeevalamuthu Mariappan mutex_lock(&ar->conf_mutex);
877f295ad91SSeevalamuthu Mariappan if (param == WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP ||
878f295ad91SSeevalamuthu Mariappan param == WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP) {
879f295ad91SSeevalamuthu Mariappan if (num != 4 || mod_id_index > (MAX_MODULE_ID_BITMAP_WORDS - 1)) {
880f295ad91SSeevalamuthu Mariappan ret = -EINVAL;
881f295ad91SSeevalamuthu Mariappan goto out;
882f295ad91SSeevalamuthu Mariappan }
883f295ad91SSeevalamuthu Mariappan ar->debug.module_id_bitmap[mod_id_index] = upper_32_bits(value);
884f295ad91SSeevalamuthu Mariappan if (!is_end) {
885f295ad91SSeevalamuthu Mariappan ret = count;
886f295ad91SSeevalamuthu Mariappan goto out;
887f295ad91SSeevalamuthu Mariappan }
888f295ad91SSeevalamuthu Mariappan } else {
889f295ad91SSeevalamuthu Mariappan if (num != 2) {
890f295ad91SSeevalamuthu Mariappan ret = -EINVAL;
891f295ad91SSeevalamuthu Mariappan goto out;
892f295ad91SSeevalamuthu Mariappan }
893f295ad91SSeevalamuthu Mariappan }
894f295ad91SSeevalamuthu Mariappan
895f295ad91SSeevalamuthu Mariappan dbglog.param = param;
896f295ad91SSeevalamuthu Mariappan dbglog.value = lower_32_bits(value);
897f295ad91SSeevalamuthu Mariappan ret = ath11k_wmi_fw_dbglog_cfg(ar, ar->debug.module_id_bitmap, &dbglog);
898f295ad91SSeevalamuthu Mariappan if (ret) {
899f295ad91SSeevalamuthu Mariappan ath11k_warn(ar->ab, "fw dbglog config failed from debugfs: %d\n",
900f295ad91SSeevalamuthu Mariappan ret);
901f295ad91SSeevalamuthu Mariappan goto out;
902f295ad91SSeevalamuthu Mariappan }
903f295ad91SSeevalamuthu Mariappan
904f295ad91SSeevalamuthu Mariappan ret = count;
905f295ad91SSeevalamuthu Mariappan
906f295ad91SSeevalamuthu Mariappan out:
907f295ad91SSeevalamuthu Mariappan mutex_unlock(&ar->conf_mutex);
908f295ad91SSeevalamuthu Mariappan return ret;
909f295ad91SSeevalamuthu Mariappan }
910f295ad91SSeevalamuthu Mariappan
911f295ad91SSeevalamuthu Mariappan static const struct file_operations fops_fw_dbglog = {
912f295ad91SSeevalamuthu Mariappan .write = ath11k_write_fw_dbglog,
913f295ad91SSeevalamuthu Mariappan .open = simple_open,
914f295ad91SSeevalamuthu Mariappan .owner = THIS_MODULE,
915f295ad91SSeevalamuthu Mariappan .llseek = default_llseek,
916f295ad91SSeevalamuthu Mariappan };
917f295ad91SSeevalamuthu Mariappan
ath11k_open_sram_dump(struct inode * inode,struct file * file)918876eb848SBaochen Qiang static int ath11k_open_sram_dump(struct inode *inode, struct file *file)
919876eb848SBaochen Qiang {
920876eb848SBaochen Qiang struct ath11k_base *ab = inode->i_private;
921876eb848SBaochen Qiang u8 *buf;
922876eb848SBaochen Qiang u32 start, end;
923876eb848SBaochen Qiang int ret;
924876eb848SBaochen Qiang
925876eb848SBaochen Qiang start = ab->hw_params.sram_dump.start;
926876eb848SBaochen Qiang end = ab->hw_params.sram_dump.end;
927876eb848SBaochen Qiang
928876eb848SBaochen Qiang buf = vmalloc(end - start + 1);
929876eb848SBaochen Qiang if (!buf)
930876eb848SBaochen Qiang return -ENOMEM;
931876eb848SBaochen Qiang
932876eb848SBaochen Qiang ret = ath11k_hif_read(ab, buf, start, end);
933876eb848SBaochen Qiang if (ret) {
934876eb848SBaochen Qiang ath11k_warn(ab, "failed to dump sram: %d\n", ret);
935876eb848SBaochen Qiang vfree(buf);
936876eb848SBaochen Qiang return ret;
937876eb848SBaochen Qiang }
938876eb848SBaochen Qiang
939876eb848SBaochen Qiang file->private_data = buf;
940876eb848SBaochen Qiang return 0;
941876eb848SBaochen Qiang }
942876eb848SBaochen Qiang
ath11k_read_sram_dump(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)943876eb848SBaochen Qiang static ssize_t ath11k_read_sram_dump(struct file *file,
944876eb848SBaochen Qiang char __user *user_buf,
945876eb848SBaochen Qiang size_t count, loff_t *ppos)
946876eb848SBaochen Qiang {
947876eb848SBaochen Qiang struct ath11k_base *ab = file->f_inode->i_private;
948876eb848SBaochen Qiang const char *buf = file->private_data;
949876eb848SBaochen Qiang int len;
950876eb848SBaochen Qiang u32 start, end;
951876eb848SBaochen Qiang
952876eb848SBaochen Qiang start = ab->hw_params.sram_dump.start;
953876eb848SBaochen Qiang end = ab->hw_params.sram_dump.end;
954876eb848SBaochen Qiang len = end - start + 1;
955876eb848SBaochen Qiang
956876eb848SBaochen Qiang return simple_read_from_buffer(user_buf, count, ppos, buf, len);
957876eb848SBaochen Qiang }
958876eb848SBaochen Qiang
ath11k_release_sram_dump(struct inode * inode,struct file * file)959876eb848SBaochen Qiang static int ath11k_release_sram_dump(struct inode *inode, struct file *file)
960876eb848SBaochen Qiang {
961876eb848SBaochen Qiang vfree(file->private_data);
962876eb848SBaochen Qiang file->private_data = NULL;
963876eb848SBaochen Qiang
964876eb848SBaochen Qiang return 0;
965876eb848SBaochen Qiang }
966876eb848SBaochen Qiang
967876eb848SBaochen Qiang static const struct file_operations fops_sram_dump = {
968876eb848SBaochen Qiang .open = ath11k_open_sram_dump,
969876eb848SBaochen Qiang .read = ath11k_read_sram_dump,
970876eb848SBaochen Qiang .release = ath11k_release_sram_dump,
971876eb848SBaochen Qiang .owner = THIS_MODULE,
972876eb848SBaochen Qiang .llseek = default_llseek,
973876eb848SBaochen Qiang };
974876eb848SBaochen Qiang
ath11k_debugfs_pdev_create(struct ath11k_base * ab)975cb4e57dbSKalle Valo int ath11k_debugfs_pdev_create(struct ath11k_base *ab)
976da3a9d3cSKalle Valo {
977da3a9d3cSKalle Valo if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
978da3a9d3cSKalle Valo return 0;
979da3a9d3cSKalle Valo
980da3a9d3cSKalle Valo debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab,
981da3a9d3cSKalle Valo &fops_simulate_fw_crash);
982da3a9d3cSKalle Valo
983da3a9d3cSKalle Valo debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab,
984da3a9d3cSKalle Valo &fops_soc_dp_stats);
985da3a9d3cSKalle Valo
986876eb848SBaochen Qiang if (ab->hw_params.sram_dump.start != 0)
987876eb848SBaochen Qiang debugfs_create_file("sram", 0400, ab->debugfs_soc, ab,
988876eb848SBaochen Qiang &fops_sram_dump);
989876eb848SBaochen Qiang
990da3a9d3cSKalle Valo return 0;
991da3a9d3cSKalle Valo }
992da3a9d3cSKalle Valo
ath11k_debugfs_pdev_destroy(struct ath11k_base * ab)993cb4e57dbSKalle Valo void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab)
994da3a9d3cSKalle Valo {
995089ba909SCarl Huang debugfs_remove_recursive(ab->debugfs_soc);
996089ba909SCarl Huang ab->debugfs_soc = NULL;
997da3a9d3cSKalle Valo }
998da3a9d3cSKalle Valo
ath11k_debugfs_soc_create(struct ath11k_base * ab)999cb4e57dbSKalle Valo int ath11k_debugfs_soc_create(struct ath11k_base *ab)
1000da3a9d3cSKalle Valo {
1001323d91d4SKalle Valo struct dentry *root;
1002323d91d4SKalle Valo bool dput_needed;
1003323d91d4SKalle Valo char name[64];
1004323d91d4SKalle Valo int ret;
1005da3a9d3cSKalle Valo
1006323d91d4SKalle Valo root = debugfs_lookup("ath11k", NULL);
1007323d91d4SKalle Valo if (!root) {
1008323d91d4SKalle Valo root = debugfs_create_dir("ath11k", NULL);
1009323d91d4SKalle Valo if (IS_ERR_OR_NULL(root))
1010323d91d4SKalle Valo return PTR_ERR(root);
1011323d91d4SKalle Valo
1012323d91d4SKalle Valo dput_needed = false;
1013323d91d4SKalle Valo } else {
1014323d91d4SKalle Valo /* a dentry from lookup() needs dput() after we don't use it */
1015323d91d4SKalle Valo dput_needed = true;
1016323d91d4SKalle Valo }
1017323d91d4SKalle Valo
1018323d91d4SKalle Valo scnprintf(name, sizeof(name), "%s-%s", ath11k_bus_str(ab->hif.bus),
1019323d91d4SKalle Valo dev_name(ab->dev));
1020323d91d4SKalle Valo
1021323d91d4SKalle Valo ab->debugfs_soc = debugfs_create_dir(name, root);
1022323d91d4SKalle Valo if (IS_ERR_OR_NULL(ab->debugfs_soc)) {
1023323d91d4SKalle Valo ret = PTR_ERR(ab->debugfs_soc);
1024323d91d4SKalle Valo goto out;
1025323d91d4SKalle Valo }
1026323d91d4SKalle Valo
1027323d91d4SKalle Valo ret = 0;
1028323d91d4SKalle Valo
1029323d91d4SKalle Valo out:
1030323d91d4SKalle Valo if (dput_needed)
1031323d91d4SKalle Valo dput(root);
1032323d91d4SKalle Valo
1033323d91d4SKalle Valo return ret;
1034da3a9d3cSKalle Valo }
1035da3a9d3cSKalle Valo
ath11k_debugfs_soc_destroy(struct ath11k_base * ab)1036cb4e57dbSKalle Valo void ath11k_debugfs_soc_destroy(struct ath11k_base *ab)
1037da3a9d3cSKalle Valo {
1038323d91d4SKalle Valo debugfs_remove_recursive(ab->debugfs_soc);
1039323d91d4SKalle Valo ab->debugfs_soc = NULL;
1040323d91d4SKalle Valo
1041323d91d4SKalle Valo /* We are not removing ath11k directory on purpose, even if it
1042323d91d4SKalle Valo * would be empty. This simplifies the directory handling and it's
1043323d91d4SKalle Valo * a minor cosmetic issue to leave an empty ath11k directory to
1044323d91d4SKalle Valo * debugfs.
1045323d91d4SKalle Valo */
1046da3a9d3cSKalle Valo }
104761a57e51SAnilkumar Kolli EXPORT_SYMBOL(ath11k_debugfs_soc_destroy);
1048da3a9d3cSKalle Valo
ath11k_debugfs_fw_stats_init(struct ath11k * ar)1049cb4e57dbSKalle Valo void ath11k_debugfs_fw_stats_init(struct ath11k *ar)
1050da3a9d3cSKalle Valo {
1051da3a9d3cSKalle Valo struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",
1052da3a9d3cSKalle Valo ar->debug.debugfs_pdev);
1053da3a9d3cSKalle Valo
1054ec8918f9SAditya Kumar Singh ar->fw_stats.debugfs_fwstats = fwstats_dir;
1055da3a9d3cSKalle Valo
1056da3a9d3cSKalle Valo /* all stats debugfs files created are under "fw_stats" directory
1057da3a9d3cSKalle Valo * created per PDEV
1058da3a9d3cSKalle Valo */
1059da3a9d3cSKalle Valo debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar,
1060da3a9d3cSKalle Valo &fops_pdev_stats);
1061da3a9d3cSKalle Valo debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,
1062da3a9d3cSKalle Valo &fops_vdev_stats);
1063da3a9d3cSKalle Valo debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar,
1064da3a9d3cSKalle Valo &fops_bcn_stats);
1065da3a9d3cSKalle Valo }
1066da3a9d3cSKalle Valo
ath11k_write_pktlog_filter(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)1067da3a9d3cSKalle Valo static ssize_t ath11k_write_pktlog_filter(struct file *file,
1068da3a9d3cSKalle Valo const char __user *ubuf,
1069da3a9d3cSKalle Valo size_t count, loff_t *ppos)
1070da3a9d3cSKalle Valo {
1071da3a9d3cSKalle Valo struct ath11k *ar = file->private_data;
1072da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab;
1073da3a9d3cSKalle Valo struct htt_rx_ring_tlv_filter tlv_filter = {0};
1074da3a9d3cSKalle Valo u32 rx_filter = 0, ring_id, filter, mode;
1075da3a9d3cSKalle Valo u8 buf[128] = {0};
1076ab18e3bcSAnilkumar Kolli int i, ret, rx_buf_sz = 0;
1077da3a9d3cSKalle Valo ssize_t rc;
1078da3a9d3cSKalle Valo
1079da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
1080da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) {
1081da3a9d3cSKalle Valo ret = -ENETDOWN;
1082da3a9d3cSKalle Valo goto out;
1083da3a9d3cSKalle Valo }
1084da3a9d3cSKalle Valo
1085da3a9d3cSKalle Valo rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1086da3a9d3cSKalle Valo if (rc < 0) {
1087da3a9d3cSKalle Valo ret = rc;
1088da3a9d3cSKalle Valo goto out;
1089da3a9d3cSKalle Valo }
1090da3a9d3cSKalle Valo buf[rc] = '\0';
1091da3a9d3cSKalle Valo
1092da3a9d3cSKalle Valo ret = sscanf(buf, "0x%x %u", &filter, &mode);
1093da3a9d3cSKalle Valo if (ret != 2) {
1094da3a9d3cSKalle Valo ret = -EINVAL;
1095da3a9d3cSKalle Valo goto out;
1096da3a9d3cSKalle Valo }
1097da3a9d3cSKalle Valo
1098da3a9d3cSKalle Valo if (filter) {
1099da3a9d3cSKalle Valo ret = ath11k_wmi_pdev_pktlog_enable(ar, filter);
1100da3a9d3cSKalle Valo if (ret) {
1101da3a9d3cSKalle Valo ath11k_warn(ar->ab,
1102da3a9d3cSKalle Valo "failed to enable pktlog filter %x: %d\n",
1103da3a9d3cSKalle Valo ar->debug.pktlog_filter, ret);
1104da3a9d3cSKalle Valo goto out;
1105da3a9d3cSKalle Valo }
1106da3a9d3cSKalle Valo } else {
1107da3a9d3cSKalle Valo ret = ath11k_wmi_pdev_pktlog_disable(ar);
1108da3a9d3cSKalle Valo if (ret) {
1109da3a9d3cSKalle Valo ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret);
1110da3a9d3cSKalle Valo goto out;
1111da3a9d3cSKalle Valo }
1112da3a9d3cSKalle Valo }
1113da3a9d3cSKalle Valo
1114ab18e3bcSAnilkumar Kolli /* Clear rx filter set for monitor mode and rx status */
1115ab18e3bcSAnilkumar Kolli for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {
1116ab18e3bcSAnilkumar Kolli ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
1117ab18e3bcSAnilkumar Kolli ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,
1118ab18e3bcSAnilkumar Kolli HAL_RXDMA_MONITOR_STATUS,
1119ab18e3bcSAnilkumar Kolli rx_buf_sz, &tlv_filter);
1120ab18e3bcSAnilkumar Kolli if (ret) {
1121ab18e3bcSAnilkumar Kolli ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");
1122ab18e3bcSAnilkumar Kolli goto out;
1123ab18e3bcSAnilkumar Kolli }
1124ab18e3bcSAnilkumar Kolli }
1125da3a9d3cSKalle Valo #define HTT_RX_FILTER_TLV_LITE_MODE \
1126da3a9d3cSKalle Valo (HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \
1127da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \
1128da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \
1129da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \
1130da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \
1131da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_MPDU_START)
1132da3a9d3cSKalle Valo
1133da3a9d3cSKalle Valo if (mode == ATH11K_PKTLOG_MODE_FULL) {
1134da3a9d3cSKalle Valo rx_filter = HTT_RX_FILTER_TLV_LITE_MODE |
1135da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_MSDU_START |
1136da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_MSDU_END |
1137da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_MPDU_END |
1138da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER |
1139da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_ATTENTION;
1140ab18e3bcSAnilkumar Kolli rx_buf_sz = DP_RX_BUFFER_SIZE;
1141da3a9d3cSKalle Valo } else if (mode == ATH11K_PKTLOG_MODE_LITE) {
1142da3a9d3cSKalle Valo ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,
1143da3a9d3cSKalle Valo HTT_PPDU_STATS_TAG_PKTLOG);
1144da3a9d3cSKalle Valo if (ret) {
1145da3a9d3cSKalle Valo ath11k_err(ar->ab, "failed to enable pktlog lite: %d\n", ret);
1146da3a9d3cSKalle Valo goto out;
1147da3a9d3cSKalle Valo }
1148da3a9d3cSKalle Valo
1149da3a9d3cSKalle Valo rx_filter = HTT_RX_FILTER_TLV_LITE_MODE;
1150ab18e3bcSAnilkumar Kolli rx_buf_sz = DP_RX_BUFFER_SIZE_LITE;
1151da3a9d3cSKalle Valo } else {
1152ab18e3bcSAnilkumar Kolli rx_buf_sz = DP_RX_BUFFER_SIZE;
1153ab18e3bcSAnilkumar Kolli tlv_filter = ath11k_mac_mon_status_filter_default;
1154ab18e3bcSAnilkumar Kolli rx_filter = tlv_filter.rx_filter;
1155ab18e3bcSAnilkumar Kolli
1156da3a9d3cSKalle Valo ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,
1157da3a9d3cSKalle Valo HTT_PPDU_STATS_TAG_DEFAULT);
1158da3a9d3cSKalle Valo if (ret) {
1159da3a9d3cSKalle Valo ath11k_err(ar->ab, "failed to send htt ppdu stats req: %d\n",
1160da3a9d3cSKalle Valo ret);
1161da3a9d3cSKalle Valo goto out;
1162da3a9d3cSKalle Valo }
1163da3a9d3cSKalle Valo }
1164da3a9d3cSKalle Valo
1165da3a9d3cSKalle Valo tlv_filter.rx_filter = rx_filter;
1166da3a9d3cSKalle Valo if (rx_filter) {
1167da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;
1168da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;
1169da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;
1170da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |
1171da3a9d3cSKalle Valo HTT_RX_FP_DATA_FILTER_FLASG3;
1172da3a9d3cSKalle Valo }
1173da3a9d3cSKalle Valo
1174da3a9d3cSKalle Valo for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {
1175da3a9d3cSKalle Valo ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
1176da3a9d3cSKalle Valo ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id,
1177da3a9d3cSKalle Valo ar->dp.mac_id + i,
1178da3a9d3cSKalle Valo HAL_RXDMA_MONITOR_STATUS,
1179ab18e3bcSAnilkumar Kolli rx_buf_sz, &tlv_filter);
1180da3a9d3cSKalle Valo
1181da3a9d3cSKalle Valo if (ret) {
1182da3a9d3cSKalle Valo ath11k_warn(ab, "failed to set rx filter for monitor status ring\n");
1183da3a9d3cSKalle Valo goto out;
1184da3a9d3cSKalle Valo }
1185da3a9d3cSKalle Valo }
1186da3a9d3cSKalle Valo
1187ab18e3bcSAnilkumar Kolli ath11k_info(ab, "pktlog mode %s\n",
1188ab18e3bcSAnilkumar Kolli ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite"));
1189da3a9d3cSKalle Valo
1190da3a9d3cSKalle Valo ar->debug.pktlog_filter = filter;
1191da3a9d3cSKalle Valo ar->debug.pktlog_mode = mode;
1192da3a9d3cSKalle Valo ret = count;
1193da3a9d3cSKalle Valo
1194da3a9d3cSKalle Valo out:
1195da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
1196da3a9d3cSKalle Valo return ret;
1197da3a9d3cSKalle Valo }
1198da3a9d3cSKalle Valo
ath11k_read_pktlog_filter(struct file * file,char __user * ubuf,size_t count,loff_t * ppos)1199da3a9d3cSKalle Valo static ssize_t ath11k_read_pktlog_filter(struct file *file,
1200da3a9d3cSKalle Valo char __user *ubuf,
1201da3a9d3cSKalle Valo size_t count, loff_t *ppos)
1202da3a9d3cSKalle Valo
1203da3a9d3cSKalle Valo {
1204da3a9d3cSKalle Valo char buf[32] = {0};
1205da3a9d3cSKalle Valo struct ath11k *ar = file->private_data;
1206da3a9d3cSKalle Valo int len = 0;
1207da3a9d3cSKalle Valo
1208da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex);
1209da3a9d3cSKalle Valo len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n",
1210da3a9d3cSKalle Valo ar->debug.pktlog_filter,
1211da3a9d3cSKalle Valo ar->debug.pktlog_mode);
1212da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex);
1213da3a9d3cSKalle Valo
1214da3a9d3cSKalle Valo return simple_read_from_buffer(ubuf, count, ppos, buf, len);
1215da3a9d3cSKalle Valo }
1216da3a9d3cSKalle Valo
1217da3a9d3cSKalle Valo static const struct file_operations fops_pktlog_filter = {
1218da3a9d3cSKalle Valo .read = ath11k_read_pktlog_filter,
1219da3a9d3cSKalle Valo .write = ath11k_write_pktlog_filter,
1220da3a9d3cSKalle Valo .open = simple_open
1221da3a9d3cSKalle Valo };
1222da3a9d3cSKalle Valo
ath11k_write_simulate_radar(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1223da3a9d3cSKalle Valo static ssize_t ath11k_write_simulate_radar(struct file *file,
1224da3a9d3cSKalle Valo const char __user *user_buf,
1225da3a9d3cSKalle Valo size_t count, loff_t *ppos)
1226da3a9d3cSKalle Valo {
1227da3a9d3cSKalle Valo struct ath11k *ar = file->private_data;
1228da3a9d3cSKalle Valo int ret;
1229da3a9d3cSKalle Valo
1230da3a9d3cSKalle Valo ret = ath11k_wmi_simulate_radar(ar);
1231da3a9d3cSKalle Valo if (ret)
1232da3a9d3cSKalle Valo return ret;
1233da3a9d3cSKalle Valo
1234da3a9d3cSKalle Valo return count;
1235da3a9d3cSKalle Valo }
1236da3a9d3cSKalle Valo
1237da3a9d3cSKalle Valo static const struct file_operations fops_simulate_radar = {
1238da3a9d3cSKalle Valo .write = ath11k_write_simulate_radar,
1239da3a9d3cSKalle Valo .open = simple_open
1240da3a9d3cSKalle Valo };
1241da3a9d3cSKalle Valo
ath11k_debug_dump_dbr_entries(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1242691425b4SVenkateswara Naralasetty static ssize_t ath11k_debug_dump_dbr_entries(struct file *file,
1243691425b4SVenkateswara Naralasetty char __user *user_buf,
1244691425b4SVenkateswara Naralasetty size_t count, loff_t *ppos)
1245691425b4SVenkateswara Naralasetty {
1246691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_dbg_data = file->private_data;
1247691425b4SVenkateswara Naralasetty static const char * const event_id_to_string[] = {"empty", "Rx", "Replenish"};
1248691425b4SVenkateswara Naralasetty int size = ATH11K_DEBUG_DBR_ENTRIES_MAX * 100;
1249691425b4SVenkateswara Naralasetty char *buf;
1250691425b4SVenkateswara Naralasetty int i, ret;
1251691425b4SVenkateswara Naralasetty int len = 0;
1252691425b4SVenkateswara Naralasetty
1253691425b4SVenkateswara Naralasetty buf = kzalloc(size, GFP_KERNEL);
1254691425b4SVenkateswara Naralasetty if (!buf)
1255691425b4SVenkateswara Naralasetty return -ENOMEM;
1256691425b4SVenkateswara Naralasetty
1257691425b4SVenkateswara Naralasetty len += scnprintf(buf + len, size - len,
1258691425b4SVenkateswara Naralasetty "-----------------------------------------\n");
1259691425b4SVenkateswara Naralasetty len += scnprintf(buf + len, size - len,
1260691425b4SVenkateswara Naralasetty "| idx | hp | tp | timestamp | event |\n");
1261691425b4SVenkateswara Naralasetty len += scnprintf(buf + len, size - len,
1262691425b4SVenkateswara Naralasetty "-----------------------------------------\n");
1263691425b4SVenkateswara Naralasetty
1264691425b4SVenkateswara Naralasetty spin_lock_bh(&dbr_dbg_data->lock);
1265691425b4SVenkateswara Naralasetty
1266691425b4SVenkateswara Naralasetty for (i = 0; i < dbr_dbg_data->num_ring_debug_entries; i++) {
1267691425b4SVenkateswara Naralasetty len += scnprintf(buf + len, size - len,
1268691425b4SVenkateswara Naralasetty "|%4u|%8u|%8u|%11llu|%8s|\n", i,
1269691425b4SVenkateswara Naralasetty dbr_dbg_data->entries[i].hp,
1270691425b4SVenkateswara Naralasetty dbr_dbg_data->entries[i].tp,
1271691425b4SVenkateswara Naralasetty dbr_dbg_data->entries[i].timestamp,
1272691425b4SVenkateswara Naralasetty event_id_to_string[dbr_dbg_data->entries[i].event]);
1273691425b4SVenkateswara Naralasetty }
1274691425b4SVenkateswara Naralasetty
1275691425b4SVenkateswara Naralasetty spin_unlock_bh(&dbr_dbg_data->lock);
1276691425b4SVenkateswara Naralasetty
1277691425b4SVenkateswara Naralasetty ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
1278691425b4SVenkateswara Naralasetty kfree(buf);
1279691425b4SVenkateswara Naralasetty
1280691425b4SVenkateswara Naralasetty return ret;
1281691425b4SVenkateswara Naralasetty }
1282691425b4SVenkateswara Naralasetty
1283691425b4SVenkateswara Naralasetty static const struct file_operations fops_debug_dump_dbr_entries = {
1284691425b4SVenkateswara Naralasetty .read = ath11k_debug_dump_dbr_entries,
1285691425b4SVenkateswara Naralasetty .open = simple_open,
1286691425b4SVenkateswara Naralasetty .owner = THIS_MODULE,
1287691425b4SVenkateswara Naralasetty .llseek = default_llseek,
1288691425b4SVenkateswara Naralasetty };
1289691425b4SVenkateswara Naralasetty
ath11k_debugfs_dbr_dbg_destroy(struct ath11k * ar,int dbr_id)1290691425b4SVenkateswara Naralasetty static void ath11k_debugfs_dbr_dbg_destroy(struct ath11k *ar, int dbr_id)
1291691425b4SVenkateswara Naralasetty {
1292691425b4SVenkateswara Naralasetty struct ath11k_debug_dbr *dbr_debug;
1293691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_dbg_data;
1294691425b4SVenkateswara Naralasetty
1295691425b4SVenkateswara Naralasetty if (!ar->debug.dbr_debug[dbr_id])
1296691425b4SVenkateswara Naralasetty return;
1297691425b4SVenkateswara Naralasetty
1298691425b4SVenkateswara Naralasetty dbr_debug = ar->debug.dbr_debug[dbr_id];
1299691425b4SVenkateswara Naralasetty dbr_dbg_data = &dbr_debug->dbr_dbg_data;
1300691425b4SVenkateswara Naralasetty
1301691425b4SVenkateswara Naralasetty debugfs_remove_recursive(dbr_debug->dbr_debugfs);
1302691425b4SVenkateswara Naralasetty kfree(dbr_dbg_data->entries);
1303691425b4SVenkateswara Naralasetty kfree(dbr_debug);
1304691425b4SVenkateswara Naralasetty ar->debug.dbr_debug[dbr_id] = NULL;
1305691425b4SVenkateswara Naralasetty }
1306691425b4SVenkateswara Naralasetty
ath11k_debugfs_dbr_dbg_init(struct ath11k * ar,int dbr_id)1307691425b4SVenkateswara Naralasetty static int ath11k_debugfs_dbr_dbg_init(struct ath11k *ar, int dbr_id)
1308691425b4SVenkateswara Naralasetty {
1309691425b4SVenkateswara Naralasetty struct ath11k_debug_dbr *dbr_debug;
1310691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_dbg_data;
1311691425b4SVenkateswara Naralasetty static const char * const dbr_id_to_str[] = {"spectral", "CFR"};
1312691425b4SVenkateswara Naralasetty
1313691425b4SVenkateswara Naralasetty if (ar->debug.dbr_debug[dbr_id])
1314691425b4SVenkateswara Naralasetty return 0;
1315691425b4SVenkateswara Naralasetty
1316691425b4SVenkateswara Naralasetty ar->debug.dbr_debug[dbr_id] = kzalloc(sizeof(*dbr_debug),
1317691425b4SVenkateswara Naralasetty GFP_KERNEL);
1318691425b4SVenkateswara Naralasetty
1319691425b4SVenkateswara Naralasetty if (!ar->debug.dbr_debug[dbr_id])
1320691425b4SVenkateswara Naralasetty return -ENOMEM;
1321691425b4SVenkateswara Naralasetty
1322691425b4SVenkateswara Naralasetty dbr_debug = ar->debug.dbr_debug[dbr_id];
1323691425b4SVenkateswara Naralasetty dbr_dbg_data = &dbr_debug->dbr_dbg_data;
1324691425b4SVenkateswara Naralasetty
1325691425b4SVenkateswara Naralasetty if (dbr_debug->dbr_debugfs)
1326691425b4SVenkateswara Naralasetty return 0;
1327691425b4SVenkateswara Naralasetty
1328691425b4SVenkateswara Naralasetty dbr_debug->dbr_debugfs = debugfs_create_dir(dbr_id_to_str[dbr_id],
1329691425b4SVenkateswara Naralasetty ar->debug.debugfs_pdev);
1330691425b4SVenkateswara Naralasetty if (IS_ERR_OR_NULL(dbr_debug->dbr_debugfs)) {
1331691425b4SVenkateswara Naralasetty if (IS_ERR(dbr_debug->dbr_debugfs))
1332691425b4SVenkateswara Naralasetty return PTR_ERR(dbr_debug->dbr_debugfs);
1333691425b4SVenkateswara Naralasetty return -ENOMEM;
1334691425b4SVenkateswara Naralasetty }
1335691425b4SVenkateswara Naralasetty
1336691425b4SVenkateswara Naralasetty dbr_debug->dbr_debug_enabled = true;
1337691425b4SVenkateswara Naralasetty dbr_dbg_data->num_ring_debug_entries = ATH11K_DEBUG_DBR_ENTRIES_MAX;
1338691425b4SVenkateswara Naralasetty dbr_dbg_data->dbr_debug_idx = 0;
1339691425b4SVenkateswara Naralasetty dbr_dbg_data->entries = kcalloc(ATH11K_DEBUG_DBR_ENTRIES_MAX,
1340691425b4SVenkateswara Naralasetty sizeof(struct ath11k_dbg_dbr_entry),
1341691425b4SVenkateswara Naralasetty GFP_KERNEL);
1342691425b4SVenkateswara Naralasetty if (!dbr_dbg_data->entries)
1343691425b4SVenkateswara Naralasetty return -ENOMEM;
1344691425b4SVenkateswara Naralasetty
1345691425b4SVenkateswara Naralasetty spin_lock_init(&dbr_dbg_data->lock);
1346691425b4SVenkateswara Naralasetty
1347691425b4SVenkateswara Naralasetty debugfs_create_file("dump_dbr_debug", 0444, dbr_debug->dbr_debugfs,
1348691425b4SVenkateswara Naralasetty dbr_dbg_data, &fops_debug_dump_dbr_entries);
1349691425b4SVenkateswara Naralasetty
1350691425b4SVenkateswara Naralasetty return 0;
1351691425b4SVenkateswara Naralasetty }
1352691425b4SVenkateswara Naralasetty
ath11k_debugfs_write_enable_dbr_dbg(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)1353691425b4SVenkateswara Naralasetty static ssize_t ath11k_debugfs_write_enable_dbr_dbg(struct file *file,
1354691425b4SVenkateswara Naralasetty const char __user *ubuf,
1355691425b4SVenkateswara Naralasetty size_t count, loff_t *ppos)
1356691425b4SVenkateswara Naralasetty {
1357691425b4SVenkateswara Naralasetty struct ath11k *ar = file->private_data;
1358691425b4SVenkateswara Naralasetty char buf[32] = {0};
1359691425b4SVenkateswara Naralasetty u32 dbr_id, enable;
1360691425b4SVenkateswara Naralasetty int ret;
1361691425b4SVenkateswara Naralasetty
1362691425b4SVenkateswara Naralasetty mutex_lock(&ar->conf_mutex);
1363691425b4SVenkateswara Naralasetty
1364691425b4SVenkateswara Naralasetty if (ar->state != ATH11K_STATE_ON) {
1365691425b4SVenkateswara Naralasetty ret = -ENETDOWN;
1366691425b4SVenkateswara Naralasetty goto out;
1367691425b4SVenkateswara Naralasetty }
1368691425b4SVenkateswara Naralasetty
1369691425b4SVenkateswara Naralasetty ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1370691425b4SVenkateswara Naralasetty if (ret < 0)
1371691425b4SVenkateswara Naralasetty goto out;
1372691425b4SVenkateswara Naralasetty
1373691425b4SVenkateswara Naralasetty buf[ret] = '\0';
1374691425b4SVenkateswara Naralasetty ret = sscanf(buf, "%u %u", &dbr_id, &enable);
1375691425b4SVenkateswara Naralasetty if (ret != 2 || dbr_id > 1 || enable > 1) {
1376691425b4SVenkateswara Naralasetty ret = -EINVAL;
1377691425b4SVenkateswara Naralasetty ath11k_warn(ar->ab, "usage: echo <dbr_id> <val> dbr_id:0-Spectral 1-CFR val:0-disable 1-enable\n");
1378691425b4SVenkateswara Naralasetty goto out;
1379691425b4SVenkateswara Naralasetty }
1380691425b4SVenkateswara Naralasetty
1381691425b4SVenkateswara Naralasetty if (enable) {
1382691425b4SVenkateswara Naralasetty ret = ath11k_debugfs_dbr_dbg_init(ar, dbr_id);
1383691425b4SVenkateswara Naralasetty if (ret) {
1384691425b4SVenkateswara Naralasetty ath11k_warn(ar->ab, "db ring module debugfs init failed: %d\n",
1385691425b4SVenkateswara Naralasetty ret);
1386691425b4SVenkateswara Naralasetty goto out;
1387691425b4SVenkateswara Naralasetty }
1388691425b4SVenkateswara Naralasetty } else {
1389691425b4SVenkateswara Naralasetty ath11k_debugfs_dbr_dbg_destroy(ar, dbr_id);
1390691425b4SVenkateswara Naralasetty }
1391691425b4SVenkateswara Naralasetty
1392691425b4SVenkateswara Naralasetty ret = count;
1393691425b4SVenkateswara Naralasetty out:
1394691425b4SVenkateswara Naralasetty mutex_unlock(&ar->conf_mutex);
1395691425b4SVenkateswara Naralasetty return ret;
1396691425b4SVenkateswara Naralasetty }
1397691425b4SVenkateswara Naralasetty
1398691425b4SVenkateswara Naralasetty static const struct file_operations fops_dbr_debug = {
1399691425b4SVenkateswara Naralasetty .write = ath11k_debugfs_write_enable_dbr_dbg,
1400691425b4SVenkateswara Naralasetty .open = simple_open,
1401691425b4SVenkateswara Naralasetty .owner = THIS_MODULE,
1402691425b4SVenkateswara Naralasetty .llseek = default_llseek,
1403691425b4SVenkateswara Naralasetty };
1404691425b4SVenkateswara Naralasetty
ath11k_write_ps_timekeeper_enable(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1405710a95f9SVenkateswara Naralasetty static ssize_t ath11k_write_ps_timekeeper_enable(struct file *file,
1406710a95f9SVenkateswara Naralasetty const char __user *user_buf,
1407710a95f9SVenkateswara Naralasetty size_t count, loff_t *ppos)
1408710a95f9SVenkateswara Naralasetty {
1409710a95f9SVenkateswara Naralasetty struct ath11k *ar = file->private_data;
1410710a95f9SVenkateswara Naralasetty ssize_t ret;
1411710a95f9SVenkateswara Naralasetty u8 ps_timekeeper_enable;
1412710a95f9SVenkateswara Naralasetty
1413710a95f9SVenkateswara Naralasetty if (kstrtou8_from_user(user_buf, count, 0, &ps_timekeeper_enable))
1414710a95f9SVenkateswara Naralasetty return -EINVAL;
1415710a95f9SVenkateswara Naralasetty
1416710a95f9SVenkateswara Naralasetty mutex_lock(&ar->conf_mutex);
1417710a95f9SVenkateswara Naralasetty
1418710a95f9SVenkateswara Naralasetty if (ar->state != ATH11K_STATE_ON) {
1419710a95f9SVenkateswara Naralasetty ret = -ENETDOWN;
1420710a95f9SVenkateswara Naralasetty goto exit;
1421710a95f9SVenkateswara Naralasetty }
1422710a95f9SVenkateswara Naralasetty
1423710a95f9SVenkateswara Naralasetty if (!ar->ps_state_enable) {
1424710a95f9SVenkateswara Naralasetty ret = -EINVAL;
1425710a95f9SVenkateswara Naralasetty goto exit;
1426710a95f9SVenkateswara Naralasetty }
1427710a95f9SVenkateswara Naralasetty
1428710a95f9SVenkateswara Naralasetty ar->ps_timekeeper_enable = !!ps_timekeeper_enable;
1429710a95f9SVenkateswara Naralasetty ret = count;
1430710a95f9SVenkateswara Naralasetty exit:
1431710a95f9SVenkateswara Naralasetty mutex_unlock(&ar->conf_mutex);
1432710a95f9SVenkateswara Naralasetty
1433710a95f9SVenkateswara Naralasetty return ret;
1434710a95f9SVenkateswara Naralasetty }
1435710a95f9SVenkateswara Naralasetty
ath11k_read_ps_timekeeper_enable(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1436710a95f9SVenkateswara Naralasetty static ssize_t ath11k_read_ps_timekeeper_enable(struct file *file,
1437710a95f9SVenkateswara Naralasetty char __user *user_buf,
1438710a95f9SVenkateswara Naralasetty size_t count, loff_t *ppos)
1439710a95f9SVenkateswara Naralasetty {
1440710a95f9SVenkateswara Naralasetty struct ath11k *ar = file->private_data;
1441710a95f9SVenkateswara Naralasetty char buf[32];
1442710a95f9SVenkateswara Naralasetty int len;
1443710a95f9SVenkateswara Naralasetty
1444710a95f9SVenkateswara Naralasetty mutex_lock(&ar->conf_mutex);
1445710a95f9SVenkateswara Naralasetty len = scnprintf(buf, sizeof(buf), "%d\n", ar->ps_timekeeper_enable);
1446710a95f9SVenkateswara Naralasetty mutex_unlock(&ar->conf_mutex);
1447710a95f9SVenkateswara Naralasetty
1448710a95f9SVenkateswara Naralasetty return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1449710a95f9SVenkateswara Naralasetty }
1450710a95f9SVenkateswara Naralasetty
1451710a95f9SVenkateswara Naralasetty static const struct file_operations fops_ps_timekeeper_enable = {
1452710a95f9SVenkateswara Naralasetty .read = ath11k_read_ps_timekeeper_enable,
1453710a95f9SVenkateswara Naralasetty .write = ath11k_write_ps_timekeeper_enable,
1454710a95f9SVenkateswara Naralasetty .open = simple_open,
1455710a95f9SVenkateswara Naralasetty .owner = THIS_MODULE,
1456710a95f9SVenkateswara Naralasetty .llseek = default_llseek,
1457710a95f9SVenkateswara Naralasetty };
1458710a95f9SVenkateswara Naralasetty
ath11k_reset_peer_ps_duration(void * data,struct ieee80211_sta * sta)1459710a95f9SVenkateswara Naralasetty static void ath11k_reset_peer_ps_duration(void *data,
1460710a95f9SVenkateswara Naralasetty struct ieee80211_sta *sta)
1461710a95f9SVenkateswara Naralasetty {
1462710a95f9SVenkateswara Naralasetty struct ath11k *ar = data;
1463710a95f9SVenkateswara Naralasetty struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
1464710a95f9SVenkateswara Naralasetty
1465710a95f9SVenkateswara Naralasetty spin_lock_bh(&ar->data_lock);
1466710a95f9SVenkateswara Naralasetty arsta->ps_total_duration = 0;
1467710a95f9SVenkateswara Naralasetty spin_unlock_bh(&ar->data_lock);
1468710a95f9SVenkateswara Naralasetty }
1469710a95f9SVenkateswara Naralasetty
ath11k_write_reset_ps_duration(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1470710a95f9SVenkateswara Naralasetty static ssize_t ath11k_write_reset_ps_duration(struct file *file,
1471710a95f9SVenkateswara Naralasetty const char __user *user_buf,
1472710a95f9SVenkateswara Naralasetty size_t count, loff_t *ppos)
1473710a95f9SVenkateswara Naralasetty {
1474710a95f9SVenkateswara Naralasetty struct ath11k *ar = file->private_data;
1475710a95f9SVenkateswara Naralasetty int ret;
1476710a95f9SVenkateswara Naralasetty u8 reset_ps_duration;
1477710a95f9SVenkateswara Naralasetty
1478710a95f9SVenkateswara Naralasetty if (kstrtou8_from_user(user_buf, count, 0, &reset_ps_duration))
1479710a95f9SVenkateswara Naralasetty return -EINVAL;
1480710a95f9SVenkateswara Naralasetty
1481710a95f9SVenkateswara Naralasetty mutex_lock(&ar->conf_mutex);
1482710a95f9SVenkateswara Naralasetty
1483710a95f9SVenkateswara Naralasetty if (ar->state != ATH11K_STATE_ON) {
1484710a95f9SVenkateswara Naralasetty ret = -ENETDOWN;
1485710a95f9SVenkateswara Naralasetty goto exit;
1486710a95f9SVenkateswara Naralasetty }
1487710a95f9SVenkateswara Naralasetty
1488710a95f9SVenkateswara Naralasetty if (!ar->ps_state_enable) {
1489710a95f9SVenkateswara Naralasetty ret = -EINVAL;
1490710a95f9SVenkateswara Naralasetty goto exit;
1491710a95f9SVenkateswara Naralasetty }
1492710a95f9SVenkateswara Naralasetty
1493710a95f9SVenkateswara Naralasetty ieee80211_iterate_stations_atomic(ar->hw,
1494710a95f9SVenkateswara Naralasetty ath11k_reset_peer_ps_duration,
1495710a95f9SVenkateswara Naralasetty ar);
1496710a95f9SVenkateswara Naralasetty
1497710a95f9SVenkateswara Naralasetty ret = count;
1498710a95f9SVenkateswara Naralasetty exit:
1499710a95f9SVenkateswara Naralasetty mutex_unlock(&ar->conf_mutex);
1500710a95f9SVenkateswara Naralasetty return ret;
1501710a95f9SVenkateswara Naralasetty }
1502710a95f9SVenkateswara Naralasetty
1503710a95f9SVenkateswara Naralasetty static const struct file_operations fops_reset_ps_duration = {
1504710a95f9SVenkateswara Naralasetty .write = ath11k_write_reset_ps_duration,
1505710a95f9SVenkateswara Naralasetty .open = simple_open,
1506710a95f9SVenkateswara Naralasetty .owner = THIS_MODULE,
1507710a95f9SVenkateswara Naralasetty .llseek = default_llseek,
1508710a95f9SVenkateswara Naralasetty };
1509710a95f9SVenkateswara Naralasetty
ath11k_peer_ps_state_disable(void * data,struct ieee80211_sta * sta)1510710a95f9SVenkateswara Naralasetty static void ath11k_peer_ps_state_disable(void *data,
1511710a95f9SVenkateswara Naralasetty struct ieee80211_sta *sta)
1512710a95f9SVenkateswara Naralasetty {
1513710a95f9SVenkateswara Naralasetty struct ath11k *ar = data;
1514710a95f9SVenkateswara Naralasetty struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
1515710a95f9SVenkateswara Naralasetty
1516710a95f9SVenkateswara Naralasetty spin_lock_bh(&ar->data_lock);
1517710a95f9SVenkateswara Naralasetty arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
1518710a95f9SVenkateswara Naralasetty arsta->ps_start_time = 0;
1519710a95f9SVenkateswara Naralasetty arsta->ps_total_duration = 0;
1520710a95f9SVenkateswara Naralasetty spin_unlock_bh(&ar->data_lock);
1521710a95f9SVenkateswara Naralasetty }
1522710a95f9SVenkateswara Naralasetty
ath11k_write_ps_state_enable(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1523710a95f9SVenkateswara Naralasetty static ssize_t ath11k_write_ps_state_enable(struct file *file,
1524710a95f9SVenkateswara Naralasetty const char __user *user_buf,
1525710a95f9SVenkateswara Naralasetty size_t count, loff_t *ppos)
1526710a95f9SVenkateswara Naralasetty {
1527710a95f9SVenkateswara Naralasetty struct ath11k *ar = file->private_data;
1528710a95f9SVenkateswara Naralasetty struct ath11k_pdev *pdev = ar->pdev;
1529710a95f9SVenkateswara Naralasetty int ret;
1530710a95f9SVenkateswara Naralasetty u32 param;
1531710a95f9SVenkateswara Naralasetty u8 ps_state_enable;
1532710a95f9SVenkateswara Naralasetty
1533710a95f9SVenkateswara Naralasetty if (kstrtou8_from_user(user_buf, count, 0, &ps_state_enable))
1534710a95f9SVenkateswara Naralasetty return -EINVAL;
1535710a95f9SVenkateswara Naralasetty
1536710a95f9SVenkateswara Naralasetty mutex_lock(&ar->conf_mutex);
1537710a95f9SVenkateswara Naralasetty
1538710a95f9SVenkateswara Naralasetty ps_state_enable = !!ps_state_enable;
1539710a95f9SVenkateswara Naralasetty
1540710a95f9SVenkateswara Naralasetty if (ar->ps_state_enable == ps_state_enable) {
1541710a95f9SVenkateswara Naralasetty ret = count;
1542710a95f9SVenkateswara Naralasetty goto exit;
1543710a95f9SVenkateswara Naralasetty }
1544710a95f9SVenkateswara Naralasetty
1545710a95f9SVenkateswara Naralasetty param = WMI_PDEV_PEER_STA_PS_STATECHG_ENABLE;
1546710a95f9SVenkateswara Naralasetty ret = ath11k_wmi_pdev_set_param(ar, param, ps_state_enable, pdev->pdev_id);
1547710a95f9SVenkateswara Naralasetty if (ret) {
1548710a95f9SVenkateswara Naralasetty ath11k_warn(ar->ab, "failed to enable ps_state_enable: %d\n",
1549710a95f9SVenkateswara Naralasetty ret);
1550710a95f9SVenkateswara Naralasetty goto exit;
1551710a95f9SVenkateswara Naralasetty }
1552710a95f9SVenkateswara Naralasetty ar->ps_state_enable = ps_state_enable;
1553710a95f9SVenkateswara Naralasetty
1554710a95f9SVenkateswara Naralasetty if (!ar->ps_state_enable) {
1555710a95f9SVenkateswara Naralasetty ar->ps_timekeeper_enable = false;
1556710a95f9SVenkateswara Naralasetty ieee80211_iterate_stations_atomic(ar->hw,
1557710a95f9SVenkateswara Naralasetty ath11k_peer_ps_state_disable,
1558710a95f9SVenkateswara Naralasetty ar);
1559710a95f9SVenkateswara Naralasetty }
1560710a95f9SVenkateswara Naralasetty
1561710a95f9SVenkateswara Naralasetty ret = count;
1562710a95f9SVenkateswara Naralasetty
1563710a95f9SVenkateswara Naralasetty exit:
1564710a95f9SVenkateswara Naralasetty mutex_unlock(&ar->conf_mutex);
1565710a95f9SVenkateswara Naralasetty
1566710a95f9SVenkateswara Naralasetty return ret;
1567710a95f9SVenkateswara Naralasetty }
1568710a95f9SVenkateswara Naralasetty
ath11k_read_ps_state_enable(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1569710a95f9SVenkateswara Naralasetty static ssize_t ath11k_read_ps_state_enable(struct file *file,
1570710a95f9SVenkateswara Naralasetty char __user *user_buf,
1571710a95f9SVenkateswara Naralasetty size_t count, loff_t *ppos)
1572710a95f9SVenkateswara Naralasetty {
1573710a95f9SVenkateswara Naralasetty struct ath11k *ar = file->private_data;
1574710a95f9SVenkateswara Naralasetty char buf[32];
1575710a95f9SVenkateswara Naralasetty int len;
1576710a95f9SVenkateswara Naralasetty
1577710a95f9SVenkateswara Naralasetty mutex_lock(&ar->conf_mutex);
1578710a95f9SVenkateswara Naralasetty len = scnprintf(buf, sizeof(buf), "%d\n", ar->ps_state_enable);
1579710a95f9SVenkateswara Naralasetty mutex_unlock(&ar->conf_mutex);
1580710a95f9SVenkateswara Naralasetty
1581710a95f9SVenkateswara Naralasetty return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1582710a95f9SVenkateswara Naralasetty }
1583710a95f9SVenkateswara Naralasetty
1584710a95f9SVenkateswara Naralasetty static const struct file_operations fops_ps_state_enable = {
1585710a95f9SVenkateswara Naralasetty .read = ath11k_read_ps_state_enable,
1586710a95f9SVenkateswara Naralasetty .write = ath11k_write_ps_state_enable,
1587710a95f9SVenkateswara Naralasetty .open = simple_open,
1588710a95f9SVenkateswara Naralasetty .owner = THIS_MODULE,
1589710a95f9SVenkateswara Naralasetty .llseek = default_llseek,
1590710a95f9SVenkateswara Naralasetty };
1591710a95f9SVenkateswara Naralasetty
ath11k_debugfs_register(struct ath11k * ar)1592cb4e57dbSKalle Valo int ath11k_debugfs_register(struct ath11k *ar)
1593da3a9d3cSKalle Valo {
1594da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab;
1595da3a9d3cSKalle Valo char pdev_name[5];
1596da3a9d3cSKalle Valo char buf[100] = {0};
1597da3a9d3cSKalle Valo
1598da3a9d3cSKalle Valo snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx);
1599da3a9d3cSKalle Valo
1600da3a9d3cSKalle Valo ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc);
1601da3a9d3cSKalle Valo if (IS_ERR(ar->debug.debugfs_pdev))
1602da3a9d3cSKalle Valo return PTR_ERR(ar->debug.debugfs_pdev);
1603da3a9d3cSKalle Valo
1604da3a9d3cSKalle Valo /* Create a symlink under ieee80211/phy* */
1605da3a9d3cSKalle Valo snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev);
1606da3a9d3cSKalle Valo debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf);
1607da3a9d3cSKalle Valo
160856292162SKalle Valo ath11k_debugfs_htt_stats_init(ar);
1609da3a9d3cSKalle Valo
1610cb4e57dbSKalle Valo ath11k_debugfs_fw_stats_init(ar);
1611da3a9d3cSKalle Valo
1612da3a9d3cSKalle Valo debugfs_create_file("ext_tx_stats", 0644,
1613da3a9d3cSKalle Valo ar->debug.debugfs_pdev, ar,
1614da3a9d3cSKalle Valo &fops_extd_tx_stats);
1615da3a9d3cSKalle Valo debugfs_create_file("ext_rx_stats", 0644,
1616da3a9d3cSKalle Valo ar->debug.debugfs_pdev, ar,
1617da3a9d3cSKalle Valo &fops_extd_rx_stats);
1618da3a9d3cSKalle Valo debugfs_create_file("pktlog_filter", 0644,
1619da3a9d3cSKalle Valo ar->debug.debugfs_pdev, ar,
1620da3a9d3cSKalle Valo &fops_pktlog_filter);
1621f295ad91SSeevalamuthu Mariappan debugfs_create_file("fw_dbglog_config", 0600,
1622f295ad91SSeevalamuthu Mariappan ar->debug.debugfs_pdev, ar,
1623f295ad91SSeevalamuthu Mariappan &fops_fw_dbglog);
1624da3a9d3cSKalle Valo
1625da3a9d3cSKalle Valo if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) {
1626da3a9d3cSKalle Valo debugfs_create_file("dfs_simulate_radar", 0200,
1627da3a9d3cSKalle Valo ar->debug.debugfs_pdev, ar,
1628da3a9d3cSKalle Valo &fops_simulate_radar);
1629da3a9d3cSKalle Valo debugfs_create_bool("dfs_block_radar_events", 0200,
1630da3a9d3cSKalle Valo ar->debug.debugfs_pdev,
1631da3a9d3cSKalle Valo &ar->dfs_block_radar_events);
1632da3a9d3cSKalle Valo }
1633da3a9d3cSKalle Valo
1634691425b4SVenkateswara Naralasetty if (ab->hw_params.dbr_debug_support)
1635691425b4SVenkateswara Naralasetty debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev,
1636691425b4SVenkateswara Naralasetty ar, &fops_dbr_debug);
1637691425b4SVenkateswara Naralasetty
1638710a95f9SVenkateswara Naralasetty debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_pdev, ar,
1639710a95f9SVenkateswara Naralasetty &fops_ps_state_enable);
1640710a95f9SVenkateswara Naralasetty
1641710a95f9SVenkateswara Naralasetty if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT,
1642710a95f9SVenkateswara Naralasetty ar->ab->wmi_ab.svc_map)) {
1643710a95f9SVenkateswara Naralasetty debugfs_create_file("ps_timekeeper_enable", 0600,
1644710a95f9SVenkateswara Naralasetty ar->debug.debugfs_pdev, ar,
1645710a95f9SVenkateswara Naralasetty &fops_ps_timekeeper_enable);
1646710a95f9SVenkateswara Naralasetty
1647710a95f9SVenkateswara Naralasetty debugfs_create_file("reset_ps_duration", 0200,
1648710a95f9SVenkateswara Naralasetty ar->debug.debugfs_pdev, ar,
1649710a95f9SVenkateswara Naralasetty &fops_reset_ps_duration);
1650710a95f9SVenkateswara Naralasetty }
1651710a95f9SVenkateswara Naralasetty
1652da3a9d3cSKalle Valo return 0;
1653da3a9d3cSKalle Valo }
1654da3a9d3cSKalle Valo
ath11k_debugfs_unregister(struct ath11k * ar)1655cb4e57dbSKalle Valo void ath11k_debugfs_unregister(struct ath11k *ar)
1656da3a9d3cSKalle Valo {
1657691425b4SVenkateswara Naralasetty struct ath11k_debug_dbr *dbr_debug;
1658691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_dbg_data;
1659691425b4SVenkateswara Naralasetty int i;
1660691425b4SVenkateswara Naralasetty
1661691425b4SVenkateswara Naralasetty for (i = 0; i < WMI_DIRECT_BUF_MAX; i++) {
1662691425b4SVenkateswara Naralasetty dbr_debug = ar->debug.dbr_debug[i];
1663691425b4SVenkateswara Naralasetty if (!dbr_debug)
1664691425b4SVenkateswara Naralasetty continue;
1665691425b4SVenkateswara Naralasetty
1666691425b4SVenkateswara Naralasetty dbr_dbg_data = &dbr_debug->dbr_dbg_data;
1667691425b4SVenkateswara Naralasetty kfree(dbr_dbg_data->entries);
1668691425b4SVenkateswara Naralasetty debugfs_remove_recursive(dbr_debug->dbr_debugfs);
1669691425b4SVenkateswara Naralasetty kfree(dbr_debug);
1670691425b4SVenkateswara Naralasetty ar->debug.dbr_debug[i] = NULL;
1671691425b4SVenkateswara Naralasetty }
1672da3a9d3cSKalle Valo }
1673fe98a613SJohn Crispin
ath11k_write_twt_add_dialog(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)1674fe98a613SJohn Crispin static ssize_t ath11k_write_twt_add_dialog(struct file *file,
1675fe98a613SJohn Crispin const char __user *ubuf,
1676fe98a613SJohn Crispin size_t count, loff_t *ppos)
1677fe98a613SJohn Crispin {
1678fe98a613SJohn Crispin struct ath11k_vif *arvif = file->private_data;
1679fe98a613SJohn Crispin struct wmi_twt_add_dialog_params params = { 0 };
16809e2747c3SManikanta Pubbisetty struct wmi_twt_enable_params twt_params = {0};
16819e2747c3SManikanta Pubbisetty struct ath11k *ar = arvif->ar;
1682fe98a613SJohn Crispin u8 buf[128] = {0};
1683fe98a613SJohn Crispin int ret;
1684fe98a613SJohn Crispin
16859e2747c3SManikanta Pubbisetty if (ar->twt_enabled == 0) {
16869e2747c3SManikanta Pubbisetty ath11k_err(ar->ab, "twt support is not enabled\n");
1687fe98a613SJohn Crispin return -EOPNOTSUPP;
1688fe98a613SJohn Crispin }
1689fe98a613SJohn Crispin
1690fe98a613SJohn Crispin ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1691fe98a613SJohn Crispin if (ret < 0)
1692fe98a613SJohn Crispin return ret;
1693fe98a613SJohn Crispin
1694fe98a613SJohn Crispin buf[ret] = '\0';
1695fe98a613SJohn Crispin ret = sscanf(buf,
1696fe98a613SJohn Crispin "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u %u %u %hhu %hhu %hhu %hhu %hhu",
1697fe98a613SJohn Crispin ¶ms.peer_macaddr[0],
1698fe98a613SJohn Crispin ¶ms.peer_macaddr[1],
1699fe98a613SJohn Crispin ¶ms.peer_macaddr[2],
1700fe98a613SJohn Crispin ¶ms.peer_macaddr[3],
1701fe98a613SJohn Crispin ¶ms.peer_macaddr[4],
1702fe98a613SJohn Crispin ¶ms.peer_macaddr[5],
1703fe98a613SJohn Crispin ¶ms.dialog_id,
1704fe98a613SJohn Crispin ¶ms.wake_intvl_us,
1705fe98a613SJohn Crispin ¶ms.wake_intvl_mantis,
1706fe98a613SJohn Crispin ¶ms.wake_dura_us,
1707fe98a613SJohn Crispin ¶ms.sp_offset_us,
1708fe98a613SJohn Crispin ¶ms.twt_cmd,
1709fe98a613SJohn Crispin ¶ms.flag_bcast,
1710fe98a613SJohn Crispin ¶ms.flag_trigger,
1711fe98a613SJohn Crispin ¶ms.flag_flow_type,
1712fe98a613SJohn Crispin ¶ms.flag_protection);
1713fe98a613SJohn Crispin if (ret != 16)
1714fe98a613SJohn Crispin return -EINVAL;
1715fe98a613SJohn Crispin
17169e2747c3SManikanta Pubbisetty /* In the case of station vif, TWT is entirely handled by
17179e2747c3SManikanta Pubbisetty * the firmware based on the input parameters in the TWT enable
17189e2747c3SManikanta Pubbisetty * WMI command that is sent to the target during assoc.
17199e2747c3SManikanta Pubbisetty * For manually testing the TWT feature, we need to first disable
17209e2747c3SManikanta Pubbisetty * TWT and send enable command again with TWT input parameter
17219e2747c3SManikanta Pubbisetty * sta_cong_timer_ms set to 0.
17229e2747c3SManikanta Pubbisetty */
17239e2747c3SManikanta Pubbisetty if (arvif->vif->type == NL80211_IFTYPE_STATION) {
17249e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);
17259e2747c3SManikanta Pubbisetty
17269e2747c3SManikanta Pubbisetty ath11k_wmi_fill_default_twt_params(&twt_params);
17279e2747c3SManikanta Pubbisetty twt_params.sta_cong_timer_ms = 0;
17289e2747c3SManikanta Pubbisetty
17299e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);
17309e2747c3SManikanta Pubbisetty }
17319e2747c3SManikanta Pubbisetty
1732fe98a613SJohn Crispin params.vdev_id = arvif->vdev_id;
1733fe98a613SJohn Crispin
1734fe98a613SJohn Crispin ret = ath11k_wmi_send_twt_add_dialog_cmd(arvif->ar, ¶ms);
1735fe98a613SJohn Crispin if (ret)
17369e2747c3SManikanta Pubbisetty goto err_twt_add_dialog;
1737fe98a613SJohn Crispin
1738fe98a613SJohn Crispin return count;
17399e2747c3SManikanta Pubbisetty
17409e2747c3SManikanta Pubbisetty err_twt_add_dialog:
17419e2747c3SManikanta Pubbisetty if (arvif->vif->type == NL80211_IFTYPE_STATION) {
17429e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);
17439e2747c3SManikanta Pubbisetty ath11k_wmi_fill_default_twt_params(&twt_params);
17449e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);
17459e2747c3SManikanta Pubbisetty }
17469e2747c3SManikanta Pubbisetty
17479e2747c3SManikanta Pubbisetty return ret;
1748fe98a613SJohn Crispin }
1749fe98a613SJohn Crispin
ath11k_write_twt_del_dialog(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)1750fe98a613SJohn Crispin static ssize_t ath11k_write_twt_del_dialog(struct file *file,
1751fe98a613SJohn Crispin const char __user *ubuf,
1752fe98a613SJohn Crispin size_t count, loff_t *ppos)
1753fe98a613SJohn Crispin {
1754fe98a613SJohn Crispin struct ath11k_vif *arvif = file->private_data;
1755fe98a613SJohn Crispin struct wmi_twt_del_dialog_params params = { 0 };
17569e2747c3SManikanta Pubbisetty struct wmi_twt_enable_params twt_params = {0};
17579e2747c3SManikanta Pubbisetty struct ath11k *ar = arvif->ar;
1758fe98a613SJohn Crispin u8 buf[64] = {0};
1759fe98a613SJohn Crispin int ret;
1760fe98a613SJohn Crispin
17619e2747c3SManikanta Pubbisetty if (ar->twt_enabled == 0) {
17629e2747c3SManikanta Pubbisetty ath11k_err(ar->ab, "twt support is not enabled\n");
1763fe98a613SJohn Crispin return -EOPNOTSUPP;
1764fe98a613SJohn Crispin }
1765fe98a613SJohn Crispin
1766fe98a613SJohn Crispin ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1767fe98a613SJohn Crispin if (ret < 0)
1768fe98a613SJohn Crispin return ret;
1769fe98a613SJohn Crispin
1770fe98a613SJohn Crispin buf[ret] = '\0';
1771fe98a613SJohn Crispin ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u",
1772fe98a613SJohn Crispin ¶ms.peer_macaddr[0],
1773fe98a613SJohn Crispin ¶ms.peer_macaddr[1],
1774fe98a613SJohn Crispin ¶ms.peer_macaddr[2],
1775fe98a613SJohn Crispin ¶ms.peer_macaddr[3],
1776fe98a613SJohn Crispin ¶ms.peer_macaddr[4],
1777fe98a613SJohn Crispin ¶ms.peer_macaddr[5],
1778fe98a613SJohn Crispin ¶ms.dialog_id);
1779fe98a613SJohn Crispin if (ret != 7)
1780fe98a613SJohn Crispin return -EINVAL;
1781fe98a613SJohn Crispin
1782fe98a613SJohn Crispin params.vdev_id = arvif->vdev_id;
1783fe98a613SJohn Crispin
1784fe98a613SJohn Crispin ret = ath11k_wmi_send_twt_del_dialog_cmd(arvif->ar, ¶ms);
1785fe98a613SJohn Crispin if (ret)
1786fe98a613SJohn Crispin return ret;
1787fe98a613SJohn Crispin
17889e2747c3SManikanta Pubbisetty if (arvif->vif->type == NL80211_IFTYPE_STATION) {
17899e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);
17909e2747c3SManikanta Pubbisetty ath11k_wmi_fill_default_twt_params(&twt_params);
17919e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);
17929e2747c3SManikanta Pubbisetty }
17939e2747c3SManikanta Pubbisetty
1794fe98a613SJohn Crispin return count;
1795fe98a613SJohn Crispin }
1796fe98a613SJohn Crispin
ath11k_write_twt_pause_dialog(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)1797fe98a613SJohn Crispin static ssize_t ath11k_write_twt_pause_dialog(struct file *file,
1798fe98a613SJohn Crispin const char __user *ubuf,
1799fe98a613SJohn Crispin size_t count, loff_t *ppos)
1800fe98a613SJohn Crispin {
1801fe98a613SJohn Crispin struct ath11k_vif *arvif = file->private_data;
1802fe98a613SJohn Crispin struct wmi_twt_pause_dialog_params params = { 0 };
1803fe98a613SJohn Crispin u8 buf[64] = {0};
1804fe98a613SJohn Crispin int ret;
1805fe98a613SJohn Crispin
1806fe98a613SJohn Crispin if (arvif->ar->twt_enabled == 0) {
1807fe98a613SJohn Crispin ath11k_err(arvif->ar->ab, "twt support is not enabled\n");
1808fe98a613SJohn Crispin return -EOPNOTSUPP;
1809fe98a613SJohn Crispin }
1810fe98a613SJohn Crispin
1811fe98a613SJohn Crispin ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1812fe98a613SJohn Crispin if (ret < 0)
1813fe98a613SJohn Crispin return ret;
1814fe98a613SJohn Crispin
1815fe98a613SJohn Crispin buf[ret] = '\0';
1816fe98a613SJohn Crispin ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u",
1817fe98a613SJohn Crispin ¶ms.peer_macaddr[0],
1818fe98a613SJohn Crispin ¶ms.peer_macaddr[1],
1819fe98a613SJohn Crispin ¶ms.peer_macaddr[2],
1820fe98a613SJohn Crispin ¶ms.peer_macaddr[3],
1821fe98a613SJohn Crispin ¶ms.peer_macaddr[4],
1822fe98a613SJohn Crispin ¶ms.peer_macaddr[5],
1823fe98a613SJohn Crispin ¶ms.dialog_id);
1824fe98a613SJohn Crispin if (ret != 7)
1825fe98a613SJohn Crispin return -EINVAL;
1826fe98a613SJohn Crispin
1827fe98a613SJohn Crispin params.vdev_id = arvif->vdev_id;
1828fe98a613SJohn Crispin
1829fe98a613SJohn Crispin ret = ath11k_wmi_send_twt_pause_dialog_cmd(arvif->ar, ¶ms);
1830fe98a613SJohn Crispin if (ret)
1831fe98a613SJohn Crispin return ret;
1832fe98a613SJohn Crispin
1833fe98a613SJohn Crispin return count;
1834fe98a613SJohn Crispin }
1835fe98a613SJohn Crispin
ath11k_write_twt_resume_dialog(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)1836fe98a613SJohn Crispin static ssize_t ath11k_write_twt_resume_dialog(struct file *file,
1837fe98a613SJohn Crispin const char __user *ubuf,
1838fe98a613SJohn Crispin size_t count, loff_t *ppos)
1839fe98a613SJohn Crispin {
1840fe98a613SJohn Crispin struct ath11k_vif *arvif = file->private_data;
1841fe98a613SJohn Crispin struct wmi_twt_resume_dialog_params params = { 0 };
1842fe98a613SJohn Crispin u8 buf[64] = {0};
1843fe98a613SJohn Crispin int ret;
1844fe98a613SJohn Crispin
1845fe98a613SJohn Crispin if (arvif->ar->twt_enabled == 0) {
1846fe98a613SJohn Crispin ath11k_err(arvif->ar->ab, "twt support is not enabled\n");
1847fe98a613SJohn Crispin return -EOPNOTSUPP;
1848fe98a613SJohn Crispin }
1849fe98a613SJohn Crispin
1850fe98a613SJohn Crispin ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
1851fe98a613SJohn Crispin if (ret < 0)
1852fe98a613SJohn Crispin return ret;
1853fe98a613SJohn Crispin
1854fe98a613SJohn Crispin buf[ret] = '\0';
1855fe98a613SJohn Crispin ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u",
1856fe98a613SJohn Crispin ¶ms.peer_macaddr[0],
1857fe98a613SJohn Crispin ¶ms.peer_macaddr[1],
1858fe98a613SJohn Crispin ¶ms.peer_macaddr[2],
1859fe98a613SJohn Crispin ¶ms.peer_macaddr[3],
1860fe98a613SJohn Crispin ¶ms.peer_macaddr[4],
1861fe98a613SJohn Crispin ¶ms.peer_macaddr[5],
1862fe98a613SJohn Crispin ¶ms.dialog_id,
1863fe98a613SJohn Crispin ¶ms.sp_offset_us,
1864fe98a613SJohn Crispin ¶ms.next_twt_size);
1865fe98a613SJohn Crispin if (ret != 9)
1866fe98a613SJohn Crispin return -EINVAL;
1867fe98a613SJohn Crispin
1868fe98a613SJohn Crispin params.vdev_id = arvif->vdev_id;
1869fe98a613SJohn Crispin
1870fe98a613SJohn Crispin ret = ath11k_wmi_send_twt_resume_dialog_cmd(arvif->ar, ¶ms);
1871fe98a613SJohn Crispin if (ret)
1872fe98a613SJohn Crispin return ret;
1873fe98a613SJohn Crispin
1874fe98a613SJohn Crispin return count;
1875fe98a613SJohn Crispin }
1876fe98a613SJohn Crispin
1877fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_add_dialog = {
1878fe98a613SJohn Crispin .write = ath11k_write_twt_add_dialog,
1879fe98a613SJohn Crispin .open = simple_open
1880fe98a613SJohn Crispin };
1881fe98a613SJohn Crispin
1882fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_del_dialog = {
1883fe98a613SJohn Crispin .write = ath11k_write_twt_del_dialog,
1884fe98a613SJohn Crispin .open = simple_open
1885fe98a613SJohn Crispin };
1886fe98a613SJohn Crispin
1887fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_pause_dialog = {
1888fe98a613SJohn Crispin .write = ath11k_write_twt_pause_dialog,
1889fe98a613SJohn Crispin .open = simple_open
1890fe98a613SJohn Crispin };
1891fe98a613SJohn Crispin
1892fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_resume_dialog = {
1893fe98a613SJohn Crispin .write = ath11k_write_twt_resume_dialog,
1894fe98a613SJohn Crispin .open = simple_open
1895fe98a613SJohn Crispin };
1896fe98a613SJohn Crispin
ath11k_debugfs_add_interface(struct ath11k_vif * arvif)1897607c467eSManikanta Pubbisetty void ath11k_debugfs_add_interface(struct ath11k_vif *arvif)
1898fe98a613SJohn Crispin {
18999e2747c3SManikanta Pubbisetty struct ath11k_base *ab = arvif->ar->ab;
19009e2747c3SManikanta Pubbisetty
19019e2747c3SManikanta Pubbisetty if (arvif->vif->type != NL80211_IFTYPE_AP &&
19029e2747c3SManikanta Pubbisetty !(arvif->vif->type == NL80211_IFTYPE_STATION &&
19039e2747c3SManikanta Pubbisetty test_bit(WMI_TLV_SERVICE_STA_TWT, ab->wmi_ab.svc_map)))
1904607c467eSManikanta Pubbisetty return;
19059e2747c3SManikanta Pubbisetty
1906fe98a613SJohn Crispin arvif->debugfs_twt = debugfs_create_dir("twt",
1907fe98a613SJohn Crispin arvif->vif->debugfs_dir);
1908fe98a613SJohn Crispin debugfs_create_file("add_dialog", 0200, arvif->debugfs_twt,
1909fe98a613SJohn Crispin arvif, &ath11k_fops_twt_add_dialog);
1910fe98a613SJohn Crispin
1911fe98a613SJohn Crispin debugfs_create_file("del_dialog", 0200, arvif->debugfs_twt,
1912fe98a613SJohn Crispin arvif, &ath11k_fops_twt_del_dialog);
1913fe98a613SJohn Crispin
1914fe98a613SJohn Crispin debugfs_create_file("pause_dialog", 0200, arvif->debugfs_twt,
1915fe98a613SJohn Crispin arvif, &ath11k_fops_twt_pause_dialog);
1916fe98a613SJohn Crispin
1917fe98a613SJohn Crispin debugfs_create_file("resume_dialog", 0200, arvif->debugfs_twt,
1918fe98a613SJohn Crispin arvif, &ath11k_fops_twt_resume_dialog);
1919fe98a613SJohn Crispin }
1920fe98a613SJohn Crispin
ath11k_debugfs_remove_interface(struct ath11k_vif * arvif)1921fe98a613SJohn Crispin void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif)
1922fe98a613SJohn Crispin {
1923607c467eSManikanta Pubbisetty if (!arvif->debugfs_twt)
1924607c467eSManikanta Pubbisetty return;
1925607c467eSManikanta Pubbisetty
1926fe98a613SJohn Crispin debugfs_remove_recursive(arvif->debugfs_twt);
1927fe98a613SJohn Crispin arvif->debugfs_twt = NULL;
1928fe98a613SJohn Crispin }
1929