Merge tag 'spi-fix-v5.5-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
[linux-2.6-microblaze.git] / drivers / staging / wfx / debug.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Debugfs interface.
4  *
5  * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
6  * Copyright (c) 2010, ST-Ericsson
7  */
8 #include <linux/debugfs.h>
9 #include <linux/seq_file.h>
10 #include <linux/crc32.h>
11
12 #include "debug.h"
13 #include "wfx.h"
14 #include "sta.h"
15 #include "main.h"
16 #include "hif_tx.h"
17 #include "hif_tx_mib.h"
18
19 #define CREATE_TRACE_POINTS
20 #include "traces.h"
21
22 static const struct trace_print_flags hif_msg_print_map[] = {
23         hif_msg_list,
24 };
25
26 static const struct trace_print_flags hif_mib_print_map[] = {
27         hif_mib_list,
28 };
29
30 static const struct trace_print_flags wfx_reg_print_map[] = {
31         wfx_reg_list,
32 };
33
34 static const char *get_symbol(unsigned long val,
35                 const struct trace_print_flags *symbol_array)
36 {
37         int i;
38
39         for (i = 0; symbol_array[i].mask != -1; i++) {
40                 if (val == symbol_array[i].mask)
41                         return symbol_array[i].name;
42         }
43
44         return "unknown";
45 }
46
47 const char *get_hif_name(unsigned long id)
48 {
49         return get_symbol(id, hif_msg_print_map);
50 }
51
52 const char *get_mib_name(unsigned long id)
53 {
54         return get_symbol(id, hif_mib_print_map);
55 }
56
57 const char *get_reg_name(unsigned long id)
58 {
59         return get_symbol(id, wfx_reg_print_map);
60 }
61
62 static int wfx_counters_show(struct seq_file *seq, void *v)
63 {
64         int ret;
65         struct wfx_dev *wdev = seq->private;
66         struct hif_mib_extended_count_table counters;
67
68         ret = hif_get_counters_table(wdev, &counters);
69         if (ret < 0)
70                 return ret;
71         if (ret > 0)
72                 return -EIO;
73
74 #define PUT_COUNTER(name) \
75         seq_printf(seq, "%24s %d\n", #name ":",\
76                    le32_to_cpu(counters.count_##name))
77
78         PUT_COUNTER(tx_packets);
79         PUT_COUNTER(tx_multicast_frames);
80         PUT_COUNTER(tx_frames_success);
81         PUT_COUNTER(tx_frame_failures);
82         PUT_COUNTER(tx_frames_retried);
83         PUT_COUNTER(tx_frames_multi_retried);
84
85         PUT_COUNTER(rts_success);
86         PUT_COUNTER(rts_failures);
87         PUT_COUNTER(ack_failures);
88
89         PUT_COUNTER(rx_packets);
90         PUT_COUNTER(rx_frames_success);
91         PUT_COUNTER(rx_packet_errors);
92         PUT_COUNTER(plcp_errors);
93         PUT_COUNTER(fcs_errors);
94         PUT_COUNTER(rx_decryption_failures);
95         PUT_COUNTER(rx_mic_failures);
96         PUT_COUNTER(rx_no_key_failures);
97         PUT_COUNTER(rx_frame_duplicates);
98         PUT_COUNTER(rx_multicast_frames);
99         PUT_COUNTER(rx_cmacicv_errors);
100         PUT_COUNTER(rx_cmac_replays);
101         PUT_COUNTER(rx_mgmt_ccmp_replays);
102
103         PUT_COUNTER(rx_beacon);
104         PUT_COUNTER(miss_beacon);
105
106 #undef PUT_COUNTER
107
108         return 0;
109 }
110 DEFINE_SHOW_ATTRIBUTE(wfx_counters);
111
112 static const char * const channel_names[] = {
113         [0] = "1M",
114         [1] = "2M",
115         [2] = "5.5M",
116         [3] = "11M",
117         /* Entries 4 and 5 does not exist */
118         [6] = "6M",
119         [7] = "9M",
120         [8] = "12M",
121         [9] = "18M",
122         [10] = "24M",
123         [11] = "36M",
124         [12] = "48M",
125         [13] = "54M",
126         [14] = "MCS0",
127         [15] = "MCS1",
128         [16] = "MCS2",
129         [17] = "MCS3",
130         [18] = "MCS4",
131         [19] = "MCS5",
132         [20] = "MCS6",
133         [21] = "MCS7",
134 };
135
136 static int wfx_rx_stats_show(struct seq_file *seq, void *v)
137 {
138         struct wfx_dev *wdev = seq->private;
139         struct hif_rx_stats *st = &wdev->rx_stats;
140         int i;
141
142         mutex_lock(&wdev->rx_stats_lock);
143         seq_printf(seq, "Timestamp: %dus\n", st->date);
144         seq_printf(seq, "Low power clock: frequency %uHz, external %s\n",
145                    st->pwr_clk_freq,
146                    st->is_ext_pwr_clk ? "yes" : "no");
147         seq_printf(seq,
148                    "N. of frames: %d, PER (x10e4): %d, Throughput: %dKbps/s\n",
149                    st->nb_rx_frame, st->per_total, st->throughput);
150         seq_puts(seq, "       Num. of      PER     RSSI      SNR      CFO\n");
151         seq_puts(seq, "        frames  (x10e4)    (dBm)     (dB)    (kHz)\n");
152         for (i = 0; i < ARRAY_SIZE(channel_names); i++) {
153                 if (channel_names[i])
154                         seq_printf(seq, "%5s %8d %8d %8d %8d %8d\n",
155                                    channel_names[i], st->nb_rx_by_rate[i],
156                                    st->per[i], st->rssi[i] / 100,
157                                    st->snr[i] / 100, st->cfo[i]);
158         }
159         mutex_unlock(&wdev->rx_stats_lock);
160
161         return 0;
162 }
163 DEFINE_SHOW_ATTRIBUTE(wfx_rx_stats);
164
165 static ssize_t wfx_send_pds_write(struct file *file,
166                                   const char __user *user_buf,
167                                   size_t count, loff_t *ppos)
168 {
169         struct wfx_dev *wdev = file->private_data;
170         char *buf;
171         int ret;
172
173         if (*ppos != 0) {
174                 dev_dbg(wdev->dev, "PDS data must be written in one transaction");
175                 return -EBUSY;
176         }
177         buf = memdup_user(user_buf, count);
178         if (IS_ERR(buf))
179                 return PTR_ERR(buf);
180         *ppos = *ppos + count;
181         ret = wfx_send_pds(wdev, buf, count);
182         kfree(buf);
183         if (ret < 0)
184                 return ret;
185         return count;
186 }
187
188 static const struct file_operations wfx_send_pds_fops = {
189         .open = simple_open,
190         .write = wfx_send_pds_write,
191 };
192
193 static ssize_t wfx_burn_slk_key_write(struct file *file,
194                                       const char __user *user_buf,
195                                       size_t count, loff_t *ppos)
196 {
197         struct wfx_dev *wdev = file->private_data;
198
199         dev_info(wdev->dev, "this driver does not support secure link\n");
200         return -EINVAL;
201 }
202
203 static const struct file_operations wfx_burn_slk_key_fops = {
204         .open = simple_open,
205         .write = wfx_burn_slk_key_write,
206 };
207
208 struct dbgfs_hif_msg {
209         struct wfx_dev *wdev;
210         struct completion complete;
211         u8 reply[1024];
212         int ret;
213 };
214
215 static ssize_t wfx_send_hif_msg_write(struct file *file,
216                                       const char __user *user_buf,
217                                       size_t count, loff_t *ppos)
218 {
219         struct dbgfs_hif_msg *context = file->private_data;
220         struct wfx_dev *wdev = context->wdev;
221         struct hif_msg *request;
222
223         if (completion_done(&context->complete)) {
224                 dev_dbg(wdev->dev, "read previous result before start a new one\n");
225                 return -EBUSY;
226         }
227         if (count < sizeof(struct hif_msg))
228                 return -EINVAL;
229
230         // wfx_cmd_send() chekc that reply buffer is wide enough, but do not
231         // return precise length read. User have to know how many bytes should
232         // be read. Filling reply buffer with a memory pattern may help user.
233         memset(context->reply, 0xFF, sizeof(context->reply));
234         request = memdup_user(user_buf, count);
235         if (IS_ERR(request))
236                 return PTR_ERR(request);
237         if (request->len != count) {
238                 kfree(request);
239                 return -EINVAL;
240         }
241         context->ret = wfx_cmd_send(wdev, request, context->reply,
242                                     sizeof(context->reply), false);
243
244         kfree(request);
245         complete(&context->complete);
246         return count;
247 }
248
249 static ssize_t wfx_send_hif_msg_read(struct file *file, char __user *user_buf,
250                                      size_t count, loff_t *ppos)
251 {
252         struct dbgfs_hif_msg *context = file->private_data;
253         int ret;
254
255         if (count > sizeof(context->reply))
256                 return -EINVAL;
257         ret = wait_for_completion_interruptible(&context->complete);
258         if (ret)
259                 return ret;
260         if (context->ret < 0)
261                 return context->ret;
262         // Be carefull, write() is waiting for a full message while read()
263         // only return a payload
264         if (copy_to_user(user_buf, context->reply, count))
265                 return -EFAULT;
266
267         return count;
268 }
269
270 static int wfx_send_hif_msg_open(struct inode *inode, struct file *file)
271 {
272         struct dbgfs_hif_msg *context = kzalloc(sizeof(*context), GFP_KERNEL);
273
274         if (!context)
275                 return -ENOMEM;
276         context->wdev = inode->i_private;
277         init_completion(&context->complete);
278         file->private_data = context;
279         return 0;
280 }
281
282 static int wfx_send_hif_msg_release(struct inode *inode, struct file *file)
283 {
284         struct dbgfs_hif_msg *context = file->private_data;
285
286         kfree(context);
287         return 0;
288 }
289
290 static const struct file_operations wfx_send_hif_msg_fops = {
291         .open = wfx_send_hif_msg_open,
292         .release = wfx_send_hif_msg_release,
293         .write = wfx_send_hif_msg_write,
294         .read = wfx_send_hif_msg_read,
295 };
296
297 int wfx_debug_init(struct wfx_dev *wdev)
298 {
299         struct dentry *d;
300
301         d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir);
302         debugfs_create_file("counters", 0444, d, wdev, &wfx_counters_fops);
303         debugfs_create_file("rx_stats", 0444, d, wdev, &wfx_rx_stats_fops);
304         debugfs_create_file("send_pds", 0200, d, wdev, &wfx_send_pds_fops);
305         debugfs_create_file("burn_slk_key", 0200, d, wdev,
306                             &wfx_burn_slk_key_fops);
307         debugfs_create_file("send_hif_msg", 0600, d, wdev,
308                             &wfx_send_hif_msg_fops);
309
310         return 0;
311 }