xref: /linux/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c (revision 2d7eaeb05ae265bd64270376e6a2c0d80244771f)
1 // SPDX-License-Identifier: BSD-3-Clause-Clear
2 /*
3  * Copyright (C) 2022 MediaTek Inc.
4  */
5 
6 #include <linux/relay.h>
7 #include "mt7996.h"
8 #include "eeprom.h"
9 #include "mcu.h"
10 #include "mac.h"
11 
12 #define FW_BIN_LOG_MAGIC	0x44d9c99a
13 
14 /** global debugfs **/
15 
16 static int
17 mt7996_implicit_txbf_set(void *data, u64 val)
18 {
19 	struct mt7996_dev *dev = data;
20 
21 	/* The existing connected stations shall reconnect to apply
22 	 * new implicit txbf configuration.
23 	 */
24 	dev->ibf = !!val;
25 
26 	return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
27 }
28 
29 static int
30 mt7996_implicit_txbf_get(void *data, u64 *val)
31 {
32 	struct mt7996_dev *dev = data;
33 
34 	*val = dev->ibf;
35 
36 	return 0;
37 }
38 
39 DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7996_implicit_txbf_get,
40 			 mt7996_implicit_txbf_set, "%lld\n");
41 
42 /* test knob of system error recovery */
43 static ssize_t
44 mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
45 			size_t count, loff_t *ppos)
46 {
47 	struct mt7996_dev *dev = file->private_data;
48 	char buf[16], *sep;
49 	int ret = 0;
50 	u16 band, val;
51 
52 	if (count >= sizeof(buf))
53 		return -EINVAL;
54 
55 	if (copy_from_user(buf, user_buf, count))
56 		return -EFAULT;
57 
58 	if (count && buf[count - 1] == '\n')
59 		buf[count - 1] = '\0';
60 	else
61 		buf[count] = '\0';
62 
63 	sep = strchr(buf, ',');
64 	if (!sep)
65 		return -EINVAL;
66 
67 	*sep = 0;
68 	if (kstrtou16(buf, 0, &band) || kstrtou16(sep + 1, 0, &val))
69 		return -EINVAL;
70 
71 	switch (val) {
72 	/*
73 	 * <band>,0: grab firmware current SER state.
74 	 * <band>,1: trigger & enable system error L1 recovery.
75 	 * <band>,2: trigger & enable system error L2 recovery.
76 	 * <band>,3: trigger & enable system error L3 rx abort.
77 	 * <band>,4: trigger & enable system error L3 tx abort
78 	 * <band>,5: trigger & enable system error L3 tx disable.
79 	 * <band>,6: trigger & enable system error L3 bf recovery.
80 	 * <band>,7: trigger & enable system error L4 mdp recovery.
81 	 * <band>,8: trigger & enable system error full recovery.
82 	 * <band>,9: trigger firmware crash.
83 	 */
84 	case UNI_CMD_SER_QUERY:
85 		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band);
86 		break;
87 	case UNI_CMD_SER_SET_RECOVER_L1:
88 	case UNI_CMD_SER_SET_RECOVER_L2:
89 	case UNI_CMD_SER_SET_RECOVER_L3_RX_ABORT:
90 	case UNI_CMD_SER_SET_RECOVER_L3_TX_ABORT:
91 	case UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE:
92 	case UNI_CMD_SER_SET_RECOVER_L3_BF:
93 	case UNI_CMD_SER_SET_RECOVER_L4_MDP:
94 		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_SET, BIT(val), band);
95 		if (ret)
96 			return ret;
97 
98 		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, val, band);
99 		break;
100 
101 	/* enable full chip reset */
102 	case UNI_CMD_SER_SET_RECOVER_FULL:
103 		mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
104 		dev->recovery.state |= MT_MCU_CMD_WDT_MASK;
105 		mt7996_reset(dev);
106 		break;
107 
108 	/* WARNING: trigger firmware crash */
109 	case UNI_CMD_SER_SET_SYSTEM_ASSERT:
110 		ret = mt7996_mcu_trigger_assert(dev);
111 		if (ret)
112 			return ret;
113 		break;
114 	default:
115 		break;
116 	}
117 
118 	return ret ? ret : count;
119 }
120 
121 static ssize_t
122 mt7996_sys_recovery_get(struct file *file, char __user *user_buf,
123 			size_t count, loff_t *ppos)
124 {
125 	struct mt7996_dev *dev = file->private_data;
126 	char *buff;
127 	int desc = 0;
128 	ssize_t ret;
129 	static const size_t bufsz = 1024;
130 
131 	buff = kmalloc(bufsz, GFP_KERNEL);
132 	if (!buff)
133 		return -ENOMEM;
134 
135 	/* HELP */
136 	desc += scnprintf(buff + desc, bufsz - desc,
137 			  "Please echo the correct value ...\n");
138 	desc += scnprintf(buff + desc, bufsz - desc,
139 			  "<band>,0: grab firmware transient SER state\n");
140 	desc += scnprintf(buff + desc, bufsz - desc,
141 			  "<band>,1: trigger system error L1 recovery\n");
142 	desc += scnprintf(buff + desc, bufsz - desc,
143 			  "<band>,2: trigger system error L2 recovery\n");
144 	desc += scnprintf(buff + desc, bufsz - desc,
145 			  "<band>,3: trigger system error L3 rx abort\n");
146 	desc += scnprintf(buff + desc, bufsz - desc,
147 			  "<band>,4: trigger system error L3 tx abort\n");
148 	desc += scnprintf(buff + desc, bufsz - desc,
149 			  "<band>,5: trigger system error L3 tx disable\n");
150 	desc += scnprintf(buff + desc, bufsz - desc,
151 			  "<band>,6: trigger system error L3 bf recovery\n");
152 	desc += scnprintf(buff + desc, bufsz - desc,
153 			  "<band>,7: trigger system error L4 mdp recovery\n");
154 	desc += scnprintf(buff + desc, bufsz - desc,
155 			  "<band>,8: trigger system error full recovery\n");
156 	desc += scnprintf(buff + desc, bufsz - desc,
157 			  "<band>,9: trigger firmware crash\n");
158 
159 	/* SER statistics */
160 	desc += scnprintf(buff + desc, bufsz - desc,
161 			  "\nlet's dump firmware SER statistics...\n");
162 	desc += scnprintf(buff + desc, bufsz - desc,
163 			  "::E  R , SER_STATUS        = 0x%08x\n",
164 			  mt76_rr(dev, MT_SWDEF_SER_STATS));
165 	desc += scnprintf(buff + desc, bufsz - desc,
166 			  "::E  R , SER_PLE_ERR       = 0x%08x\n",
167 			  mt76_rr(dev, MT_SWDEF_PLE_STATS));
168 	desc += scnprintf(buff + desc, bufsz - desc,
169 			  "::E  R , SER_PLE_ERR_1     = 0x%08x\n",
170 			  mt76_rr(dev, MT_SWDEF_PLE1_STATS));
171 	desc += scnprintf(buff + desc, bufsz - desc,
172 			  "::E  R , SER_PLE_ERR_AMSDU = 0x%08x\n",
173 			  mt76_rr(dev, MT_SWDEF_PLE_AMSDU_STATS));
174 	desc += scnprintf(buff + desc, bufsz - desc,
175 			  "::E  R , SER_PSE_ERR       = 0x%08x\n",
176 			  mt76_rr(dev, MT_SWDEF_PSE_STATS));
177 	desc += scnprintf(buff + desc, bufsz - desc,
178 			  "::E  R , SER_PSE_ERR_1     = 0x%08x\n",
179 			  mt76_rr(dev, MT_SWDEF_PSE1_STATS));
180 	desc += scnprintf(buff + desc, bufsz - desc,
181 			  "::E  R , SER_LMAC_WISR6_B0 = 0x%08x\n",
182 			  mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN0_STATS));
183 	desc += scnprintf(buff + desc, bufsz - desc,
184 			  "::E  R , SER_LMAC_WISR6_B1 = 0x%08x\n",
185 			  mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN1_STATS));
186 	desc += scnprintf(buff + desc, bufsz - desc,
187 			  "::E  R , SER_LMAC_WISR6_B2 = 0x%08x\n",
188 			  mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN2_STATS));
189 	desc += scnprintf(buff + desc, bufsz - desc,
190 			  "::E  R , SER_LMAC_WISR7_B0 = 0x%08x\n",
191 			  mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN0_STATS));
192 	desc += scnprintf(buff + desc, bufsz - desc,
193 			  "::E  R , SER_LMAC_WISR7_B1 = 0x%08x\n",
194 			  mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS));
195 	desc += scnprintf(buff + desc, bufsz - desc,
196 			  "::E  R , SER_LMAC_WISR7_B2 = 0x%08x\n",
197 			  mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN2_STATS));
198 	desc += scnprintf(buff + desc, bufsz - desc,
199 			  "\nSYS_RESET_COUNT: WM %d, WA %d\n",
200 			  dev->recovery.wm_reset_count,
201 			  dev->recovery.wa_reset_count);
202 
203 	ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
204 	kfree(buff);
205 	return ret;
206 }
207 
208 static const struct file_operations mt7996_sys_recovery_ops = {
209 	.write = mt7996_sys_recovery_set,
210 	.read = mt7996_sys_recovery_get,
211 	.open = simple_open,
212 	.llseek = default_llseek,
213 };
214 
215 static int
216 mt7996_radar_trigger(void *data, u64 val)
217 {
218 #define RADAR_MAIN_CHAIN	1
219 #define RADAR_BACKGROUND	2
220 	struct mt7996_dev *dev = data;
221 	struct mt7996_phy *phy = mt7996_band_phy(dev, NL80211_BAND_5GHZ);
222 	struct cfg80211_chan_def *chandef;
223 	int rdd_idx, ret;
224 
225 	if (!phy || !val || val > RADAR_BACKGROUND)
226 		return -EINVAL;
227 
228 	if (test_bit(MT76_SCANNING, &phy->mt76->state))
229 		return -EBUSY;
230 
231 	if (val == RADAR_BACKGROUND) {
232 		if (!dev->rdd2_phy || !cfg80211_chandef_valid(&dev->rdd2_chandef)) {
233 			dev_err(dev->mt76.dev, "Background radar is not enabled\n");
234 			return -EINVAL;
235 		}
236 		chandef = &dev->rdd2_chandef;
237 	} else {
238 		chandef = &phy->mt76->chandef;
239 	}
240 
241 	rdd_idx = mt7996_get_rdd_idx(phy, val == RADAR_BACKGROUND);
242 	if (rdd_idx < 0) {
243 		dev_err(dev->mt76.dev, "No RDD found\n");
244 		return -EINVAL;
245 	}
246 
247 	ret = cfg80211_chandef_dfs_required(dev->mt76.hw->wiphy, chandef,
248 					    NL80211_IFTYPE_AP);
249 	if (ret <= 0)
250 		return ret;
251 
252 	return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, rdd_idx, 0);
253 }
254 
255 DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL,
256 			 mt7996_radar_trigger, "%lld\n");
257 
258 static int
259 mt7996_rdd_monitor(struct seq_file *s, void *data)
260 {
261 	struct mt7996_dev *dev = dev_get_drvdata(s->private);
262 	struct cfg80211_chan_def *chandef = &dev->rdd2_chandef;
263 	const char *bw;
264 	int ret = 0;
265 
266 	mutex_lock(&dev->mt76.mutex);
267 
268 	if (!cfg80211_chandef_valid(chandef)) {
269 		ret = -EINVAL;
270 		goto out;
271 	}
272 
273 	if (!dev->rdd2_phy) {
274 		seq_puts(s, "not running\n");
275 		goto out;
276 	}
277 
278 	switch (chandef->width) {
279 	case NL80211_CHAN_WIDTH_40:
280 		bw = "40";
281 		break;
282 	case NL80211_CHAN_WIDTH_80:
283 		bw = "80";
284 		break;
285 	case NL80211_CHAN_WIDTH_160:
286 		bw = "160";
287 		break;
288 	case NL80211_CHAN_WIDTH_80P80:
289 		bw = "80P80";
290 		break;
291 	default:
292 		bw = "20";
293 		break;
294 	}
295 
296 	seq_printf(s, "channel %d (%d MHz) width %s MHz center1: %d MHz\n",
297 		   chandef->chan->hw_value, chandef->chan->center_freq,
298 		   bw, chandef->center_freq1);
299 out:
300 	mutex_unlock(&dev->mt76.mutex);
301 
302 	return ret;
303 }
304 
305 static int
306 mt7996_fw_debug_wm_set(void *data, u64 val)
307 {
308 	struct mt7996_dev *dev = data;
309 	enum {
310 		DEBUG_TXCMD = 62,
311 		DEBUG_CMD_RPT_TX,
312 		DEBUG_CMD_RPT_TRIG,
313 		DEBUG_SPL,
314 		DEBUG_RPT_RX,
315 		DEBUG_RPT_RA = 68,
316 	} debug;
317 	bool tx, rx, en;
318 	int ret;
319 
320 	dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0;
321 
322 	if (dev->fw_debug_bin)
323 		val = MCU_FW_LOG_RELAY;
324 	else
325 		val = dev->fw_debug_wm;
326 
327 	tx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(1));
328 	rx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(2));
329 	en = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(0));
330 
331 	ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, val);
332 	if (ret)
333 		return ret;
334 
335 	for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) {
336 		if (debug == 67)
337 			continue;
338 
339 		if (debug == DEBUG_RPT_RX)
340 			val = en && rx;
341 		else
342 			val = en && tx;
343 
344 		ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val);
345 		if (ret)
346 			return ret;
347 	}
348 
349 	return 0;
350 }
351 
352 static int
353 mt7996_fw_debug_wm_get(void *data, u64 *val)
354 {
355 	struct mt7996_dev *dev = data;
356 
357 	*val = dev->fw_debug_wm;
358 
359 	return 0;
360 }
361 
362 DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wm, mt7996_fw_debug_wm_get,
363 			 mt7996_fw_debug_wm_set, "%lld\n");
364 
365 static int
366 mt7996_fw_debug_wa_set(void *data, u64 val)
367 {
368 	struct mt7996_dev *dev = data;
369 	int ret;
370 
371 	dev->fw_debug_wa = val ? MCU_FW_LOG_TO_HOST : 0;
372 
373 	ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, dev->fw_debug_wa);
374 	if (ret)
375 		return ret;
376 
377 	return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), MCU_WA_PARAM_PDMA_RX,
378 				 !!dev->fw_debug_wa, 0);
379 }
380 
381 static int
382 mt7996_fw_debug_wa_get(void *data, u64 *val)
383 {
384 	struct mt7996_dev *dev = data;
385 
386 	*val = dev->fw_debug_wa;
387 
388 	return 0;
389 }
390 
391 DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wa, mt7996_fw_debug_wa_get,
392 			 mt7996_fw_debug_wa_set, "%lld\n");
393 
394 static struct dentry *
395 create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
396 		   struct rchan_buf *buf, int *is_global)
397 {
398 	struct dentry *f;
399 
400 	f = debugfs_create_file("fwlog_data", mode, parent, buf,
401 				&relay_file_operations);
402 	if (IS_ERR(f))
403 		return NULL;
404 
405 	*is_global = 1;
406 
407 	return f;
408 }
409 
410 static int
411 remove_buf_file_cb(struct dentry *f)
412 {
413 	debugfs_remove(f);
414 
415 	return 0;
416 }
417 
418 static int
419 mt7996_fw_debug_bin_set(void *data, u64 val)
420 {
421 	static struct rchan_callbacks relay_cb = {
422 		.create_buf_file = create_buf_file_cb,
423 		.remove_buf_file = remove_buf_file_cb,
424 	};
425 	struct mt7996_dev *dev = data;
426 
427 	if (!dev->relay_fwlog)
428 		dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir,
429 					      1500, 512, &relay_cb, NULL);
430 	if (!dev->relay_fwlog)
431 		return -ENOMEM;
432 
433 	dev->fw_debug_bin = val;
434 
435 	relay_reset(dev->relay_fwlog);
436 
437 	return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
438 }
439 
440 static int
441 mt7996_fw_debug_bin_get(void *data, u64 *val)
442 {
443 	struct mt7996_dev *dev = data;
444 
445 	*val = dev->fw_debug_bin;
446 
447 	return 0;
448 }
449 
450 DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get,
451 			 mt7996_fw_debug_bin_set, "%lld\n");
452 
453 static int
454 mt7996_fw_util_wa_show(struct seq_file *file, void *data)
455 {
456 	struct mt7996_dev *dev = file->private;
457 
458 	if (dev->fw_debug_wa)
459 		return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY),
460 					 MCU_WA_PARAM_CPU_UTIL, 0, 0);
461 
462 	return 0;
463 }
464 
465 DEFINE_SHOW_ATTRIBUTE(mt7996_fw_util_wa);
466 
467 static void
468 mt7996_ampdu_stat_read_phy(struct mt7996_phy *phy, struct seq_file *file)
469 {
470 	struct mt7996_dev *dev = phy->dev;
471 	int bound[15], range[8], i;
472 	u8 band_idx = phy->mt76->band_idx;
473 
474 	/* Tx ampdu stat */
475 	for (i = 0; i < ARRAY_SIZE(range); i++)
476 		range[i] = mt76_rr(dev, MT_MIB_ARNG(band_idx, i));
477 
478 	for (i = 0; i < ARRAY_SIZE(bound); i++)
479 		bound[i] = MT_MIB_ARNCR_RANGE(range[i / 2], i % 2) + 1;
480 
481 	seq_printf(file, "\nPhy %s, Phy band %d\n",
482 		   wiphy_name(phy->mt76->hw->wiphy), band_idx);
483 
484 	seq_printf(file, "Length: %8d | ", bound[0]);
485 	for (i = 0; i < ARRAY_SIZE(bound) - 1; i++)
486 		seq_printf(file, "%3d -%3d | ",
487 			   bound[i] + 1, bound[i + 1]);
488 
489 	seq_puts(file, "\nCount:  ");
490 	for (i = 0; i < ARRAY_SIZE(bound); i++)
491 		seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]);
492 	seq_puts(file, "\n");
493 
494 	seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt);
495 }
496 
497 static void
498 mt7996_txbf_stat_read_phy(struct mt7996_phy *phy, struct seq_file *s)
499 {
500 	struct mt76_mib_stats *mib = &phy->mib;
501 	static const char * const bw[] = {
502 		"BW20", "BW40", "BW80", "BW160", "BW320"
503 	};
504 
505 	/* Tx Beamformer monitor */
506 	seq_puts(s, "\nTx Beamformer applied PPDU counts: ");
507 
508 	seq_printf(s, "iBF: %d, eBF: %d\n",
509 		   mib->tx_bf_ibf_ppdu_cnt,
510 		   mib->tx_bf_ebf_ppdu_cnt);
511 
512 	/* Tx Beamformer Rx feedback monitor */
513 	seq_puts(s, "Tx Beamformer Rx feedback statistics: ");
514 
515 	seq_printf(s, "All: %d, EHT: %d, HE: %d, VHT: %d, HT: %d, ",
516 		   mib->tx_bf_rx_fb_all_cnt,
517 		   mib->tx_bf_rx_fb_eht_cnt,
518 		   mib->tx_bf_rx_fb_he_cnt,
519 		   mib->tx_bf_rx_fb_vht_cnt,
520 		   mib->tx_bf_rx_fb_ht_cnt);
521 
522 	seq_printf(s, "%s, NC: %d, NR: %d\n",
523 		   bw[mib->tx_bf_rx_fb_bw],
524 		   mib->tx_bf_rx_fb_nc_cnt,
525 		   mib->tx_bf_rx_fb_nr_cnt);
526 
527 	/* Tx Beamformee Rx NDPA & Tx feedback report */
528 	seq_printf(s, "Tx Beamformee successful feedback frames: %d\n",
529 		   mib->tx_bf_fb_cpl_cnt);
530 	seq_printf(s, "Tx Beamformee feedback triggered counts: %d\n",
531 		   mib->tx_bf_fb_trig_cnt);
532 
533 	/* Tx SU & MU counters */
534 	seq_printf(s, "Tx multi-user Beamforming counts: %d\n",
535 		   mib->tx_mu_bf_cnt);
536 	seq_printf(s, "Tx multi-user MPDU counts: %d\n", mib->tx_mu_mpdu_cnt);
537 	seq_printf(s, "Tx multi-user successful MPDU counts: %d\n",
538 		   mib->tx_mu_acked_mpdu_cnt);
539 	seq_printf(s, "Tx single-user successful MPDU counts: %d\n",
540 		   mib->tx_su_acked_mpdu_cnt);
541 
542 	seq_puts(s, "\n");
543 }
544 
545 static void
546 mt7996_tx_stats_show_phy(struct seq_file *file, struct mt7996_phy *phy)
547 {
548 	struct mt76_mib_stats *mib = &phy->mib;
549 	u32 attempts, success, per;
550 	int i;
551 
552 	mt7996_mac_update_stats(phy);
553 	mt7996_ampdu_stat_read_phy(phy, file);
554 
555 	attempts = mib->tx_mpdu_attempts_cnt;
556 	success = mib->tx_mpdu_success_cnt;
557 	per = attempts ? 100 - success * 100 / attempts : 100;
558 	seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts);
559 	seq_printf(file, "Tx success: %8u (MPDUs)\n", success);
560 	seq_printf(file, "Tx PER: %u%%\n", per);
561 
562 	mt7996_txbf_stat_read_phy(phy, file);
563 
564 	/* Tx amsdu info */
565 	seq_puts(file, "Tx MSDU statistics:\n");
566 	for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) {
567 		seq_printf(file, "AMSDU pack count of %d MSDU in TXD: %8d ",
568 			   i + 1, mib->tx_amsdu[i]);
569 		if (mib->tx_amsdu_cnt)
570 			seq_printf(file, "(%3d%%)\n",
571 				   mib->tx_amsdu[i] * 100 / mib->tx_amsdu_cnt);
572 		else
573 			seq_puts(file, "\n");
574 	}
575 }
576 
577 static int
578 mt7996_tx_stats_show(struct seq_file *file, void *data)
579 {
580 	struct mt7996_dev *dev = file->private;
581 	struct mt7996_phy *phy = &dev->phy;
582 
583 	mutex_lock(&dev->mt76.mutex);
584 
585 	mt7996_tx_stats_show_phy(file, phy);
586 	phy = mt7996_phy2(dev);
587 	if (phy)
588 		mt7996_tx_stats_show_phy(file, phy);
589 	phy = mt7996_phy3(dev);
590 	if (phy)
591 		mt7996_tx_stats_show_phy(file, phy);
592 
593 	mutex_unlock(&dev->mt76.mutex);
594 
595 	return 0;
596 }
597 
598 DEFINE_SHOW_ATTRIBUTE(mt7996_tx_stats);
599 
600 enum {
601 	MT_QUEUE_PORT_HIF,
602 	MT_QUEUE_PORT_CPU,
603 	MT_QUEUE_PORT_LMAC,
604 	MT_QUEUE_PORT_PLE_CTRL,
605 };
606 
607 struct mt7996_hw_queue {
608 	const char *name;
609 	u8 pid;
610 	u8 qid;
611 	u8 tgid;
612 };
613 
614 #define MT7996_HW_QUEUE(_name, _pid, _qid, _tgid)	\
615 	{ .name = _name, .pid = MT_QUEUE_PORT_##_pid, .qid = _qid, .tgid = _tgid }
616 
617 static const struct mt7996_hw_queue mt7996_ple_queues[32] = {
618 	[0] = MT7996_HW_QUEUE("CPU Q0", CPU, 0, 0),
619 	[1] = MT7996_HW_QUEUE("CPU Q1", CPU, 1, 0),
620 	[2] = MT7996_HW_QUEUE("CPU Q2", CPU, 2, 0),
621 	[3] = MT7996_HW_QUEUE("CPU Q3", CPU, 3, 0),
622 	[4] = MT7996_HW_QUEUE("ALTX Q0", LMAC, 0x10, 0),
623 	[5] = MT7996_HW_QUEUE("BMC Q0", LMAC, 0x11, 0),
624 	[6] = MT7996_HW_QUEUE("BCN Q0", LMAC, 0x12, 0),
625 	[7] = MT7996_HW_QUEUE("PSMP Q0", LMAC, 0x13, 0),
626 	[8] = MT7996_HW_QUEUE("ALTX Q1", LMAC, 0x10, 1),
627 	[9] = MT7996_HW_QUEUE("BMC Q1", LMAC, 0x11, 1),
628 	[10] = MT7996_HW_QUEUE("BCN Q1", LMAC, 0x12, 1),
629 	[11] = MT7996_HW_QUEUE("PSMP Q1", LMAC, 0x13, 1),
630 	[12] = MT7996_HW_QUEUE("ALTX Q2", LMAC, 0x1c, 2),
631 	[13] = MT7996_HW_QUEUE("BMC Q2", LMAC, 0x1d, 2),
632 	[14] = MT7996_HW_QUEUE("BCN Q2", LMAC, 0x1e, 2),
633 	[15] = MT7996_HW_QUEUE("PSMP Q2", LMAC, 0x1f, 2),
634 	[16] = MT7996_HW_QUEUE("NAF Q", LMAC, 0x18, 0),
635 	[17] = MT7996_HW_QUEUE("NBCN Q", LMAC, 0x19, 0),
636 	[20] = MT7996_HW_QUEUE("FIXFID Q", LMAC, 0x1a, 0),
637 	[28] = MT7996_HW_QUEUE("RLS4 Q", PLE_CTRL, 0x7c, 0),
638 	[29] = MT7996_HW_QUEUE("RLS3 Q", PLE_CTRL, 0x7d, 0),
639 	[30] = MT7996_HW_QUEUE("RLS2 Q", PLE_CTRL, 0x7e, 0),
640 	[31] = MT7996_HW_QUEUE("RLS Q", PLE_CTRL, 0x7f, 0),
641 };
642 
643 static const struct mt7996_hw_queue mt7996_pse_queues[32] = {
644 	[0] = MT7996_HW_QUEUE("CPU Q0", CPU, 0, 0),
645 	[1] = MT7996_HW_QUEUE("CPU Q1", CPU, 1, 0),
646 	[2] = MT7996_HW_QUEUE("CPU Q2", CPU, 2, 0),
647 	[3] = MT7996_HW_QUEUE("CPU Q3", CPU, 3, 0),
648 	[16] = MT7996_HW_QUEUE("LMAC Q", LMAC, 0, 0),
649 	[17] = MT7996_HW_QUEUE("MDP TX Q0", LMAC, 1, 0),
650 	[18] = MT7996_HW_QUEUE("MDP RX Q", LMAC, 2, 0),
651 	[19] = MT7996_HW_QUEUE("SEC TX Q0", LMAC, 3, 0),
652 	[20] = MT7996_HW_QUEUE("SEC RX Q", LMAC, 4, 0),
653 	[21] = MT7996_HW_QUEUE("SFD PARK Q", LMAC, 5, 0),
654 	[22] = MT7996_HW_QUEUE("MDP TXIOC Q0", LMAC, 6, 0),
655 	[23] = MT7996_HW_QUEUE("MDP RXIOC Q0", LMAC, 7, 0),
656 	[24] = MT7996_HW_QUEUE("MDP TX Q1", LMAC, 0x11, 0),
657 	[25] = MT7996_HW_QUEUE("SEC TX Q1", LMAC, 0x13, 0),
658 	[26] = MT7996_HW_QUEUE("MDP TXIOC Q1", LMAC, 0x16, 0),
659 	[27] = MT7996_HW_QUEUE("MDP RXIOC Q1", LMAC, 0x17, 0),
660 	[28] = MT7996_HW_QUEUE("CPU Q4", CPU, 4, 0),
661 	[31] = MT7996_HW_QUEUE("RLS Q", PLE_CTRL, 0x1f, 0),
662 };
663 
664 static const struct mt7996_hw_queue mt7996_pse_queues_1[32] = {
665 	[0] = MT7996_HW_QUEUE("MDP TDPIOC Q0", LMAC, 0x08, 0),
666 	[1] = MT7996_HW_QUEUE("MDP RDPIOC Q0", LMAC, 0x09, 0),
667 	[2] = MT7996_HW_QUEUE("MDP TDPIOC Q1", LMAC, 0x18, 0),
668 	[3] = MT7996_HW_QUEUE("MDP RDPIOC Q1", LMAC, 0x19, 0),
669 	[4] = MT7996_HW_QUEUE("MDP TDPIOC Q2", LMAC, 0x28, 0),
670 	[5] = MT7996_HW_QUEUE("MDP RDPIOC Q2", LMAC, 0x29, 0),
671 	[7] = MT7996_HW_QUEUE("MDP RDPIOC Q3", LMAC, 0x39, 0),
672 	[8] = MT7996_HW_QUEUE("MDP TX Q2", LMAC, 0x21, 0),
673 	[9] = MT7996_HW_QUEUE("SEC TX Q2", LMAC, 0x23, 0),
674 	[10] = MT7996_HW_QUEUE("MDP TXIOC Q2", LMAC, 0x26, 0),
675 	[11] = MT7996_HW_QUEUE("MDP RXIOC Q2", LMAC, 0x27, 0),
676 	[15] = MT7996_HW_QUEUE("MDP RXIOC Q3", LMAC, 0x37, 0),
677 	[16] = MT7996_HW_QUEUE("HIF Q0", HIF, 0, 0),
678 	[17] = MT7996_HW_QUEUE("HIF Q1", HIF, 1, 0),
679 	[18] = MT7996_HW_QUEUE("HIF Q2", HIF, 2, 0),
680 	[19] = MT7996_HW_QUEUE("HIF Q3", HIF, 3, 0),
681 	[20] = MT7996_HW_QUEUE("HIF Q4", HIF, 4, 0),
682 	[21] = MT7996_HW_QUEUE("HIF Q5", HIF, 5, 0),
683 	[22] = MT7996_HW_QUEUE("HIF Q6", HIF, 6, 0),
684 	[23] = MT7996_HW_QUEUE("HIF Q7", HIF, 7, 0),
685 	[24] = MT7996_HW_QUEUE("HIF Q8", HIF, 8, 0),
686 	[25] = MT7996_HW_QUEUE("HIF Q9", HIF, 9, 0),
687 	[26] = MT7996_HW_QUEUE("HIF Q10", HIF, 10, 0),
688 	[27] = MT7996_HW_QUEUE("HIF Q11", HIF, 11, 0),
689 	[28] = MT7996_HW_QUEUE("HIF Q12", HIF, 12, 0),
690 	[29] = MT7996_HW_QUEUE("HIF Q13", HIF, 13, 0),
691 };
692 
693 static void
694 mt7996_fl_queue_show(struct seq_file *s, struct mt7996_dev *dev, bool pse,
695 		     const struct mt7996_hw_queue *q, u16 wlan_idx)
696 {
697 	static DEFINE_SPINLOCK(lock);
698 	u32 val, fid, num;
699 
700 	val = MT_FL_Q0_CTRL_EXECUTE |
701 	      u32_encode_bits(q->qid, MT_FL_Q0_CTRL_QID) |
702 	      u32_encode_bits(q->tgid, MT_FL_Q0_CTRL_TGID) |
703 	      u32_encode_bits(q->pid, MT_FL_Q0_CTRL_PID) |
704 	      u32_encode_bits(wlan_idx, MT_FL_Q0_CTRL_WLAN_IDX);
705 
706 	spin_lock(&lock);
707 	mt76_wr(dev, pse ? MT_PSE_FL_Q0_CTRL : MT_FL_Q0_CTRL, val);
708 	fid = mt76_rr(dev, pse ? MT_PSE_FL_Q2_CTRL : MT_FL_Q2_CTRL);
709 	num = mt76_rr(dev, pse ? MT_PSE_FL_Q3_CTRL : MT_FL_Q3_CTRL);
710 	spin_unlock(&lock);
711 
712 	seq_printf(s, "tail/head fid 0x%04x/0x%04x, packets %u",
713 		   u32_get_bits(fid, MT_FL_Q2_CTRL_TAIL_FID),
714 		   u32_get_bits(fid, MT_FL_Q2_CTRL_HEAD_FID),
715 		   u32_get_bits(num, MT_FL_Q3_CTRL_PKT_NUM));
716 }
717 
718 static void
719 mt7996_fl_queues_show(struct seq_file *s, struct mt7996_dev *dev, bool pse,
720 		      const struct mt7996_hw_queue *queues, unsigned long empty)
721 {
722 	unsigned int i;
723 
724 	for_each_clear_bit(i, &empty, 32) {
725 		if (!queues[i].name)
726 			continue;
727 
728 		seq_printf(s, "\t%s: ", queues[i].name);
729 		mt7996_fl_queue_show(s, dev, pse, &queues[i], 0);
730 		seq_puts(s, "\n");
731 	}
732 }
733 
734 static void
735 mt7996_freepg_show(struct seq_file *s, u32 cnt, u32 head_tail)
736 {
737 	seq_printf(s, "\tfree pages 0x%04x, free for all 0x%04x, head/tail 0x%04x/0x%04x\n",
738 		   u32_get_bits(cnt, MT_FREEPG_CNT_FREE),
739 		   u32_get_bits(cnt, MT_FREEPG_CNT_FFA),
740 		   u32_get_bits(head_tail, MT_FREEPG_HEAD_TAIL_HEAD),
741 		   u32_get_bits(head_tail, MT_FREEPG_HEAD_TAIL_TAIL));
742 }
743 
744 static void
745 mt7996_pg_group_show(struct seq_file *s, const char *name, u32 group, u32 info)
746 {
747 	seq_printf(s, "\t%s group: max/min quota 0x%04x/0x%04x, used/reserved 0x%04x/0x%04x\n",
748 		   name, u32_get_bits(group, MT_PG_GROUP_MAX_QUOTA),
749 		   u32_get_bits(group, MT_PG_GROUP_MIN_QUOTA),
750 		   u32_get_bits(info, MT_PG_INFO_SRC_CNT),
751 		   u32_get_bits(info, MT_PG_INFO_RSV_CNT));
752 }
753 
754 static unsigned int
755 mt7996_ple_sta_cr_num(struct mt7996_dev *dev)
756 {
757 	return is_mt7996(&dev->mt76) ? 34 : 17;
758 }
759 
760 static unsigned int
761 mt7996_ple_sta_cr(struct mt7996_dev *dev, unsigned int n)
762 {
763 	/* MT7992 and MT7990 keep the last station group in the
764 	 * extension register that follows the first 32 groups
765 	 */
766 	if (!is_mt7996(&dev->mt76) && n == mt7996_ple_sta_cr_num(dev) - 1)
767 		return 32;
768 
769 	return n;
770 }
771 
772 static void
773 mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
774 {
775 	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
776 	struct mt7996_vif *mvif = msta->vif;
777 	struct ieee80211_link_sta *link_sta;
778 	struct seq_file *s = data;
779 	struct ieee80211_vif *vif;
780 	unsigned int link_id;
781 
782 	vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
783 
784 	rcu_read_lock();
785 
786 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
787 		struct mt7996_sta_link *msta_link;
788 		struct mt76_vif_link *mlink;
789 		struct mt7996_phy *phy;
790 		unsigned int cr;
791 		u16 wlan_idx;
792 		u8 ac;
793 
794 		mlink = rcu_dereference(mvif->mt76.link[link_id]);
795 		if (!mlink)
796 			continue;
797 
798 		phy = mt7996_vif_link_phy(container_of(mlink,
799 						       struct mt7996_vif_link,
800 						       mt76));
801 		if (!phy)
802 			continue;
803 
804 		msta_link = mt7996_sta_link(msta, link_id);
805 		if (!msta_link)
806 			continue;
807 
808 		wlan_idx = msta_link->wcid.idx;
809 		cr = mt7996_ple_sta_cr(phy->dev, wlan_idx / 32);
810 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
811 			struct mt7996_hw_queue q = {
812 				.pid = MT_QUEUE_PORT_LMAC,
813 				.qid = ac,
814 			};
815 
816 			if (mt76_rr(phy->dev, MT_PLE_AC_QEMPTY(ac, cr)) &
817 			    BIT(wlan_idx % 32))
818 				continue;
819 
820 			seq_printf(s, "\tSTA %pM wcid %u AC%u%u: ", sta->addr,
821 				   wlan_idx, mlink->wmm_idx, ac);
822 			mt7996_fl_queue_show(s, phy->dev, false, &q, wlan_idx);
823 			seq_puts(s, "\n");
824 		}
825 	}
826 
827 	rcu_read_unlock();
828 }
829 
830 static int
831 mt7996_hw_queues_show(struct seq_file *s, void *data)
832 {
833 	struct mt7996_dev *dev = s->private;
834 
835 	seq_puts(s, "PLE page info:\n");
836 	mt7996_freepg_show(s, mt76_rr(dev, MT_PLE_FREEPG_CNT),
837 			   mt76_rr(dev, MT_PLE_FREEPG_HEAD_TAIL));
838 	mt7996_pg_group_show(s, "HIF", mt76_rr(dev, MT_PLE_PG_HIF_GROUP),
839 			     mt76_rr(dev, MT_PLE_HIF_PG_INFO));
840 
841 	seq_puts(s, "PLE non-empty queue info:\n");
842 	mt7996_fl_queues_show(s, dev, false, mt7996_ple_queues,
843 			      mt76_rr(dev, MT_FL_Q_EMPTY));
844 	ieee80211_iterate_stations_atomic(dev->mphy.hw,
845 					  mt7996_sta_hw_queue_read, s);
846 
847 	seq_puts(s, "PSE non-empty queue info:\n");
848 	mt7996_fl_queues_show(s, dev, true, mt7996_pse_queues,
849 			      mt76_rr(dev, MT_PSE_QUEUE_EMPTY));
850 	mt7996_fl_queues_show(s, dev, true, mt7996_pse_queues_1,
851 			      mt76_rr(dev, MT_PSE_QUEUE_EMPTY_1));
852 
853 	return 0;
854 }
855 
856 DEFINE_SHOW_ATTRIBUTE(mt7996_hw_queues);
857 
858 static int
859 mt7996_xmit_queues_show(struct seq_file *file, void *data)
860 {
861 	struct mt7996_dev *dev = file->private;
862 	struct mt7996_phy *phy;
863 	struct {
864 		struct mt76_queue *q;
865 		char *queue;
866 	} queue_map[] = {
867 		{ dev->mphy.q_tx[MT_TXQ_BE],	 "  MAIN0"  },
868 		{ NULL,				 "  MAIN1"  },
869 		{ NULL,				 "  MAIN2"  },
870 		{ dev->mt76.q_mcu[MT_MCUQ_WM],	 "  MCUWM"  },
871 		{ dev->mt76.q_mcu[MT_MCUQ_WA],	 "  MCUWA"  },
872 		{ dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWDL" },
873 	};
874 	int i;
875 
876 	phy = mt7996_phy2(dev);
877 	if (phy)
878 		queue_map[1].q = phy->mt76->q_tx[MT_TXQ_BE];
879 
880 	phy = mt7996_phy3(dev);
881 	if (phy)
882 		queue_map[2].q = phy->mt76->q_tx[MT_TXQ_BE];
883 
884 	seq_puts(file, "     queue | hw-queued |      head |      tail |\n");
885 	for (i = 0; i < ARRAY_SIZE(queue_map); i++) {
886 		struct mt76_queue *q = queue_map[i].q;
887 
888 		if (!q)
889 			continue;
890 
891 		seq_printf(file, "   %s | %9d | %9d | %9d |\n",
892 			   queue_map[i].queue, q->queued, q->head,
893 			   q->tail);
894 	}
895 
896 	return 0;
897 }
898 
899 DEFINE_SHOW_ATTRIBUTE(mt7996_xmit_queues);
900 
901 static int
902 mt7996_twt_stats(struct seq_file *s, void *data)
903 {
904 	struct mt7996_dev *dev = dev_get_drvdata(s->private);
905 	struct mt7996_twt_flow *iter;
906 
907 	rcu_read_lock();
908 
909 	seq_puts(s, "     wcid |       id |    flags |      exp | mantissa");
910 	seq_puts(s, " | duration |            tsf |\n");
911 	list_for_each_entry_rcu(iter, &dev->twt_list, list)
912 		seq_printf(s,
913 			   "%9d | %8d | %5c%c%c%c | %8d | %8d | %8d | %14lld |\n",
914 			   iter->wcid, iter->id,
915 			   iter->sched ? 's' : 'u',
916 			   iter->protection ? 'p' : '-',
917 			   iter->trigger ? 't' : '-',
918 			   iter->flowtype ? '-' : 'a',
919 			   iter->exp, iter->mantissa,
920 			   iter->duration, iter->tsf);
921 
922 	rcu_read_unlock();
923 
924 	return 0;
925 }
926 
927 /* The index of RF registers use the generic regidx, combined with two parts:
928  * WF selection [31:24] and offset [23:0].
929  */
930 static int
931 mt7996_rf_regval_get(void *data, u64 *val)
932 {
933 	struct mt7996_dev *dev = data;
934 	u32 regval;
935 	int ret;
936 
937 	ret = mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, &regval, false);
938 	if (ret)
939 		return ret;
940 
941 	*val = regval;
942 
943 	return 0;
944 }
945 
946 static int
947 mt7996_rf_regval_set(void *data, u64 val)
948 {
949 	struct mt7996_dev *dev = data;
950 	u32 val32 = val;
951 
952 	return mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, &val32, true);
953 }
954 
955 DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get,
956 			 mt7996_rf_regval_set, "0x%08llx\n");
957 
958 int mt7996_init_debugfs(struct mt7996_dev *dev)
959 {
960 	struct dentry *dir;
961 
962 	dir = mt76_register_debugfs_fops(&dev->mphy, NULL);
963 
964 	debugfs_create_file("hw-queues", 0400, dir, dev,
965 			    &mt7996_hw_queues_fops);
966 	debugfs_create_file("xmit-queues", 0400, dir, dev,
967 			    &mt7996_xmit_queues_fops);
968 	debugfs_create_file("tx_stats", 0400, dir, dev, &mt7996_tx_stats_fops);
969 	debugfs_create_file("sys_recovery", 0600, dir, dev,
970 			    &mt7996_sys_recovery_ops);
971 	debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
972 	debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
973 	debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
974 	/* TODO: wm fw cpu utilization */
975 	debugfs_create_file("fw_util_wa", 0400, dir, dev,
976 			    &mt7996_fw_util_wa_fops);
977 	debugfs_create_file("implicit_txbf", 0600, dir, dev,
978 			    &fops_implicit_txbf);
979 	debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
980 				    mt7996_twt_stats);
981 	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
982 
983 	debugfs_create_u32("dfs_hw_pattern", 0400, dir, &dev->hw_pattern);
984 	debugfs_create_file("radar_trigger", 0200, dir, dev,
985 			    &fops_radar_trigger);
986 	debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
987 				    mt7996_rdd_monitor);
988 
989 	dev->debugfs_dir = dir;
990 
991 	return 0;
992 }
993 
994 static void
995 mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
996 			   const void *data, int len)
997 {
998 	static DEFINE_SPINLOCK(lock);
999 	unsigned long flags;
1000 	void *dest;
1001 
1002 	spin_lock_irqsave(&lock, flags);
1003 	dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4);
1004 	if (dest) {
1005 		*(u32 *)dest = hdrlen + len;
1006 		dest += 4;
1007 
1008 		if (hdrlen) {
1009 			memcpy(dest, hdr, hdrlen);
1010 			dest += hdrlen;
1011 		}
1012 
1013 		memcpy(dest, data, len);
1014 		relay_flush(dev->relay_fwlog);
1015 	}
1016 	spin_unlock_irqrestore(&lock, flags);
1017 }
1018 
1019 void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len)
1020 {
1021 	struct {
1022 		__le32 magic;
1023 		u8 version;
1024 		u8 _rsv;
1025 		__le16 serial_id;
1026 		__le32 timestamp;
1027 		__le16 msg_type;
1028 		__le16 len;
1029 	} hdr = {
1030 		.version = 0x1,
1031 		.magic = cpu_to_le32(FW_BIN_LOG_MAGIC),
1032 		.msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR),
1033 	};
1034 
1035 	if (!dev->relay_fwlog)
1036 		return;
1037 
1038 	hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++);
1039 	hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0)));
1040 	hdr.len = *(__le16 *)data;
1041 	mt7996_debugfs_write_fwlog(dev, &hdr, sizeof(hdr), data, len);
1042 }
1043 
1044 bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
1045 {
1046 	if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC)
1047 		return false;
1048 
1049 	if (dev->relay_fwlog)
1050 		mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
1051 
1052 	return true;
1053 }
1054 
1055 #ifdef CONFIG_MAC80211_DEBUGFS
1056 /** per-station debugfs **/
1057 
1058 static int
1059 mt7996_queues_show(struct seq_file *s, void *data)
1060 {
1061 	struct ieee80211_sta *sta = s->private;
1062 
1063 	mt7996_sta_hw_queue_read(s, sta);
1064 
1065 	return 0;
1066 }
1067 
1068 DEFINE_SHOW_ATTRIBUTE(mt7996_queues);
1069 
1070 void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1071 			    struct ieee80211_sta *sta, struct dentry *dir)
1072 {
1073 	debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops);
1074 }
1075 
1076 static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file,
1077 					      const char __user *user_buf,
1078 					      size_t count, loff_t *ppos)
1079 {
1080 #define SHORT_PREAMBLE 0
1081 #define LONG_PREAMBLE 1
1082 	struct ieee80211_link_sta *link_sta = file->private_data;
1083 	struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
1084 	struct mt7996_phy *link_phy = mt7996_vif_link_phy(&msta->vif->deflink);
1085 	struct mt7996_sta_link *msta_link;
1086 	struct ra_rate phy = {};
1087 	struct mt7996_dev *dev;
1088 	char buf[100];
1089 	int ret;
1090 	u16 gi, ltf;
1091 
1092 	if (!link_phy)
1093 		return -EINVAL;
1094 
1095 	if (count >= sizeof(buf))
1096 		return -EINVAL;
1097 
1098 	if (copy_from_user(buf, user_buf, count))
1099 		return -EFAULT;
1100 
1101 	if (count && buf[count - 1] == '\n')
1102 		buf[count - 1] = '\0';
1103 	else
1104 		buf[count] = '\0';
1105 
1106 	/* mode - cck: 0, ofdm: 1, ht: 2, gf: 3, vht: 4, he_su: 8, he_er: 9 EHT: 15
1107 	 * bw - bw20: 0, bw40: 1, bw80: 2, bw160: 3, BW320: 4
1108 	 * mcs - cck: 0~4, ofdm: 0~7, ht: 0~32, vht: 0~9, he_su: 0~11, he_er: 0~2, eht: 0~13
1109 	 * nss - vht: 1~4, he: 1~4, eht: 1~4, others: ignore
1110 	 * gi - (ht/vht) lgi: 0, sgi: 1; (he) 0.8us: 0, 1.6us: 1, 3.2us: 2
1111 	 * preamble - short: 1, long: 0
1112 	 * stbc - off: 0, on: 1
1113 	 * ldpc - off: 0, on: 1
1114 	 * spe - off: 0, on: 1
1115 	 * ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2
1116 	 */
1117 	dev = link_phy->dev;
1118 	if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu %hhu %hhu %hu",
1119 		   &phy.mode, &phy.bw, &phy.mcs, &phy.nss, &gi,
1120 		   &phy.preamble, &phy.stbc, &phy.ldpc, &phy.spe, &ltf) != 10) {
1121 		dev_warn(dev->mt76.dev,
1122 			 "format: Mode BW MCS NSS GI Preamble STBC LDPC SPE ltf\n");
1123 		return -EINVAL;
1124 	}
1125 
1126 	mutex_lock(&dev->mt76.mutex);
1127 
1128 	msta_link = mt7996_sta_link_protected(dev, msta, link_sta->link_id);
1129 	if (!msta_link) {
1130 		ret = -EINVAL;
1131 		goto out;
1132 	}
1133 	phy.wlan_idx = cpu_to_le16(msta_link->wcid.idx);
1134 	phy.gi = cpu_to_le16(gi);
1135 	phy.ltf = cpu_to_le16(ltf);
1136 	phy.ldpc = phy.ldpc ? 7 : 0;
1137 	phy.preamble = phy.preamble ? SHORT_PREAMBLE : LONG_PREAMBLE;
1138 
1139 	ret = mt7996_mcu_set_fixed_rate_ctrl(dev, &phy, 0);
1140 	if (ret)
1141 		goto out;
1142 
1143 	ret = count;
1144 out:
1145 	mutex_unlock(&dev->mt76.mutex);
1146 	return ret;
1147 }
1148 
1149 static const struct file_operations fops_fixed_rate = {
1150 	.write = mt7996_link_sta_fixed_rate_set,
1151 	.open = simple_open,
1152 	.owner = THIS_MODULE,
1153 	.llseek = default_llseek,
1154 };
1155 
1156 void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1157 				 struct ieee80211_link_sta *link_sta,
1158 				 struct dentry *dir)
1159 {
1160 	debugfs_create_file("fixed_rate", 0600, dir, link_sta, &fops_fixed_rate);
1161 }
1162 
1163 #endif
1164