Linux wireless drivers development
 help / color / mirror / Atom feed
From: Eason Lai <eason.lai@mediatek.com>
To: <nbd@nbd.name>, <lorenzo@kernel.org>
Cc: <linux-wireless@vger.kernel.org>,
	<linux-mediatek@lists.infradead.org>, <kun.wu@mediatek.com>,
	<deren.wu@mediatek.com>, <sean.wang@mediatek.com>,
	<quan.zhou@mediatek.com>, <ryder.lee@mediatek.com>,
	<leon.yen@mediatek.com>, <litien.chang@mediatek.com>,
	<jb.tsai@mediatek.com>, <eason.lai@mediatek.com>,
	yin-chun.tsai <yin-chun.tsai@mediatek.com>
Subject: [PATCH] wifi: mt76: mt7925/mt7928: add debug cmds to check TX/RX status
Date: Wed, 14 Oct 2026 13:26:40 +0800	[thread overview]
Message-ID: <20261014052641.1609444-1-eason.lai@mediatek.com> (raw)

From: "yin-chun.tsai" <yin-chun.tsai@mediatek.com>

Add some debug cmd including, WTBL, DMASHDL, PLE, and PSE info
for checking TX/RX path status.

Signed-off-by: yin-chun.tsai <yin-chun.tsai@mediatek.com>
---
 .../wireless/mediatek/mt76/mt7925/debugfs.c   | 531 ++++++++++++++++++
 .../net/wireless/mediatek/mt76/mt7925/regs.h  |  15 +
 drivers/net/wireless/mediatek/mt76/mt792x.h   |   1 +
 .../net/wireless/mediatek/mt76/mt792x_regs.h  |  70 +++
 4 files changed, 617 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index d01ff49de47a..442275dcc128 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -2,6 +2,7 @@
 /* Copyright (C) 2023 MediaTek Inc. */
 
 #include "mt7925.h"
+#include "mac.h"
 #include "mcu.h"
 
 static int
@@ -286,6 +287,524 @@ static int mt7925_chip_reset(void *data, u64 val)
 
 DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
 
+/* WTBL dump
+ *
+ * Usage:
+ *   echo <idx> > /sys/kernel/debug/ieee80211/phy1/mt76/wtbl_idx
+ *   cat  /sys/kernel/debug/ieee80211/phy1/mt76/get_wtbl
+ *
+ * LMAC WTBL has 36 dwords per entry (connac3x).
+ */
+#define MT792x_WTBL_LMAC_DWORDS	36
+
+static int
+mt7925_wtbl_idx_set(void *data, u64 val)
+{
+	struct mt792x_dev *dev = data;
+
+	dev->wtbl_dump_idx = (u32)val;
+
+	return 0;
+}
+
+static int
+mt7925_wtbl_idx_get(void *data, u64 *val)
+{
+	struct mt792x_dev *dev = data;
+
+	*val = dev->wtbl_dump_idx;
+
+	return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_wtbl_idx, mt7925_wtbl_idx_get,
+			 mt7925_wtbl_idx_set, "%llu\n");
+
+static int
+mt7925_wtbl_read(struct seq_file *s, void *data)
+{
+	struct mt792x_dev *dev = dev_get_drvdata(s->private);
+	u32 idx = dev->wtbl_dump_idx;
+	u32 wdu_cr, wducr_val, base;
+	u32 dw0, dw1;
+	int i;
+
+	if (!mt76_is_mmio(&dev->mt76)) {
+		seq_puts(s, "WTBL direct dump not supported on this interface\n");
+		return 0;
+	}
+
+	if (idx >= MT792x_WTBL_SIZE) {
+		seq_printf(s, "Invalid WTBL idx %u (max %u)\n",
+			   idx, MT792x_WTBL_SIZE - 1);
+		return 0;
+	}
+
+	mt792x_mutex_acquire(dev);
+
+	/* mt7925_mac_wtbl_lmac_addr() writes WDUCR group selection and
+	 * returns the mapped register address for DW 0 of this WCID.
+	 */
+	base = mt7925_mac_wtbl_lmac_addr(dev, idx, 0);
+	wdu_cr = is_mt7928(&dev->mt76) ? MT7928_WTBLON_TOP_WDUCR :
+					  MT7925_WTBLON_TOP_WDUCR;
+	wducr_val = mt76_rr(dev, wdu_cr);
+
+	/* DW0[15:0] + DW1[31:0] encode the link address */
+	dw0 = mt76_rr(dev, base);
+	dw1 = mt76_rr(dev, base + 4);
+
+	seq_printf(s, "Dump WTBL info of WLAN_IDX: %u\n", idx);
+	seq_printf(s, "LMAC WTBL Addr: group:0x%08x=0x%08x addr: 0x%08x\n",
+		   wdu_cr, wducr_val, base);
+	seq_printf(s, "LinkAddr: %02x:%02x:%02x:%02x:%02x:%02x\n",
+		   dw1 & 0xff, (dw1 >> 8) & 0xff,
+		   (dw1 >> 16) & 0xff, (dw1 >> 24) & 0xff,
+		   dw0 & 0xff, (dw0 >> 8) & 0xff);
+
+	seq_puts(s, "\nLMAC WTBL raw data:\n");
+	for (i = 0; i < MT792x_WTBL_LMAC_DWORDS; i++)
+		seq_printf(s, "DW%02d: 0x%08x\n", i,
+			   mt76_rr(dev, base + i * 4));
+
+	mt792x_mutex_release(dev);
+
+	return 0;
+}
+
+/* mt7925_dmashdl_read: standard DMASHDL (MT7925/MT7927 only).
+ * MT7928 uses DMASHDL Lite; see mt7928_dmashdl_lite_read below.
+ */
+static int
+mt7925_dmashdl_read(struct seq_file *s, void *data)
+{
+	struct mt792x_dev *dev = dev_get_drvdata(s->private);
+	u32 total_src = 0, total_rsv = 0;
+	u32 ffa_cnt, free_pg_cnt;
+	u32 ple_rsv, ple_src;
+	u32 val;
+	int i;
+
+	if (!mt76_is_mmio(&dev->mt76)) {
+		seq_puts(s, "DMASHDL dump not supported on this interface\n");
+		return 0;
+	}
+
+	mt792x_mutex_acquire(dev);
+
+	seq_puts(s, "DMASHDL Info:\n");
+
+	val = mt76_rr(dev, MT_DMASHDL_REFILL);
+	seq_printf(s, "\tRefill control(0x%08x): 0x%08x\n",
+		   MT_DMASHDL_REFILL, val);
+
+	val = mt76_rr(dev, MT_DMASHDL_PKT_MAX_SIZE);
+	seq_printf(s, "\tPkt max size(0x%08x): 0x%08x\n",
+		   MT_DMASHDL_PKT_MAX_SIZE, val);
+	seq_printf(s, "\t\tPLE pkt max=0x%03lx, PSE pkt max=0x%03lx\n",
+		   FIELD_GET(MT_DMASHDL_PKT_MAX_SIZE_PLE, val),
+		   FIELD_GET(MT_DMASHDL_PKT_MAX_SIZE_PSE, val));
+
+	val = mt76_rr(dev, MT_DMASHDL_ERROR_FLAG_CTRL);
+	seq_printf(s, "\tError flag ctrl(0x%08x): 0x%08x\n",
+		   MT_DMASHDL_ERROR_FLAG_CTRL, val);
+
+	for (i = 0; i < 15; i++) {
+		u32 quota = mt76_rr(dev, MT_DMASHDL_GROUP_QUOTA(i));
+		u32 status = mt76_rr(dev, MT_DMASHDL_STATUS_RD_GP(i));
+		u32 rsv = FIELD_GET(MT_DMASHDL_STATUS_RD_GP_RSV, status);
+		u32 src = FIELD_GET(MT_DMASHDL_STATUS_RD_GP_SRC, status);
+
+		seq_printf(s, "\tGroup %2d: quota(min/max)=0x%03lx/0x%03lx, used/rsv=0x%03x/0x%03x\n",
+			   i,
+			   FIELD_GET(MT_DMASHDL_GROUP_QUOTA_MIN, quota),
+			   FIELD_GET(MT_DMASHDL_GROUP_QUOTA_MAX, quota),
+			   src, rsv);
+		total_src += src;
+		total_rsv += rsv;
+	}
+
+	val = mt76_rr(dev, MT_DMASHDL_STATUS_RD);
+	free_pg_cnt = FIELD_GET(MT_DMASHDL_STATUS_RD_FREE_PG, val);
+	ffa_cnt = FIELD_GET(MT_DMASHDL_STATUS_RD_FFA, val);
+	seq_printf(s, "\tStatus_RD(0x%08x): 0x%08x\n",
+		   MT_DMASHDL_STATUS_RD, val);
+	seq_printf(s, "\t\tfree page cnt=0x%03x, ffa cnt=0x%03x\n",
+		   free_pg_cnt, ffa_cnt);
+
+	/* Counter mismatch check against PLE HIF page counters */
+	seq_puts(s, "\nDMASHDL Counter Check:\n");
+	val = mt76_rr(dev, MT_PLE_HIF_PG_INFO);
+	ple_rsv = FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val);
+	ple_src = FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val);
+	seq_printf(s, "\tPLE: used/rsv pages of HIF group=0x%03x/0x%03x\n",
+		   ple_src, ple_rsv);
+	seq_printf(s, "\tDMASHDL: total used pages of group0~14=0x%03x\n",
+		   total_src);
+	if (ple_src != total_src)
+		seq_puts(s, "\t[MISMATCH] PLE used pages != DMASHDL total used\n");
+	seq_printf(s, "\tDMASHDL: total rsv pages of group0~14=0x%03x\n",
+		   total_rsv);
+	seq_printf(s, "\tDMASHDL: total ffa pages=0x%03x, total free pages=0x%03x\n",
+		   ffa_cnt, free_pg_cnt);
+	if (free_pg_cnt != total_rsv + ffa_cnt)
+		seq_puts(s, "\t[MISMATCH] free pages != rsv + ffa\n");
+	if (free_pg_cnt != ple_rsv)
+		seq_puts(s, "\t[MISMATCH] free pages != PLE rsv pages\n");
+	if (ple_src == total_src && free_pg_cnt == total_rsv + ffa_cnt &&
+	    free_pg_cnt == ple_rsv)
+		seq_puts(s, "\tDMASHDL: no counter mismatch\n");
+
+	mt792x_mutex_release(dev);
+	return 0;
+}
+
+/* mt7928_dmashdl_lite_read: DMASHDL Lite (MT7928 only, base 0x20026200).
+ * Unlike full DMASHDL, Lite splits runtime state into separate registers:
+ *   GROUP_CONTROL (+0x100): min/max quota (config)
+ *   GROUP_SRC_CNT (+0x200): pages sourced (in-use) per group (runtime)
+ *   GROUP_ACK_CNT (+0x300): pages acknowledged (returned) per group (runtime)
+ *   GROUP_VALID   (+0x400): bitmap of groups with active page allocations
+ */
+static int
+mt7928_dmashdl_lite_read(struct seq_file *s, void *data)
+{
+	struct mt792x_dev *dev = dev_get_drvdata(s->private);
+	u32 disable0, disable1, valid0, valid1;
+	u32 total_src = 0, total_ack = 0;
+	u32 ple_rsv, ple_src;
+	u32 val;
+	int i;
+
+	if (!mt76_is_mmio(&dev->mt76)) {
+		seq_puts(s, "DMASHDL Lite dump not supported on this interface\n");
+		return 0;
+	}
+
+	mt792x_mutex_acquire(dev);
+
+	seq_puts(s, "DMASHDL Lite Info (MT7928):\n");
+
+	val = mt76_rr(dev, MT_DMASHDL_LITE_MAIN_CONTROL);
+	seq_printf(s, "\tMain control(0x%08x): 0x%08x\n",
+		   MT_DMASHDL_LITE_MAIN_CONTROL, val);
+
+	val = mt76_rr(dev, MT_DMASHDL_LITE_PAGE_SIZE);
+	seq_printf(s, "\tPage size(0x%08x): 0x%08x\n",
+		   MT_DMASHDL_LITE_PAGE_SIZE, val);
+	seq_printf(s, "\t\tPSE page size=0x%04lx, PLE page size=0x%04lx\n",
+		   FIELD_GET(MT_DMASHDL_LITE_PSE_PAGE_SIZE_MASK, val),
+		   FIELD_GET(MT_DMASHDL_LITE_PLE_PAGE_SIZE_MASK, val));
+
+	val = mt76_rr(dev, MT_DMASHDL_LITE_PKT_MAX_SIZE);
+	seq_printf(s, "\tPkt max size(0x%08x): 0x%08x\n",
+		   MT_DMASHDL_LITE_PKT_MAX_SIZE, val);
+	seq_printf(s, "\t\tPSE pkt max=0x%04lx, PLE pkt max=0x%04lx\n",
+		   FIELD_GET(MT_DMASHDL_LITE_PSE_PKT_MAX_SIZE_MASK, val),
+		   FIELD_GET(MT_DMASHDL_LITE_PLE_PKT_MAX_SIZE_MASK, val));
+
+	val = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_SN_CHK0);
+	seq_printf(s, "\tGroup SN check[31:0](0x%08x):  0x%08x\n",
+		   MT_DMASHDL_LITE_GROUP_SN_CHK0, val);
+	val = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_SN_CHK1);
+	seq_printf(s, "\tGroup SN check[63:32](0x%08x): 0x%08x\n",
+		   MT_DMASHDL_LITE_GROUP_SN_CHK1, val);
+	val = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_UDF_CHK0);
+	seq_printf(s, "\tGroup UDF check[31:0](0x%08x):  0x%08x\n",
+		   MT_DMASHDL_LITE_GROUP_UDF_CHK0, val);
+	val = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_UDF_CHK1);
+	seq_printf(s, "\tGroup UDF check[63:32](0x%08x): 0x%08x\n",
+		   MT_DMASHDL_LITE_GROUP_UDF_CHK1, val);
+
+	disable0 = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_DISABLE0);
+	seq_printf(s, "\tGroup disable[31:0](0x%08x):  0x%08x\n",
+		   MT_DMASHDL_LITE_GROUP_DISABLE0, disable0);
+	disable1 = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_DISABLE1);
+	seq_printf(s, "\tGroup disable[63:32](0x%08x): 0x%08x\n",
+		   MT_DMASHDL_LITE_GROUP_DISABLE1, disable1);
+
+	valid0 = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_VALID_0_31);
+	seq_printf(s, "\tGroup valid[31:0](0x%08x):  0x%08x\n",
+		   MT_DMASHDL_LITE_GROUP_VALID_0_31, valid0);
+	valid1 = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_VALID_32_63);
+	seq_printf(s, "\tGroup valid[63:32](0x%08x): 0x%08x\n",
+		   MT_DMASHDL_LITE_GROUP_VALID_32_63, valid1);
+
+	seq_puts(s, "\tQ mapping (q2group):\n");
+	for (i = 0; i < 8; i++) {
+		val = mt76_rr(dev, MT_DMASHDL_LITE_Q_MAP(i));
+		seq_printf(s, "\t\tQ_MAP[%d](0x%08x): 0x%08x\n",
+			   i, MT_DMASHDL_LITE_Q_MAP(i), val);
+	}
+
+	seq_puts(s, "\tGroup status (skipping disabled+zero quota):\n");
+	for (i = 0; i < DMASHDL_LITE_GROUP_NUM; i++) {
+		u32 disable = i < 32 ? disable0 : disable1;
+		bool enabled = !(disable & BIT(i % 32));
+		u32 quota, min_q, max_q, src, ack_reg;
+		u32 pktin, add_ret, ret;
+
+		quota = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_QUOTA(i));
+		if (!enabled && !quota)
+			continue;
+
+		min_q = FIELD_GET(MT_DMASHDL_LITE_GROUP_MIN_QUOTA_MASK, quota);
+		max_q = FIELD_GET(MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK, quota);
+		src     = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_SRC_CNT(i));
+		src     = FIELD_GET(MT_DMASHDL_LITE_GROUP_SRC_CNT_MASK, src);
+		ack_reg = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_ACK_CNT(i));
+		pktin   = FIELD_GET(MT_DMASHDL_LITE_ACK_PKTIN_CNT,   ack_reg);
+		add_ret = FIELD_GET(MT_DMASHDL_LITE_ACK_ADD_RET_CNT, ack_reg);
+		ret     = FIELD_GET(MT_DMASHDL_LITE_ACK_RET_CNT,     ack_reg);
+
+		seq_printf(s, "\tGroup %2d: quota(min/max)=0x%03x/0x%03x, src=0x%02x, ack(add_ret/pktin/ret)=0x%02x/0x%02x/0x%02x%s\n",
+			   i, min_q, max_q, src, add_ret, pktin, ret,
+			   enabled ? "" : " [disabled]");
+		if (enabled) {
+			total_src += src;
+			total_ack += ret;
+		}
+	}
+	seq_printf(s, "\tTotal (enabled groups): src=0x%03x, ack_ret=0x%03x\n",
+		   total_src, total_ack);
+
+	/* Counter mismatch check against PLE HIF page counters */
+	seq_puts(s, "\nDMASHDL Lite Counter Check:\n");
+	val = mt76_rr(dev, MT_PLE_HIF_PG_INFO);
+	ple_rsv = FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val);
+	ple_src = FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val);
+	seq_printf(s, "\tPLE: used/rsv pages of HIF group=0x%03x/0x%03x\n",
+		   ple_src, ple_rsv);
+	seq_printf(s, "\tDMASHDL Lite: total src=0x%03x, total ack_ret=0x%03x\n",
+		   total_src, total_ack);
+	if (ple_src != total_src)
+		seq_puts(s, "\t[MISMATCH] PLE used pages != DMASHDL Lite total src\n");
+	else
+		seq_puts(s, "\tDMASHDL Lite: no counter mismatch\n");
+
+	seq_puts(s, "\nPLE Page Counters:\n");
+
+	val = mt76_rr(dev, MT_PLE_FREEPG_CNT);
+	seq_printf(s, "\tPLE free pages(0x%08x): total=0x%03lx, ffa=0x%03lx\n",
+		   MT_PLE_FREEPG_CNT,
+		   FIELD_GET(MT_WF_FREEPG_FREE_CNT, val),
+		   FIELD_GET(MT_WF_FREEPG_FFA_CNT, val));
+
+	val = mt76_rr(dev, MT_PLE_HIF_WMTXD_PG_INFO);
+	seq_printf(s, "\tPLE WMTXD group(0x%08x):     used/rsv=0x%03lx/0x%03lx\n",
+		   MT_PLE_HIF_WMTXD_PG_INFO,
+		   FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val),
+		   FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val));
+
+	val = mt76_rr(dev, MT_PLE_HIF_TXCMD_PG_INFO);
+	seq_printf(s, "\tPLE HIF_TXCMD group(0x%08x): used/rsv=0x%03lx/0x%03lx\n",
+		   MT_PLE_HIF_TXCMD_PG_INFO,
+		   FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val),
+		   FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val));
+
+	val = mt76_rr(dev, MT_PLE_CPU_PG_INFO);
+	seq_printf(s, "\tPLE CPU group(0x%08x):       used/rsv=0x%03lx/0x%03lx\n",
+		   MT_PLE_CPU_PG_INFO,
+		   FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val),
+		   FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val));
+
+	mt792x_mutex_release(dev);
+	return 0;
+}
+
+static int
+mt7925_ple_read(struct seq_file *s, void *data)
+{
+	struct mt792x_dev *dev = dev_get_drvdata(s->private);
+	u32 pbuf_ctrl, val;
+	u32 fpg_cnt, ffa_cnt, fpg_head, fpg_tail;
+	u32 max_q, min_q, rsv_pg, used_pg;
+
+	if (!mt76_is_mmio(&dev->mt76)) {
+		seq_puts(s, "PLE dump not supported on this interface\n");
+		return 0;
+	}
+
+	mt792x_mutex_acquire(dev);
+
+	pbuf_ctrl = mt76_rr(dev, MT_PLE_PBUF_CTRL);
+	seq_puts(s, "PLE Configuration Info:\n");
+	seq_printf(s, "\tPacket Buffer Control(0x%08x): 0x%08x\n",
+		   MT_PLE_PBUF_CTRL, pbuf_ctrl);
+	seq_printf(s, "\t\tPage Size=%d(%d bytes/page), Offset=%d, Total=%d pages\n",
+		   (int)FIELD_GET(MT_WF_PBUF_PAGE_SIZE, pbuf_ctrl),
+		   FIELD_GET(MT_WF_PBUF_PAGE_SIZE, pbuf_ctrl) ? 128 : 64,
+		   (int)FIELD_GET(MT_WF_PBUF_PBUF_OFFSET, pbuf_ctrl),
+		   (int)FIELD_GET(MT_WF_PBUF_TOTAL_PAGES, pbuf_ctrl));
+
+	val = mt76_rr(dev, MT_PLE_INT_N9_ERR_STS);
+	seq_printf(s, "\tINT_N9_ERR_STS(0x%08x): 0x%08x\n",
+		   MT_PLE_INT_N9_ERR_STS, val);
+	val = mt76_rr(dev, MT_PLE_INT_N9_ERR_STS_1);
+	seq_printf(s, "\tINT_N9_ERR_STS_1(0x%08x): 0x%08x\n",
+		   MT_PLE_INT_N9_ERR_STS_1, val);
+
+	seq_puts(s, "PLE Page Flow Control:\n");
+
+	val = mt76_rr(dev, MT_PLE_FREEPG_CNT);
+	fpg_cnt = FIELD_GET(MT_WF_FREEPG_FREE_CNT, val);
+	ffa_cnt = FIELD_GET(MT_WF_FREEPG_FFA_CNT, val);
+	seq_printf(s, "\tFree page counter(0x%08x): 0x%08x\n",
+		   MT_PLE_FREEPG_CNT, val);
+	seq_printf(s, "\t\ttotal free=0x%03x, ffa=0x%03x\n", fpg_cnt, ffa_cnt);
+
+	val = mt76_rr(dev, MT_PLE_FREEPG_HEAD_TAIL);
+	fpg_head = FIELD_GET(MT_WF_FREEPG_HEAD, val);
+	fpg_tail = FIELD_GET(MT_WF_FREEPG_TAIL, val);
+	seq_printf(s, "\tFree page head/tail(0x%08x): 0x%08x\n",
+		   MT_PLE_FREEPG_HEAD_TAIL, val);
+	seq_printf(s, "\t\ttail/head=0x%03x/0x%03x\n", fpg_tail, fpg_head);
+
+	/* HIF group */
+	val = mt76_rr(dev, MT_PLE_PG_HIF_GROUP);
+	max_q = FIELD_GET(MT_WF_GROUP_QUOTA_MAX, val);
+	min_q = FIELD_GET(MT_WF_GROUP_QUOTA_MIN, val);
+	seq_printf(s, "\tHIF group quota(0x%08x): 0x%08x (max/min=0x%03x/0x%03x)\n",
+		   MT_PLE_PG_HIF_GROUP, val, max_q, min_q);
+	val = mt76_rr(dev, MT_PLE_HIF_PG_INFO);
+	used_pg = FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val);
+	rsv_pg  = FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val);
+	seq_printf(s, "\tHIF group status(0x%08x): 0x%08x (used/rsv=0x%03x/0x%03x)\n",
+		   MT_PLE_HIF_PG_INFO, val, used_pg, rsv_pg);
+
+	/* WMTXD group */
+	val = mt76_rr(dev, MT_PLE_PG_HIF_WMTXD_GROUP);
+	max_q = FIELD_GET(MT_WF_GROUP_QUOTA_MAX, val);
+	min_q = FIELD_GET(MT_WF_GROUP_QUOTA_MIN, val);
+	seq_printf(s, "\tWMTXD group quota(0x%08x): 0x%08x (max/min=0x%03x/0x%03x)\n",
+		   MT_PLE_PG_HIF_WMTXD_GROUP, val, max_q, min_q);
+	val = mt76_rr(dev, MT_PLE_HIF_WMTXD_PG_INFO);
+	used_pg = FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val);
+	rsv_pg  = FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val);
+	seq_printf(s, "\tWMTXD group status(0x%08x): 0x%08x (used/rsv=0x%03x/0x%03x)\n",
+		   MT_PLE_HIF_WMTXD_PG_INFO, val, used_pg, rsv_pg);
+
+	/* HIF_TXCMD group */
+	val = mt76_rr(dev, MT_PLE_PG_HIF_TXCMD_GROUP);
+	max_q = FIELD_GET(MT_WF_GROUP_QUOTA_MAX, val);
+	min_q = FIELD_GET(MT_WF_GROUP_QUOTA_MIN, val);
+	seq_printf(s, "\tHIF_TXCMD group quota(0x%08x): 0x%08x (max/min=0x%03x/0x%03x)\n",
+		   MT_PLE_PG_HIF_TXCMD_GROUP, val, max_q, min_q);
+	val = mt76_rr(dev, MT_PLE_HIF_TXCMD_PG_INFO);
+	used_pg = FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val);
+	rsv_pg  = FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val);
+	seq_printf(s, "\tHIF_TXCMD group status(0x%08x): 0x%08x (used/rsv=0x%03x/0x%03x)\n",
+		   MT_PLE_HIF_TXCMD_PG_INFO, val, used_pg, rsv_pg);
+
+	/* CPU group */
+	val = mt76_rr(dev, MT_PLE_PG_CPU_GROUP);
+	max_q = FIELD_GET(MT_WF_GROUP_QUOTA_MAX, val);
+	min_q = FIELD_GET(MT_WF_GROUP_QUOTA_MIN, val);
+	seq_printf(s, "\tCPU group quota(0x%08x): 0x%08x (max/min=0x%03x/0x%03x)\n",
+		   MT_PLE_PG_CPU_GROUP, val, max_q, min_q);
+	val = mt76_rr(dev, MT_PLE_CPU_PG_INFO);
+	used_pg = FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val);
+	rsv_pg  = FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val);
+	seq_printf(s, "\tCPU group status(0x%08x): 0x%08x (used/rsv=0x%03x/0x%03x)\n",
+		   MT_PLE_CPU_PG_INFO, val, used_pg, rsv_pg);
+
+	val = mt76_rr(dev, MT_PLE_QUEUE_EMPTY);
+	seq_printf(s, "\tQueue empty(0x%08x): 0x%08x%s\n",
+		   MT_PLE_QUEUE_EMPTY, val,
+		   (val & MT_PLE_QUEUE_EMPTY_ALL_AC) ? " [ALL AC EMPTY]" : "");
+
+	mt792x_mutex_release(dev);
+	return 0;
+}
+
+static int
+mt7925_pse_read(struct seq_file *s, void *data)
+{
+	struct mt792x_dev *dev = dev_get_drvdata(s->private);
+	u32 pbuf_ctrl, val;
+	u32 fpg_cnt, ffa_cnt, fpg_head, fpg_tail;
+	u32 max_q, min_q, rsv_pg, used_pg;
+	static const struct {
+		const char *name;
+		u32 qreg, ireg;
+	} pse_groups[] = {
+		{ "HIF0",  MT_PSE_PG_HIF0_GROUP,  MT_PSE_HIF0_PG_INFO },
+		{ "HIF1",  MT_PSE_PG_HIF1_GROUP,  MT_PSE_HIF1_PG_INFO },
+		{ "HIF2",  MT_PSE_PG_HIF2_GROUP,  MT_PSE_HIF2_PG_INFO },
+		{ "CPU",   MT_PSE_PG_CPU_GROUP,   MT_PSE_CPU_PG_INFO },
+		{ "PLE",   MT_PSE_PG_PLE_GROUP,   MT_PSE_PLE_PG_INFO },
+		{ "LMAC0", MT_PSE_PG_LMAC0_GROUP, MT_PSE_LMAC0_PG_INFO },
+		{ "LMAC1", MT_PSE_PG_LMAC1_GROUP, MT_PSE_LMAC1_PG_INFO },
+		{ "LMAC2", MT_PSE_PG_LMAC2_GROUP, MT_PSE_LMAC2_PG_INFO },
+		{ "LMAC3", MT_PSE_PG_LMAC3_GROUP, MT_PSE_LMAC3_PG_INFO },
+		{ "MDP",   MT_PSE_PG_MDP_GROUP,   MT_PSE_MDP_PG_INFO },
+	};
+	int i;
+
+	if (!mt76_is_mmio(&dev->mt76)) {
+		seq_puts(s, "PSE dump not supported on this interface\n");
+		return 0;
+	}
+
+	mt792x_mutex_acquire(dev);
+
+	pbuf_ctrl = mt76_rr(dev, MT_PSE_PBUF_CTRL);
+	seq_puts(s, "PSE Configuration Info:\n");
+	seq_printf(s, "\tPacket Buffer Control(0x%08x): 0x%08x\n",
+		   MT_PSE_PBUF_CTRL, pbuf_ctrl);
+	seq_printf(s, "\t\tPage Size=%d(%d bytes/page), Offset=%d, Total=%d pages\n",
+		   (int)FIELD_GET(MT_WF_PBUF_PAGE_SIZE, pbuf_ctrl),
+		   FIELD_GET(MT_WF_PBUF_PAGE_SIZE, pbuf_ctrl) ? 256 : 128,
+		   (int)FIELD_GET(MT_WF_PBUF_PBUF_OFFSET, pbuf_ctrl),
+		   (int)FIELD_GET(MT_WF_PBUF_TOTAL_PAGES, pbuf_ctrl));
+
+	val = mt76_rr(dev, MT_PSE_INT_N9_ERR_STS);
+	seq_printf(s, "\tINT_N9_ERR_STS(0x%08x): 0x%08x\n",
+		   MT_PSE_INT_N9_ERR_STS, val);
+	val = mt76_rr(dev, MT_PSE_INT_N9_ERR1_STS);
+	seq_printf(s, "\tINT_N9_ERR1_STS(0x%08x): 0x%08x\n",
+		   MT_PSE_INT_N9_ERR1_STS, val);
+
+	val = mt76_rr(dev, MT_PSE_QUEUE_EMPTY);
+	seq_printf(s, "\tQueue empty(0x%08x): 0x%08x\n",
+		   MT_PSE_QUEUE_EMPTY, val);
+
+	seq_puts(s, "PSE Page Flow Control:\n");
+
+	val = mt76_rr(dev, MT_PSE_FREEPG_CNT);
+	fpg_cnt = FIELD_GET(MT_WF_FREEPG_FREE_CNT, val);
+	ffa_cnt = FIELD_GET(MT_WF_FREEPG_FFA_CNT, val);
+	seq_printf(s, "\tFree page counter(0x%08x): 0x%08x\n",
+		   MT_PSE_FREEPG_CNT, val);
+	seq_printf(s, "\t\ttotal free=0x%03x, ffa=0x%03x\n", fpg_cnt, ffa_cnt);
+
+	val = mt76_rr(dev, MT_PSE_FREEPG_HEAD_TAIL);
+	fpg_head = FIELD_GET(MT_WF_FREEPG_HEAD, val);
+	fpg_tail = FIELD_GET(MT_WF_FREEPG_TAIL, val);
+	seq_printf(s, "\tFree page head/tail(0x%08x): 0x%08x\n",
+		   MT_PSE_FREEPG_HEAD_TAIL, val);
+	seq_printf(s, "\t\ttail/head=0x%03x/0x%03x\n", fpg_tail, fpg_head);
+
+	for (i = 0; i < ARRAY_SIZE(pse_groups); i++) {
+		u32 qreg = pse_groups[i].qreg, ireg = pse_groups[i].ireg;
+
+		val = mt76_rr(dev, qreg);
+		max_q = FIELD_GET(MT_WF_GROUP_QUOTA_MAX, val);
+		min_q = FIELD_GET(MT_WF_GROUP_QUOTA_MIN, val);
+		seq_printf(s, "\t%s group quota(0x%08x): 0x%08x (max/min=0x%03x/0x%03x)\n",
+			   pse_groups[i].name, qreg, val, max_q, min_q);
+
+		val = mt76_rr(dev, ireg);
+		used_pg = FIELD_GET(MT_WF_PG_INFO_SRC_CNT, val);
+		rsv_pg  = FIELD_GET(MT_WF_PG_INFO_RSV_CNT, val);
+		seq_printf(s, "\t%s group status(0x%08x): 0x%08x (used/rsv=0x%03x/0x%03x)\n",
+			   pse_groups[i].name, ireg, val, used_pg, rsv_pg);
+	}
+
+	mt792x_mutex_release(dev);
+	return 0;
+}
+
 int mt7925_init_debugfs(struct mt792x_dev *dev)
 {
 	struct dentry *dir;
@@ -313,5 +832,17 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
 				    mt792x_pm_stats);
 	debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
 
+	debugfs_create_file("wtbl_idx", 0600, dir, dev, &fops_wtbl_idx);
+	debugfs_create_devm_seqfile(dev->mt76.dev, "get_wtbl", dir,
+				    mt7925_wtbl_read);
+	debugfs_create_devm_seqfile(dev->mt76.dev, "dmashdl_info", dir,
+				    is_mt7928(&dev->mt76) ?
+				    mt7928_dmashdl_lite_read :
+				    mt7925_dmashdl_read);
+	debugfs_create_devm_seqfile(dev->mt76.dev, "ple_info", dir,
+				    mt7925_ple_read);
+	debugfs_create_devm_seqfile(dev->mt76.dev, "pse_info", dir,
+				    mt7925_pse_read);
+
 	return 0;
 }
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
index e040dff321dd..8d7e995c8fb8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
@@ -131,8 +131,23 @@
 #define MT_DMASHDL_LITE_GROUP_UDF_CHK0		MT_DMASHDL_LITE(0x020)
 #define MT_DMASHDL_LITE_GROUP_UDF_CHK1		MT_DMASHDL_LITE(0x024)
 #define MT_DMASHDL_LITE_Q_MAPPING0		MT_DMASHDL_LITE(0x028)
+#define MT_DMASHDL_LITE_Q_MAP(_n)	MT_DMASHDL_LITE(0x028 + ((_n) << 2))
 #define MT_DMASHDL_LITE_GROUP0_QUOTA		MT_DMASHDL_LITE(0x100)
+#define MT_DMASHDL_LITE_GROUP_QUOTA(_n)	MT_DMASHDL_LITE(0x100 + ((_n) << 2))
 #define MT_DMASHDL_LITE_GROUP_MIN_QUOTA_MASK		GENMASK(12, 0)
 #define MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK		GENMASK(28, 16)
 
+/* DMASHDL Lite per-group runtime status registers */
+#define MT_DMASHDL_LITE_GROUP_SRC_CNT(_n)	\
+	MT_DMASHDL_LITE(0x200 + ((_n) << 2))
+#define MT_DMASHDL_LITE_GROUP_SRC_CNT_MASK	GENMASK(12, 0)
+#define MT_DMASHDL_LITE_GROUP_ACK_CNT(_n)	\
+	MT_DMASHDL_LITE(0x300 + ((_n) << 2))
+/* ACK_CNT[23:0]: [7:0]=pktin_cnt, [15:8]=add_return_cnt, [23:16]=return_cnt */
+#define MT_DMASHDL_LITE_ACK_PKTIN_CNT		GENMASK(7, 0)
+#define MT_DMASHDL_LITE_ACK_ADD_RET_CNT		GENMASK(15, 8)
+#define MT_DMASHDL_LITE_ACK_RET_CNT		GENMASK(23, 16)
+#define MT_DMASHDL_LITE_GROUP_VALID_0_31	MT_DMASHDL_LITE(0x400)
+#define MT_DMASHDL_LITE_GROUP_VALID_32_63	MT_DMASHDL_LITE(0x404)
+
 #endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index a7d71e007da0..361e87a0f867 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -324,6 +324,7 @@ struct mt792x_dev {
 
 	u8 fw_debug;
 	u8 fw_features;
+	u32 wtbl_dump_idx;
 
 	struct mt76_connac_pm pm;
 	struct mt76_connac_coredump coredump;
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
index 3587f61e098a..be15ca62ef6e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
@@ -526,4 +526,74 @@
 #define MT7927_PCIE2AP_REMAP_WF_0_54_VAL	0x00001807
 #define MT7927_SEMA_OWN_STA			0x40000
 #define MT7927_SEMA_OWN_STA_REP			0x40400
+
+/* PLE additional registers for debugfs */
+#define MT_PLE_PBUF_CTRL		MT_PLE(0x004)
+#define MT_PLE_PG_HIF_GROUP		MT_PLE(0x00c)
+#define MT_PLE_PG_HIF_WMTXD_GROUP	MT_PLE(0x010)
+#define MT_PLE_PG_HIF_TXCMD_GROUP	MT_PLE(0x014)
+#define MT_PLE_PG_CPU_GROUP		MT_PLE(0x018)
+#define MT_PLE_INT_N9_ERR_STS		MT_PLE(0x304)
+#define MT_PLE_INT_N9_ERR_STS_1	MT_PLE(0x308)
+#define MT_PLE_QUEUE_EMPTY		MT_PLE(0x360)
+#define MT_PLE_QUEUE_EMPTY_ALL_AC	BIT(24)
+#define MT_PLE_FREEPG_CNT		MT_PLE(0x3a0)
+#define MT_PLE_FREEPG_HEAD_TAIL		MT_PLE(0x3a4)
+#define MT_PLE_HIF_PG_INFO		MT_PLE(0x3a8)
+#define MT_PLE_HIF_WMTXD_PG_INFO	MT_PLE(0x3ac)
+#define MT_PLE_HIF_TXCMD_PG_INFO	MT_PLE(0x3b0)
+#define MT_PLE_CPU_PG_INFO		MT_PLE(0x3b4)
+
+/* Common field masks shared by PLE/PSE group quota and page info registers */
+#define MT_WF_GROUP_QUOTA_MAX		GENMASK(27, 16)
+#define MT_WF_GROUP_QUOTA_MIN		GENMASK(11, 0)
+#define MT_WF_PG_INFO_SRC_CNT		GENMASK(27, 16)
+#define MT_WF_PG_INFO_RSV_CNT		GENMASK(11, 0)
+#define MT_WF_FREEPG_FFA_CNT		GENMASK(27, 16)
+#define MT_WF_FREEPG_FREE_CNT		GENMASK(11, 0)
+#define MT_WF_FREEPG_TAIL		GENMASK(27, 16)
+#define MT_WF_FREEPG_HEAD		GENMASK(11, 0)
+#define MT_WF_PBUF_PAGE_SIZE		BIT(31)
+#define MT_WF_PBUF_PBUF_OFFSET		GENMASK(25, 17)
+#define MT_WF_PBUF_TOTAL_PAGES		GENMASK(11, 0)
+
+/* PSE */
+#define MT_PSE(ofs)			(MT_PSE_BASE + (ofs))
+#define MT_PSE_PBUF_CTRL		MT_PSE(0x004)
+#define MT_PSE_INT_N9_ERR_STS		MT_PSE(0x034)
+#define MT_PSE_INT_N9_ERR1_STS		MT_PSE(0x038)
+#define MT_PSE_QUEUE_EMPTY		MT_PSE(0x0b0)
+#define MT_PSE_PG_HIF0_GROUP		MT_PSE(0x110)
+#define MT_PSE_PG_HIF1_GROUP		MT_PSE(0x114)
+#define MT_PSE_PG_CPU_GROUP		MT_PSE(0x118)
+#define MT_PSE_PG_PLE_GROUP		MT_PSE(0x11c)
+#define MT_PSE_PG_LMAC0_GROUP		MT_PSE(0x124)
+#define MT_PSE_PG_LMAC1_GROUP		MT_PSE(0x128)
+#define MT_PSE_PG_LMAC2_GROUP		MT_PSE(0x12c)
+#define MT_PSE_PG_LMAC3_GROUP		MT_PSE(0x130)
+#define MT_PSE_PG_MDP_GROUP		MT_PSE(0x134)
+#define MT_PSE_PG_HIF2_GROUP		MT_PSE(0x140)
+#define MT_PSE_HIF0_PG_INFO		MT_PSE(0x150)
+#define MT_PSE_HIF1_PG_INFO		MT_PSE(0x154)
+#define MT_PSE_CPU_PG_INFO		MT_PSE(0x158)
+#define MT_PSE_PLE_PG_INFO		MT_PSE(0x15c)
+#define MT_PSE_LMAC0_PG_INFO		MT_PSE(0x164)
+#define MT_PSE_LMAC1_PG_INFO		MT_PSE(0x168)
+#define MT_PSE_LMAC2_PG_INFO		MT_PSE(0x16c)
+#define MT_PSE_LMAC3_PG_INFO		MT_PSE(0x170)
+#define MT_PSE_MDP_PG_INFO		MT_PSE(0x174)
+#define MT_PSE_HIF2_PG_INFO		MT_PSE(0x180)
+#define MT_PSE_FREEPG_CNT		MT_PSE(0x3a0)
+#define MT_PSE_FREEPG_HEAD_TAIL		MT_PSE(0x3a4)
+
+/* DMASHDL additional registers for debugfs */
+#define MT_DMASHDL_ERROR_FLAG_CTRL	MT_DMA_SHDL(0x0dc)
+#define MT_DMASHDL_STATUS_RD		MT_DMA_SHDL(0x100)
+#define MT_DMASHDL_STATUS_RD_FREE_PG	GENMASK(27, 16)
+#define MT_DMASHDL_STATUS_RD_FFA	GENMASK(11, 0)
+#define MT_DMASHDL_STATUS_RD_GP(_n)	MT_DMA_SHDL(0x140 + ((_n) << 2))
+#define MT_DMASHDL_STATUS_RD_GP_RSV	GENMASK(27, 16)
+#define MT_DMASHDL_STATUS_RD_GP_SRC	GENMASK(11, 0)
+#define MT_DMASHDL_RD_GP_PKT_CNT(_n)	MT_DMA_SHDL(0x180 + ((_n) << 2))
+
 #endif /* __MT792X_REGS_H */
-- 
2.18.0


                 reply	other threads:[~2026-10-14  5:26 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261014052641.1609444-1-eason.lai@mediatek.com \
    --to=eason.lai@mediatek.com \
    --cc=deren.wu@mediatek.com \
    --cc=jb.tsai@mediatek.com \
    --cc=kun.wu@mediatek.com \
    --cc=leon.yen@mediatek.com \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=litien.chang@mediatek.com \
    --cc=lorenzo@kernel.org \
    --cc=nbd@nbd.name \
    --cc=quan.zhou@mediatek.com \
    --cc=ryder.lee@mediatek.com \
    --cc=sean.wang@mediatek.com \
    --cc=yin-chun.tsai@mediatek.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox