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