1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2009-2012 Realtek Corporation.*/ |
3 | |
4 | #include "wifi.h" |
5 | #include "cam.h" |
6 | |
7 | #include <linux/moduleparam.h> |
8 | #include <linux/vmalloc.h> |
9 | |
10 | #ifdef CONFIG_RTLWIFI_DEBUG |
11 | void _rtl_dbg_print(struct rtl_priv *rtlpriv, u64 comp, int level, |
12 | const char *fmt, ...) |
13 | { |
14 | if (unlikely((comp & rtlpriv->cfg->mod_params->debug_mask) && |
15 | level <= rtlpriv->cfg->mod_params->debug_level)) { |
16 | struct va_format vaf; |
17 | va_list args; |
18 | |
19 | va_start(args, fmt); |
20 | |
21 | vaf.fmt = fmt; |
22 | vaf.va = &args; |
23 | |
24 | pr_info("%pV" , &vaf); |
25 | |
26 | va_end(args); |
27 | } |
28 | } |
29 | EXPORT_SYMBOL_GPL(_rtl_dbg_print); |
30 | |
31 | void _rtl_dbg_print_data(struct rtl_priv *rtlpriv, u64 comp, int level, |
32 | const char *titlestring, |
33 | const void *hexdata, int hexdatalen) |
34 | { |
35 | if (unlikely(((comp) & rtlpriv->cfg->mod_params->debug_mask) && |
36 | ((level) <= rtlpriv->cfg->mod_params->debug_level))) { |
37 | pr_info("In process \"%s\" (pid %i): %s\n" , |
38 | current->comm, current->pid, titlestring); |
39 | print_hex_dump_bytes("" , DUMP_PREFIX_NONE, |
40 | hexdata, hexdatalen); |
41 | } |
42 | } |
43 | EXPORT_SYMBOL_GPL(_rtl_dbg_print_data); |
44 | |
45 | struct rtl_debugfs_priv { |
46 | struct rtl_priv *rtlpriv; |
47 | int (*cb_read)(struct seq_file *m, void *v); |
48 | ssize_t (*cb_write)(struct file *filp, const char __user *buffer, |
49 | size_t count, loff_t *loff); |
50 | u32 cb_data; |
51 | }; |
52 | |
53 | static struct dentry *debugfs_topdir; |
54 | |
55 | static int rtl_debug_get_common(struct seq_file *m, void *v) |
56 | { |
57 | struct rtl_debugfs_priv *debugfs_priv = m->private; |
58 | |
59 | return debugfs_priv->cb_read(m, v); |
60 | } |
61 | |
62 | static int dl_debug_open_common(struct inode *inode, struct file *file) |
63 | { |
64 | return single_open(file, rtl_debug_get_common, inode->i_private); |
65 | } |
66 | |
67 | static const struct file_operations file_ops_common = { |
68 | .open = dl_debug_open_common, |
69 | .read = seq_read, |
70 | .llseek = seq_lseek, |
71 | .release = single_release, |
72 | }; |
73 | |
74 | static int rtl_debug_get_mac_page(struct seq_file *m, void *v) |
75 | { |
76 | struct rtl_debugfs_priv *debugfs_priv = m->private; |
77 | struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; |
78 | u32 page = debugfs_priv->cb_data; |
79 | int i, n; |
80 | int max = 0xff; |
81 | |
82 | for (n = 0; n <= max; ) { |
83 | seq_printf(m, fmt: "\n%8.8x " , n + page); |
84 | for (i = 0; i < 4 && n <= max; i++, n += 4) |
85 | seq_printf(m, fmt: "%8.8x " , |
86 | rtl_read_dword(rtlpriv, addr: (page | n))); |
87 | } |
88 | seq_puts(m, s: "\n" ); |
89 | return 0; |
90 | } |
91 | |
92 | #define RTL_DEBUG_IMPL_MAC_SERIES(page, addr) \ |
93 | static struct rtl_debugfs_priv rtl_debug_priv_mac_ ##page = { \ |
94 | .cb_read = rtl_debug_get_mac_page, \ |
95 | .cb_data = addr, \ |
96 | } |
97 | |
98 | RTL_DEBUG_IMPL_MAC_SERIES(0, 0x0000); |
99 | RTL_DEBUG_IMPL_MAC_SERIES(1, 0x0100); |
100 | RTL_DEBUG_IMPL_MAC_SERIES(2, 0x0200); |
101 | RTL_DEBUG_IMPL_MAC_SERIES(3, 0x0300); |
102 | RTL_DEBUG_IMPL_MAC_SERIES(4, 0x0400); |
103 | RTL_DEBUG_IMPL_MAC_SERIES(5, 0x0500); |
104 | RTL_DEBUG_IMPL_MAC_SERIES(6, 0x0600); |
105 | RTL_DEBUG_IMPL_MAC_SERIES(7, 0x0700); |
106 | RTL_DEBUG_IMPL_MAC_SERIES(10, 0x1000); |
107 | RTL_DEBUG_IMPL_MAC_SERIES(11, 0x1100); |
108 | RTL_DEBUG_IMPL_MAC_SERIES(12, 0x1200); |
109 | RTL_DEBUG_IMPL_MAC_SERIES(13, 0x1300); |
110 | RTL_DEBUG_IMPL_MAC_SERIES(14, 0x1400); |
111 | RTL_DEBUG_IMPL_MAC_SERIES(15, 0x1500); |
112 | RTL_DEBUG_IMPL_MAC_SERIES(16, 0x1600); |
113 | RTL_DEBUG_IMPL_MAC_SERIES(17, 0x1700); |
114 | |
115 | static int rtl_debug_get_bb_page(struct seq_file *m, void *v) |
116 | { |
117 | struct rtl_debugfs_priv *debugfs_priv = m->private; |
118 | struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; |
119 | struct ieee80211_hw *hw = rtlpriv->hw; |
120 | u32 page = debugfs_priv->cb_data; |
121 | int i, n; |
122 | int max = 0xff; |
123 | |
124 | for (n = 0; n <= max; ) { |
125 | seq_printf(m, fmt: "\n%8.8x " , n + page); |
126 | for (i = 0; i < 4 && n <= max; i++, n += 4) |
127 | seq_printf(m, fmt: "%8.8x " , |
128 | rtl_get_bbreg(hw, regaddr: (page | n), bitmask: 0xffffffff)); |
129 | } |
130 | seq_puts(m, s: "\n" ); |
131 | return 0; |
132 | } |
133 | |
134 | #define RTL_DEBUG_IMPL_BB_SERIES(page, addr) \ |
135 | static struct rtl_debugfs_priv rtl_debug_priv_bb_ ##page = { \ |
136 | .cb_read = rtl_debug_get_bb_page, \ |
137 | .cb_data = addr, \ |
138 | } |
139 | |
140 | RTL_DEBUG_IMPL_BB_SERIES(8, 0x0800); |
141 | RTL_DEBUG_IMPL_BB_SERIES(9, 0x0900); |
142 | RTL_DEBUG_IMPL_BB_SERIES(a, 0x0a00); |
143 | RTL_DEBUG_IMPL_BB_SERIES(b, 0x0b00); |
144 | RTL_DEBUG_IMPL_BB_SERIES(c, 0x0c00); |
145 | RTL_DEBUG_IMPL_BB_SERIES(d, 0x0d00); |
146 | RTL_DEBUG_IMPL_BB_SERIES(e, 0x0e00); |
147 | RTL_DEBUG_IMPL_BB_SERIES(f, 0x0f00); |
148 | RTL_DEBUG_IMPL_BB_SERIES(18, 0x1800); |
149 | RTL_DEBUG_IMPL_BB_SERIES(19, 0x1900); |
150 | RTL_DEBUG_IMPL_BB_SERIES(1a, 0x1a00); |
151 | RTL_DEBUG_IMPL_BB_SERIES(1b, 0x1b00); |
152 | RTL_DEBUG_IMPL_BB_SERIES(1c, 0x1c00); |
153 | RTL_DEBUG_IMPL_BB_SERIES(1d, 0x1d00); |
154 | RTL_DEBUG_IMPL_BB_SERIES(1e, 0x1e00); |
155 | RTL_DEBUG_IMPL_BB_SERIES(1f, 0x1f00); |
156 | |
157 | static int rtl_debug_get_reg_rf(struct seq_file *m, void *v) |
158 | { |
159 | struct rtl_debugfs_priv *debugfs_priv = m->private; |
160 | struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; |
161 | struct ieee80211_hw *hw = rtlpriv->hw; |
162 | enum radio_path rfpath = debugfs_priv->cb_data; |
163 | int i, n; |
164 | int max = 0x40; |
165 | |
166 | if (IS_HARDWARE_TYPE_8822B(rtlpriv)) |
167 | max = 0xff; |
168 | |
169 | seq_printf(m, fmt: "\nPATH(%d)" , rfpath); |
170 | |
171 | for (n = 0; n <= max; ) { |
172 | seq_printf(m, fmt: "\n%8.8x " , n); |
173 | for (i = 0; i < 4 && n <= max; n += 1, i++) |
174 | seq_printf(m, fmt: "%8.8x " , |
175 | rtl_get_rfreg(hw, rfpath, regaddr: n, bitmask: 0xffffffff)); |
176 | } |
177 | seq_puts(m, s: "\n" ); |
178 | return 0; |
179 | } |
180 | |
181 | #define RTL_DEBUG_IMPL_RF_SERIES(page, addr) \ |
182 | static struct rtl_debugfs_priv rtl_debug_priv_rf_ ##page = { \ |
183 | .cb_read = rtl_debug_get_reg_rf, \ |
184 | .cb_data = addr, \ |
185 | } |
186 | |
187 | RTL_DEBUG_IMPL_RF_SERIES(a, RF90_PATH_A); |
188 | RTL_DEBUG_IMPL_RF_SERIES(b, RF90_PATH_B); |
189 | |
190 | static int rtl_debug_get_cam_register(struct seq_file *m, void *v) |
191 | { |
192 | struct rtl_debugfs_priv *debugfs_priv = m->private; |
193 | struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; |
194 | int start = debugfs_priv->cb_data; |
195 | u32 target_cmd = 0; |
196 | u32 target_val = 0; |
197 | u8 entry_i = 0; |
198 | u32 ulstatus; |
199 | int i = 100, j = 0; |
200 | int end = (start + 11 > TOTAL_CAM_ENTRY ? TOTAL_CAM_ENTRY : start + 11); |
201 | |
202 | /* This dump the current register page */ |
203 | seq_printf(m, |
204 | fmt: "\n#################### SECURITY CAM (%d-%d) ##################\n" , |
205 | start, end - 1); |
206 | |
207 | for (j = start; j < end; j++) { |
208 | seq_printf(m, fmt: "\nD: %2x > " , j); |
209 | for (entry_i = 0; entry_i < CAM_CONTENT_COUNT; entry_i++) { |
210 | /* polling bit, and No Write enable, and address */ |
211 | target_cmd = entry_i + CAM_CONTENT_COUNT * j; |
212 | target_cmd = target_cmd | BIT(31); |
213 | |
214 | /* Check polling bit is clear */ |
215 | while ((i--) >= 0) { |
216 | ulstatus = |
217 | rtl_read_dword(rtlpriv, |
218 | addr: rtlpriv->cfg->maps[RWCAM]); |
219 | if (ulstatus & BIT(31)) |
220 | continue; |
221 | else |
222 | break; |
223 | } |
224 | |
225 | rtl_write_dword(rtlpriv, addr: rtlpriv->cfg->maps[RWCAM], |
226 | val32: target_cmd); |
227 | target_val = rtl_read_dword(rtlpriv, |
228 | addr: rtlpriv->cfg->maps[RCAMO]); |
229 | seq_printf(m, fmt: "%8.8x " , target_val); |
230 | } |
231 | } |
232 | seq_puts(m, s: "\n" ); |
233 | return 0; |
234 | } |
235 | |
236 | #define RTL_DEBUG_IMPL_CAM_SERIES(page, addr) \ |
237 | static struct rtl_debugfs_priv rtl_debug_priv_cam_ ##page = { \ |
238 | .cb_read = rtl_debug_get_cam_register, \ |
239 | .cb_data = addr, \ |
240 | } |
241 | |
242 | RTL_DEBUG_IMPL_CAM_SERIES(1, 0); |
243 | RTL_DEBUG_IMPL_CAM_SERIES(2, 11); |
244 | RTL_DEBUG_IMPL_CAM_SERIES(3, 22); |
245 | |
246 | static int rtl_debug_get_btcoex(struct seq_file *m, void *v) |
247 | { |
248 | struct rtl_debugfs_priv *debugfs_priv = m->private; |
249 | struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; |
250 | |
251 | if (rtlpriv->cfg->ops->get_btc_status()) |
252 | rtlpriv->btcoexist.btc_ops->btc_display_bt_coex_info(rtlpriv, |
253 | m); |
254 | |
255 | seq_puts(m, s: "\n" ); |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | static struct rtl_debugfs_priv rtl_debug_priv_btcoex = { |
261 | .cb_read = rtl_debug_get_btcoex, |
262 | .cb_data = 0, |
263 | }; |
264 | |
265 | static ssize_t rtl_debugfs_set_write_reg(struct file *filp, |
266 | const char __user *buffer, |
267 | size_t count, loff_t *loff) |
268 | { |
269 | struct rtl_debugfs_priv *debugfs_priv = filp->private_data; |
270 | struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; |
271 | char tmp[32 + 1]; |
272 | int tmp_len; |
273 | u32 addr, val, len; |
274 | int num; |
275 | |
276 | if (count < 3) |
277 | return -EFAULT; |
278 | |
279 | tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count); |
280 | |
281 | if (copy_from_user(to: tmp, from: buffer, n: tmp_len)) |
282 | return -EFAULT; |
283 | |
284 | tmp[tmp_len] = '\0'; |
285 | |
286 | /* write BB/MAC register */ |
287 | num = sscanf(tmp, "%x %x %x" , &addr, &val, &len); |
288 | |
289 | if (num != 3) |
290 | return -EINVAL; |
291 | |
292 | switch (len) { |
293 | case 1: |
294 | rtl_write_byte(rtlpriv, addr, val8: (u8)val); |
295 | break; |
296 | case 2: |
297 | rtl_write_word(rtlpriv, addr, val16: (u16)val); |
298 | break; |
299 | case 4: |
300 | rtl_write_dword(rtlpriv, addr, val32: val); |
301 | break; |
302 | default: |
303 | /*printk("error write length=%d", len);*/ |
304 | break; |
305 | } |
306 | |
307 | return count; |
308 | } |
309 | |
310 | static struct rtl_debugfs_priv rtl_debug_priv_write_reg = { |
311 | .cb_write = rtl_debugfs_set_write_reg, |
312 | }; |
313 | |
314 | static ssize_t rtl_debugfs_set_write_h2c(struct file *filp, |
315 | const char __user *buffer, |
316 | size_t count, loff_t *loff) |
317 | { |
318 | struct rtl_debugfs_priv *debugfs_priv = filp->private_data; |
319 | struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; |
320 | struct ieee80211_hw *hw = rtlpriv->hw; |
321 | char tmp[32 + 1]; |
322 | int tmp_len; |
323 | u8 h2c_len, h2c_data_packed[8]; |
324 | int h2c_data[8]; /* idx 0: cmd */ |
325 | int i; |
326 | |
327 | if (count < 3) |
328 | return -EFAULT; |
329 | |
330 | tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count); |
331 | |
332 | if (copy_from_user(to: tmp, from: buffer, n: tmp_len)) |
333 | return -EFAULT; |
334 | |
335 | tmp[tmp_len] = '\0'; |
336 | |
337 | h2c_len = sscanf(tmp, "%X %X %X %X %X %X %X %X" , |
338 | &h2c_data[0], &h2c_data[1], |
339 | &h2c_data[2], &h2c_data[3], |
340 | &h2c_data[4], &h2c_data[5], |
341 | &h2c_data[6], &h2c_data[7]); |
342 | |
343 | if (h2c_len == 0) |
344 | return -EINVAL; |
345 | |
346 | for (i = 0; i < h2c_len; i++) |
347 | h2c_data_packed[i] = (u8)h2c_data[i]; |
348 | |
349 | rtlpriv->cfg->ops->fill_h2c_cmd(hw, h2c_data_packed[0], |
350 | h2c_len - 1, |
351 | &h2c_data_packed[1]); |
352 | |
353 | return count; |
354 | } |
355 | |
356 | static struct rtl_debugfs_priv rtl_debug_priv_write_h2c = { |
357 | .cb_write = rtl_debugfs_set_write_h2c, |
358 | }; |
359 | |
360 | static ssize_t rtl_debugfs_set_write_rfreg(struct file *filp, |
361 | const char __user *buffer, |
362 | size_t count, loff_t *loff) |
363 | { |
364 | struct rtl_debugfs_priv *debugfs_priv = filp->private_data; |
365 | struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; |
366 | struct ieee80211_hw *hw = rtlpriv->hw; |
367 | char tmp[32 + 1]; |
368 | int tmp_len; |
369 | int num; |
370 | int path; |
371 | u32 addr, bitmask, data; |
372 | |
373 | if (count < 3) |
374 | return -EFAULT; |
375 | |
376 | tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count); |
377 | |
378 | if (copy_from_user(to: tmp, from: buffer, n: tmp_len)) |
379 | return -EFAULT; |
380 | |
381 | tmp[tmp_len] = '\0'; |
382 | |
383 | num = sscanf(tmp, "%X %X %X %X" , |
384 | &path, &addr, &bitmask, &data); |
385 | |
386 | if (num != 4) { |
387 | rtl_dbg(rtlpriv, COMP_ERR, DBG_DMESG, |
388 | "Format is <path> <addr> <mask> <data>\n" ); |
389 | return -EINVAL; |
390 | } |
391 | |
392 | rtl_set_rfreg(hw, rfpath: path, regaddr: addr, bitmask, data); |
393 | |
394 | return count; |
395 | } |
396 | |
397 | static struct rtl_debugfs_priv rtl_debug_priv_write_rfreg = { |
398 | .cb_write = rtl_debugfs_set_write_rfreg, |
399 | }; |
400 | |
401 | static int rtl_debugfs_close(struct inode *inode, struct file *filp) |
402 | { |
403 | return 0; |
404 | } |
405 | |
406 | static ssize_t rtl_debugfs_common_write(struct file *filp, |
407 | const char __user *buffer, |
408 | size_t count, loff_t *loff) |
409 | { |
410 | struct rtl_debugfs_priv *debugfs_priv = filp->private_data; |
411 | |
412 | return debugfs_priv->cb_write(filp, buffer, count, loff); |
413 | } |
414 | |
415 | static const struct file_operations file_ops_common_write = { |
416 | .owner = THIS_MODULE, |
417 | .write = rtl_debugfs_common_write, |
418 | .open = simple_open, |
419 | .release = rtl_debugfs_close, |
420 | }; |
421 | |
422 | #define RTL_DEBUGFS_ADD_CORE(name, mode, fopname) \ |
423 | do { \ |
424 | rtl_debug_priv_ ##name.rtlpriv = rtlpriv; \ |
425 | debugfs_create_file(#name, mode, parent, \ |
426 | &rtl_debug_priv_ ##name, \ |
427 | &file_ops_ ##fopname); \ |
428 | } while (0) |
429 | |
430 | #define RTL_DEBUGFS_ADD(name) \ |
431 | RTL_DEBUGFS_ADD_CORE(name, S_IFREG | 0444, common) |
432 | #define RTL_DEBUGFS_ADD_W(name) \ |
433 | RTL_DEBUGFS_ADD_CORE(name, S_IFREG | 0222, common_write) |
434 | |
435 | void rtl_debug_add_one(struct ieee80211_hw *hw) |
436 | { |
437 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
438 | struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); |
439 | struct dentry *parent; |
440 | |
441 | snprintf(buf: rtlpriv->dbg.debugfs_name, size: 18, fmt: "%pMF" , rtlefuse->dev_addr); |
442 | |
443 | rtlpriv->dbg.debugfs_dir = |
444 | debugfs_create_dir(name: rtlpriv->dbg.debugfs_name, parent: debugfs_topdir); |
445 | |
446 | parent = rtlpriv->dbg.debugfs_dir; |
447 | |
448 | RTL_DEBUGFS_ADD(mac_0); |
449 | RTL_DEBUGFS_ADD(mac_1); |
450 | RTL_DEBUGFS_ADD(mac_2); |
451 | RTL_DEBUGFS_ADD(mac_3); |
452 | RTL_DEBUGFS_ADD(mac_4); |
453 | RTL_DEBUGFS_ADD(mac_5); |
454 | RTL_DEBUGFS_ADD(mac_6); |
455 | RTL_DEBUGFS_ADD(mac_7); |
456 | RTL_DEBUGFS_ADD(bb_8); |
457 | RTL_DEBUGFS_ADD(bb_9); |
458 | RTL_DEBUGFS_ADD(bb_a); |
459 | RTL_DEBUGFS_ADD(bb_b); |
460 | RTL_DEBUGFS_ADD(bb_c); |
461 | RTL_DEBUGFS_ADD(bb_d); |
462 | RTL_DEBUGFS_ADD(bb_e); |
463 | RTL_DEBUGFS_ADD(bb_f); |
464 | RTL_DEBUGFS_ADD(mac_10); |
465 | RTL_DEBUGFS_ADD(mac_11); |
466 | RTL_DEBUGFS_ADD(mac_12); |
467 | RTL_DEBUGFS_ADD(mac_13); |
468 | RTL_DEBUGFS_ADD(mac_14); |
469 | RTL_DEBUGFS_ADD(mac_15); |
470 | RTL_DEBUGFS_ADD(mac_16); |
471 | RTL_DEBUGFS_ADD(mac_17); |
472 | RTL_DEBUGFS_ADD(bb_18); |
473 | RTL_DEBUGFS_ADD(bb_19); |
474 | RTL_DEBUGFS_ADD(bb_1a); |
475 | RTL_DEBUGFS_ADD(bb_1b); |
476 | RTL_DEBUGFS_ADD(bb_1c); |
477 | RTL_DEBUGFS_ADD(bb_1d); |
478 | RTL_DEBUGFS_ADD(bb_1e); |
479 | RTL_DEBUGFS_ADD(bb_1f); |
480 | RTL_DEBUGFS_ADD(rf_a); |
481 | RTL_DEBUGFS_ADD(rf_b); |
482 | |
483 | RTL_DEBUGFS_ADD(cam_1); |
484 | RTL_DEBUGFS_ADD(cam_2); |
485 | RTL_DEBUGFS_ADD(cam_3); |
486 | |
487 | RTL_DEBUGFS_ADD(btcoex); |
488 | |
489 | RTL_DEBUGFS_ADD_W(write_reg); |
490 | RTL_DEBUGFS_ADD_W(write_h2c); |
491 | RTL_DEBUGFS_ADD_W(write_rfreg); |
492 | } |
493 | EXPORT_SYMBOL_GPL(rtl_debug_add_one); |
494 | |
495 | void rtl_debug_remove_one(struct ieee80211_hw *hw) |
496 | { |
497 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
498 | |
499 | debugfs_remove_recursive(dentry: rtlpriv->dbg.debugfs_dir); |
500 | rtlpriv->dbg.debugfs_dir = NULL; |
501 | } |
502 | EXPORT_SYMBOL_GPL(rtl_debug_remove_one); |
503 | |
504 | void rtl_debugfs_add_topdir(void) |
505 | { |
506 | debugfs_topdir = debugfs_create_dir(name: "rtlwifi" , NULL); |
507 | } |
508 | |
509 | void rtl_debugfs_remove_topdir(void) |
510 | { |
511 | debugfs_remove_recursive(dentry: debugfs_topdir); |
512 | } |
513 | |
514 | #endif |
515 | |