* [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose
@ 2026-08-24 3:07 Eason Lai
2026-08-25 4:00 ` kernel test robot
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Eason Lai @ 2026-08-24 3:07 UTC (permalink / raw)
To: nbd, lorenzo
Cc: linux-wireless, linux-mediatek, kun.wu, deren.wu, sean.wang,
quan.zhou, ryder.lee, leon.yen, litien.chang, jb.tsai,
michael.shih, eason.lai
From: Michael Shih <michael.shih@mediatek.com>
Usage:
(1) Connect STA to AP (You can use dhclient CMD to ask for DHCP IP, to use ping to AP)
DHCP IP CMD: dhclient ${WLAN_IF} -v
Example: dhclient wlan0 -v
## (To simulate traffic) Ping 10 packets for every second to AP
ping 192.168.0.XXX -i0.1
(2) (Get stats) Use command, phyX is your WiFi interface:
cat /sys/kernel/debug/ieee80211/phyX/mt76/stats
(3) You will see below stats info:
(STA) connected AP MAC Address = a8:42:a1:46:c4:6b
BssIdx = [0]
StaRecIdx = [1],
RSSI = -25
Link_Speed = 2402000 (kbit/s)
temperature = 40
Tx success = 716
Tx fail to Rcv ACK after retry = 0
Rx Mpdu = 23
Rx Fcs Error = 1 (MPDU)
Rx FIFO full = 0 (MPDU)
TRX_AGG_RANGE[0] = 0 (PPDU)
TRX_AGG_RANGE[1] = 0 (PPDU)
TRX_AGG_RANGE[2] = 0 (PPDU)
TRX_AGG_RANGE[3] = 0 (PPDU)
TRX_AGG_RANGE[4] = 0 (PPDU)
TRX_AGG_RANGE[5] = 0 (PPDU)
TRX_AGG_RANGE[6] = 0 (PPDU)
TRX_AGG_RANGE[7] = 0 (PPDU)
MPDUs in AMPDUs transmitted = 0 (MPDU)
MPDUs in AMPDUs transmitted with ACK reply = 0 (MPDU)
rate1_tx_cnt = 714
rate1_fail_cnt = 223
train_up = 2310
train_down = 3080
is_force_tx_stream = 0
is_force_se_off = 0
max_ampdu_factor = 9
tx_rate_up_penalty = 0
low_traffic_mode = 2
low_traffic_count = 0
low_traffic_dashboard = 3
dynamic_sgi_state = 0
dynamic_sgi_score = 2
dynamic_bw_state = 0
dynamic_gband_256qam_state = 3
vht_non_sp_rate_state = 0
Last TX Rate = 24M, BW20, OFDM, BCC, SPE24
Chip Out TX Power = +16.5 dBm
Cur Rate Index = 0
Rate index[0] --> NSS2_MCS11, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[1] NSS2_MCS9, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[2] NSS2_MCS7, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[3] NSS2_MCS4, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[4] NSS2_MCS3, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[5] NSS1_MCS2, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[6] NSS1_MCS1, BW80, SGI, HE_SU, LDPC
Rate index[7] NSS1_MCS0, BW80, SGI, HE_SU, LDPC
mib state:
=== Rx Related Counters ===
Rx with CRC = 24020 (MPDU)
Rx drop due to out of resource = 0 (MPDU)
Rx Mpdu = 93223 (MPDU)
Rx AMpdu = 668 (PPDU)
Rx PF Drop = 0 (MPDU)
Rx Len Mismatch = 5853 (PPDU)
=== Phy/Timing Related Counters ===
ChannelIdleCnt = 1040434
CCA_NAV_Tx_Time = 18510036
Rx_MDRDY_CNT = 166580 (PPDU)
CCK_MDRDY = 7782
OFDM_MDRDY = 11912731
OFDM_GREEN_MDRDY = 0
Prim CCA Time = 17917650
Sec CCA Time = 14922034
Prim ED Time = 5677528
=== Tx Related Counters(Generic) ===
BeaconTxCnt = 0
Tx 40MHz Cnt = 279 (MPDU)
Tx 80MHz Cnt = 44 (MPDU)
Tx 160MHz Cnt = 712 (MPDU)
=== BSSID[0] Related Counters ===
BA Miss Cnt = 210 (PPDU)
RTS Tx Cnt = 1003 (MPDU)
Frame Retry Cnt = 239 (MPDU)
Frame Retry 2 Cnt = 71 (MPDU)
RTS Retry Cnt = 152 (MPDU)
Ack Failed Cnt = 319 (PPDU)
=== AMPDU Related Counters ===
Tx AMPDU_Pkt_Cnt = 705 (PPDU)
Tx AMPDU_MPDU_Pkt_Cnt = 713 (MPDU)
AMPDU Tx success = 503 (MPDU)
AMPDU Tx fail count = 210 (MPDU), PER=29.4%
Signed-off-by: Michael Shih <michael.shih@mediatek.com>
---
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 177 +++
.../wireless/mediatek/mt76/mt7925/Makefile | 2 +-
.../wireless/mediatek/mt76/mt7925/debugfs.c | 1054 +++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/stats.c | 585 +++++++++
.../net/wireless/mediatek/mt76/mt7925/stats.h | 305 +++++
5 files changed, 2122 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/stats.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/stats.h
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 0ededa569e9a..72cc69750f47 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1483,6 +1483,183 @@ enum {
MT_NIC_CAP_EML_CAP = 0x22,
};
+enum {
+ UNI_CMD_ID_STATISTICS_BASIC = 0,
+ UNI_CMD_ID_STATISTICS_LINK_QUALITY = 1,
+ UNI_CMD_ID_STATISTICS_STA = 2,
+ UNI_CMD_ID_STATISTICS_BUG_REPORT = 3,
+ UNI_CMD_ID_STATISTICS_TX_STATS = 4,
+ UNI_CMD_ID_STATISTICS_ALL_STATS = 5,
+ UNI_CMD_ID_STATISTICS_EML_STATS = 6,
+ UNI_CMD_ID_STATISTICS_REGULAR_STATS = 7,
+ UNI_CMD_ID_STATISTICS_BSS_LINK_QUALITY = 8,
+ UNI_CMD_ID_STATISTICS_ML_CHNL_COND = 9,
+ UNI_CMD_ID_STATISTICS_WTBL = 10,
+ UNI_CMD_ID_STATISTICS_LINK_LAYER_STATS = 128,
+ UNI_CMD_ID_STATISTICS_PPDU_LATENCY = 129,
+ UNI_CMD_ID_STATISTICS_CURRENT_TX_RATE = 130,
+ UNI_CMD_ID_STATISTICS_SET_TX_BITRATE_MONITOR = 131,
+ UNI_CMD_ID_STATISTICS_GET_PREDICT_TX_BITRATE = 132,
+ UNI_CMD_ID_STATISTICS_SET_ATPS_TYPE_RATE_MODE = 133,
+ UNI_CMD_ID_STATISTICS_SET_BEACON_REPORT = 134,
+ UNI_CMD_ID_STATISTICS_BSS_CURRENT_TX_RATE = 135,
+ UNI_CMD_ID_STATISTICS_GET_BSS_PREDICT_TX_BITRATE = 136,
+ UNI_CMD_ID_STATISTICS_MAX_NUM
+};
+
+enum {
+ UNI_CMD_MIB_DATA,
+};
+
+enum mt7925_mib_counter {
+ UNI_CMD_MIB_CNT_RX_FCS_ERR = 0,
+ UNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW = 1,
+ UNI_CMD_MIB_CNT_RX_MPDU = 2,
+ UNI_CMD_MIB_CNT_AMPDU_RX_COUNT = 3,
+ UNI_CMD_MIB_CNT_RX_TOTAL_BYTE = 4,
+ UNI_CMD_MIB_CNT_RX_VALID_AMPDU_SF = 5,
+ UNI_CMD_MIB_CNT_RX_VALID_BYTE = 6,
+ UNI_CMD_MIB_CNT_CHANNEL_IDLE = 7,
+ UNI_CMD_MIB_CNT_VEC_DROP = 8,
+ UNI_CMD_MIB_CNT_DELIMITER_FAIL = 9,
+ UNI_CMD_MIB_CNT_VEC_MISMATCH = 10,
+ UNI_CMD_MIB_CNT_MDRDY = 11,
+ UNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME = 12,
+ UNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME = 13,
+ UNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME = 14,
+ UNI_CMD_MIB_CNT_PF_DROP = 15,
+ UNI_CMD_MIB_CNT_LEN_MISMATCH = 16,
+ UNI_CMD_MIB_CNT_P_CCA_TIME = 17,
+ UNI_CMD_MIB_CNT_S_CCA_TIME = 18,
+ UNI_CMD_MIB_CNT_CCA_NAV_TX_TIME = 19,
+ UNI_CMD_MIB_CNT_P_ED_TIME = 20,
+ UNI_CMD_MIB_CNT_BCN_TX = 21,
+ UNI_CMD_MIB_CNT_TX_BW_20MHZ = 22,
+ UNI_CMD_MIB_CNT_TX_BW_40MHZ = 23,
+ UNI_CMD_MIB_CNT_TX_BW_80MHZ = 24,
+ UNI_CMD_MIB_CNT_TX_BW_160MHZ = 25,
+ UNI_CMD_RMAC_CNT_OBSS_AIRTIME = 26,
+ UNI_CMD_RMAC_CNT_NONWIFI_AIRTIME = 27,
+ UNI_CMD_MIB_CNT_TX_DUR_CNT = 28,
+ UNI_CMD_MIB_CNT_RX_DUR_CNT = 29,
+ UNI_CMD_MIB_CNT_BA_CNT = 30,
+ UNI_CMD_MIB_CNT_MAC2PHY_TX_TIME = 31,
+ UNI_CMD_MIB_CNT_RX_OUT_OF_RANGE_COUNT = 32,
+ UNI_CMD_MIB_CNT_IBF_TX = 33,
+ UNI_CMD_MIB_CNT_EBF_TX = 34,
+ UNI_CMD_MIB_CNT_MUBF_TX = 35,
+ UNI_CMD_MIB_CNT_RX_BF_VHTFBK = 36,
+ UNI_CMD_MIB_CNT_RX_BF_HTFBK = 37,
+ UNI_CMD_MIB_CNT_RX_BF_HEFBK = 38,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_BFPOLL_TRI = 39,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_TRI = 40,
+ UNI_CMD_MIB_CNT_BFEE_RX_FBKCQI = 41,
+ UNI_CMD_MIB_CNT_BFEE_RX_NDP = 42,
+ UNI_CMD_MIB_CNT_RX_BF_IBF_UPT = 43,
+ UNI_CMD_MIB_CNT_RX_BF_EBF_UPT = 44,
+ UNI_CMD_MIB_CNT_BFEE_SP_ABORT = 45,
+ UNI_CMD_MIB_CNT_BFEE_TB_LEN_ERR = 46,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_MUTE = 47,
+ UNI_CMD_MIB_CNT_BFEE_TMAC_ABORT = 48,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_CPL = 49,
+ UNI_CMD_MIB_CNT_BFEE_COANT_BLKTX = 50,
+ UNI_CMD_MIB_CNT_BFEE_FBK_SEG = 51,
+ UNI_CMD_MIB_CNT_NAV_TIME = 52,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT1 = 53,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT0 = 54,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT3 = 55,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT2 = 56,
+ UNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT = 57,
+ UNI_CMD_MIB_CNT_BSS1_RTS_TX_CNT = 58,
+ UNI_CMD_MIB_CNT_BSS2_RTS_TX_CNT = 59,
+ UNI_CMD_MIB_CNT_BSS3_RTS_TX_CNT = 60,
+ UNI_CMD_MIB_CNT_BSS0_RTS_RETRY = 61,
+ UNI_CMD_MIB_CNT_BSS1_RTS_RETRY = 62,
+ UNI_CMD_MIB_CNT_BSS2_RTS_RETRY = 63,
+ UNI_CMD_MIB_CNT_BSS3_RTS_RETRY = 64,
+ UNI_CMD_MIB_CNT_BSS0_BA_MISS = 65,
+ UNI_CMD_MIB_CNT_BSS1_BA_MISS = 66,
+ UNI_CMD_MIB_CNT_BSS2_BA_MISS = 67,
+ UNI_CMD_MIB_CNT_BSS3_BA_MISS = 68,
+ UNI_CMD_MIB_CNT_BSS0_ACK_FAIL = 69,
+ UNI_CMD_MIB_CNT_BSS1_ACK_FAIL = 70,
+ UNI_CMD_MIB_CNT_BSS2_ACK_FAIL = 71,
+ UNI_CMD_MIB_CNT_BSS3_ACK_FAIL = 72,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY = 73,
+ UNI_CMD_MIB_CNT_BSS1_FRAME_RETRY = 74,
+ UNI_CMD_MIB_CNT_BSS2_FRAME_RETRY = 75,
+ UNI_CMD_MIB_CNT_BSS3_FRAME_RETRY = 76,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2 = 77,
+ UNI_CMD_MIB_CNT_BSS1_FRAME_RETRY_2 = 78,
+ UNI_CMD_MIB_CNT_BSS2_FRAME_RETRY_2 = 79,
+ UNI_CMD_MIB_CNT_BSS3_FRAME_RETRY_2 = 80,
+ UNI_CMD_MIB_CNT_RX_A1_SEARCH = 81,
+ UNI_CMD_MIB_CNT_RX_DROP_MPDU = 82,
+ UNI_CMD_MIB_CNT_RX_UNWANTED = 83,
+ UNI_CMD_MIB_CNT_RX_FCS_OK = 84,
+ UNI_CMD_MIB_CNT_SU_TX_OK = 85,
+ UNI_CMD_MIB_CNT_TX_FULL_BW = 86,
+ UNI_CMD_MIB_CNT_TX_AUTO_BW = 87,
+ UNI_CMD_MIB_CNT_CCA_SAMPLE_IDLE = 88,
+ UNI_CMD_MIB_CNT_CCA_SAMPLE_ACTIVE = 89,
+ UNI_CMD_MIB_CNT_CCA_TIME = 90,
+ UNI_CMD_MIB_CNT_S_20BW_CCA_TIME = 91,
+ UNI_CMD_MIB_CNT_S_40BW_CCA_TIME = 92,
+ UNI_CMD_MIB_CNT_S_80BW_CCA_TIME = 93,
+ UNI_CMD_MIB_CNT_S_160BW_CCA_TIME = 94,
+ UNI_CMD_MIB_CNT_S_P20BW_0_ED_TIME = 95,
+ UNI_CMD_MIB_CNT_S_P20BW_1_ED_TIME = 96,
+ UNI_CMD_MIB_CNT_S_P20BW_2_ED_TIME = 97,
+ UNI_CMD_MIB_CNT_S_P20BW_3_ED_TIME = 98,
+ UNI_CMD_MIB_CNT_S_P20BW_4_ED_TIME = 99,
+ UNI_CMD_MIB_CNT_S_P20BW_5_ED_TIME = 100,
+ UNI_CMD_MIB_CNT_S_P20BW_6_ED_TIME = 101,
+ UNI_CMD_MIB_CNT_S_P20BW_7_ED_TIME = 102,
+ UNI_CMD_MIB_CNT_S_P20BW_8_ED_TIME = 103,
+ UNI_CMD_MIB_CNT_S_P20BW_9_ED_TIME = 104,
+ UNI_CMD_MIB_CNT_S_P20BW_10_ED_TIME = 105,
+ UNI_CMD_MIB_CNT_S_P20BW_11_ED_TIME = 106,
+ UNI_CMD_MIB_CNT_S_P20BW_12_ED_TIME = 107,
+ UNI_CMD_MIB_CNT_S_P20BW_13_ED_TIME = 108,
+ UNI_CMD_MIB_CNT_S_P20BW_14_ED_TIME = 109,
+ UNI_CMD_MIB_CNT_S_P20BW_15_ED_TIME = 110,
+ UNI_CMD_MIB_CNT_TX_BW_320MHZ = 111,
+ UNI_CMD_MIB_CNT_TX_DDLMT_RNG0 = 112, /* Rng0~Rng4 112~116 */
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_3 = 117, /* 117~120 */
+ UNI_CMD_MIB_CNT_BSS0_TX = 121, /* 121~124 */
+ UNI_CMD_MIB_CNT_BSS0_TX_DATA = 125, /* 125~128 */
+ UNI_CMD_MIB_CNT_BSS0_TX_BYTE = 129, /* 129~132 */
+ UNI_CMD_MIB_CNT_RX_OK_BSS0 = 133, /* 133~136 */
+ UNI_CMD_MIB_CNT_RX_BYTE_BSS0 = 137, /* 137~140 */
+ UNI_CMD_MIB_CNT_RX_DATA_BSS0 = 141, /* 141~144 */
+ UNI_CMD_MIB_CNT_MBSS0_TX_OK = 145, /* 145~160 */
+ UNI_CMD_MIB_CNT_MBSS0_TX_BYTE = 161, /* 161~176 */
+ UNI_CMD_MIB_CNT_RX_OK_MBSS0 = 177, /* 177~192 */
+ UNI_CMD_MIB_CNT_RX_BYTE_MBSS0 = 193, /* 193~208 */
+ UNI_CMD_MIB_CNT_AMPDU = 209,
+ UNI_CMD_MIB_CNT_AMPDU_MPDU = 210,
+ UNI_CMD_MIB_CNT_AMPDU_ACKED = 211,
+ UNI_CMD_MIB_CNT_MAX_NUM
+};
+
+enum ENUM_UNI_CMD_GET_STATISTICS_TAG {
+ UNI_CMD_GET_STATISTICS_TAG_BASIC = 0,
+ UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1,
+ UNI_CMD_GET_STATISTICS_TAG_STA = 2,
+ UNI_CMD_GET_STATISTICS_TAG_BUG_REPORT = 3,
+ UNI_CMD_GET_STATISTICS_TAG_EML_STATS = 6,
+ UNI_CMD_GET_STATISTICS_TAG_REGULAR_STATS = 7,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_LINK_QUALITY = 8,
+ UNI_CMD_GET_STATISTICS_TAG_GET_ML_CHNL_COND = 9,
+ UNI_CMD_GET_STATISTICS_TAG_STAT_WTBL = 10,
+ UNI_CMD_GET_STATISTICS_TAG_LINK_LAYER_STATS = 0x80,
+ UNI_CMD_GET_STATISTICS_TAG_PPDU_LATENCY,
+ UNI_CMD_GET_STATISTICS_TAG_CURRENT_TX_RATE,
+ UNI_CMD_GET_STATISTICS_TAG_BEACON_REPORT = 0x86,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_CURRENT_TX_RATE = 0x87,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_PRED_TX_BITRATE = 0x88,
+};
+
#define UNI_WOW_DETECT_TYPE_MAGIC BIT(0)
#define UNI_WOW_DETECT_TYPE_ANY BIT(1)
#define UNI_WOW_DETECT_TYPE_DISCONNECT BIT(2)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index f9dcc0bba393..3863a89b8974 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
obj-$(CONFIG_MT7925E) += mt7925e.o
obj-$(CONFIG_MT7925U) += mt7925u.o
-mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o
+mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o stats.o
mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7925e-y := pci.o pci_mac.o pci_mcu.o
mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index d01ff49de47a..e3d2d9ef20fe 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -3,6 +3,7 @@
#include "mt7925.h"
#include "mcu.h"
+#include "stats.h"
static int
mt7925_reg_set(void *data, u64 val)
@@ -286,6 +287,1057 @@ static int mt7925_chip_reset(void *data, u64 val)
DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
+/* fixedrate input format (12 hex digits):
+ * 0x[WCID(2)][Mode][BW][MCS][Nss][SGI][Preamble][STBC][LDPC][SPE_EN][HeLtf]
+ * The RA tag is fixed to SET_FIXED_RATE internally.
+ * WCID is 2 nibbles, all others are 1 nibble.
+ *
+ * [WCID] Wireless Client ID
+ * [Mode] CCK=0, OFDM=1, HT=2, GF=3, VHT=4, PLR=5, HE_SU=8, HE_ER_SU=9,
+ * HE_TRIG=10, HE_MU=11, EHT_ER=13, EHT_TB=14, EHT_SU and EHT_MU=15
+ * [BW] BW20=0, BW40=1, BW80=2, BW160=3, BW320=4
+ * [MCS] CCK=0~3, OFDM=0~7, HT=0~32, VHT=0~9, HE=0~11, EHT=0~13, EHT_ER=14~15
+ * [Nss] 1~8
+ * [GI] HT/VHT: 0=Long, 1=Short; HE/EHT: 0=0.8us, 1=1.6us, 2=3.2us
+ * [Preamble] Long=0, Short=other
+ * [STBC] Enable=1, Disable=0
+ * [LDPC] BCC=0, LDPC=1 (driver sends the FW's "on" value 7)
+ * [SPE_EN] spatial extension index
+ * [HeLtf] 1X=0, 2X=1, 4X=2 (NOTE: EHT modes require HeLtf=1)
+ */
+static int
+mt7925_fixedrate_set(void *data, u64 val)
+{
+ struct mt792x_dev *dev = data;
+ struct mt7925_ra_fixed_rate_v1 rate = {};
+
+ dev->fixed_rate = val;
+
+ rate.wlan_idx = cpu_to_le16((val >> 40) & 0xff);
+ rate.phy_mode = (val >> 36) & 0xf;
+ rate.bw = (val >> 32) & 0xf;
+ rate.mcs = (val >> 28) & 0xf;
+ rate.nss = (val >> 24) & 0xf;
+ rate.short_gi = cpu_to_le16((val >> 20) & 0xf);
+ rate.short_preamble = (val >> 16) & 0xf;
+ rate.stbc = (val >> 12) & 0xf;
+ rate.ecc = ((val >> 8) & 0xf) ? 7 : 0;
+ rate.spe = (val >> 4) & 0xf;
+ rate.he_ltf = cpu_to_le16((val >> 0) & 0xf);
+
+ mt792x_mutex_acquire(dev);
+ mt7925_mcu_set_fixed_rate(dev, &rate);
+ mt792x_mutex_release(dev);
+
+ return 0;
+}
+
+static int
+mt7925_fixedrate_get(void *data, u64 *val)
+{
+ struct mt792x_dev *dev = data;
+
+ *val = dev->fixed_rate;
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_fixedrate, mt7925_fixedrate_get,
+ mt7925_fixedrate_set, "0x%llx\n");
+
+/* autorate input format (6 hex digits):
+ * 0x[Tag(2)][WCID(2)][En][Mode]
+ * The RA tag is fixed to SET_AUTO_RATE internally.
+ * WCID is 2 nibbles, En and Mode are 1 nibble each.
+ *
+ * [WCID] Wireless Client ID
+ * [En] Auto rate: Enable=1, Disable=0
+ * [Mode] don't care (RA picks the rate)
+ */
+static int
+mt7925_autorate_set(void *data, u64 val)
+{
+ struct mt792x_dev *dev = data;
+ u16 wlan_idx = (val >> 8) & 0xff;
+ bool enable = (val >> 4) & 0xf;
+ u8 mode = val & 0xf;
+
+ dev->auto_rate = val;
+
+ mt792x_mutex_acquire(dev);
+ mt7925_mcu_set_auto_rate(dev, wlan_idx, enable, mode);
+ mt792x_mutex_release(dev);
+
+ return 0;
+}
+
+static int
+mt7925_autorate_get(void *data, u64 *val)
+{
+ struct mt792x_dev *dev = data;
+
+ *val = dev->auto_rate;
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_autorate, mt7925_autorate_get,
+ mt7925_autorate_set, "0x%llx\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;
+}
+
+static int
+mt7925_vefuse_read(struct seq_file *s, void *data)
+{
+ struct mt792x_dev *dev = dev_get_drvdata(s->private);
+ u16 size = dev->vefuse_cap.size;
+ int off = 0;
+ u8 *buf;
+ int ret = 0;
+
+ if (!dev->vefuse_cap.present || !size) {
+ seq_puts(s, "vefuse: no region in FW image\n");
+ return 0;
+ }
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mt792x_mutex_acquire(dev);
+ while (off < (int)size) {
+ u16 chunk = min_t(u16, 512, size - off);
+
+ ret = mt7925_mcu_read_vefuse(dev, off, chunk, buf + off);
+ if (ret)
+ break;
+ off += chunk;
+ }
+ mt792x_mutex_release(dev);
+
+ if (ret) {
+ seq_printf(s, "vefuse: read failed at 0x%04x (%d)\n", off, ret);
+ kfree(buf);
+ return 0;
+ }
+
+ seq_printf(s, "vefuse content: %u bytes\n", size);
+ for (off = 0; off < (int)size; off += 16)
+ seq_printf(s, "%04x: %*ph\n", off,
+ (int)min_t(u16, 16, size - off), buf + off);
+
+ kfree(buf);
+
+ return 0;
+}
+
+/* send one perf_ind carrying the written value in every tx/rx byte field */
+static int mt7925_perf_ind_set(void *data, u64 val)
+{
+ struct mt792x_dev *dev = data;
+
+ return mt792x_perf_ind_trigger(dev, val);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_perf_ind, NULL, mt7925_perf_ind_set, "%lld\n");
+
+static int mt7925_perf_ind_enable_get(void *data, u64 *val)
+{
+ struct mt792x_dev *dev = data;
+
+ *val = dev->perf.enabled;
+
+ return 0;
+}
+
+static int mt7925_perf_ind_enable_set(void *data, u64 val)
+{
+ struct mt792x_dev *dev = data;
+
+ dev->perf.enabled = !!val;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_perf_ind_enable, mt7925_perf_ind_enable_get,
+ mt7925_perf_ind_enable_set, "%lld\n");
+
+static int
+mt7925_coex_info(struct seq_file *s, void *data)
+{
+#define MT7925_CHIP_CONFIG_RESP_SIZE 320
+ struct mt792x_dev *dev = dev_get_drvdata(s->private);
+ u8 resp[MT7925_CHIP_CONFIG_RESP_SIZE], resp_type;
+ int i, ret;
+
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_mcu_chip_config_query(dev, "coexBwcGetModeInfo 0",
+ &resp_type, resp, sizeof(resp));
+ mt792x_mutex_release(dev);
+
+ if (ret < 0)
+ return ret;
+
+ if (!ret)
+ seq_puts(s, "no reply\n");
+ else if (resp_type == CHIP_CONFIG_TYPE_ASCII)
+ seq_printf(s, "%.*s\n", ret, resp);
+ else
+ for (i = 0; i < ret; i += 16)
+ seq_printf(s, "%04x: %*ph\n", i,
+ min_t(int, 16, ret - i), resp + i);
+
+ 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%03x, PSE pkt max=0x%03x\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%03x/0x%03x, 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%04x, PLE page size=0x%04x\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%04x, PLE pkt max=0x%04x\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++) {
+ bool enabled = !((i < 32 ? disable0 : disable1) >> (i % 32) & 1);
+ u32 quota, src, ack_reg;
+ u32 pktin, add_ret, ret;
+
+ quota = mt76_rr(dev, MT_DMASHDL_LITE_GROUP_QUOTA(i));
+ if (!enabled && !quota)
+ continue;
+
+ src = FIELD_GET(MT_DMASHDL_LITE_GROUP_SRC_CNT_MASK,
+ mt76_rr(dev, MT_DMASHDL_LITE_GROUP_SRC_CNT(i)));
+ 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,
+ FIELD_GET(MT_DMASHDL_LITE_GROUP_MIN_QUOTA_MASK, quota),
+ FIELD_GET(MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK, quota),
+ 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%03x, ffa=0x%03x\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%03x/0x%03x\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%03x/0x%03x\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%03x/0x%03x\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;
+
+ 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);
+
+#define MT7925_PSE_GROUP(_name, _qreg, _ireg) \
+do { \
+ 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" _name " group quota(0x%08x): 0x%08x" \
+ " (max/min=0x%03x/0x%03x)\n", _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" _name " group status(0x%08x): 0x%08x" \
+ " (used/rsv=0x%03x/0x%03x)\n", _ireg, val, used_pg, rsv_pg);\
+} while (0)
+
+ MT7925_PSE_GROUP("HIF0", MT_PSE_PG_HIF0_GROUP, MT_PSE_HIF0_PG_INFO);
+ MT7925_PSE_GROUP("HIF1", MT_PSE_PG_HIF1_GROUP, MT_PSE_HIF1_PG_INFO);
+ MT7925_PSE_GROUP("HIF2", MT_PSE_PG_HIF2_GROUP, MT_PSE_HIF2_PG_INFO);
+ MT7925_PSE_GROUP("CPU", MT_PSE_PG_CPU_GROUP, MT_PSE_CPU_PG_INFO);
+ MT7925_PSE_GROUP("PLE", MT_PSE_PG_PLE_GROUP, MT_PSE_PLE_PG_INFO);
+ MT7925_PSE_GROUP("LMAC0", MT_PSE_PG_LMAC0_GROUP, MT_PSE_LMAC0_PG_INFO);
+ MT7925_PSE_GROUP("LMAC1", MT_PSE_PG_LMAC1_GROUP, MT_PSE_LMAC1_PG_INFO);
+ MT7925_PSE_GROUP("LMAC2", MT_PSE_PG_LMAC2_GROUP, MT_PSE_LMAC2_PG_INFO);
+ MT7925_PSE_GROUP("LMAC3", MT_PSE_PG_LMAC3_GROUP, MT_PSE_LMAC3_PG_INFO);
+ MT7925_PSE_GROUP("MDP", MT_PSE_PG_MDP_GROUP, MT_PSE_MDP_PG_INFO);
+
+#undef MT7925_PSE_GROUP
+
+ mt792x_mutex_release(dev);
+ return 0;
+}
+
+static const u32 stats_query_mib_counters[] = {
+ UNI_CMD_MIB_CNT_RX_FCS_ERR,
+ UNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW,
+ UNI_CMD_MIB_CNT_RX_MPDU,
+ UNI_CMD_MIB_CNT_AMPDU_RX_COUNT,
+ UNI_CMD_MIB_CNT_PF_DROP,
+ UNI_CMD_MIB_CNT_LEN_MISMATCH,
+ UNI_CMD_MIB_CNT_CHANNEL_IDLE,
+ UNI_CMD_MIB_CNT_CCA_NAV_TX_TIME,
+ UNI_CMD_MIB_CNT_MDRDY,
+ UNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_P_CCA_TIME,
+ UNI_CMD_MIB_CNT_S_CCA_TIME,
+ UNI_CMD_MIB_CNT_P_ED_TIME,
+ UNI_CMD_MIB_CNT_BCN_TX,
+ UNI_CMD_MIB_CNT_TX_BW_40MHZ,
+ UNI_CMD_MIB_CNT_TX_BW_80MHZ,
+ UNI_CMD_MIB_CNT_TX_BW_160MHZ,
+ UNI_CMD_MIB_CNT_BSS0_BA_MISS,
+ UNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2,
+ UNI_CMD_MIB_CNT_BSS0_RTS_RETRY,
+ UNI_CMD_MIB_CNT_BSS0_ACK_FAIL,
+ UNI_CMD_MIB_CNT_AMPDU,
+ UNI_CMD_MIB_CNT_AMPDU_MPDU,
+ UNI_CMD_MIB_CNT_AMPDU_ACKED,
+};
+
+/* Dump the whole firmware auto-rate table and mark the entry currently in use
+ * with "-->"
+ */
+static void
+mt7925_dump_auto_rate_table(struct seq_file *s,
+ const struct mt7925_wtbl_rate *wr)
+{
+ u8 fcap = wr->fcap;
+ u8 rate_idx = wr->rate_idx;
+ u8 cbrn = wr->cbrn;
+ int i;
+
+ seq_printf(s, "%-22s Cur Rate Index = %u\n", " ", rate_idx);
+
+ for (i = 0; i < MT7925_AUTO_RATE_NUM; i++) {
+ u16 rate_code = le16_to_cpu(wr->rate_code[i]);
+ u8 txmode = MT7925_HW_RATE_TO_MODE(rate_code);
+ u8 rate = MT7925_HW_RATE_TO_MCS(rate_code);
+ u8 nsts = MT7925_HW_RATE_TO_NSS(rate_code) + 1;
+ u8 stbc = MT7925_HW_RATE_TO_STBC(rate_code);
+ u8 dcm = MT7925_HW_RATE_TO_DCM(rate_code);
+ u8 ersu106t = MT7925_HW_RATE_TO_106T(rate_code);
+ u8 sgi = mt7925_wtbl_get_sgi(wr, txmode, fcap);
+ u8 ldpc = mt7925_wtbl_get_ldpc(wr, txmode);
+ const char *mode_str = "N/A";
+
+ if (dcm)
+ rate = MT7925_HW_RATE_UNMASK_DCM(rate);
+ if (ersu106t)
+ rate = MT7925_HW_RATE_UNMASK_106T(rate);
+
+ if (txmode < ARRAY_SIZE(mt7925_tx_mode_str))
+ mode_str = mt7925_tx_mode_str[txmode];
+
+ seq_printf(s, "Rate index[%d] %s", i,
+ rate_idx == i ? "--> " : " ");
+
+ /* rate/MCS field */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ",
+ mt7925_tx_rate_cck_str[rate & 0x3]);
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_hw_rate_ofdm_str(rate));
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF)
+ seq_printf(s, "MCS%d, ", rate);
+ else
+ seq_printf(s, "%s%d_MCS%d, ",
+ stbc ? "NSTS" : "NSS", nsts, rate);
+
+ /* bandwidth: mirror the CCK/OFDM -> BW20 and cbrn handling */
+ if (txmode == MT7925_TX_RATE_MODE_CCK ||
+ txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_tx_rate_bw_str[0]);
+ else if (i > cbrn)
+ seq_printf(s, "%s, ",
+ fcap < 5 ?
+ (fcap > MT7925_BW_20 ?
+ mt7925_tx_rate_bw_str[fcap - 1] :
+ mt7925_tx_rate_bw_str[fcap]) :
+ mt7925_tx_rate_bw_str[5]);
+ else
+ seq_printf(s, "%s, ",
+ fcap < 5 ? mt7925_tx_rate_bw_str[fcap] :
+ mt7925_tx_rate_bw_str[5]);
+
+ /* GI / preamble */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ", rate < 4 ? "LP" : "SP");
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ ; /* OFDM has no GI/preamble to print */
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF ||
+ txmode == MT7925_TX_RATE_MODE_VHT ||
+ txmode == MT7925_TX_RATE_MODE_PLR)
+ seq_printf(s, "%s, ", sgi == 0 ? "LGI" : "SGI");
+ else
+ seq_printf(s, "%s, ",
+ sgi == 0 ? "SGI" :
+ (sgi == 1 ? "MGI" : "LGI"));
+
+ /* mode, DCM, 106t, STBC, coding */
+ seq_printf(s, "%s%s%s%s%s\n",
+ mode_str,
+ dcm ? ", DCM" : "", ersu106t ? ", 106t" : "",
+ stbc ? ", STBC, " : ", ",
+ (ldpc == 0 ||
+ txmode == MT7925_TX_RATE_MODE_CCK ||
+ txmode == MT7925_TX_RATE_MODE_OFDM) ?
+ "BCC" : "LDPC");
+ }
+}
+
+static int mt792x_stats(struct seq_file *s, void *data)
+{
+ s8 rssi = 0;
+ u8 band_idx = 0;
+ bool wtbl_rate_valid = false;
+ int ret = 0;
+ int i = 0;
+ u32 stats_value = 0;
+ u32 tx_link_speed_kbps = 0;
+ u64 tx_total = 0;
+ u64 tx_fail = 0;
+ u64 tx_per = 0;
+ /* UNI_CMD_MIB_DATA */
+ u64 mib_values[ARRAY_SIZE(stats_query_mib_counters)];
+ struct mt792x_dev *dev = dev_get_drvdata(s->private);
+ struct mt7925_wtbl_rate wtbl_rate;
+ struct mt7925_sta_stats *stats = NULL;
+
+ band_idx = dev->mphy.band_idx;
+
+ stats = kzalloc(sizeof(*stats), GFP_KERNEL);
+ if (!stats)
+ return -ENOMEM;
+
+ /* Get STA_STATS */
+ mt792x_mutex_acquire(dev);
+ ret = mt792x_mcu_get_stat(dev, stats);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get MIB_STATS */
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_mcu_get_mib_info(dev, band_idx, stats_query_mib_counters,
+ ARRAY_SIZE(stats_query_mib_counters), mib_values);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get Link_Speed and RSSI */
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_mcu_get_link_quality(dev, stats->bss_idx, &rssi, &tx_link_speed_kbps);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get the firmware auto-rate table (best effort; do not abort the
+ * whole stats dump if the WTBL query is not available).
+ */
+ mt792x_mutex_acquire(dev);
+ if (!mt792x_mcu_get_wtbl_rate(dev, stats->wtbl_idx, &wtbl_rate))
+ wtbl_rate_valid = true;
+ mt792x_mutex_release(dev);
+
+ /* Print STA_STATS info */
+ seq_printf(s, "(STA) connected AP MAC Address = %pM\n", stats->mac_addr);
+ seq_printf(s, "%-22s BssIdx = [%u]\n", " ", stats->bss_idx);
+ seq_printf(s, "%-22s StaRecIdx = [%u]\n",
+ " ", stats->sta_idx);
+ seq_printf(s, "%-22s RSSI = %d\n", " ", rssi);
+ seq_printf(s, "%-22s Link_Speed = %u (kbit/s)\n", " ", tx_link_speed_kbps);
+
+ seq_printf(s, "%-22s temperature = %u\n",
+ " ", stats->temperature);
+
+ stats_value = le32_to_cpu(stats->transmit_count) - le32_to_cpu(stats->transmit_fail_count);
+ seq_printf(s, "%-22s Tx success = %u\n",
+ " ", stats_value);
+
+ stats_value = le32_to_cpu(stats->tx_fail_count) - le32_to_cpu(stats->tx_life_timeout_count);
+ seq_printf(s, "%-22s Tx fail to Rcv ACK after retry = %u\n",
+ " ", stats_value);
+
+ seq_printf(s, "%-22s Rx Mpdu = %u\n",
+ " ", le32_to_cpu(stats->mib[band_idx].rx_mpdu_cnt));
+
+ seq_printf(s, "%-22s Rx Fcs Error = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].fcs_error));
+ seq_printf(s, "%-22s Rx FIFO full = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].rx_fifo_full));
+
+ for (i = 0; i < STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM; i++)
+ seq_printf(s, "%-22s TRX_AGG_RANGE[%d] = %u (PPDU)\n",
+ " ", i, le32_to_cpu(stats->mib[band_idx].tx_range_ampdu_cnt[i]));
+
+ seq_printf(s, "%-22s MPDUs in AMPDUs transmitted = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].ampdu_tx_sf_cnt));
+ seq_printf(s, "%-22s MPDUs in AMPDUs transmitted with ACK reply = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].ampdu_tx_ack_sf_cnt));
+ seq_printf(s, "%-22s rate1_tx_cnt = %u\n",
+ " ", le32_to_cpu(stats->rate1_tx_cnt));
+ seq_printf(s, "%-22s rate1_fail_cnt = %u\n",
+ " ", le32_to_cpu(stats->rate1_fail_cnt));
+ seq_printf(s, "%-22s train_up = %u\n",
+ " ", le16_to_cpu(stats->train_up));
+ seq_printf(s, "%-22s train_down = %u\n",
+ " ", le16_to_cpu(stats->train_down));
+ seq_printf(s, "%-22s is_force_tx_stream = %u\n",
+ " ", stats->is_force_tx_stream);
+ seq_printf(s, "%-22s is_force_se_off = %u\n",
+ " ", stats->is_force_se_off);
+ seq_printf(s, "%-22s max_ampdu_factor = %u\n",
+ " ", stats->max_ampdu_factor);
+ seq_printf(s, "%-22s tx_rate_up_penalty = %u\n",
+ " ", stats->tx_rate_up_penalty);
+ seq_printf(s, "%-22s low_traffic_mode = %u\n",
+ " ", stats->low_traffic_mode);
+ seq_printf(s, "%-22s low_traffic_count = %u\n",
+ " ", stats->low_traffic_count);
+ seq_printf(s, "%-22s low_traffic_dashboard = %u\n",
+ " ", stats->low_traffic_dashboard);
+ seq_printf(s, "%-22s dynamic_sgi_state = %u\n",
+ " ", stats->dynamic_sgi_state);
+ seq_printf(s, "%-22s dynamic_sgi_score = %u\n",
+ " ", stats->dynamic_sgi_score);
+ seq_printf(s, "%-22s dynamic_bw_state = %u\n",
+ " ", stats->dynamic_bw_state);
+ seq_printf(s, "%-22s dynamic_gband_256qam_state = %u\n",
+ " ", stats->dynamic_gband_256qam_state);
+ seq_printf(s, "%-22s vht_non_sp_rate_state = %u\n",
+ " ", stats->vht_non_sp_rate_state);
+
+ /* Decode the TX Vector BBP latch to show the current TX MCS rate */
+ if (band_idx < MT7925_STA_STATS_BAND_NUM)
+ mt7925_print_last_tx_rate(s, stats->tx_vector[band_idx].txv);
+
+ /* Dump the whole firmware auto-rate table and mark the entry currently */
+ if (wtbl_rate_valid)
+ mt7925_dump_auto_rate_table(s, &wtbl_rate);
+
+ seq_puts(s, "\nmib state:\n");
+ /* ===Rx Related Counters=== */
+ seq_puts(s, "=== Rx Related Counters ===\n");
+ seq_printf(s, "%-22s Rx with CRC = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_FCS_ERR]);
+ seq_printf(s, "%-22s Rx drop due to out of resource = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_FIFO_OVERFLOW]);
+ seq_printf(s, "%-22s Rx Mpdu = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_MPDU]);
+ seq_printf(s, "%-22s Rx AMpdu = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_RX_COUNT]);
+ seq_printf(s, "%-22s Rx PF Drop = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_PF_DROP]);
+ seq_printf(s, "%-22s Rx Len Mismatch = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_LEN_MISMATCH]);
+
+ /* ===Phy/Timing Related Counters=== */
+ seq_puts(s, "\n=== Phy/Timing Related Counters ===\n");
+ seq_printf(s, "%-22s ChannelIdleCnt = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_CHANNEL_IDLE]);
+ seq_printf(s, "%-22s CCA_NAV_Tx_Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_CCA_NAV_TX_TIME]);
+ seq_printf(s, "%-22s Rx_MDRDY_CNT = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_MDRDY]);
+ seq_printf(s, "%-22s CCK_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_CCK_MDRDY_TIME]);
+ seq_printf(s, "%-22s OFDM_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME]);
+ seq_printf(s, "%-22s OFDM_GREEN_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME]);
+ seq_printf(s, "%-22s Prim CCA Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_P_CCA_TIME]);
+ seq_printf(s, "%-22s Sec CCA Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_S_CCA_TIME]);
+ seq_printf(s, "%-22s Prim ED Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_P_ED_TIME]);
+
+ /* ===Tx Related Counters(Generic)=== */
+ seq_puts(s, "\n=== Tx Related Counters(Generic) ===\n");
+ seq_printf(s, "%-22s BeaconTxCnt = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_BCN_TX]);
+ seq_printf(s, "%-22s Tx 40MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_40MHZ]);
+ seq_printf(s, "%-22s Tx 80MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_80MHZ]);
+ seq_printf(s, "%-22s Tx 160MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_160MHZ]);
+
+ /* ===BSSID[0] Related Counters=== */
+ seq_puts(s, "\n=== BSSID[0] Related Counters ===\n");
+ seq_printf(s, "%-22s BA Miss Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_BA_MISS]);
+ seq_printf(s, "%-22s RTS Tx Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_RTS_TX_CNT]);
+ seq_printf(s, "%-22s Frame Retry Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY]);
+ seq_printf(s, "%-22s Frame Retry 2 Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY_2]);
+ seq_printf(s, "%-22s RTS Retry Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_RTS_RETRY]);
+ seq_printf(s, "%-22s Ack Failed Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_ACK_FAIL]);
+
+ /* ===AMPDU Related Counters=== */
+ seq_puts(s, "\n=== AMPDU Related Counters ===\n");
+ seq_printf(s, "%-22s Tx AMPDU_Pkt_Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU]);
+ seq_printf(s, "%-22s Tx AMPDU_MPDU_Pkt_Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_MPDU]);
+ seq_printf(s, "%-22s AMPDU Tx success = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_ACKED]);
+
+ tx_total = mib_values[QUERY_MIB_CNT_AMPDU_MPDU];
+ tx_fail = mib_values[QUERY_MIB_CNT_AMPDU_MPDU] - mib_values[QUERY_MIB_CNT_AMPDU_ACKED];
+ tx_per = tx_total == 0 ? 0 : (1000 * (tx_fail)) / tx_total;
+ seq_printf(s, "%-22s AMPDU Tx fail count = %llu (MPDU), PER=%llu.%1llu%%\n",
+ " ",
+ tx_fail,
+ tx_per / 10, tx_per % 10);
+
+out:
+ kfree(stats);
+ return ret;
+}
+
int mt7925_init_debugfs(struct mt792x_dev *dev)
{
struct dentry *dir;
@@ -313,5 +1365,7 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
mt792x_pm_stats);
debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
+ debugfs_create_devm_seqfile(dev->mt76.dev, "stats", dir,
+ mt792x_stats);
return 0;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.c b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c
new file mode 100644
index 000000000000..9a47953062bc
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include "mt7925.h"
+#include "mcu.h"
+#include "mac.h"
+#include "stats.h"
+
+#define MT7925_STA_STATS_VERSION 1
+#define MT7925_STA_STATS_VALID 1U
+
+#define MT7925_MIB_MAX_CNT 32
+#define MT7925_MIB_CNT_MAX_NUM 512 /* firmware UNI_CMD_MIB_CNT_MAX_NUM upper bound */
+
+#define MT7925_LQ_BAND_NUM 4 /* Link quality band */
+
+const char * const mt7925_tx_mode_str[] = {
+ "CCK", "OFDM", "MM", "GF", "VHT", "PLR",
+ "PLRP", "ALR", "HE_SU", "HE_ER", "HE_TRIG", "HE_MU",
+ "EHT_ER", "TOF", "EHT_TRIG", "EHT_MU"
+};
+
+const char * const mt7925_tx_rate_cck_str[] = {
+ "1M", "2M", "5.5M", "11M", "N/A"
+};
+
+const char * const mt7925_tx_rate_bw_str[] = {
+ "BW20", "BW40", "BW80", "BW160/BW8080", "BW320", "N/A"
+};
+
+struct mt7925_sta_stats_req {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ u8 sta_rec_idx;
+ u8 rsv1;
+ u8 lls_read_clear;
+ u8 reset_counter;
+} __packed;
+
+struct mt7925_sta_stats_event {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ struct mt7925_sta_stats stats;
+} __packed;
+
+/* WTBL auto-rate request TLV, mirror Gen4m struct UNI_CMD_STAT_WTBL.
+ * The RA tag reuses UNI_CMD_ID_STATISTICS_WTBL (10).
+ */
+struct mt7925_wtbl_rate_req {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ __le16 wlan_idx;
+ __le16 rsv1;
+} __packed;
+
+/* WTBL auto-rate event TLV. Firmware answers cmd tag 10 with event tag 12. */
+struct mt7925_wtbl_rate_event {
+ u8 rsv[4];
+ struct mt7925_wtbl_rate rate;
+} __packed;
+
+/* fixed field, mirror UNI_(CMD|EVENT)_MIB_INFO */
+struct mt7925_mib_hdr {
+ u8 band_idx;
+ u8 rsv[3];
+} __packed;
+
+/* One requested/returned MIB counter, mirror UNI_(CMD|EVENT)_MIB_DATA_T. */
+struct mt7925_mib_data {
+ __le16 tag; /* UNI_(CMD|EVENT)_MIB_DATA = 0 */
+ __le16 len;
+ __le32 counter; /* MIB counter ID (enum mt7925_mib_counter) */
+ __le64 data; /* only valid on event; ignored on cmd */
+} __packed;
+
+/* per-band link quality entry, mirror UNI_LINK_QUALITY */
+struct mt7925_link_quality {
+ s8 rssi; /* moving-average beacon RSSI of connected AP */
+ s8 link_quality; /* always 0 */
+ __le16 link_speed; /* TX rate1, unit 500 Kb/s */
+ u8 medium_busy_pct; /* always 0 */
+ u8 is_ready; /* 0: fields invalid, 1: valid */
+ u8 rsv[2];
+} __packed;
+
+/* request TLV, mirror UNI_CMD_LINK_QUALITY (header + tag/len only) */
+struct mt7925_lq_req {
+ u8 rsv[4]; /* UNI_CMD_GET_STATISTICS fixed field */
+ __le16 tag; /* UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1 */
+ __le16 len;
+} __packed;
+
+/* event TLV, mirror UNI_EVENT_LINK_QUALITY */
+struct mt7925_lq_event {
+ u8 rsv[4]; /* UNI_EVENT_STATISTICS fixed field */
+ __le16 tag; /* UNI_EVENT_ID_STATISTICS_LINK_QUALITY = 1 */
+ __le16 len;
+ struct mt7925_link_quality lq[MT7925_LQ_BAND_NUM];
+} __packed;
+
+static void
+mt7925_get_ap_sta_rec_idx_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mt7925_sta_rec_lookup *lookup = data;
+ struct mt792x_vif *mvif;
+ struct mt792x_sta *msta;
+
+ if (lookup->sta_rec_idx >= 0)
+ return;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return;
+
+ mvif = (struct mt792x_vif *)vif->drv_priv;
+ msta = mvif->wep_sta;
+ if (!msta)
+ return;
+
+ if (!msta->deflink.wcid.sta)
+ return;
+
+ lookup->sta_rec_idx = msta->deflink.wcid.idx;
+}
+
+int
+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx)
+{
+ struct mt7925_sta_rec_lookup lookup = {
+ .sta_rec_idx = -1,
+ };
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+ ieee80211_iterate_active_interfaces(dev->mphy.hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_get_ap_sta_rec_idx_iter,
+ &lookup);
+
+ if (lookup.sta_rec_idx < 0)
+ return -ENOTCONN;
+
+ if (lookup.sta_rec_idx > U8_MAX)
+ return -ERANGE;
+
+ *sta_rec_idx = lookup.sta_rec_idx;
+
+ return 0;
+}
+
+/* Decode the TX Vector BBP latch and print the last TX rate */
+void
+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3])
+{
+ const char *mode_str = "N/A";
+ u8 rate, txmode, frmode, sgi, ldpc, nsts, stbc, spe, dcm, ersu106t;
+ s8 txpwr, pos_txpwr;
+ u32 txv[3];
+
+ txv[0] = le32_to_cpu(le_txv[0]);
+ txv[1] = le32_to_cpu(le_txv[1]);
+ txv[2] = le32_to_cpu(le_txv[2]);
+
+ if (txv[0] == 0xffffffff) {
+ seq_printf(s, "%-22s %s = %s\n", " ", "Last TX Rate", "N/A");
+ seq_printf(s, "%-22s %s = %s\n",
+ " ", "Chip Out TX Power", "N/A");
+ return;
+ }
+
+ rate = MT7925_TXV_GET_TX_RATE(txv);
+ txmode = MT7925_TXV_GET_TX_MODE(txv);
+ frmode = MT7925_TXV_GET_TX_FRMODE(txv);
+ nsts = MT7925_TXV_GET_TX_NSTS(txv) + 1;
+ sgi = MT7925_TXV_GET_TX_SGI(txv);
+ ldpc = MT7925_TXV_GET_TX_LDPC(txv);
+ stbc = MT7925_TXV_GET_TX_STBC(txv);
+ txpwr = MT7925_TXV_GET_TX_PWR(txv);
+ spe = MT7925_TXV_GET_TX_SPE_IDX(txv);
+ dcm = MT7925_TXV_GET_TX_DCM(txv);
+ ersu106t = MT7925_TXV_GET_TX_106T(txv);
+
+ if (dcm)
+ rate = MT7925_TXV_RATE_UNMASK_DCM(rate);
+ if (ersu106t)
+ rate = MT7925_TXV_RATE_UNMASK_106T(rate);
+
+ if (txmode < ARRAY_SIZE(mt7925_tx_mode_str))
+ mode_str = mt7925_tx_mode_str[txmode];
+
+ seq_printf(s, "%-22s %s = ", " ", "Last TX Rate");
+
+ /* rate/MCS field */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ",
+ rate < 4 ? mt7925_tx_rate_cck_str[rate] :
+ (((rate >= 5) && (rate <= 7)) ?
+ mt7925_tx_rate_cck_str[rate - 4] :
+ mt7925_tx_rate_cck_str[4]));
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_hw_rate_ofdm_str(rate));
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF)
+ seq_printf(s, "MCS%d, ", rate);
+ else
+ seq_printf(s, "%s%d_MCS%d, ",
+ stbc ? "NSTS" : "NSS", nsts, rate);
+
+ /* bandwidth */
+ seq_printf(s, "%s, ",
+ frmode < 5 ? mt7925_tx_rate_bw_str[frmode] :
+ mt7925_tx_rate_bw_str[5]);
+
+ /* GI / preamble */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ", rate < 4 ? "LP" : "SP");
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ ; /* OFDM has no GI/preamble to print */
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF ||
+ txmode == MT7925_TX_RATE_MODE_VHT ||
+ txmode == MT7925_TX_RATE_MODE_PLR)
+ seq_printf(s, "%s, ", sgi == 0 ? "LGI" : "SGI");
+ else
+ seq_printf(s, "%s, ",
+ sgi == 0 ? "SGI" : (sgi == 1 ? "MGI" : "LGI"));
+
+ /* mode, DCM, 106t, STBC, coding, SPE */
+ seq_printf(s, "%s%s%s%s%s%s%d\n",
+ mode_str,
+ dcm ? ", DCM" : "", ersu106t ? ", 106t" : "",
+ stbc ? ", STBC, " : ", ", ldpc == 0 ? "BCC" : "LDPC",
+ ", SPE", spe);
+
+ /* chip out TX power (unit: 0.5 dBm) */
+ pos_txpwr = (txpwr < 0) ? (~txpwr + 1) : txpwr;
+ seq_printf(s, "%-22s %s = %c%d.%1d dBm\n",
+ " ", "Chip Out TX Power",
+ (txpwr < 0) ? '-' : '+',
+ (pos_txpwr / 2), 5 * (pos_txpwr % 2));
+}
+
+/* Guard interval used for the auto-rate entries, mirror Gen4m
+ * connac3x_get_sgi_info_from_fw(): pick the per-mode/per-BW flag.
+ */
+u8
+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap)
+{
+ bool he = (txmode == MT7925_TX_RATE_MODE_HE_SU ||
+ txmode == MT7925_TX_RATE_MODE_HE_ER ||
+ txmode == MT7925_TX_RATE_MODE_HE_TRIG ||
+ txmode == MT7925_TX_RATE_MODE_HE_MU);
+ bool eht = (txmode == MT7925_TX_RATE_MODE_EHT_ER ||
+ txmode == MT7925_TX_RATE_MODE_EHT_TRIG ||
+ txmode == MT7925_TX_RATE_MODE_EHT_MU);
+
+ if (eht)
+ return wr->gi_eht;
+
+ switch (fcap) {
+ case MT7925_BW_20:
+ return he ? wr->g2_he : wr->g2;
+ case MT7925_BW_40:
+ return he ? wr->g4_he : wr->g4;
+ case MT7925_BW_80:
+ return he ? wr->g8_he : wr->g8;
+ case MT7925_BW_160:
+ return he ? wr->g16_he : wr->g16;
+ default:
+ return 0;
+ }
+}
+
+/* Coding used for the auto-rate entries, mirror Gen4m
+ * connac3x_wtbl_get_ldpc_info_from_fw(): 0 = BCC, 1 = LDPC.
+ */
+u8
+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode)
+{
+ switch (txmode) {
+ case MT7925_TX_RATE_MODE_HTMIX:
+ case MT7925_TX_RATE_MODE_HTGF:
+ return wr->ht_ldpc;
+ case MT7925_TX_RATE_MODE_VHT:
+ return wr->vht_ldpc;
+ case MT7925_TX_RATE_MODE_HE_SU:
+ case MT7925_TX_RATE_MODE_HE_ER:
+ case MT7925_TX_RATE_MODE_HE_TRIG:
+ case MT7925_TX_RATE_MODE_HE_MU:
+ return wr->he_ldpc;
+ case MT7925_TX_RATE_MODE_EHT_ER:
+ case MT7925_TX_RATE_MODE_EHT_TRIG:
+ case MT7925_TX_RATE_MODE_EHT_MU:
+ return wr->eht_ldpc;
+ default:
+ return 0;
+ }
+}
+
+int mt792x_mcu_get_stat(struct mt792x_dev *dev,
+ struct mt7925_sta_stats *stats)
+{
+ u16 len = 0;
+ int ret = 0;
+ struct mt7925_sta_stats_event *event = NULL;
+ struct sk_buff *skb = NULL;
+ struct mt7925_sta_stats_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_STA),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ .lls_read_clear = false,
+ .reset_counter = false,
+ };
+
+ ret = mt7925_get_ap_sta_rec_idx(dev, &req.sta_rec_idx);
+ if (ret)
+ return ret;
+
+ if (!stats)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO),
+ &req, sizeof(req), true, &skb);
+
+ if (ret)
+ return ret;
+
+ event = (struct mt7925_sta_stats_event *)skb->data;
+
+ if (le16_to_cpu(event->tag) != UNI_CMD_ID_STATISTICS_STA) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ len = le16_to_cpu(event->len);
+ if (len < sizeof(*event) - sizeof(event->rsv) ||
+ len > skb->len - sizeof(event->rsv) || !IS_ALIGNED(len, 4)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ if (event->stats.version != MT7925_STA_STATS_VERSION) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ if (!(le32_to_cpu(event->stats.flags) & MT7925_STA_STATS_VALID)) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ memcpy(stats, &event->stats, sizeof(*stats));
+
+out:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,
+ const u32 *counters, u8 counter_num,
+ u64 *values)
+{
+ u8 *req;
+ u8 *tlv, *tlv_end;
+ u16 req_len;
+ int i, ret;
+ struct mt7925_mib_hdr *req_hdr;
+ struct mt7925_mib_data *req_tlv;
+ struct sk_buff *skb;
+
+ if (!counters || !values || !counter_num ||
+ counter_num > MT7925_MIB_MAX_CNT)
+ return -EINVAL;
+
+ /* clear output; counters not returned by firmware stay 0 */
+ memset(values, 0, counter_num * sizeof(*values));
+
+ req_len = sizeof(struct mt7925_mib_hdr) +
+ counter_num * sizeof(struct mt7925_mib_data);
+
+ req = kzalloc(req_len, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req_hdr = (struct mt7925_mib_hdr *)req;
+ req_hdr->band_idx = band_idx;
+
+ req_tlv = (struct mt7925_mib_data *)(req + sizeof(*req_hdr));
+ for (i = 0; i < counter_num; i++) {
+ req_tlv[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA);
+ req_tlv[i].len = cpu_to_le16(sizeof(struct mt7925_mib_data));
+ req_tlv[i].counter = cpu_to_le32(counters[i]);
+ }
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_MIB_INFO), req,
+ req_len, true, &skb);
+ kfree(req);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(struct mt7925_mib_hdr)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ /* walk the returned TLVs, match each counter ID to the request */
+ tlv = skb->data + sizeof(struct mt7925_mib_hdr);
+ tlv_end = skb->data + skb->len;
+
+ while (tlv + sizeof(struct mt7925_mib_data) <= tlv_end) {
+ struct mt7925_mib_data *evt_tlv =
+ (struct mt7925_mib_data *)tlv;
+ u16 tag = le16_to_cpu(evt_tlv->tag);
+ u16 len = le16_to_cpu(evt_tlv->len);
+ u32 counter;
+
+ if (len < sizeof(struct mt7925_mib_data) ||
+ tlv + len > tlv_end)
+ break;
+
+ if (tag == UNI_CMD_MIB_DATA) {
+ counter = le32_to_cpu(evt_tlv->counter);
+
+ if (counter < MT7925_MIB_CNT_MAX_NUM) {
+ for (i = 0; i < counter_num; i++) {
+ if (counters[i] == counter) {
+ values[i] =
+ le64_to_cpu(evt_tlv->data);
+ break;
+ }
+ }
+ }
+ }
+
+ tlv += len;
+ }
+
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+
+ return ret;
+}
+
+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx)
+{
+ char * const ofdm_str[] = {
+ "6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M", "N/A"
+ };
+
+ switch (ofdm_idx) {
+ case 11: return ofdm_str[0]; /* 6M */
+ case 15: return ofdm_str[1]; /* 9M */
+ case 10: return ofdm_str[2]; /* 12M */
+ case 14: return ofdm_str[3]; /* 18M */
+ case 9: return ofdm_str[4]; /* 24M */
+ case 13: return ofdm_str[5]; /* 36M */
+ case 8: return ofdm_str[6]; /* 48M */
+ case 12: return ofdm_str[7]; /* 54M */
+ default: return ofdm_str[8];
+ }
+}
+
+/* mt792x_mcu_get_wtbl_rate - fetch the firmware auto-rate table for a WCID
+ * @dev: device
+ * @wlan_idx: HW WTBL / WCID index (see mt7925_sta_stats.wtbl_idx)
+ * @rate: out, filled with the auto-rate table snapshot
+ *
+ * Mirror of Gen4m wlanoidGetStatWtbl(): send UNI_CMD_GET_STAT_INFO with the
+ * WTBL rate TLV (cmd tag 10) and parse the reply (event tag 12) into @rate.
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,
+ struct mt7925_wtbl_rate *rate)
+{
+ int ret;
+ struct mt7925_wtbl_rate_event *event = NULL;
+ struct sk_buff *skb = NULL;
+ struct mt7925_wtbl_rate_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_WTBL),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ .wlan_idx = cpu_to_le16(wlan_idx),
+ };
+
+ if (!rate)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO),
+ &req, sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(*event)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ event = (struct mt7925_wtbl_rate_event *)skb->data;
+
+ if (le16_to_cpu(event->rate.tag) != MT7925_UNI_EVENT_STATISTICS_WTBL) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ memcpy(rate, &event->rate, sizeof(*rate));
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+/**
+ * mt7925_mcu_get_link_quality - query RSSI and TX link speed for a band
+ * @dev: device
+ * @band_idx: band/BSS index (0..3)
+ * @rssi: out, moving-average beacon RSSI in dBm (may be NULL)
+ * @tx_link_speed_kbps: out, TX rate in kbit/s (may be NULL)
+ *
+ * Return: 0 on success, -ENOENT if the band's link quality is not ready.
+ *
+ * Only RSSI and TX rate come from firmware here; for RX rate read wcid->rate.
+ */
+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,
+ s8 *rssi, u32 *tx_link_speed_kbps)
+{
+ int ret;
+ struct mt7925_lq_event *event;
+ struct mt7925_link_quality *lq;
+ struct sk_buff *skb;
+ struct mt7925_lq_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_LINK_QUALITY),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ };
+
+ if (band_idx >= MT7925_LQ_BAND_NUM)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO), &req,
+ sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(*event)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ event = (struct mt7925_lq_event *)skb->data;
+
+ if (le16_to_cpu(event->tag) != UNI_CMD_ID_STATISTICS_LINK_QUALITY) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ lq = &event->lq[band_idx];
+ if (!lq->is_ready) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (rssi)
+ *rssi = lq->rssi;
+
+ /* firmware reports TX rate in 500 Kb/s units -> kbit/s */
+ if (tx_link_speed_kbps)
+ *tx_link_speed_kbps = le16_to_cpu(lq->link_speed) * 500;
+
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.h b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h
new file mode 100644
index 000000000000..2e29b20cf27b
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#ifndef __MT7925_STATS_H
+#define __MT7925_STATS_H
+
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include "../mt76_connac_mcu.h"
+
+#define MT7925_STA_STATS_AC_NUM 4
+#define MT7925_STA_STATS_BAND_NUM 2
+#define MT7925_STA_STATS_AGG_RANGE_NUM 16
+#define MT7925_STA_STATS_TX_QUALITY_NUM 4
+#define MT7925_TX_MODE_NUM 16
+#define MT7925_TX_RATE_CCK_NUM 5
+#define MT7925_TX_RATE_BW_NUM 6
+
+/* HW rate code (16-bit) field extract, mirror Gen4m CONNAC3X_HW_TX_RATE_TO_*.
+ * The auto-rate table stores one rate code per entry.
+ */
+#define MT7925_HW_RATE_TO_MCS(_x) (((_x) & GENMASK(5, 0)) >> 0)
+#define MT7925_HW_RATE_TO_MODE(_x) (((_x) & GENMASK(9, 6)) >> 6)
+#define MT7925_HW_RATE_TO_NSS(_x) (((_x) & GENMASK(13, 10)) >> 10)
+#define MT7925_HW_RATE_TO_STBC(_x) (((_x) & BIT(14)) >> 14)
+#define MT7925_HW_RATE_TO_DCM(_x) (((_x) & BIT(4)) >> 4)
+#define MT7925_HW_RATE_TO_106T(_x) (((_x) & BIT(5)) >> 5)
+#define MT7925_HW_RATE_UNMASK_DCM(_r) ((u8)(_r) & 0xef)
+#define MT7925_HW_RATE_UNMASK_106T(_r) ((u8)(_r) & 0xdf)
+
+/* Number of entries in the firmware auto-rate table (mirror Gen4m
+ * AUTO_RATE_NUM). RA cycles through these and reports which one is in use
+ * via wtbl->rate_idx.
+ */
+#define MT7925_AUTO_RATE_NUM 8
+
+/* Firmware EVENT_STATISTICS sub-event tags. The WTBL rate request uses cmd tag
+ * UNI_CMD_ID_STATISTICS_WTBL (10) but the firmware replies with event tag 12.
+ */
+#define MT7925_UNI_EVENT_STATISTICS_WTBL 12
+
+/* CONNAC3X TX Vector BBP latch bit fields (mirror cmm_asic_connac3x.h).
+ * These operate on the 3-DWORD tx_vector[band].txv[3] array.
+ */
+#define MT7925_TXV_GET_TX_RATE(_txv) ((_txv)[2] & 0x7f)
+#define MT7925_TXV_GET_TX_LDPC(_txv) (((_txv)[2] & (0x1 << 7)) >> 7)
+#define MT7925_TXV_GET_TX_STBC(_txv) (((_txv)[0] & (0x3 << 6)) >> 6)
+#define MT7925_TXV_GET_TX_FRMODE(_txv) (((_txv)[0] & (0x7 << 8)) >> 8)
+#define MT7925_TXV_GET_TX_MODE(_txv) (((_txv)[0] & (0xf << 12)) >> 12)
+#define MT7925_TXV_GET_TX_NSTS(_txv) (((_txv)[2] & (0xfU << 28)) >> 28)
+#define MT7925_TXV_GET_TX_PWR(_txv) (((_txv)[0] & (0xff << 16)) >> 16)
+#define MT7925_TXV_GET_TX_SGI(_txv) (((_txv)[1] & (0x3 << 26)) >> 26)
+#define MT7925_TXV_GET_TX_SPE_IDX(_txv) (((_txv)[0] & (0x1f << 0)) >> 0)
+#define MT7925_TXV_GET_TX_DCM(_txv) (((_txv)[2] & (0x1 << 4)) >> 4)
+#define MT7925_TXV_GET_TX_106T(_txv) (((_txv)[2] & (0x1 << 5)) >> 5)
+#define MT7925_TXV_RATE_UNMASK_DCM(_r) ((u8)(_r) & 0xef)
+#define MT7925_TXV_RATE_UNMASK_106T(_r) ((u8)(_r) & 0xdf)
+
+extern const char * const mt7925_tx_mode_str[MT7925_TX_MODE_NUM];
+extern const char * const mt7925_tx_rate_cck_str[MT7925_TX_RATE_CCK_NUM];
+extern const char * const mt7925_tx_rate_bw_str[MT7925_TX_RATE_BW_NUM];
+
+/* TX rate mode, mirror Gen4m TX_RATE_MODE_* (see nic_rate.h). Used to decode
+ * both the TX Vector BBP latch and the firmware auto-rate table rate codes.
+ */
+enum mt7925_tx_rate_mode {
+ MT7925_TX_RATE_MODE_CCK = 0,
+ MT7925_TX_RATE_MODE_OFDM = 1,
+ MT7925_TX_RATE_MODE_HTMIX = 2,
+ MT7925_TX_RATE_MODE_HTGF = 3,
+ MT7925_TX_RATE_MODE_VHT = 4,
+ MT7925_TX_RATE_MODE_PLR = 5,
+ MT7925_TX_RATE_MODE_HE_SU = 8,
+ MT7925_TX_RATE_MODE_HE_ER = 9,
+ MT7925_TX_RATE_MODE_HE_TRIG = 10,
+ MT7925_TX_RATE_MODE_HE_MU = 11,
+ MT7925_TX_RATE_MODE_EHT_ER = 13,
+ MT7925_TX_RATE_MODE_EHT_TRIG = 14,
+ MT7925_TX_RATE_MODE_EHT_MU = 15,
+};
+
+enum stats_query_mib_array_index {
+ QUERY_MIB_CNT_RX_FCS_ERR = 0,
+ QUERY_MIB_CNT_RX_FIFO_OVERFLOW,
+ QUERY_MIB_CNT_RX_MPDU,
+ QUERY_MIB_CNT_AMPDU_RX_COUNT,
+ QUERY_MIB_CNT_PF_DROP,
+ QUERY_MIB_CNT_LEN_MISMATCH,
+ QUERY_MIB_CNT_CHANNEL_IDLE,
+ QUERY_MIB_CNT_CCA_NAV_TX_TIME,
+ QUERY_MIB_CNT_MDRDY,
+ QUERY_MIB_CNT_RX_CCK_MDRDY_TIME,
+ QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,
+ QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,
+ QUERY_MIB_CNT_P_CCA_TIME,
+ QUERY_MIB_CNT_S_CCA_TIME,
+ QUERY_MIB_CNT_P_ED_TIME,
+ QUERY_MIB_CNT_BCN_TX,
+ QUERY_MIB_CNT_TX_BW_40MHZ,
+ QUERY_MIB_CNT_TX_BW_80MHZ,
+ QUERY_MIB_CNT_TX_BW_160MHZ,
+ QUERY_MIB_CNT_BSS0_BA_MISS,
+ QUERY_MIB_CNT_BSS0_RTS_TX_CNT,
+ QUERY_MIB_CNT_BSS0_FRAME_RETRY,
+ QUERY_MIB_CNT_BSS0_FRAME_RETRY_2,
+ QUERY_MIB_CNT_BSS0_RTS_RETRY,
+ QUERY_MIB_CNT_BSS0_ACK_FAIL,
+ QUERY_MIB_CNT_AMPDU,
+ QUERY_MIB_CNT_AMPDU_MPDU,
+ QUERY_MIB_CNT_AMPDU_ACKED,
+ QUERY_MIB_CNT_MAX_NUM,
+};
+
+enum stats_trx_agg_range {
+ STAT_MIB_CNT_TRX_AGG_RANGE0_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE1_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE2_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE3_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE4_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE5_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE6_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE7_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM
+};
+
+enum {
+ MT7925_BW_20 = 0,
+ MT7925_BW_40,
+ MT7925_BW_80,
+ MT7925_BW_160,
+};
+
+struct mt7925_sta_stats_ac {
+ __le32 tx_fail;
+ __le32 tx_retry;
+} __packed;
+
+struct mt7925_sta_stats_tx_vector {
+ __le32 txv[3];
+} __packed;
+
+struct mt7925_sta_stats_mib {
+ __le32 rx_mpdu_cnt;
+ __le32 fcs_error;
+ __le32 rx_fifo_full;
+ __le32 ampdu_tx_sf_cnt;
+ __le32 ampdu_tx_ack_sf_cnt;
+ __le32 tx_range_ampdu_cnt[MT7925_STA_STATS_AGG_RANGE_NUM];
+} __packed;
+
+/* Firmware EVENT_STA_STATISTICS body. */
+struct mt7925_sta_stats {
+ u8 version;
+ u8 rsv[3];
+ __le32 flags;
+
+ u8 sta_idx;
+ u8 bss_idx;
+ u8 wtbl_idx;
+ u8 rsv1;
+
+ u8 mac_addr[ETH_ALEN];
+ u8 per;
+ u8 rcpi;
+
+ __le32 phy_mode;
+ __le16 link_speed; /* 0.5 Mbit/s units */
+ u8 link_quality;
+ u8 link_reserved;
+
+ __le32 tx_count;
+ __le32 tx_fail_count;
+ __le32 tx_life_timeout_count;
+ __le32 tx_done_air_time;
+ __le32 transmit_count;
+ __le32 transmit_fail_count;
+
+ struct mt7925_sta_stats_ac ac[MT7925_STA_STATS_AC_NUM];
+
+ u8 temperature;
+ u8 skip_ar;
+ u8 ar_table_idx;
+ u8 rate_entry_idx;
+ u8 rate_entry_idx_prev;
+ u8 tx_sgi_detect_pass_cnt;
+ u8 ave_per;
+ u8 rsv2;
+
+ __le32 agg_range_ctrl[2];
+ u8 range_mode;
+ u8 rsv3[3];
+ __le32 agg_range_ctrl_ext[6];
+
+ u8 ar_state_curr;
+ u8 ar_state_prev;
+ u8 ar_action_type;
+ u8 highest_rate_cnt;
+ u8 lowest_rate_cnt;
+ u8 rsv4;
+ __le16 train_up;
+ __le16 train_down;
+ u8 rsv5[2];
+ __le32 rate1_tx_cnt;
+ __le32 rate1_fail_cnt;
+
+ struct mt7925_sta_stats_tx_vector tx_vector[MT7925_STA_STATS_BAND_NUM];
+ struct mt7925_sta_stats_mib mib[MT7925_STA_STATS_BAND_NUM];
+
+ u8 is_force_tx_stream;
+ u8 is_force_se_off;
+ __le16 ra_running_cnt;
+ u8 ra_status;
+ u8 max_ampdu_factor;
+ u8 tx_quality[MT7925_STA_STATS_TX_QUALITY_NUM];
+ u8 tx_rate_up_penalty;
+ u8 low_traffic_mode;
+ u8 low_traffic_count;
+ u8 low_traffic_dashboard;
+ u8 dynamic_sgi_state;
+ u8 dynamic_sgi_score;
+ u8 dynamic_bw_state;
+ u8 dynamic_gband_256qam_state;
+ u8 vht_non_sp_rate_state;
+ u8 rsv6[3];
+ u8 rsv7[2];
+} __packed;
+
+struct mt7925_sta_rec_lookup {
+ int sta_rec_idx;
+};
+
+/* Firmware auto-rate table snapshot, mirror Gen4m struct UNI_EVENT_STAT_WTBL.
+ *
+ * The firmware RA maintains up to MT7925_AUTO_RATE_NUM candidate rates in
+ * rate_code[]. rate_idx tells which entry is currently in use, so a dump can
+ * point at it with "-->". fcap is the bandwidth capability and the ldpc/sgi
+ * flags describe the per-mode coding/guard-interval used for those rates.
+ */
+struct mt7925_wtbl_rate {
+ __le16 tag;
+ __le16 len;
+ u8 rx_rcpi0;
+ u8 rx_rcpi1;
+ u8 cbrn;
+ u8 fcap;
+ u8 spe_idx;
+ u8 g2; /* SGI enabled, non-HE, BW20 */
+ u8 g4; /* SGI enabled, non-HE, BW40 */
+ u8 g8; /* SGI enabled, non-HE, BW80 */
+ u8 g16; /* SGI enabled, non-HE, BW160 */
+ u8 g2_he; /* GI, HE, BW20 */
+ u8 g4_he; /* GI, HE, BW40 */
+ u8 g8_he; /* GI, HE, BW80 */
+ u8 g16_he; /* GI, HE, BW160 */
+ u8 gi_eht; /* GI, EHT */
+ u8 ht_ldpc;
+ u8 vht_ldpc;
+ u8 he_ldpc;
+ u8 eht_ldpc;
+ u8 af;
+ u8 rate_idx; /* index into rate_code[] currently in use */
+ __le16 rate_code[MT7925_AUTO_RATE_NUM];
+ __le32 rsv[4];
+} __packed;
+
+int
+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx);
+
+void
+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3]);
+
+u8
+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap);
+
+u8
+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode);
+
+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx);
+
+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,
+ struct mt7925_wtbl_rate *rate);
+
+int mt792x_mcu_get_stat(struct mt792x_dev *dev,
+ struct mt7925_sta_stats *stats);
+
+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,
+ const u32 *counters, u8 counter_num,
+ u64 *values);
+
+/* GET_STAT_INFO (UNI_CMD_ID_GET_STATISTICS = 0x23),
+ * sub-command UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1.
+ *
+ * @band_idx: band/BSS index (0..3)
+ * @rssi: out, moving-average beacon RSSI (dBm), may be NULL
+ * @tx_link_speed_kbps: out, TX link speed in kbit/s, may be NULL
+ *
+ * Returns 0 on success, -ENOENT if the band's link quality is not ready.
+ *
+ * NOTE: firmware provides only RSSI and TX rate here. RX rate/bandwidth are
+ * not in this event; read wcid->rate for the RX rate.
+ */
+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,
+ s8 *rssi, u32 *tx_link_speed_kbps);
+
+#endif
--
2.45.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose
2026-08-24 3:07 [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose Eason Lai
@ 2026-08-25 4:00 ` kernel test robot
2026-08-25 7:55 ` kernel test robot
2026-08-27 5:54 ` [PATCH v2] " Eason Lai
2 siblings, 0 replies; 5+ messages in thread
From: kernel test robot @ 2026-08-25 4:00 UTC (permalink / raw)
To: Eason Lai, nbd, lorenzo
Cc: oe-kbuild-all, linux-wireless, linux-mediatek, kun.wu, deren.wu,
sean.wang, quan.zhou, ryder.lee, leon.yen, litien.chang, jb.tsai,
michael.shih, eason.lai
Hi Eason,
kernel test robot noticed the following build errors:
[auto build test ERROR on wireless-next/main]
[also build test ERROR on linus/master next-20260821]
[cannot apply to wireless/main v7.2]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Eason-Lai/wifi-mt76-mt7928-Add-debugfs-for-stats-to-provide-debug-purpose/20260824-110746
base: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
patch link: https://lore.kernel.org/r/20260824030746.60706-1-eason.lai%40mediatek.com
patch subject: [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260825/202608251101.iQGUrSd0-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260825/202608251101.iQGUrSd0-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608251101.iQGUrSd0-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_fixedrate_set':
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:312:16: error: variable 'rate' has initializer but incomplete type
312 | struct mt7925_ra_fixed_rate_v1 rate = {};
| ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:312:40: error: storage size of 'rate' isn't known
312 | struct mt7925_ra_fixed_rate_v1 rate = {};
| ^~~~
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:314:12: error: 'struct mt792x_dev' has no member named 'fixed_rate'
314 | dev->fixed_rate = val;
| ^~
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:329:9: error: implicit declaration of function 'mt7925_mcu_set_fixed_rate'; did you mean 'mt7925_mcu_get_rx_rate'? [-Werror=implicit-function-declaration]
329 | mt7925_mcu_set_fixed_rate(dev, &rate);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
| mt7925_mcu_get_rx_rate
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:312:40: warning: unused variable 'rate' [-Wunused-variable]
312 | struct mt7925_ra_fixed_rate_v1 rate = {};
| ^~~~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_fixedrate_get':
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:340:19: error: 'struct mt792x_dev' has no member named 'fixed_rate'
340 | *val = dev->fixed_rate;
| ^~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_autorate_set':
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:364:12: error: 'struct mt792x_dev' has no member named 'auto_rate'
364 | dev->auto_rate = val;
| ^~
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:367:9: error: implicit declaration of function 'mt7925_mcu_set_auto_rate'; did you mean 'mt7925_mcu_get_rx_rate'? [-Werror=implicit-function-declaration]
367 | mt7925_mcu_set_auto_rate(dev, wlan_idx, enable, mode);
| ^~~~~~~~~~~~~~~~~~~~~~~~
| mt7925_mcu_get_rx_rate
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_autorate_get':
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:378:19: error: 'struct mt792x_dev' has no member named 'auto_rate'
378 | *val = dev->auto_rate;
| ^~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_wtbl_idx_set':
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:400:12: error: 'struct mt792x_dev' has no member named 'wtbl_dump_idx'
400 | dev->wtbl_dump_idx = (u32)val;
| ^~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_wtbl_idx_get':
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:410:19: error: 'struct mt792x_dev' has no member named 'wtbl_dump_idx'
410 | *val = dev->wtbl_dump_idx;
| ^~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_wtbl_read':
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:422:22: error: 'struct mt792x_dev' has no member named 'wtbl_dump_idx'
422 | u32 idx = dev->wtbl_dump_idx;
| ^~
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:443:16: error: implicit declaration of function 'mt7925_mac_wtbl_lmac_addr'; did you mean 'mt7925_mac_wtbl_update'? [-Werror=implicit-function-declaration]
443 | base = mt7925_mac_wtbl_lmac_addr(dev, idx, 0);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
| mt7925_mac_wtbl_update
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_vefuse_read':
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:474:23: error: 'struct mt792x_dev' has no member named 'vefuse_cap'
474 | u16 size = dev->vefuse_cap.size;
| ^~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:479:17: error: 'struct mt792x_dev' has no member named 'vefuse_cap'
479 | if (!dev->vefuse_cap.present || !size) {
| ^~
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:492:23: error: implicit declaration of function 'mt7925_mcu_read_vefuse'; did you mean 'mt7925_mcu_del_dev'? [-Werror=implicit-function-declaration]
492 | ret = mt7925_mcu_read_vefuse(dev, off, chunk, buf + off);
| ^~~~~~~~~~~~~~~~~~~~~~
| mt7925_mcu_del_dev
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_perf_ind_set':
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:520:16: error: implicit declaration of function 'mt792x_perf_ind_trigger'; did you mean 'mt7925_perf_ind_set'? [-Werror=implicit-function-declaration]
520 | return mt792x_perf_ind_trigger(dev, val);
| ^~~~~~~~~~~~~~~~~~~~~~~
| mt7925_perf_ind_set
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_perf_ind_enable_get':
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:529:19: error: 'struct mt792x_dev' has no member named 'perf'
529 | *val = dev->perf.enabled;
| ^~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_perf_ind_enable_set':
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:538:12: error: 'struct mt792x_dev' has no member named 'perf'
538 | dev->perf.enabled = !!val;
| ^~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_coex_info':
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:555:15: error: implicit declaration of function 'mt7925_mcu_chip_config_query'; did you mean 'mt7925_mcu_chip_config'? [-Werror=implicit-function-declaration]
555 | ret = mt7925_mcu_chip_config_query(dev, "coexBwcGetModeInfo 0",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
| mt7925_mcu_chip_config
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:564:31: error: 'CHIP_CONFIG_TYPE_ASCII' undeclared (first use in this function)
564 | else if (resp_type == CHIP_CONFIG_TYPE_ASCII)
| ^~~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:564:31: note: each undeclared identifier is reported only once for each function it appears in
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c: In function 'mt7925_dmashdl_read':
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:603:45: warning: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'long unsigned int' [-Wformat=]
603 | seq_printf(s, "\t\tPLE pkt max=0x%03x, PSE pkt max=0x%03x\n",
| ~~~^
| |
| unsigned int
| %03lx
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:603:65: warning: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'long unsigned int' [-Wformat=]
603 | seq_printf(s, "\t\tPLE pkt max=0x%03x, PSE pkt max=0x%03x\n",
| ~~~^
| |
| unsigned int
| %03lx
In file included from drivers/net/wireless/mediatek/mt76/mt7925/../mt76_connac.h:7,
from drivers/net/wireless/mediatek/mt76/mt7925/../mt76_connac_mcu.h:7,
from drivers/net/wireless/mediatek/mt76/mt7925/../mt792x.h:10,
from drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h:7,
from drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:4:
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:607:28: error: 'MT_DMASHDL_ERROR_FLAG_CTRL' undeclared (first use in this function)
607 | val = mt76_rr(dev, MT_DMASHDL_ERROR_FLAG_CTRL);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/../mt76.h:1196:69: note: in definition of macro 'mt76_rr'
1196 | #define mt76_rr(dev, ...) (dev)->mt76.bus->rr(&((dev)->mt76), __VA_ARGS__)
| ^~~~~~~~~~~
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:613:43: error: implicit declaration of function 'MT_DMASHDL_STATUS_RD_GP' [-Werror=implicit-function-declaration]
613 | u32 status = mt76_rr(dev, MT_DMASHDL_STATUS_RD_GP(i));
| ^~~~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/../mt76.h:1196:69: note: in definition of macro 'mt76_rr'
1196 | #define mt76_rr(dev, ...) (dev)->mt76.bus->rr(&((dev)->mt76), __VA_ARGS__)
| ^~~~~~~~~~~
In file included from <command-line>:
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:614:37: error: 'MT_DMASHDL_STATUS_RD_GP_RSV' undeclared (first use in this function)
614 | u32 rsv = FIELD_GET(MT_DMASHDL_STATUS_RD_GP_RSV, status);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:682:23: note: in definition of macro '__compiletime_assert'
682 | if (!(condition)) \
| ^~~~~~~~~
include/linux/compiler_types.h:702:9: note: in expansion of macro '_compiletime_assert'
702 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ^~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:40:37: note: in expansion of macro 'compiletime_assert'
40 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ^~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:79:9: note: in expansion of macro 'BUILD_BUG_ON_MSG'
79 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
| ^~~~~~~~~~~~~~~~
include/linux/bitfield.h:63:43: note: in expansion of macro '__unsigned_scalar_typeof'
63 | #define __bf_cast_unsigned(type, x) ((__unsigned_scalar_typeof(type))(x))
| ^~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:79:26: note: in expansion of macro '__bf_cast_unsigned'
79 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
| ^~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:177:17: note: in expansion of macro '__BF_FIELD_CHECK_REG'
177 | __BF_FIELD_CHECK_REG(_mask, _reg, "FIELD_GET: "); \
| ^~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:614:27: note: in expansion of macro 'FIELD_GET'
614 | u32 rsv = FIELD_GET(MT_DMASHDL_STATUS_RD_GP_RSV, status);
| ^~~~~~~~~
>> drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:615:37: error: 'MT_DMASHDL_STATUS_RD_GP_SRC' undeclared (first use in this function)
615 | u32 src = FIELD_GET(MT_DMASHDL_STATUS_RD_GP_SRC, status);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:682:23: note: in definition of macro '__compiletime_assert'
682 | if (!(condition)) \
| ^~~~~~~~~
include/linux/compiler_types.h:702:9: note: in expansion of macro '_compiletime_assert'
702 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ^~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:40:37: note: in expansion of macro 'compiletime_assert'
40 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ^~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:79:9: note: in expansion of macro 'BUILD_BUG_ON_MSG'
79 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
| ^~~~~~~~~~~~~~~~
include/linux/bitfield.h:63:43: note: in expansion of macro '__unsigned_scalar_typeof'
63 | #define __bf_cast_unsigned(type, x) ((__unsigned_scalar_typeof(type))(x))
| ^~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:79:26: note: in expansion of macro '__bf_cast_unsigned'
79 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
| ^~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:177:17: note: in expansion of macro '__BF_FIELD_CHECK_REG'
177 | __BF_FIELD_CHECK_REG(_mask, _reg, "FIELD_GET: "); \
| ^~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:615:27: note: in expansion of macro 'FIELD_GET'
615 | u32 src = FIELD_GET(MT_DMASHDL_STATUS_RD_GP_SRC, status);
| ^~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:617:65: warning: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'long unsigned int' [-Wformat=]
617 | seq_printf(s, "\tGroup %2d: quota(min/max)=0x%03x/0x%03x, used/rsv=0x%03x/0x%03x\n",
| ~~~^
| |
| unsigned int
| %03lx
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:617:72: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'long unsigned int' [-Wformat=]
617 | seq_printf(s, "\tGroup %2d: quota(min/max)=0x%03x/0x%03x, used/rsv=0x%03x/0x%03x\n",
| ~~~^
| |
| unsigned int
| %03lx
In file included from drivers/net/wireless/mediatek/mt76/mt7925/../mt76_connac.h:7,
from drivers/net/wireless/mediatek/mt76/mt7925/../mt76_connac_mcu.h:7,
from drivers/net/wireless/mediatek/mt76/mt7925/../mt792x.h:10,
from drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h:7,
from drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:4:
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:626:28: error: 'MT_DMASHDL_STATUS_RD' undeclared (first use in this function)
626 | val = mt76_rr(dev, MT_DMASHDL_STATUS_RD);
| ^~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/../mt76.h:1196:69: note: in definition of macro 'mt76_rr'
1196 | #define mt76_rr(dev, ...) (dev)->mt76.bus->rr(&((dev)->mt76), __VA_ARGS__)
| ^~~~~~~~~~~
In file included from <command-line>:
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:627:33: error: 'MT_DMASHDL_STATUS_RD_FREE_PG' undeclared (first use in this function)
627 | free_pg_cnt = FIELD_GET(MT_DMASHDL_STATUS_RD_FREE_PG, val);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:682:23: note: in definition of macro '__compiletime_assert'
682 | if (!(condition)) \
| ^~~~~~~~~
include/linux/compiler_types.h:702:9: note: in expansion of macro '_compiletime_assert'
702 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ^~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:40:37: note: in expansion of macro 'compiletime_assert'
40 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ^~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:79:9: note: in expansion of macro 'BUILD_BUG_ON_MSG'
79 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
| ^~~~~~~~~~~~~~~~
include/linux/bitfield.h:63:43: note: in expansion of macro '__unsigned_scalar_typeof'
63 | #define __bf_cast_unsigned(type, x) ((__unsigned_scalar_typeof(type))(x))
| ^~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:79:26: note: in expansion of macro '__bf_cast_unsigned'
79 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
| ^~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:177:17: note: in expansion of macro '__BF_FIELD_CHECK_REG'
177 | __BF_FIELD_CHECK_REG(_mask, _reg, "FIELD_GET: "); \
| ^~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:627:23: note: in expansion of macro 'FIELD_GET'
627 | free_pg_cnt = FIELD_GET(MT_DMASHDL_STATUS_RD_FREE_PG, val);
| ^~~~~~~~~
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c:628:29: error: 'MT_DMASHDL_STATUS_RD_FFA' undeclared (first use in this function)
628 | ffa_cnt = FIELD_GET(MT_DMASHDL_STATUS_RD_FFA, val);
| ^~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:682:23: note: in definition of macro '__compiletime_assert'
682 | if (!(condition)) \
| ^~~~~~~~~
include/linux/compiler_types.h:702:9: note: in expansion of macro '_compiletime_assert'
702 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ^~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:40:37: note: in expansion of macro 'compiletime_assert'
40 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ^~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:79:9: note: in expansion of macro 'BUILD_BUG_ON_MSG'
79 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
| ^~~~~~~~~~~~~~~~
include/linux/bitfield.h:63:43: note: in expansion of macro '__unsigned_scalar_typeof'
63 | #define __bf_cast_unsigned(type, x) ((__unsigned_scalar_typeof(type))(x))
| ^~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:79:26: note: in expansion of macro '__bf_cast_unsigned'
79 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
| ^~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:177:17: note: in expansion of macro '__BF_FIELD_CHECK_REG'
177 | __BF_FIELD_CHECK_REG(_mask, _reg, "FIELD_GET: "); \
vim +/rate +312 drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
289
290 /* fixedrate input format (12 hex digits):
291 * 0x[WCID(2)][Mode][BW][MCS][Nss][SGI][Preamble][STBC][LDPC][SPE_EN][HeLtf]
292 * The RA tag is fixed to SET_FIXED_RATE internally.
293 * WCID is 2 nibbles, all others are 1 nibble.
294 *
295 * [WCID] Wireless Client ID
296 * [Mode] CCK=0, OFDM=1, HT=2, GF=3, VHT=4, PLR=5, HE_SU=8, HE_ER_SU=9,
297 * HE_TRIG=10, HE_MU=11, EHT_ER=13, EHT_TB=14, EHT_SU and EHT_MU=15
298 * [BW] BW20=0, BW40=1, BW80=2, BW160=3, BW320=4
299 * [MCS] CCK=0~3, OFDM=0~7, HT=0~32, VHT=0~9, HE=0~11, EHT=0~13, EHT_ER=14~15
300 * [Nss] 1~8
301 * [GI] HT/VHT: 0=Long, 1=Short; HE/EHT: 0=0.8us, 1=1.6us, 2=3.2us
302 * [Preamble] Long=0, Short=other
303 * [STBC] Enable=1, Disable=0
304 * [LDPC] BCC=0, LDPC=1 (driver sends the FW's "on" value 7)
305 * [SPE_EN] spatial extension index
306 * [HeLtf] 1X=0, 2X=1, 4X=2 (NOTE: EHT modes require HeLtf=1)
307 */
308 static int
309 mt7925_fixedrate_set(void *data, u64 val)
310 {
311 struct mt792x_dev *dev = data;
> 312 struct mt7925_ra_fixed_rate_v1 rate = {};
313
> 314 dev->fixed_rate = val;
315
316 rate.wlan_idx = cpu_to_le16((val >> 40) & 0xff);
317 rate.phy_mode = (val >> 36) & 0xf;
318 rate.bw = (val >> 32) & 0xf;
319 rate.mcs = (val >> 28) & 0xf;
320 rate.nss = (val >> 24) & 0xf;
321 rate.short_gi = cpu_to_le16((val >> 20) & 0xf);
322 rate.short_preamble = (val >> 16) & 0xf;
323 rate.stbc = (val >> 12) & 0xf;
324 rate.ecc = ((val >> 8) & 0xf) ? 7 : 0;
325 rate.spe = (val >> 4) & 0xf;
326 rate.he_ltf = cpu_to_le16((val >> 0) & 0xf);
327
328 mt792x_mutex_acquire(dev);
> 329 mt7925_mcu_set_fixed_rate(dev, &rate);
330 mt792x_mutex_release(dev);
331
332 return 0;
333 }
334
335 static int
336 mt7925_fixedrate_get(void *data, u64 *val)
337 {
338 struct mt792x_dev *dev = data;
339
340 *val = dev->fixed_rate;
341
342 return 0;
343 }
> 344 DEFINE_DEBUGFS_ATTRIBUTE(fops_fixedrate, mt7925_fixedrate_get,
345 mt7925_fixedrate_set, "0x%llx\n");
346
347 /* autorate input format (6 hex digits):
348 * 0x[Tag(2)][WCID(2)][En][Mode]
349 * The RA tag is fixed to SET_AUTO_RATE internally.
350 * WCID is 2 nibbles, En and Mode are 1 nibble each.
351 *
352 * [WCID] Wireless Client ID
353 * [En] Auto rate: Enable=1, Disable=0
354 * [Mode] don't care (RA picks the rate)
355 */
356 static int
357 mt7925_autorate_set(void *data, u64 val)
358 {
359 struct mt792x_dev *dev = data;
360 u16 wlan_idx = (val >> 8) & 0xff;
361 bool enable = (val >> 4) & 0xf;
362 u8 mode = val & 0xf;
363
> 364 dev->auto_rate = val;
365
366 mt792x_mutex_acquire(dev);
> 367 mt7925_mcu_set_auto_rate(dev, wlan_idx, enable, mode);
368 mt792x_mutex_release(dev);
369
370 return 0;
371 }
372
373 static int
374 mt7925_autorate_get(void *data, u64 *val)
375 {
376 struct mt792x_dev *dev = data;
377
378 *val = dev->auto_rate;
379
380 return 0;
381 }
> 382 DEFINE_DEBUGFS_ATTRIBUTE(fops_autorate, mt7925_autorate_get,
383 mt7925_autorate_set, "0x%llx\n");
384
385 /* WTBL dump
386 *
387 * Usage:
388 * echo <idx> > /sys/kernel/debug/ieee80211/phy1/mt76/wtbl_idx
389 * cat /sys/kernel/debug/ieee80211/phy1/mt76/get_wtbl
390 *
391 * LMAC WTBL has 36 dwords per entry (connac3x).
392 */
393 #define MT792x_WTBL_LMAC_DWORDS 36
394
395 static int
396 mt7925_wtbl_idx_set(void *data, u64 val)
397 {
398 struct mt792x_dev *dev = data;
399
> 400 dev->wtbl_dump_idx = (u32)val;
401
402 return 0;
403 }
404
405 static int
406 mt7925_wtbl_idx_get(void *data, u64 *val)
407 {
408 struct mt792x_dev *dev = data;
409
410 *val = dev->wtbl_dump_idx;
411
412 return 0;
413 }
414
> 415 DEFINE_DEBUGFS_ATTRIBUTE(fops_wtbl_idx, mt7925_wtbl_idx_get,
416 mt7925_wtbl_idx_set, "%llu\n");
417
418 static int
> 419 mt7925_wtbl_read(struct seq_file *s, void *data)
420 {
421 struct mt792x_dev *dev = dev_get_drvdata(s->private);
422 u32 idx = dev->wtbl_dump_idx;
423 u32 wdu_cr, wducr_val, base;
424 u32 dw0, dw1;
425 int i;
426
427 if (!mt76_is_mmio(&dev->mt76)) {
428 seq_puts(s, "WTBL direct dump not supported on this interface\n");
429 return 0;
430 }
431
432 if (idx >= MT792x_WTBL_SIZE) {
433 seq_printf(s, "Invalid WTBL idx %u (max %u)\n",
434 idx, MT792x_WTBL_SIZE - 1);
435 return 0;
436 }
437
438 mt792x_mutex_acquire(dev);
439
440 /* mt7925_mac_wtbl_lmac_addr() writes WDUCR group selection and
441 * returns the mapped register address for DW 0 of this WCID.
442 */
> 443 base = mt7925_mac_wtbl_lmac_addr(dev, idx, 0);
444 wdu_cr = is_mt7928(&dev->mt76) ? MT7928_WTBLON_TOP_WDUCR :
445 MT7925_WTBLON_TOP_WDUCR;
446 wducr_val = mt76_rr(dev, wdu_cr);
447
448 /* DW0[15:0] + DW1[31:0] encode the link address */
449 dw0 = mt76_rr(dev, base);
450 dw1 = mt76_rr(dev, base + 4);
451
452 seq_printf(s, "Dump WTBL info of WLAN_IDX: %u\n", idx);
453 seq_printf(s, "LMAC WTBL Addr: group:0x%08x=0x%08x addr: 0x%08x\n",
454 wdu_cr, wducr_val, base);
455 seq_printf(s, "LinkAddr: %02x:%02x:%02x:%02x:%02x:%02x\n",
456 dw1 & 0xff, (dw1 >> 8) & 0xff,
457 (dw1 >> 16) & 0xff, (dw1 >> 24) & 0xff,
458 dw0 & 0xff, (dw0 >> 8) & 0xff);
459
460 seq_puts(s, "\nLMAC WTBL raw data:\n");
461 for (i = 0; i < MT792x_WTBL_LMAC_DWORDS; i++)
462 seq_printf(s, "DW%02d: 0x%08x\n", i,
463 mt76_rr(dev, base + i * 4));
464
465 mt792x_mutex_release(dev);
466
467 return 0;
468 }
469
470 static int
> 471 mt7925_vefuse_read(struct seq_file *s, void *data)
472 {
473 struct mt792x_dev *dev = dev_get_drvdata(s->private);
> 474 u16 size = dev->vefuse_cap.size;
475 int off = 0;
476 u8 *buf;
477 int ret = 0;
478
479 if (!dev->vefuse_cap.present || !size) {
480 seq_puts(s, "vefuse: no region in FW image\n");
481 return 0;
482 }
483
484 buf = kzalloc(size, GFP_KERNEL);
485 if (!buf)
486 return -ENOMEM;
487
488 mt792x_mutex_acquire(dev);
489 while (off < (int)size) {
490 u16 chunk = min_t(u16, 512, size - off);
491
> 492 ret = mt7925_mcu_read_vefuse(dev, off, chunk, buf + off);
493 if (ret)
494 break;
495 off += chunk;
496 }
497 mt792x_mutex_release(dev);
498
499 if (ret) {
500 seq_printf(s, "vefuse: read failed at 0x%04x (%d)\n", off, ret);
501 kfree(buf);
502 return 0;
503 }
504
505 seq_printf(s, "vefuse content: %u bytes\n", size);
506 for (off = 0; off < (int)size; off += 16)
507 seq_printf(s, "%04x: %*ph\n", off,
508 (int)min_t(u16, 16, size - off), buf + off);
509
510 kfree(buf);
511
512 return 0;
513 }
514
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose
2026-08-24 3:07 [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose Eason Lai
2026-08-25 4:00 ` kernel test robot
@ 2026-08-25 7:55 ` kernel test robot
2026-08-27 5:54 ` [PATCH v2] " Eason Lai
2 siblings, 0 replies; 5+ messages in thread
From: kernel test robot @ 2026-08-25 7:55 UTC (permalink / raw)
To: Eason Lai, nbd, lorenzo
Cc: oe-kbuild-all, linux-wireless, linux-mediatek, kun.wu, deren.wu,
sean.wang, quan.zhou, ryder.lee, leon.yen, litien.chang, jb.tsai,
michael.shih, eason.lai
Hi Eason,
kernel test robot noticed the following build errors:
[auto build test ERROR on wireless-next/main]
[also build test ERROR on linus/master next-20260821]
[cannot apply to wireless/main v7.2]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Eason-Lai/wifi-mt76-mt7928-Add-debugfs-for-stats-to-provide-debug-purpose/20260824-110746
base: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
patch link: https://lore.kernel.org/r/20260824030746.60706-1-eason.lai%40mediatek.com
patch subject: [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose
config: parisc-allyesconfig (https://download.01.org/0day-ci/archive/20260825/202608251526.VgKMT8Lv-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 16.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260825/202608251526.VgKMT8Lv-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608251526.VgKMT8Lv-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from drivers/net/wireless/mediatek/mt76/mt7996/init.c:13:
>> drivers/net/wireless/mediatek/mt76/mt7996/mcu.h:1000:9: error: redeclaration of enumerator 'UNI_CMD_MIB_DATA'
1000 | UNI_CMD_MIB_DATA,
| ^~~~~~~~~~~~~~~~
In file included from drivers/net/wireless/mediatek/mt76/mt7996/mcu.h:9:
drivers/net/wireless/mediatek/mt76/mt7996/../mt76_connac_mcu.h:1511:9: note: previous definition of 'UNI_CMD_MIB_DATA' with type 'enum <anonymous>'
1511 | UNI_CMD_MIB_DATA,
| ^~~~~~~~~~~~~~~~
vim +/UNI_CMD_MIB_DATA +1000 drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
98686cd21624c7 Shayne Chen 2022-11-22 998
98686cd21624c7 Shayne Chen 2022-11-22 999 enum {
98686cd21624c7 Shayne Chen 2022-11-22 @1000 UNI_CMD_MIB_DATA,
98686cd21624c7 Shayne Chen 2022-11-22 1001 };
98686cd21624c7 Shayne Chen 2022-11-22 1002
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose
2026-08-24 3:07 [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose Eason Lai
2026-08-25 4:00 ` kernel test robot
2026-08-25 7:55 ` kernel test robot
@ 2026-08-27 5:54 ` Eason Lai
2026-08-27 8:44 ` [PATCH v3] " Eason Lai
2 siblings, 1 reply; 5+ messages in thread
From: Eason Lai @ 2026-08-27 5:54 UTC (permalink / raw)
To: nbd, lorenzo
Cc: linux-wireless, linux-mediatek, kun.wu, deren.wu, sean.wang,
quan.zhou, ryder.lee, leon.yen, litien.chang, jb.tsai,
michael.shih, eason.lai, kernel test robot
From: Michael Shih <michael.shih@mediatek.com>
Usage:
(1) Connect STA to AP (You may use dhclient CMD to ask for DHCP IP, to use ping to AP)
DHCP IP CMD: dhclient ${WLAN_IF} -v
Example: dhclient wlan0 -v
## (To simulate traffic) Ping 10 packets for every second to AP
ping 192.168.0.XXX -i0.1
(2) (Get stats) Use command, phyX is your WiFi interface:
cat /sys/kernel/debug/ieee80211/phyX/mt76/stats
(3) You will see below stats info:
(STA) connected AP MAC Address = a8:42:a1:46:c4:6b
BssIdx = [0]
StaRecIdx = [1],
RSSI = -25
Link_Speed = 2402000 (kbit/s)
temperature = 40
Tx success = 716
Tx fail to Rcv ACK after retry = 0
Rx Mpdu = 23
Rx Fcs Error = 1 (MPDU)
Rx FIFO full = 0 (MPDU)
TRX_AGG_RANGE[0] = 0 (PPDU)
TRX_AGG_RANGE[1] = 0 (PPDU)
TRX_AGG_RANGE[2] = 0 (PPDU)
TRX_AGG_RANGE[3] = 0 (PPDU)
TRX_AGG_RANGE[4] = 0 (PPDU)
TRX_AGG_RANGE[5] = 0 (PPDU)
TRX_AGG_RANGE[6] = 0 (PPDU)
TRX_AGG_RANGE[7] = 0 (PPDU)
MPDUs in AMPDUs transmitted = 0 (MPDU)
MPDUs in AMPDUs transmitted with ACK reply = 0 (MPDU)
rate1_tx_cnt = 714
rate1_fail_cnt = 223
train_up = 2310
train_down = 3080
is_force_tx_stream = 0
is_force_se_off = 0
max_ampdu_factor = 9
tx_rate_up_penalty = 0
low_traffic_mode = 2
low_traffic_count = 0
low_traffic_dashboard = 3
dynamic_sgi_state = 0
dynamic_sgi_score = 2
dynamic_bw_state = 0
dynamic_gband_256qam_state = 3
vht_non_sp_rate_state = 0
Last TX Rate = 24M, BW20, OFDM, BCC, SPE24
Chip Out TX Power = +16.5 dBm
Cur Rate Index = 0
Rate index[0] --> NSS2_MCS11, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[1] NSS2_MCS9, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[2] NSS2_MCS7, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[3] NSS2_MCS4, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[4] NSS2_MCS3, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[5] NSS1_MCS2, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[6] NSS1_MCS1, BW80, SGI, HE_SU, LDPC
Rate index[7] NSS1_MCS0, BW80, SGI, HE_SU, LDPC
mib state:
=== Rx Related Counters ===
Rx with CRC = 24020 (MPDU)
Rx drop due to out of resource = 0 (MPDU)
Rx Mpdu = 93223 (MPDU)
Rx AMpdu = 668 (PPDU)
Rx PF Drop = 0 (MPDU)
Rx Len Mismatch = 5853 (PPDU)
=== Phy/Timing Related Counters ===
ChannelIdleCnt = 1040434
CCA_NAV_Tx_Time = 18510036
Rx_MDRDY_CNT = 166580 (PPDU)
CCK_MDRDY = 7782
OFDM_MDRDY = 11912731
OFDM_GREEN_MDRDY = 0
Prim CCA Time = 17917650
Sec CCA Time = 14922034
Prim ED Time = 5677528
=== Tx Related Counters(Generic) ===
BeaconTxCnt = 0
Tx 40MHz Cnt = 279 (MPDU)
Tx 80MHz Cnt = 44 (MPDU)
Tx 160MHz Cnt = 712 (MPDU)
=== BSSID[0] Related Counters ===
BA Miss Cnt = 210 (PPDU)
RTS Tx Cnt = 1003 (MPDU)
Frame Retry Cnt = 239 (MPDU)
Frame Retry 2 Cnt = 71 (MPDU)
RTS Retry Cnt = 152 (MPDU)
Ack Failed Cnt = 319 (PPDU)
=== AMPDU Related Counters ===
Tx AMPDU_Pkt_Cnt = 705 (PPDU)
Tx AMPDU_MPDU_Pkt_Cnt = 713 (MPDU)
AMPDU Tx success = 503 (MPDU)
AMPDU Tx fail count = 210 (MPDU), PER=29.4%
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202608251101.iQGUrSd0-lkp@intel.com/
Closes: https://lore.kernel.org/oe-kbuild-all/202608251526.VgKMT8Lv-lkp@intel.com/
Signed-off-by: Michael Shih <michael.shih@mediatek.com>
---
v2: (1) Remove redundant code to fix build errors.
(2) Move UNI_CMD_MIB_DATA enum definition from 'mt76/mt76_connac_mcu.h' to
'mt7925/stats.h' to avoid redeclartion with 'mt76/mt7996/mcu.h'
---
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 173 ++++++
.../wireless/mediatek/mt76/mt7925/Makefile | 2 +-
.../wireless/mediatek/mt76/mt7925/debugfs.c | 343 ++++++++++
.../net/wireless/mediatek/mt76/mt7925/stats.c | 585 ++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/stats.h | 309 +++++++++
5 files changed, 1411 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/stats.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/stats.h
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 0ededa569e9a..bf0f105c786e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1483,6 +1483,179 @@ enum {
MT_NIC_CAP_EML_CAP = 0x22,
};
+enum {
+ UNI_CMD_ID_STATISTICS_BASIC = 0,
+ UNI_CMD_ID_STATISTICS_LINK_QUALITY = 1,
+ UNI_CMD_ID_STATISTICS_STA = 2,
+ UNI_CMD_ID_STATISTICS_BUG_REPORT = 3,
+ UNI_CMD_ID_STATISTICS_TX_STATS = 4,
+ UNI_CMD_ID_STATISTICS_ALL_STATS = 5,
+ UNI_CMD_ID_STATISTICS_EML_STATS = 6,
+ UNI_CMD_ID_STATISTICS_REGULAR_STATS = 7,
+ UNI_CMD_ID_STATISTICS_BSS_LINK_QUALITY = 8,
+ UNI_CMD_ID_STATISTICS_ML_CHNL_COND = 9,
+ UNI_CMD_ID_STATISTICS_WTBL = 10,
+ UNI_CMD_ID_STATISTICS_LINK_LAYER_STATS = 128,
+ UNI_CMD_ID_STATISTICS_PPDU_LATENCY = 129,
+ UNI_CMD_ID_STATISTICS_CURRENT_TX_RATE = 130,
+ UNI_CMD_ID_STATISTICS_SET_TX_BITRATE_MONITOR = 131,
+ UNI_CMD_ID_STATISTICS_GET_PREDICT_TX_BITRATE = 132,
+ UNI_CMD_ID_STATISTICS_SET_ATPS_TYPE_RATE_MODE = 133,
+ UNI_CMD_ID_STATISTICS_SET_BEACON_REPORT = 134,
+ UNI_CMD_ID_STATISTICS_BSS_CURRENT_TX_RATE = 135,
+ UNI_CMD_ID_STATISTICS_GET_BSS_PREDICT_TX_BITRATE = 136,
+ UNI_CMD_ID_STATISTICS_MAX_NUM
+};
+
+enum mt7925_mib_counter {
+ UNI_CMD_MIB_CNT_RX_FCS_ERR = 0,
+ UNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW = 1,
+ UNI_CMD_MIB_CNT_RX_MPDU = 2,
+ UNI_CMD_MIB_CNT_AMPDU_RX_COUNT = 3,
+ UNI_CMD_MIB_CNT_RX_TOTAL_BYTE = 4,
+ UNI_CMD_MIB_CNT_RX_VALID_AMPDU_SF = 5,
+ UNI_CMD_MIB_CNT_RX_VALID_BYTE = 6,
+ UNI_CMD_MIB_CNT_CHANNEL_IDLE = 7,
+ UNI_CMD_MIB_CNT_VEC_DROP = 8,
+ UNI_CMD_MIB_CNT_DELIMITER_FAIL = 9,
+ UNI_CMD_MIB_CNT_VEC_MISMATCH = 10,
+ UNI_CMD_MIB_CNT_MDRDY = 11,
+ UNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME = 12,
+ UNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME = 13,
+ UNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME = 14,
+ UNI_CMD_MIB_CNT_PF_DROP = 15,
+ UNI_CMD_MIB_CNT_LEN_MISMATCH = 16,
+ UNI_CMD_MIB_CNT_P_CCA_TIME = 17,
+ UNI_CMD_MIB_CNT_S_CCA_TIME = 18,
+ UNI_CMD_MIB_CNT_CCA_NAV_TX_TIME = 19,
+ UNI_CMD_MIB_CNT_P_ED_TIME = 20,
+ UNI_CMD_MIB_CNT_BCN_TX = 21,
+ UNI_CMD_MIB_CNT_TX_BW_20MHZ = 22,
+ UNI_CMD_MIB_CNT_TX_BW_40MHZ = 23,
+ UNI_CMD_MIB_CNT_TX_BW_80MHZ = 24,
+ UNI_CMD_MIB_CNT_TX_BW_160MHZ = 25,
+ UNI_CMD_RMAC_CNT_OBSS_AIRTIME = 26,
+ UNI_CMD_RMAC_CNT_NONWIFI_AIRTIME = 27,
+ UNI_CMD_MIB_CNT_TX_DUR_CNT = 28,
+ UNI_CMD_MIB_CNT_RX_DUR_CNT = 29,
+ UNI_CMD_MIB_CNT_BA_CNT = 30,
+ UNI_CMD_MIB_CNT_MAC2PHY_TX_TIME = 31,
+ UNI_CMD_MIB_CNT_RX_OUT_OF_RANGE_COUNT = 32,
+ UNI_CMD_MIB_CNT_IBF_TX = 33,
+ UNI_CMD_MIB_CNT_EBF_TX = 34,
+ UNI_CMD_MIB_CNT_MUBF_TX = 35,
+ UNI_CMD_MIB_CNT_RX_BF_VHTFBK = 36,
+ UNI_CMD_MIB_CNT_RX_BF_HTFBK = 37,
+ UNI_CMD_MIB_CNT_RX_BF_HEFBK = 38,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_BFPOLL_TRI = 39,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_TRI = 40,
+ UNI_CMD_MIB_CNT_BFEE_RX_FBKCQI = 41,
+ UNI_CMD_MIB_CNT_BFEE_RX_NDP = 42,
+ UNI_CMD_MIB_CNT_RX_BF_IBF_UPT = 43,
+ UNI_CMD_MIB_CNT_RX_BF_EBF_UPT = 44,
+ UNI_CMD_MIB_CNT_BFEE_SP_ABORT = 45,
+ UNI_CMD_MIB_CNT_BFEE_TB_LEN_ERR = 46,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_MUTE = 47,
+ UNI_CMD_MIB_CNT_BFEE_TMAC_ABORT = 48,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_CPL = 49,
+ UNI_CMD_MIB_CNT_BFEE_COANT_BLKTX = 50,
+ UNI_CMD_MIB_CNT_BFEE_FBK_SEG = 51,
+ UNI_CMD_MIB_CNT_NAV_TIME = 52,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT1 = 53,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT0 = 54,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT3 = 55,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT2 = 56,
+ UNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT = 57,
+ UNI_CMD_MIB_CNT_BSS1_RTS_TX_CNT = 58,
+ UNI_CMD_MIB_CNT_BSS2_RTS_TX_CNT = 59,
+ UNI_CMD_MIB_CNT_BSS3_RTS_TX_CNT = 60,
+ UNI_CMD_MIB_CNT_BSS0_RTS_RETRY = 61,
+ UNI_CMD_MIB_CNT_BSS1_RTS_RETRY = 62,
+ UNI_CMD_MIB_CNT_BSS2_RTS_RETRY = 63,
+ UNI_CMD_MIB_CNT_BSS3_RTS_RETRY = 64,
+ UNI_CMD_MIB_CNT_BSS0_BA_MISS = 65,
+ UNI_CMD_MIB_CNT_BSS1_BA_MISS = 66,
+ UNI_CMD_MIB_CNT_BSS2_BA_MISS = 67,
+ UNI_CMD_MIB_CNT_BSS3_BA_MISS = 68,
+ UNI_CMD_MIB_CNT_BSS0_ACK_FAIL = 69,
+ UNI_CMD_MIB_CNT_BSS1_ACK_FAIL = 70,
+ UNI_CMD_MIB_CNT_BSS2_ACK_FAIL = 71,
+ UNI_CMD_MIB_CNT_BSS3_ACK_FAIL = 72,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY = 73,
+ UNI_CMD_MIB_CNT_BSS1_FRAME_RETRY = 74,
+ UNI_CMD_MIB_CNT_BSS2_FRAME_RETRY = 75,
+ UNI_CMD_MIB_CNT_BSS3_FRAME_RETRY = 76,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2 = 77,
+ UNI_CMD_MIB_CNT_BSS1_FRAME_RETRY_2 = 78,
+ UNI_CMD_MIB_CNT_BSS2_FRAME_RETRY_2 = 79,
+ UNI_CMD_MIB_CNT_BSS3_FRAME_RETRY_2 = 80,
+ UNI_CMD_MIB_CNT_RX_A1_SEARCH = 81,
+ UNI_CMD_MIB_CNT_RX_DROP_MPDU = 82,
+ UNI_CMD_MIB_CNT_RX_UNWANTED = 83,
+ UNI_CMD_MIB_CNT_RX_FCS_OK = 84,
+ UNI_CMD_MIB_CNT_SU_TX_OK = 85,
+ UNI_CMD_MIB_CNT_TX_FULL_BW = 86,
+ UNI_CMD_MIB_CNT_TX_AUTO_BW = 87,
+ UNI_CMD_MIB_CNT_CCA_SAMPLE_IDLE = 88,
+ UNI_CMD_MIB_CNT_CCA_SAMPLE_ACTIVE = 89,
+ UNI_CMD_MIB_CNT_CCA_TIME = 90,
+ UNI_CMD_MIB_CNT_S_20BW_CCA_TIME = 91,
+ UNI_CMD_MIB_CNT_S_40BW_CCA_TIME = 92,
+ UNI_CMD_MIB_CNT_S_80BW_CCA_TIME = 93,
+ UNI_CMD_MIB_CNT_S_160BW_CCA_TIME = 94,
+ UNI_CMD_MIB_CNT_S_P20BW_0_ED_TIME = 95,
+ UNI_CMD_MIB_CNT_S_P20BW_1_ED_TIME = 96,
+ UNI_CMD_MIB_CNT_S_P20BW_2_ED_TIME = 97,
+ UNI_CMD_MIB_CNT_S_P20BW_3_ED_TIME = 98,
+ UNI_CMD_MIB_CNT_S_P20BW_4_ED_TIME = 99,
+ UNI_CMD_MIB_CNT_S_P20BW_5_ED_TIME = 100,
+ UNI_CMD_MIB_CNT_S_P20BW_6_ED_TIME = 101,
+ UNI_CMD_MIB_CNT_S_P20BW_7_ED_TIME = 102,
+ UNI_CMD_MIB_CNT_S_P20BW_8_ED_TIME = 103,
+ UNI_CMD_MIB_CNT_S_P20BW_9_ED_TIME = 104,
+ UNI_CMD_MIB_CNT_S_P20BW_10_ED_TIME = 105,
+ UNI_CMD_MIB_CNT_S_P20BW_11_ED_TIME = 106,
+ UNI_CMD_MIB_CNT_S_P20BW_12_ED_TIME = 107,
+ UNI_CMD_MIB_CNT_S_P20BW_13_ED_TIME = 108,
+ UNI_CMD_MIB_CNT_S_P20BW_14_ED_TIME = 109,
+ UNI_CMD_MIB_CNT_S_P20BW_15_ED_TIME = 110,
+ UNI_CMD_MIB_CNT_TX_BW_320MHZ = 111,
+ UNI_CMD_MIB_CNT_TX_DDLMT_RNG0 = 112, /* Rng0~Rng4 112~116 */
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_3 = 117, /* 117~120 */
+ UNI_CMD_MIB_CNT_BSS0_TX = 121, /* 121~124 */
+ UNI_CMD_MIB_CNT_BSS0_TX_DATA = 125, /* 125~128 */
+ UNI_CMD_MIB_CNT_BSS0_TX_BYTE = 129, /* 129~132 */
+ UNI_CMD_MIB_CNT_RX_OK_BSS0 = 133, /* 133~136 */
+ UNI_CMD_MIB_CNT_RX_BYTE_BSS0 = 137, /* 137~140 */
+ UNI_CMD_MIB_CNT_RX_DATA_BSS0 = 141, /* 141~144 */
+ UNI_CMD_MIB_CNT_MBSS0_TX_OK = 145, /* 145~160 */
+ UNI_CMD_MIB_CNT_MBSS0_TX_BYTE = 161, /* 161~176 */
+ UNI_CMD_MIB_CNT_RX_OK_MBSS0 = 177, /* 177~192 */
+ UNI_CMD_MIB_CNT_RX_BYTE_MBSS0 = 193, /* 193~208 */
+ UNI_CMD_MIB_CNT_AMPDU = 209,
+ UNI_CMD_MIB_CNT_AMPDU_MPDU = 210,
+ UNI_CMD_MIB_CNT_AMPDU_ACKED = 211,
+ UNI_CMD_MIB_CNT_MAX_NUM
+};
+
+enum ENUM_UNI_CMD_GET_STATISTICS_TAG {
+ UNI_CMD_GET_STATISTICS_TAG_BASIC = 0,
+ UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1,
+ UNI_CMD_GET_STATISTICS_TAG_STA = 2,
+ UNI_CMD_GET_STATISTICS_TAG_BUG_REPORT = 3,
+ UNI_CMD_GET_STATISTICS_TAG_EML_STATS = 6,
+ UNI_CMD_GET_STATISTICS_TAG_REGULAR_STATS = 7,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_LINK_QUALITY = 8,
+ UNI_CMD_GET_STATISTICS_TAG_GET_ML_CHNL_COND = 9,
+ UNI_CMD_GET_STATISTICS_TAG_STAT_WTBL = 10,
+ UNI_CMD_GET_STATISTICS_TAG_LINK_LAYER_STATS = 0x80,
+ UNI_CMD_GET_STATISTICS_TAG_PPDU_LATENCY,
+ UNI_CMD_GET_STATISTICS_TAG_CURRENT_TX_RATE,
+ UNI_CMD_GET_STATISTICS_TAG_BEACON_REPORT = 0x86,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_CURRENT_TX_RATE = 0x87,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_PRED_TX_BITRATE = 0x88,
+};
+
#define UNI_WOW_DETECT_TYPE_MAGIC BIT(0)
#define UNI_WOW_DETECT_TYPE_ANY BIT(1)
#define UNI_WOW_DETECT_TYPE_DISCONNECT BIT(2)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index f9dcc0bba393..3863a89b8974 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
obj-$(CONFIG_MT7925E) += mt7925e.o
obj-$(CONFIG_MT7925U) += mt7925u.o
-mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o
+mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o stats.o
mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7925e-y := pci.o pci_mac.o pci_mcu.o
mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index d01ff49de47a..d96a1979a196 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -3,6 +3,7 @@
#include "mt7925.h"
#include "mcu.h"
+#include "stats.h"
static int
mt7925_reg_set(void *data, u64 val)
@@ -286,6 +287,345 @@ static int mt7925_chip_reset(void *data, u64 val)
DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
+static const u32 stats_query_mib_counters[] = {
+ UNI_CMD_MIB_CNT_RX_FCS_ERR,
+ UNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW,
+ UNI_CMD_MIB_CNT_RX_MPDU,
+ UNI_CMD_MIB_CNT_AMPDU_RX_COUNT,
+ UNI_CMD_MIB_CNT_PF_DROP,
+ UNI_CMD_MIB_CNT_LEN_MISMATCH,
+ UNI_CMD_MIB_CNT_CHANNEL_IDLE,
+ UNI_CMD_MIB_CNT_CCA_NAV_TX_TIME,
+ UNI_CMD_MIB_CNT_MDRDY,
+ UNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_P_CCA_TIME,
+ UNI_CMD_MIB_CNT_S_CCA_TIME,
+ UNI_CMD_MIB_CNT_P_ED_TIME,
+ UNI_CMD_MIB_CNT_BCN_TX,
+ UNI_CMD_MIB_CNT_TX_BW_40MHZ,
+ UNI_CMD_MIB_CNT_TX_BW_80MHZ,
+ UNI_CMD_MIB_CNT_TX_BW_160MHZ,
+ UNI_CMD_MIB_CNT_BSS0_BA_MISS,
+ UNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2,
+ UNI_CMD_MIB_CNT_BSS0_RTS_RETRY,
+ UNI_CMD_MIB_CNT_BSS0_ACK_FAIL,
+ UNI_CMD_MIB_CNT_AMPDU,
+ UNI_CMD_MIB_CNT_AMPDU_MPDU,
+ UNI_CMD_MIB_CNT_AMPDU_ACKED,
+};
+
+/* Dump the whole firmware auto-rate table and mark the entry currently in use
+ * with "-->"
+ */
+static void
+mt7925_dump_auto_rate_table(struct seq_file *s,
+ const struct mt7925_wtbl_rate *wr)
+{
+ u8 fcap = wr->fcap;
+ u8 rate_idx = wr->rate_idx;
+ u8 cbrn = wr->cbrn;
+ int i;
+
+ seq_printf(s, "%-22s Cur Rate Index = %u\n", " ", rate_idx);
+
+ for (i = 0; i < MT7925_AUTO_RATE_NUM; i++) {
+ u16 rate_code = le16_to_cpu(wr->rate_code[i]);
+ u8 txmode = MT7925_HW_RATE_TO_MODE(rate_code);
+ u8 rate = MT7925_HW_RATE_TO_MCS(rate_code);
+ u8 nsts = MT7925_HW_RATE_TO_NSS(rate_code) + 1;
+ u8 stbc = MT7925_HW_RATE_TO_STBC(rate_code);
+ u8 dcm = MT7925_HW_RATE_TO_DCM(rate_code);
+ u8 ersu106t = MT7925_HW_RATE_TO_106T(rate_code);
+ u8 sgi = mt7925_wtbl_get_sgi(wr, txmode, fcap);
+ u8 ldpc = mt7925_wtbl_get_ldpc(wr, txmode);
+ const char *mode_str = "N/A";
+
+ if (dcm)
+ rate = MT7925_HW_RATE_UNMASK_DCM(rate);
+ if (ersu106t)
+ rate = MT7925_HW_RATE_UNMASK_106T(rate);
+
+ if (txmode < ARRAY_SIZE(mt7925_tx_mode_str))
+ mode_str = mt7925_tx_mode_str[txmode];
+
+ seq_printf(s, "Rate index[%d] %s", i,
+ rate_idx == i ? "--> " : " ");
+
+ /* rate/MCS field */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ",
+ mt7925_tx_rate_cck_str[rate & 0x3]);
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_hw_rate_ofdm_str(rate));
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF)
+ seq_printf(s, "MCS%d, ", rate);
+ else
+ seq_printf(s, "%s%d_MCS%d, ",
+ stbc ? "NSTS" : "NSS", nsts, rate);
+
+ /* bandwidth: mirror the CCK/OFDM -> BW20 and cbrn handling */
+ if (txmode == MT7925_TX_RATE_MODE_CCK ||
+ txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_tx_rate_bw_str[0]);
+ else if (i > cbrn)
+ seq_printf(s, "%s, ",
+ fcap < 5 ?
+ (fcap > MT7925_BW_20 ?
+ mt7925_tx_rate_bw_str[fcap - 1] :
+ mt7925_tx_rate_bw_str[fcap]) :
+ mt7925_tx_rate_bw_str[5]);
+ else
+ seq_printf(s, "%s, ",
+ fcap < 5 ? mt7925_tx_rate_bw_str[fcap] :
+ mt7925_tx_rate_bw_str[5]);
+
+ /* GI / preamble */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ", rate < 4 ? "LP" : "SP");
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ ; /* OFDM has no GI/preamble to print */
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF ||
+ txmode == MT7925_TX_RATE_MODE_VHT ||
+ txmode == MT7925_TX_RATE_MODE_PLR)
+ seq_printf(s, "%s, ", sgi == 0 ? "LGI" : "SGI");
+ else
+ seq_printf(s, "%s, ",
+ sgi == 0 ? "SGI" :
+ (sgi == 1 ? "MGI" : "LGI"));
+
+ /* mode, DCM, 106t, STBC, coding */
+ seq_printf(s, "%s%s%s%s%s\n",
+ mode_str,
+ dcm ? ", DCM" : "", ersu106t ? ", 106t" : "",
+ stbc ? ", STBC, " : ", ",
+ (ldpc == 0 ||
+ txmode == MT7925_TX_RATE_MODE_CCK ||
+ txmode == MT7925_TX_RATE_MODE_OFDM) ?
+ "BCC" : "LDPC");
+ }
+}
+
+static int mt792x_stats(struct seq_file *s, void *data)
+{
+ s8 rssi = 0;
+ u8 band_idx = 0;
+ bool wtbl_rate_valid = false;
+ int ret = 0;
+ int i = 0;
+ u32 stats_value = 0;
+ u32 tx_link_speed_kbps = 0;
+ u64 tx_total = 0;
+ u64 tx_fail = 0;
+ u64 tx_per = 0;
+ /* UNI_CMD_MIB_DATA */
+ u64 mib_values[ARRAY_SIZE(stats_query_mib_counters)];
+ struct mt792x_dev *dev = dev_get_drvdata(s->private);
+ struct mt7925_wtbl_rate wtbl_rate;
+ struct mt7925_sta_stats *stats = NULL;
+
+ band_idx = dev->mphy.band_idx;
+
+ stats = kzalloc(sizeof(*stats), GFP_KERNEL);
+ if (!stats)
+ return -ENOMEM;
+
+ /* Get STA_STATS */
+ mt792x_mutex_acquire(dev);
+ ret = mt792x_mcu_get_stat(dev, stats);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get MIB_STATS */
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_mcu_get_mib_info(dev, band_idx, stats_query_mib_counters,
+ ARRAY_SIZE(stats_query_mib_counters), mib_values);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get Link_Speed and RSSI */
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_mcu_get_link_quality(dev, stats->bss_idx, &rssi, &tx_link_speed_kbps);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get the firmware auto-rate table (best effort; do not abort the
+ * whole stats dump if the WTBL query is not available).
+ */
+ mt792x_mutex_acquire(dev);
+ if (!mt792x_mcu_get_wtbl_rate(dev, stats->wtbl_idx, &wtbl_rate))
+ wtbl_rate_valid = true;
+ mt792x_mutex_release(dev);
+
+ /* Print STA_STATS info */
+ seq_printf(s, "(STA) connected AP MAC Address = %pM\n", stats->mac_addr);
+ seq_printf(s, "%-22s BssIdx = [%u]\n", " ", stats->bss_idx);
+ seq_printf(s, "%-22s StaRecIdx = [%u]\n",
+ " ", stats->sta_idx);
+ seq_printf(s, "%-22s RSSI = %d\n", " ", rssi);
+ seq_printf(s, "%-22s Link_Speed = %u (kbit/s)\n", " ", tx_link_speed_kbps);
+
+ seq_printf(s, "%-22s temperature = %u\n",
+ " ", stats->temperature);
+
+ stats_value = le32_to_cpu(stats->transmit_count) - le32_to_cpu(stats->transmit_fail_count);
+ seq_printf(s, "%-22s Tx success = %u\n",
+ " ", stats_value);
+
+ stats_value = le32_to_cpu(stats->tx_fail_count) - le32_to_cpu(stats->tx_life_timeout_count);
+ seq_printf(s, "%-22s Tx fail to Rcv ACK after retry = %u\n",
+ " ", stats_value);
+
+ seq_printf(s, "%-22s Rx Mpdu = %u\n",
+ " ", le32_to_cpu(stats->mib[band_idx].rx_mpdu_cnt));
+
+ seq_printf(s, "%-22s Rx Fcs Error = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].fcs_error));
+ seq_printf(s, "%-22s Rx FIFO full = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].rx_fifo_full));
+
+ for (i = 0; i < STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM; i++)
+ seq_printf(s, "%-22s TRX_AGG_RANGE[%d] = %u (PPDU)\n",
+ " ", i, le32_to_cpu(stats->mib[band_idx].tx_range_ampdu_cnt[i]));
+
+ seq_printf(s, "%-22s MPDUs in AMPDUs transmitted = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].ampdu_tx_sf_cnt));
+ seq_printf(s, "%-22s MPDUs in AMPDUs transmitted with ACK reply = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].ampdu_tx_ack_sf_cnt));
+ seq_printf(s, "%-22s rate1_tx_cnt = %u\n",
+ " ", le32_to_cpu(stats->rate1_tx_cnt));
+ seq_printf(s, "%-22s rate1_fail_cnt = %u\n",
+ " ", le32_to_cpu(stats->rate1_fail_cnt));
+ seq_printf(s, "%-22s train_up = %u\n",
+ " ", le16_to_cpu(stats->train_up));
+ seq_printf(s, "%-22s train_down = %u\n",
+ " ", le16_to_cpu(stats->train_down));
+ seq_printf(s, "%-22s is_force_tx_stream = %u\n",
+ " ", stats->is_force_tx_stream);
+ seq_printf(s, "%-22s is_force_se_off = %u\n",
+ " ", stats->is_force_se_off);
+ seq_printf(s, "%-22s max_ampdu_factor = %u\n",
+ " ", stats->max_ampdu_factor);
+ seq_printf(s, "%-22s tx_rate_up_penalty = %u\n",
+ " ", stats->tx_rate_up_penalty);
+ seq_printf(s, "%-22s low_traffic_mode = %u\n",
+ " ", stats->low_traffic_mode);
+ seq_printf(s, "%-22s low_traffic_count = %u\n",
+ " ", stats->low_traffic_count);
+ seq_printf(s, "%-22s low_traffic_dashboard = %u\n",
+ " ", stats->low_traffic_dashboard);
+ seq_printf(s, "%-22s dynamic_sgi_state = %u\n",
+ " ", stats->dynamic_sgi_state);
+ seq_printf(s, "%-22s dynamic_sgi_score = %u\n",
+ " ", stats->dynamic_sgi_score);
+ seq_printf(s, "%-22s dynamic_bw_state = %u\n",
+ " ", stats->dynamic_bw_state);
+ seq_printf(s, "%-22s dynamic_gband_256qam_state = %u\n",
+ " ", stats->dynamic_gband_256qam_state);
+ seq_printf(s, "%-22s vht_non_sp_rate_state = %u\n",
+ " ", stats->vht_non_sp_rate_state);
+
+ /* Decode the TX Vector BBP latch to show the current TX MCS rate */
+ if (band_idx < MT7925_STA_STATS_BAND_NUM)
+ mt7925_print_last_tx_rate(s, stats->tx_vector[band_idx].txv);
+
+ /* Dump the whole firmware auto-rate table and mark the entry currently */
+ if (wtbl_rate_valid)
+ mt7925_dump_auto_rate_table(s, &wtbl_rate);
+
+ seq_puts(s, "\nmib state:\n");
+ /* ===Rx Related Counters=== */
+ seq_puts(s, "=== Rx Related Counters ===\n");
+ seq_printf(s, "%-22s Rx with CRC = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_FCS_ERR]);
+ seq_printf(s, "%-22s Rx drop due to out of resource = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_FIFO_OVERFLOW]);
+ seq_printf(s, "%-22s Rx Mpdu = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_MPDU]);
+ seq_printf(s, "%-22s Rx AMpdu = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_RX_COUNT]);
+ seq_printf(s, "%-22s Rx PF Drop = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_PF_DROP]);
+ seq_printf(s, "%-22s Rx Len Mismatch = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_LEN_MISMATCH]);
+
+ /* ===Phy/Timing Related Counters=== */
+ seq_puts(s, "\n=== Phy/Timing Related Counters ===\n");
+ seq_printf(s, "%-22s ChannelIdleCnt = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_CHANNEL_IDLE]);
+ seq_printf(s, "%-22s CCA_NAV_Tx_Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_CCA_NAV_TX_TIME]);
+ seq_printf(s, "%-22s Rx_MDRDY_CNT = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_MDRDY]);
+ seq_printf(s, "%-22s CCK_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_CCK_MDRDY_TIME]);
+ seq_printf(s, "%-22s OFDM_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME]);
+ seq_printf(s, "%-22s OFDM_GREEN_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME]);
+ seq_printf(s, "%-22s Prim CCA Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_P_CCA_TIME]);
+ seq_printf(s, "%-22s Sec CCA Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_S_CCA_TIME]);
+ seq_printf(s, "%-22s Prim ED Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_P_ED_TIME]);
+
+ /* ===Tx Related Counters(Generic)=== */
+ seq_puts(s, "\n=== Tx Related Counters(Generic) ===\n");
+ seq_printf(s, "%-22s BeaconTxCnt = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_BCN_TX]);
+ seq_printf(s, "%-22s Tx 40MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_40MHZ]);
+ seq_printf(s, "%-22s Tx 80MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_80MHZ]);
+ seq_printf(s, "%-22s Tx 160MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_160MHZ]);
+
+ /* ===BSSID[0] Related Counters=== */
+ seq_puts(s, "\n=== BSSID[0] Related Counters ===\n");
+ seq_printf(s, "%-22s BA Miss Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_BA_MISS]);
+ seq_printf(s, "%-22s RTS Tx Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_RTS_TX_CNT]);
+ seq_printf(s, "%-22s Frame Retry Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY]);
+ seq_printf(s, "%-22s Frame Retry 2 Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY_2]);
+ seq_printf(s, "%-22s RTS Retry Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_RTS_RETRY]);
+ seq_printf(s, "%-22s Ack Failed Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_ACK_FAIL]);
+
+ /* ===AMPDU Related Counters=== */
+ seq_puts(s, "\n=== AMPDU Related Counters ===\n");
+ seq_printf(s, "%-22s Tx AMPDU_Pkt_Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU]);
+ seq_printf(s, "%-22s Tx AMPDU_MPDU_Pkt_Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_MPDU]);
+ seq_printf(s, "%-22s AMPDU Tx success = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_ACKED]);
+
+ tx_total = mib_values[QUERY_MIB_CNT_AMPDU_MPDU];
+ tx_fail = mib_values[QUERY_MIB_CNT_AMPDU_MPDU] - mib_values[QUERY_MIB_CNT_AMPDU_ACKED];
+ tx_per = tx_total == 0 ? 0 : (1000 * (tx_fail)) / tx_total;
+ seq_printf(s, "%-22s AMPDU Tx fail count = %llu (MPDU), PER=%llu.%1llu%%\n",
+ " ",
+ tx_fail,
+ tx_per / 10, tx_per % 10);
+
+out:
+ kfree(stats);
+ return ret;
+}
+
int mt7925_init_debugfs(struct mt792x_dev *dev)
{
struct dentry *dir;
@@ -313,5 +653,8 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
mt792x_pm_stats);
debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
+ debugfs_create_devm_seqfile(dev->mt76.dev, "stats", dir,
+ mt792x_stats);
+
return 0;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.c b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c
new file mode 100644
index 000000000000..9a47953062bc
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include "mt7925.h"
+#include "mcu.h"
+#include "mac.h"
+#include "stats.h"
+
+#define MT7925_STA_STATS_VERSION 1
+#define MT7925_STA_STATS_VALID 1U
+
+#define MT7925_MIB_MAX_CNT 32
+#define MT7925_MIB_CNT_MAX_NUM 512 /* firmware UNI_CMD_MIB_CNT_MAX_NUM upper bound */
+
+#define MT7925_LQ_BAND_NUM 4 /* Link quality band */
+
+const char * const mt7925_tx_mode_str[] = {
+ "CCK", "OFDM", "MM", "GF", "VHT", "PLR",
+ "PLRP", "ALR", "HE_SU", "HE_ER", "HE_TRIG", "HE_MU",
+ "EHT_ER", "TOF", "EHT_TRIG", "EHT_MU"
+};
+
+const char * const mt7925_tx_rate_cck_str[] = {
+ "1M", "2M", "5.5M", "11M", "N/A"
+};
+
+const char * const mt7925_tx_rate_bw_str[] = {
+ "BW20", "BW40", "BW80", "BW160/BW8080", "BW320", "N/A"
+};
+
+struct mt7925_sta_stats_req {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ u8 sta_rec_idx;
+ u8 rsv1;
+ u8 lls_read_clear;
+ u8 reset_counter;
+} __packed;
+
+struct mt7925_sta_stats_event {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ struct mt7925_sta_stats stats;
+} __packed;
+
+/* WTBL auto-rate request TLV, mirror Gen4m struct UNI_CMD_STAT_WTBL.
+ * The RA tag reuses UNI_CMD_ID_STATISTICS_WTBL (10).
+ */
+struct mt7925_wtbl_rate_req {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ __le16 wlan_idx;
+ __le16 rsv1;
+} __packed;
+
+/* WTBL auto-rate event TLV. Firmware answers cmd tag 10 with event tag 12. */
+struct mt7925_wtbl_rate_event {
+ u8 rsv[4];
+ struct mt7925_wtbl_rate rate;
+} __packed;
+
+/* fixed field, mirror UNI_(CMD|EVENT)_MIB_INFO */
+struct mt7925_mib_hdr {
+ u8 band_idx;
+ u8 rsv[3];
+} __packed;
+
+/* One requested/returned MIB counter, mirror UNI_(CMD|EVENT)_MIB_DATA_T. */
+struct mt7925_mib_data {
+ __le16 tag; /* UNI_(CMD|EVENT)_MIB_DATA = 0 */
+ __le16 len;
+ __le32 counter; /* MIB counter ID (enum mt7925_mib_counter) */
+ __le64 data; /* only valid on event; ignored on cmd */
+} __packed;
+
+/* per-band link quality entry, mirror UNI_LINK_QUALITY */
+struct mt7925_link_quality {
+ s8 rssi; /* moving-average beacon RSSI of connected AP */
+ s8 link_quality; /* always 0 */
+ __le16 link_speed; /* TX rate1, unit 500 Kb/s */
+ u8 medium_busy_pct; /* always 0 */
+ u8 is_ready; /* 0: fields invalid, 1: valid */
+ u8 rsv[2];
+} __packed;
+
+/* request TLV, mirror UNI_CMD_LINK_QUALITY (header + tag/len only) */
+struct mt7925_lq_req {
+ u8 rsv[4]; /* UNI_CMD_GET_STATISTICS fixed field */
+ __le16 tag; /* UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1 */
+ __le16 len;
+} __packed;
+
+/* event TLV, mirror UNI_EVENT_LINK_QUALITY */
+struct mt7925_lq_event {
+ u8 rsv[4]; /* UNI_EVENT_STATISTICS fixed field */
+ __le16 tag; /* UNI_EVENT_ID_STATISTICS_LINK_QUALITY = 1 */
+ __le16 len;
+ struct mt7925_link_quality lq[MT7925_LQ_BAND_NUM];
+} __packed;
+
+static void
+mt7925_get_ap_sta_rec_idx_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mt7925_sta_rec_lookup *lookup = data;
+ struct mt792x_vif *mvif;
+ struct mt792x_sta *msta;
+
+ if (lookup->sta_rec_idx >= 0)
+ return;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return;
+
+ mvif = (struct mt792x_vif *)vif->drv_priv;
+ msta = mvif->wep_sta;
+ if (!msta)
+ return;
+
+ if (!msta->deflink.wcid.sta)
+ return;
+
+ lookup->sta_rec_idx = msta->deflink.wcid.idx;
+}
+
+int
+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx)
+{
+ struct mt7925_sta_rec_lookup lookup = {
+ .sta_rec_idx = -1,
+ };
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+ ieee80211_iterate_active_interfaces(dev->mphy.hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_get_ap_sta_rec_idx_iter,
+ &lookup);
+
+ if (lookup.sta_rec_idx < 0)
+ return -ENOTCONN;
+
+ if (lookup.sta_rec_idx > U8_MAX)
+ return -ERANGE;
+
+ *sta_rec_idx = lookup.sta_rec_idx;
+
+ return 0;
+}
+
+/* Decode the TX Vector BBP latch and print the last TX rate */
+void
+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3])
+{
+ const char *mode_str = "N/A";
+ u8 rate, txmode, frmode, sgi, ldpc, nsts, stbc, spe, dcm, ersu106t;
+ s8 txpwr, pos_txpwr;
+ u32 txv[3];
+
+ txv[0] = le32_to_cpu(le_txv[0]);
+ txv[1] = le32_to_cpu(le_txv[1]);
+ txv[2] = le32_to_cpu(le_txv[2]);
+
+ if (txv[0] == 0xffffffff) {
+ seq_printf(s, "%-22s %s = %s\n", " ", "Last TX Rate", "N/A");
+ seq_printf(s, "%-22s %s = %s\n",
+ " ", "Chip Out TX Power", "N/A");
+ return;
+ }
+
+ rate = MT7925_TXV_GET_TX_RATE(txv);
+ txmode = MT7925_TXV_GET_TX_MODE(txv);
+ frmode = MT7925_TXV_GET_TX_FRMODE(txv);
+ nsts = MT7925_TXV_GET_TX_NSTS(txv) + 1;
+ sgi = MT7925_TXV_GET_TX_SGI(txv);
+ ldpc = MT7925_TXV_GET_TX_LDPC(txv);
+ stbc = MT7925_TXV_GET_TX_STBC(txv);
+ txpwr = MT7925_TXV_GET_TX_PWR(txv);
+ spe = MT7925_TXV_GET_TX_SPE_IDX(txv);
+ dcm = MT7925_TXV_GET_TX_DCM(txv);
+ ersu106t = MT7925_TXV_GET_TX_106T(txv);
+
+ if (dcm)
+ rate = MT7925_TXV_RATE_UNMASK_DCM(rate);
+ if (ersu106t)
+ rate = MT7925_TXV_RATE_UNMASK_106T(rate);
+
+ if (txmode < ARRAY_SIZE(mt7925_tx_mode_str))
+ mode_str = mt7925_tx_mode_str[txmode];
+
+ seq_printf(s, "%-22s %s = ", " ", "Last TX Rate");
+
+ /* rate/MCS field */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ",
+ rate < 4 ? mt7925_tx_rate_cck_str[rate] :
+ (((rate >= 5) && (rate <= 7)) ?
+ mt7925_tx_rate_cck_str[rate - 4] :
+ mt7925_tx_rate_cck_str[4]));
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_hw_rate_ofdm_str(rate));
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF)
+ seq_printf(s, "MCS%d, ", rate);
+ else
+ seq_printf(s, "%s%d_MCS%d, ",
+ stbc ? "NSTS" : "NSS", nsts, rate);
+
+ /* bandwidth */
+ seq_printf(s, "%s, ",
+ frmode < 5 ? mt7925_tx_rate_bw_str[frmode] :
+ mt7925_tx_rate_bw_str[5]);
+
+ /* GI / preamble */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ", rate < 4 ? "LP" : "SP");
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ ; /* OFDM has no GI/preamble to print */
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF ||
+ txmode == MT7925_TX_RATE_MODE_VHT ||
+ txmode == MT7925_TX_RATE_MODE_PLR)
+ seq_printf(s, "%s, ", sgi == 0 ? "LGI" : "SGI");
+ else
+ seq_printf(s, "%s, ",
+ sgi == 0 ? "SGI" : (sgi == 1 ? "MGI" : "LGI"));
+
+ /* mode, DCM, 106t, STBC, coding, SPE */
+ seq_printf(s, "%s%s%s%s%s%s%d\n",
+ mode_str,
+ dcm ? ", DCM" : "", ersu106t ? ", 106t" : "",
+ stbc ? ", STBC, " : ", ", ldpc == 0 ? "BCC" : "LDPC",
+ ", SPE", spe);
+
+ /* chip out TX power (unit: 0.5 dBm) */
+ pos_txpwr = (txpwr < 0) ? (~txpwr + 1) : txpwr;
+ seq_printf(s, "%-22s %s = %c%d.%1d dBm\n",
+ " ", "Chip Out TX Power",
+ (txpwr < 0) ? '-' : '+',
+ (pos_txpwr / 2), 5 * (pos_txpwr % 2));
+}
+
+/* Guard interval used for the auto-rate entries, mirror Gen4m
+ * connac3x_get_sgi_info_from_fw(): pick the per-mode/per-BW flag.
+ */
+u8
+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap)
+{
+ bool he = (txmode == MT7925_TX_RATE_MODE_HE_SU ||
+ txmode == MT7925_TX_RATE_MODE_HE_ER ||
+ txmode == MT7925_TX_RATE_MODE_HE_TRIG ||
+ txmode == MT7925_TX_RATE_MODE_HE_MU);
+ bool eht = (txmode == MT7925_TX_RATE_MODE_EHT_ER ||
+ txmode == MT7925_TX_RATE_MODE_EHT_TRIG ||
+ txmode == MT7925_TX_RATE_MODE_EHT_MU);
+
+ if (eht)
+ return wr->gi_eht;
+
+ switch (fcap) {
+ case MT7925_BW_20:
+ return he ? wr->g2_he : wr->g2;
+ case MT7925_BW_40:
+ return he ? wr->g4_he : wr->g4;
+ case MT7925_BW_80:
+ return he ? wr->g8_he : wr->g8;
+ case MT7925_BW_160:
+ return he ? wr->g16_he : wr->g16;
+ default:
+ return 0;
+ }
+}
+
+/* Coding used for the auto-rate entries, mirror Gen4m
+ * connac3x_wtbl_get_ldpc_info_from_fw(): 0 = BCC, 1 = LDPC.
+ */
+u8
+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode)
+{
+ switch (txmode) {
+ case MT7925_TX_RATE_MODE_HTMIX:
+ case MT7925_TX_RATE_MODE_HTGF:
+ return wr->ht_ldpc;
+ case MT7925_TX_RATE_MODE_VHT:
+ return wr->vht_ldpc;
+ case MT7925_TX_RATE_MODE_HE_SU:
+ case MT7925_TX_RATE_MODE_HE_ER:
+ case MT7925_TX_RATE_MODE_HE_TRIG:
+ case MT7925_TX_RATE_MODE_HE_MU:
+ return wr->he_ldpc;
+ case MT7925_TX_RATE_MODE_EHT_ER:
+ case MT7925_TX_RATE_MODE_EHT_TRIG:
+ case MT7925_TX_RATE_MODE_EHT_MU:
+ return wr->eht_ldpc;
+ default:
+ return 0;
+ }
+}
+
+int mt792x_mcu_get_stat(struct mt792x_dev *dev,
+ struct mt7925_sta_stats *stats)
+{
+ u16 len = 0;
+ int ret = 0;
+ struct mt7925_sta_stats_event *event = NULL;
+ struct sk_buff *skb = NULL;
+ struct mt7925_sta_stats_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_STA),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ .lls_read_clear = false,
+ .reset_counter = false,
+ };
+
+ ret = mt7925_get_ap_sta_rec_idx(dev, &req.sta_rec_idx);
+ if (ret)
+ return ret;
+
+ if (!stats)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO),
+ &req, sizeof(req), true, &skb);
+
+ if (ret)
+ return ret;
+
+ event = (struct mt7925_sta_stats_event *)skb->data;
+
+ if (le16_to_cpu(event->tag) != UNI_CMD_ID_STATISTICS_STA) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ len = le16_to_cpu(event->len);
+ if (len < sizeof(*event) - sizeof(event->rsv) ||
+ len > skb->len - sizeof(event->rsv) || !IS_ALIGNED(len, 4)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ if (event->stats.version != MT7925_STA_STATS_VERSION) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ if (!(le32_to_cpu(event->stats.flags) & MT7925_STA_STATS_VALID)) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ memcpy(stats, &event->stats, sizeof(*stats));
+
+out:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,
+ const u32 *counters, u8 counter_num,
+ u64 *values)
+{
+ u8 *req;
+ u8 *tlv, *tlv_end;
+ u16 req_len;
+ int i, ret;
+ struct mt7925_mib_hdr *req_hdr;
+ struct mt7925_mib_data *req_tlv;
+ struct sk_buff *skb;
+
+ if (!counters || !values || !counter_num ||
+ counter_num > MT7925_MIB_MAX_CNT)
+ return -EINVAL;
+
+ /* clear output; counters not returned by firmware stay 0 */
+ memset(values, 0, counter_num * sizeof(*values));
+
+ req_len = sizeof(struct mt7925_mib_hdr) +
+ counter_num * sizeof(struct mt7925_mib_data);
+
+ req = kzalloc(req_len, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req_hdr = (struct mt7925_mib_hdr *)req;
+ req_hdr->band_idx = band_idx;
+
+ req_tlv = (struct mt7925_mib_data *)(req + sizeof(*req_hdr));
+ for (i = 0; i < counter_num; i++) {
+ req_tlv[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA);
+ req_tlv[i].len = cpu_to_le16(sizeof(struct mt7925_mib_data));
+ req_tlv[i].counter = cpu_to_le32(counters[i]);
+ }
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_MIB_INFO), req,
+ req_len, true, &skb);
+ kfree(req);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(struct mt7925_mib_hdr)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ /* walk the returned TLVs, match each counter ID to the request */
+ tlv = skb->data + sizeof(struct mt7925_mib_hdr);
+ tlv_end = skb->data + skb->len;
+
+ while (tlv + sizeof(struct mt7925_mib_data) <= tlv_end) {
+ struct mt7925_mib_data *evt_tlv =
+ (struct mt7925_mib_data *)tlv;
+ u16 tag = le16_to_cpu(evt_tlv->tag);
+ u16 len = le16_to_cpu(evt_tlv->len);
+ u32 counter;
+
+ if (len < sizeof(struct mt7925_mib_data) ||
+ tlv + len > tlv_end)
+ break;
+
+ if (tag == UNI_CMD_MIB_DATA) {
+ counter = le32_to_cpu(evt_tlv->counter);
+
+ if (counter < MT7925_MIB_CNT_MAX_NUM) {
+ for (i = 0; i < counter_num; i++) {
+ if (counters[i] == counter) {
+ values[i] =
+ le64_to_cpu(evt_tlv->data);
+ break;
+ }
+ }
+ }
+ }
+
+ tlv += len;
+ }
+
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+
+ return ret;
+}
+
+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx)
+{
+ char * const ofdm_str[] = {
+ "6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M", "N/A"
+ };
+
+ switch (ofdm_idx) {
+ case 11: return ofdm_str[0]; /* 6M */
+ case 15: return ofdm_str[1]; /* 9M */
+ case 10: return ofdm_str[2]; /* 12M */
+ case 14: return ofdm_str[3]; /* 18M */
+ case 9: return ofdm_str[4]; /* 24M */
+ case 13: return ofdm_str[5]; /* 36M */
+ case 8: return ofdm_str[6]; /* 48M */
+ case 12: return ofdm_str[7]; /* 54M */
+ default: return ofdm_str[8];
+ }
+}
+
+/* mt792x_mcu_get_wtbl_rate - fetch the firmware auto-rate table for a WCID
+ * @dev: device
+ * @wlan_idx: HW WTBL / WCID index (see mt7925_sta_stats.wtbl_idx)
+ * @rate: out, filled with the auto-rate table snapshot
+ *
+ * Mirror of Gen4m wlanoidGetStatWtbl(): send UNI_CMD_GET_STAT_INFO with the
+ * WTBL rate TLV (cmd tag 10) and parse the reply (event tag 12) into @rate.
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,
+ struct mt7925_wtbl_rate *rate)
+{
+ int ret;
+ struct mt7925_wtbl_rate_event *event = NULL;
+ struct sk_buff *skb = NULL;
+ struct mt7925_wtbl_rate_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_WTBL),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ .wlan_idx = cpu_to_le16(wlan_idx),
+ };
+
+ if (!rate)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO),
+ &req, sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(*event)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ event = (struct mt7925_wtbl_rate_event *)skb->data;
+
+ if (le16_to_cpu(event->rate.tag) != MT7925_UNI_EVENT_STATISTICS_WTBL) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ memcpy(rate, &event->rate, sizeof(*rate));
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+/**
+ * mt7925_mcu_get_link_quality - query RSSI and TX link speed for a band
+ * @dev: device
+ * @band_idx: band/BSS index (0..3)
+ * @rssi: out, moving-average beacon RSSI in dBm (may be NULL)
+ * @tx_link_speed_kbps: out, TX rate in kbit/s (may be NULL)
+ *
+ * Return: 0 on success, -ENOENT if the band's link quality is not ready.
+ *
+ * Only RSSI and TX rate come from firmware here; for RX rate read wcid->rate.
+ */
+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,
+ s8 *rssi, u32 *tx_link_speed_kbps)
+{
+ int ret;
+ struct mt7925_lq_event *event;
+ struct mt7925_link_quality *lq;
+ struct sk_buff *skb;
+ struct mt7925_lq_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_LINK_QUALITY),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ };
+
+ if (band_idx >= MT7925_LQ_BAND_NUM)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO), &req,
+ sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(*event)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ event = (struct mt7925_lq_event *)skb->data;
+
+ if (le16_to_cpu(event->tag) != UNI_CMD_ID_STATISTICS_LINK_QUALITY) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ lq = &event->lq[band_idx];
+ if (!lq->is_ready) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (rssi)
+ *rssi = lq->rssi;
+
+ /* firmware reports TX rate in 500 Kb/s units -> kbit/s */
+ if (tx_link_speed_kbps)
+ *tx_link_speed_kbps = le16_to_cpu(lq->link_speed) * 500;
+
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.h b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h
new file mode 100644
index 000000000000..a70b9eb3aa80
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h
@@ -0,0 +1,309 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#ifndef __MT7925_STATS_H
+#define __MT7925_STATS_H
+
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include "../mt76_connac_mcu.h"
+
+#define MT7925_STA_STATS_AC_NUM 4
+#define MT7925_STA_STATS_BAND_NUM 2
+#define MT7925_STA_STATS_AGG_RANGE_NUM 16
+#define MT7925_STA_STATS_TX_QUALITY_NUM 4
+#define MT7925_TX_MODE_NUM 16
+#define MT7925_TX_RATE_CCK_NUM 5
+#define MT7925_TX_RATE_BW_NUM 6
+
+/* HW rate code (16-bit) field extract, mirror Gen4m CONNAC3X_HW_TX_RATE_TO_*.
+ * The auto-rate table stores one rate code per entry.
+ */
+#define MT7925_HW_RATE_TO_MCS(_x) (((_x) & GENMASK(5, 0)) >> 0)
+#define MT7925_HW_RATE_TO_MODE(_x) (((_x) & GENMASK(9, 6)) >> 6)
+#define MT7925_HW_RATE_TO_NSS(_x) (((_x) & GENMASK(13, 10)) >> 10)
+#define MT7925_HW_RATE_TO_STBC(_x) (((_x) & BIT(14)) >> 14)
+#define MT7925_HW_RATE_TO_DCM(_x) (((_x) & BIT(4)) >> 4)
+#define MT7925_HW_RATE_TO_106T(_x) (((_x) & BIT(5)) >> 5)
+#define MT7925_HW_RATE_UNMASK_DCM(_r) ((u8)(_r) & 0xef)
+#define MT7925_HW_RATE_UNMASK_106T(_r) ((u8)(_r) & 0xdf)
+
+/* Number of entries in the firmware auto-rate table (mirror Gen4m
+ * AUTO_RATE_NUM). RA cycles through these and reports which one is in use
+ * via wtbl->rate_idx.
+ */
+#define MT7925_AUTO_RATE_NUM 8
+
+/* Firmware EVENT_STATISTICS sub-event tags. The WTBL rate request uses cmd tag
+ * UNI_CMD_ID_STATISTICS_WTBL (10) but the firmware replies with event tag 12.
+ */
+#define MT7925_UNI_EVENT_STATISTICS_WTBL 12
+
+/* CONNAC3X TX Vector BBP latch bit fields (mirror cmm_asic_connac3x.h).
+ * These operate on the 3-DWORD tx_vector[band].txv[3] array.
+ */
+#define MT7925_TXV_GET_TX_RATE(_txv) ((_txv)[2] & 0x7f)
+#define MT7925_TXV_GET_TX_LDPC(_txv) (((_txv)[2] & (0x1 << 7)) >> 7)
+#define MT7925_TXV_GET_TX_STBC(_txv) (((_txv)[0] & (0x3 << 6)) >> 6)
+#define MT7925_TXV_GET_TX_FRMODE(_txv) (((_txv)[0] & (0x7 << 8)) >> 8)
+#define MT7925_TXV_GET_TX_MODE(_txv) (((_txv)[0] & (0xf << 12)) >> 12)
+#define MT7925_TXV_GET_TX_NSTS(_txv) (((_txv)[2] & (0xfU << 28)) >> 28)
+#define MT7925_TXV_GET_TX_PWR(_txv) (((_txv)[0] & (0xff << 16)) >> 16)
+#define MT7925_TXV_GET_TX_SGI(_txv) (((_txv)[1] & (0x3 << 26)) >> 26)
+#define MT7925_TXV_GET_TX_SPE_IDX(_txv) (((_txv)[0] & (0x1f << 0)) >> 0)
+#define MT7925_TXV_GET_TX_DCM(_txv) (((_txv)[2] & (0x1 << 4)) >> 4)
+#define MT7925_TXV_GET_TX_106T(_txv) (((_txv)[2] & (0x1 << 5)) >> 5)
+#define MT7925_TXV_RATE_UNMASK_DCM(_r) ((u8)(_r) & 0xef)
+#define MT7925_TXV_RATE_UNMASK_106T(_r) ((u8)(_r) & 0xdf)
+
+extern const char * const mt7925_tx_mode_str[MT7925_TX_MODE_NUM];
+extern const char * const mt7925_tx_rate_cck_str[MT7925_TX_RATE_CCK_NUM];
+extern const char * const mt7925_tx_rate_bw_str[MT7925_TX_RATE_BW_NUM];
+
+/* TX rate mode, mirror Gen4m TX_RATE_MODE_* (see nic_rate.h). Used to decode
+ * both the TX Vector BBP latch and the firmware auto-rate table rate codes.
+ */
+enum mt7925_tx_rate_mode {
+ MT7925_TX_RATE_MODE_CCK = 0,
+ MT7925_TX_RATE_MODE_OFDM = 1,
+ MT7925_TX_RATE_MODE_HTMIX = 2,
+ MT7925_TX_RATE_MODE_HTGF = 3,
+ MT7925_TX_RATE_MODE_VHT = 4,
+ MT7925_TX_RATE_MODE_PLR = 5,
+ MT7925_TX_RATE_MODE_HE_SU = 8,
+ MT7925_TX_RATE_MODE_HE_ER = 9,
+ MT7925_TX_RATE_MODE_HE_TRIG = 10,
+ MT7925_TX_RATE_MODE_HE_MU = 11,
+ MT7925_TX_RATE_MODE_EHT_ER = 13,
+ MT7925_TX_RATE_MODE_EHT_TRIG = 14,
+ MT7925_TX_RATE_MODE_EHT_MU = 15,
+};
+
+enum {
+ UNI_CMD_MIB_DATA,
+};
+
+enum stats_query_mib_array_index {
+ QUERY_MIB_CNT_RX_FCS_ERR = 0,
+ QUERY_MIB_CNT_RX_FIFO_OVERFLOW,
+ QUERY_MIB_CNT_RX_MPDU,
+ QUERY_MIB_CNT_AMPDU_RX_COUNT,
+ QUERY_MIB_CNT_PF_DROP,
+ QUERY_MIB_CNT_LEN_MISMATCH,
+ QUERY_MIB_CNT_CHANNEL_IDLE,
+ QUERY_MIB_CNT_CCA_NAV_TX_TIME,
+ QUERY_MIB_CNT_MDRDY,
+ QUERY_MIB_CNT_RX_CCK_MDRDY_TIME,
+ QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,
+ QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,
+ QUERY_MIB_CNT_P_CCA_TIME,
+ QUERY_MIB_CNT_S_CCA_TIME,
+ QUERY_MIB_CNT_P_ED_TIME,
+ QUERY_MIB_CNT_BCN_TX,
+ QUERY_MIB_CNT_TX_BW_40MHZ,
+ QUERY_MIB_CNT_TX_BW_80MHZ,
+ QUERY_MIB_CNT_TX_BW_160MHZ,
+ QUERY_MIB_CNT_BSS0_BA_MISS,
+ QUERY_MIB_CNT_BSS0_RTS_TX_CNT,
+ QUERY_MIB_CNT_BSS0_FRAME_RETRY,
+ QUERY_MIB_CNT_BSS0_FRAME_RETRY_2,
+ QUERY_MIB_CNT_BSS0_RTS_RETRY,
+ QUERY_MIB_CNT_BSS0_ACK_FAIL,
+ QUERY_MIB_CNT_AMPDU,
+ QUERY_MIB_CNT_AMPDU_MPDU,
+ QUERY_MIB_CNT_AMPDU_ACKED,
+ QUERY_MIB_CNT_MAX_NUM,
+};
+
+enum stats_trx_agg_range {
+ STAT_MIB_CNT_TRX_AGG_RANGE0_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE1_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE2_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE3_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE4_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE5_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE6_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE7_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM
+};
+
+enum {
+ MT7925_BW_20 = 0,
+ MT7925_BW_40,
+ MT7925_BW_80,
+ MT7925_BW_160,
+};
+
+struct mt7925_sta_stats_ac {
+ __le32 tx_fail;
+ __le32 tx_retry;
+} __packed;
+
+struct mt7925_sta_stats_tx_vector {
+ __le32 txv[3];
+} __packed;
+
+struct mt7925_sta_stats_mib {
+ __le32 rx_mpdu_cnt;
+ __le32 fcs_error;
+ __le32 rx_fifo_full;
+ __le32 ampdu_tx_sf_cnt;
+ __le32 ampdu_tx_ack_sf_cnt;
+ __le32 tx_range_ampdu_cnt[MT7925_STA_STATS_AGG_RANGE_NUM];
+} __packed;
+
+/* Firmware EVENT_STA_STATISTICS body. */
+struct mt7925_sta_stats {
+ u8 version;
+ u8 rsv[3];
+ __le32 flags;
+
+ u8 sta_idx;
+ u8 bss_idx;
+ u8 wtbl_idx;
+ u8 rsv1;
+
+ u8 mac_addr[ETH_ALEN];
+ u8 per;
+ u8 rcpi;
+
+ __le32 phy_mode;
+ __le16 link_speed; /* 0.5 Mbit/s units */
+ u8 link_quality;
+ u8 link_reserved;
+
+ __le32 tx_count;
+ __le32 tx_fail_count;
+ __le32 tx_life_timeout_count;
+ __le32 tx_done_air_time;
+ __le32 transmit_count;
+ __le32 transmit_fail_count;
+
+ struct mt7925_sta_stats_ac ac[MT7925_STA_STATS_AC_NUM];
+
+ u8 temperature;
+ u8 skip_ar;
+ u8 ar_table_idx;
+ u8 rate_entry_idx;
+ u8 rate_entry_idx_prev;
+ u8 tx_sgi_detect_pass_cnt;
+ u8 ave_per;
+ u8 rsv2;
+
+ __le32 agg_range_ctrl[2];
+ u8 range_mode;
+ u8 rsv3[3];
+ __le32 agg_range_ctrl_ext[6];
+
+ u8 ar_state_curr;
+ u8 ar_state_prev;
+ u8 ar_action_type;
+ u8 highest_rate_cnt;
+ u8 lowest_rate_cnt;
+ u8 rsv4;
+ __le16 train_up;
+ __le16 train_down;
+ u8 rsv5[2];
+ __le32 rate1_tx_cnt;
+ __le32 rate1_fail_cnt;
+
+ struct mt7925_sta_stats_tx_vector tx_vector[MT7925_STA_STATS_BAND_NUM];
+ struct mt7925_sta_stats_mib mib[MT7925_STA_STATS_BAND_NUM];
+
+ u8 is_force_tx_stream;
+ u8 is_force_se_off;
+ __le16 ra_running_cnt;
+ u8 ra_status;
+ u8 max_ampdu_factor;
+ u8 tx_quality[MT7925_STA_STATS_TX_QUALITY_NUM];
+ u8 tx_rate_up_penalty;
+ u8 low_traffic_mode;
+ u8 low_traffic_count;
+ u8 low_traffic_dashboard;
+ u8 dynamic_sgi_state;
+ u8 dynamic_sgi_score;
+ u8 dynamic_bw_state;
+ u8 dynamic_gband_256qam_state;
+ u8 vht_non_sp_rate_state;
+ u8 rsv6[3];
+ u8 rsv7[2];
+} __packed;
+
+struct mt7925_sta_rec_lookup {
+ int sta_rec_idx;
+};
+
+/* Firmware auto-rate table snapshot, mirror Gen4m struct UNI_EVENT_STAT_WTBL.
+ *
+ * The firmware RA maintains up to MT7925_AUTO_RATE_NUM candidate rates in
+ * rate_code[]. rate_idx tells which entry is currently in use, so a dump can
+ * point at it with "-->". fcap is the bandwidth capability and the ldpc/sgi
+ * flags describe the per-mode coding/guard-interval used for those rates.
+ */
+struct mt7925_wtbl_rate {
+ __le16 tag;
+ __le16 len;
+ u8 rx_rcpi0;
+ u8 rx_rcpi1;
+ u8 cbrn;
+ u8 fcap;
+ u8 spe_idx;
+ u8 g2; /* SGI enabled, non-HE, BW20 */
+ u8 g4; /* SGI enabled, non-HE, BW40 */
+ u8 g8; /* SGI enabled, non-HE, BW80 */
+ u8 g16; /* SGI enabled, non-HE, BW160 */
+ u8 g2_he; /* GI, HE, BW20 */
+ u8 g4_he; /* GI, HE, BW40 */
+ u8 g8_he; /* GI, HE, BW80 */
+ u8 g16_he; /* GI, HE, BW160 */
+ u8 gi_eht; /* GI, EHT */
+ u8 ht_ldpc;
+ u8 vht_ldpc;
+ u8 he_ldpc;
+ u8 eht_ldpc;
+ u8 af;
+ u8 rate_idx; /* index into rate_code[] currently in use */
+ __le16 rate_code[MT7925_AUTO_RATE_NUM];
+ __le32 rsv[4];
+} __packed;
+
+int
+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx);
+
+void
+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3]);
+
+u8
+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap);
+
+u8
+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode);
+
+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx);
+
+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,
+ struct mt7925_wtbl_rate *rate);
+
+int mt792x_mcu_get_stat(struct mt792x_dev *dev,
+ struct mt7925_sta_stats *stats);
+
+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,
+ const u32 *counters, u8 counter_num,
+ u64 *values);
+
+/* GET_STAT_INFO (UNI_CMD_ID_GET_STATISTICS = 0x23),
+ * sub-command UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1.
+ *
+ * @band_idx: band/BSS index (0..3)
+ * @rssi: out, moving-average beacon RSSI (dBm), may be NULL
+ * @tx_link_speed_kbps: out, TX link speed in kbit/s, may be NULL
+ *
+ * Returns 0 on success, -ENOENT if the band's link quality is not ready.
+ *
+ * NOTE: firmware provides only RSSI and TX rate here. RX rate/bandwidth are
+ * not in this event; read wcid->rate for the RX rate.
+ */
+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,
+ s8 *rssi, u32 *tx_link_speed_kbps);
+
+#endif
--
2.45.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose
2026-08-27 5:54 ` [PATCH v2] " Eason Lai
@ 2026-08-27 8:44 ` Eason Lai
0 siblings, 0 replies; 5+ messages in thread
From: Eason Lai @ 2026-08-27 8:44 UTC (permalink / raw)
To: nbd, lorenzo
Cc: linux-wireless, linux-mediatek, kun.wu, deren.wu, sean.wang,
quan.zhou, ryder.lee, leon.yen, litien.chang, jb.tsai,
michael.shih, eason.lai, kernel test robot
From: Michael Shih <michael.shih@mediatek.com>
Usage:
(1) Connect STA to AP (You may use dhclient CMD to ask for DHCP IP, to use ping to AP)
DHCP IP CMD: dhclient ${WLAN_IF} -v
Example: dhclient wlan0 -v
## (To simulate traffic) Ping 10 packets for every second to AP
ping 192.168.0.XXX -i0.1
(2) (Get stats) Use command, phyX is your WiFi interface:
cat /sys/kernel/debug/ieee80211/phyX/mt76/stats
(3) You will see below stats info:
(STA) connected AP MAC Address = a8:42:a1:46:c4:6b
BssIdx = [0]
StaRecIdx = [1],
RSSI = -25
Link_Speed = 2402000 (kbit/s)
temperature = 40
Tx success = 716
Tx fail to Rcv ACK after retry = 0
Rx Mpdu = 23
Rx Fcs Error = 1 (MPDU)
Rx FIFO full = 0 (MPDU)
TRX_AGG_RANGE[0] = 0 (PPDU)
TRX_AGG_RANGE[1] = 0 (PPDU)
TRX_AGG_RANGE[2] = 0 (PPDU)
TRX_AGG_RANGE[3] = 0 (PPDU)
TRX_AGG_RANGE[4] = 0 (PPDU)
TRX_AGG_RANGE[5] = 0 (PPDU)
TRX_AGG_RANGE[6] = 0 (PPDU)
TRX_AGG_RANGE[7] = 0 (PPDU)
MPDUs in AMPDUs transmitted = 0 (MPDU)
MPDUs in AMPDUs transmitted with ACK reply = 0 (MPDU)
rate1_tx_cnt = 714
rate1_fail_cnt = 223
train_up = 2310
train_down = 3080
is_force_tx_stream = 0
is_force_se_off = 0
max_ampdu_factor = 9
tx_rate_up_penalty = 0
low_traffic_mode = 2
low_traffic_count = 0
low_traffic_dashboard = 3
dynamic_sgi_state = 0
dynamic_sgi_score = 2
dynamic_bw_state = 0
dynamic_gband_256qam_state = 3
vht_non_sp_rate_state = 0
Last TX Rate = 24M, BW20, OFDM, BCC, SPE24
Chip Out TX Power = +16.5 dBm
Cur Rate Index = 0
Rate index[0] --> NSS2_MCS11, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[1] NSS2_MCS9, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[2] NSS2_MCS7, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[3] NSS2_MCS4, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[4] NSS2_MCS3, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[5] NSS1_MCS2, BW160/BW8080, SGI, HE_SU, LDPC
Rate index[6] NSS1_MCS1, BW80, SGI, HE_SU, LDPC
Rate index[7] NSS1_MCS0, BW80, SGI, HE_SU, LDPC
mib state:
=== Rx Related Counters ===
Rx with CRC = 24020 (MPDU)
Rx drop due to out of resource = 0 (MPDU)
Rx Mpdu = 93223 (MPDU)
Rx AMpdu = 668 (PPDU)
Rx PF Drop = 0 (MPDU)
Rx Len Mismatch = 5853 (PPDU)
=== Phy/Timing Related Counters ===
ChannelIdleCnt = 1040434
CCA_NAV_Tx_Time = 18510036
Rx_MDRDY_CNT = 166580 (PPDU)
CCK_MDRDY = 7782
OFDM_MDRDY = 11912731
OFDM_GREEN_MDRDY = 0
Prim CCA Time = 17917650
Sec CCA Time = 14922034
Prim ED Time = 5677528
=== Tx Related Counters(Generic) ===
BeaconTxCnt = 0
Tx 40MHz Cnt = 279 (MPDU)
Tx 80MHz Cnt = 44 (MPDU)
Tx 160MHz Cnt = 712 (MPDU)
=== BSSID[0] Related Counters ===
BA Miss Cnt = 210 (PPDU)
RTS Tx Cnt = 1003 (MPDU)
Frame Retry Cnt = 239 (MPDU)
Frame Retry 2 Cnt = 71 (MPDU)
RTS Retry Cnt = 152 (MPDU)
Ack Failed Cnt = 319 (PPDU)
=== AMPDU Related Counters ===
Tx AMPDU_Pkt_Cnt = 705 (PPDU)
Tx AMPDU_MPDU_Pkt_Cnt = 713 (MPDU)
AMPDU Tx success = 503 (MPDU)
AMPDU Tx fail count = 210 (MPDU), PER=29.4%
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202608251101.iQGUrSd0-lkp@intel.com/
Closes: https://lore.kernel.org/oe-kbuild-all/202608251526.VgKMT8Lv-lkp@intel.com/
Signed-off-by: Michael Shih <michael.shih@mediatek.com>
---
v2: (1) Remove redundant code to fix build errors.
(2) Move UNI_CMD_MIB_DATA enum definition from 'mt76/mt76_connac_mcu.h' to
'mt7925/stats.h' to avoid redeclartion with 'mt76/mt7996/mcu.h'
v3: Fixed build errors due to __udivdi3
---
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 173 ++++++
.../wireless/mediatek/mt76/mt7925/Makefile | 2 +-
.../wireless/mediatek/mt76/mt7925/debugfs.c | 346 +++++++++++
.../net/wireless/mediatek/mt76/mt7925/stats.c | 585 ++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/stats.h | 309 +++++++++
5 files changed, 1414 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/stats.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/stats.h
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 0ededa569e9a..bf0f105c786e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1483,6 +1483,179 @@ enum {
MT_NIC_CAP_EML_CAP = 0x22,
};
+enum {
+ UNI_CMD_ID_STATISTICS_BASIC = 0,
+ UNI_CMD_ID_STATISTICS_LINK_QUALITY = 1,
+ UNI_CMD_ID_STATISTICS_STA = 2,
+ UNI_CMD_ID_STATISTICS_BUG_REPORT = 3,
+ UNI_CMD_ID_STATISTICS_TX_STATS = 4,
+ UNI_CMD_ID_STATISTICS_ALL_STATS = 5,
+ UNI_CMD_ID_STATISTICS_EML_STATS = 6,
+ UNI_CMD_ID_STATISTICS_REGULAR_STATS = 7,
+ UNI_CMD_ID_STATISTICS_BSS_LINK_QUALITY = 8,
+ UNI_CMD_ID_STATISTICS_ML_CHNL_COND = 9,
+ UNI_CMD_ID_STATISTICS_WTBL = 10,
+ UNI_CMD_ID_STATISTICS_LINK_LAYER_STATS = 128,
+ UNI_CMD_ID_STATISTICS_PPDU_LATENCY = 129,
+ UNI_CMD_ID_STATISTICS_CURRENT_TX_RATE = 130,
+ UNI_CMD_ID_STATISTICS_SET_TX_BITRATE_MONITOR = 131,
+ UNI_CMD_ID_STATISTICS_GET_PREDICT_TX_BITRATE = 132,
+ UNI_CMD_ID_STATISTICS_SET_ATPS_TYPE_RATE_MODE = 133,
+ UNI_CMD_ID_STATISTICS_SET_BEACON_REPORT = 134,
+ UNI_CMD_ID_STATISTICS_BSS_CURRENT_TX_RATE = 135,
+ UNI_CMD_ID_STATISTICS_GET_BSS_PREDICT_TX_BITRATE = 136,
+ UNI_CMD_ID_STATISTICS_MAX_NUM
+};
+
+enum mt7925_mib_counter {
+ UNI_CMD_MIB_CNT_RX_FCS_ERR = 0,
+ UNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW = 1,
+ UNI_CMD_MIB_CNT_RX_MPDU = 2,
+ UNI_CMD_MIB_CNT_AMPDU_RX_COUNT = 3,
+ UNI_CMD_MIB_CNT_RX_TOTAL_BYTE = 4,
+ UNI_CMD_MIB_CNT_RX_VALID_AMPDU_SF = 5,
+ UNI_CMD_MIB_CNT_RX_VALID_BYTE = 6,
+ UNI_CMD_MIB_CNT_CHANNEL_IDLE = 7,
+ UNI_CMD_MIB_CNT_VEC_DROP = 8,
+ UNI_CMD_MIB_CNT_DELIMITER_FAIL = 9,
+ UNI_CMD_MIB_CNT_VEC_MISMATCH = 10,
+ UNI_CMD_MIB_CNT_MDRDY = 11,
+ UNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME = 12,
+ UNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME = 13,
+ UNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME = 14,
+ UNI_CMD_MIB_CNT_PF_DROP = 15,
+ UNI_CMD_MIB_CNT_LEN_MISMATCH = 16,
+ UNI_CMD_MIB_CNT_P_CCA_TIME = 17,
+ UNI_CMD_MIB_CNT_S_CCA_TIME = 18,
+ UNI_CMD_MIB_CNT_CCA_NAV_TX_TIME = 19,
+ UNI_CMD_MIB_CNT_P_ED_TIME = 20,
+ UNI_CMD_MIB_CNT_BCN_TX = 21,
+ UNI_CMD_MIB_CNT_TX_BW_20MHZ = 22,
+ UNI_CMD_MIB_CNT_TX_BW_40MHZ = 23,
+ UNI_CMD_MIB_CNT_TX_BW_80MHZ = 24,
+ UNI_CMD_MIB_CNT_TX_BW_160MHZ = 25,
+ UNI_CMD_RMAC_CNT_OBSS_AIRTIME = 26,
+ UNI_CMD_RMAC_CNT_NONWIFI_AIRTIME = 27,
+ UNI_CMD_MIB_CNT_TX_DUR_CNT = 28,
+ UNI_CMD_MIB_CNT_RX_DUR_CNT = 29,
+ UNI_CMD_MIB_CNT_BA_CNT = 30,
+ UNI_CMD_MIB_CNT_MAC2PHY_TX_TIME = 31,
+ UNI_CMD_MIB_CNT_RX_OUT_OF_RANGE_COUNT = 32,
+ UNI_CMD_MIB_CNT_IBF_TX = 33,
+ UNI_CMD_MIB_CNT_EBF_TX = 34,
+ UNI_CMD_MIB_CNT_MUBF_TX = 35,
+ UNI_CMD_MIB_CNT_RX_BF_VHTFBK = 36,
+ UNI_CMD_MIB_CNT_RX_BF_HTFBK = 37,
+ UNI_CMD_MIB_CNT_RX_BF_HEFBK = 38,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_BFPOLL_TRI = 39,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_TRI = 40,
+ UNI_CMD_MIB_CNT_BFEE_RX_FBKCQI = 41,
+ UNI_CMD_MIB_CNT_BFEE_RX_NDP = 42,
+ UNI_CMD_MIB_CNT_RX_BF_IBF_UPT = 43,
+ UNI_CMD_MIB_CNT_RX_BF_EBF_UPT = 44,
+ UNI_CMD_MIB_CNT_BFEE_SP_ABORT = 45,
+ UNI_CMD_MIB_CNT_BFEE_TB_LEN_ERR = 46,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_MUTE = 47,
+ UNI_CMD_MIB_CNT_BFEE_TMAC_ABORT = 48,
+ UNI_CMD_MIB_CNT_BFEE_TXFBK_CPL = 49,
+ UNI_CMD_MIB_CNT_BFEE_COANT_BLKTX = 50,
+ UNI_CMD_MIB_CNT_BFEE_FBK_SEG = 51,
+ UNI_CMD_MIB_CNT_NAV_TIME = 52,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT1 = 53,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT0 = 54,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT3 = 55,
+ UNI_CMD_MIB_CNT_TX_ABORT_CNT2 = 56,
+ UNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT = 57,
+ UNI_CMD_MIB_CNT_BSS1_RTS_TX_CNT = 58,
+ UNI_CMD_MIB_CNT_BSS2_RTS_TX_CNT = 59,
+ UNI_CMD_MIB_CNT_BSS3_RTS_TX_CNT = 60,
+ UNI_CMD_MIB_CNT_BSS0_RTS_RETRY = 61,
+ UNI_CMD_MIB_CNT_BSS1_RTS_RETRY = 62,
+ UNI_CMD_MIB_CNT_BSS2_RTS_RETRY = 63,
+ UNI_CMD_MIB_CNT_BSS3_RTS_RETRY = 64,
+ UNI_CMD_MIB_CNT_BSS0_BA_MISS = 65,
+ UNI_CMD_MIB_CNT_BSS1_BA_MISS = 66,
+ UNI_CMD_MIB_CNT_BSS2_BA_MISS = 67,
+ UNI_CMD_MIB_CNT_BSS3_BA_MISS = 68,
+ UNI_CMD_MIB_CNT_BSS0_ACK_FAIL = 69,
+ UNI_CMD_MIB_CNT_BSS1_ACK_FAIL = 70,
+ UNI_CMD_MIB_CNT_BSS2_ACK_FAIL = 71,
+ UNI_CMD_MIB_CNT_BSS3_ACK_FAIL = 72,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY = 73,
+ UNI_CMD_MIB_CNT_BSS1_FRAME_RETRY = 74,
+ UNI_CMD_MIB_CNT_BSS2_FRAME_RETRY = 75,
+ UNI_CMD_MIB_CNT_BSS3_FRAME_RETRY = 76,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2 = 77,
+ UNI_CMD_MIB_CNT_BSS1_FRAME_RETRY_2 = 78,
+ UNI_CMD_MIB_CNT_BSS2_FRAME_RETRY_2 = 79,
+ UNI_CMD_MIB_CNT_BSS3_FRAME_RETRY_2 = 80,
+ UNI_CMD_MIB_CNT_RX_A1_SEARCH = 81,
+ UNI_CMD_MIB_CNT_RX_DROP_MPDU = 82,
+ UNI_CMD_MIB_CNT_RX_UNWANTED = 83,
+ UNI_CMD_MIB_CNT_RX_FCS_OK = 84,
+ UNI_CMD_MIB_CNT_SU_TX_OK = 85,
+ UNI_CMD_MIB_CNT_TX_FULL_BW = 86,
+ UNI_CMD_MIB_CNT_TX_AUTO_BW = 87,
+ UNI_CMD_MIB_CNT_CCA_SAMPLE_IDLE = 88,
+ UNI_CMD_MIB_CNT_CCA_SAMPLE_ACTIVE = 89,
+ UNI_CMD_MIB_CNT_CCA_TIME = 90,
+ UNI_CMD_MIB_CNT_S_20BW_CCA_TIME = 91,
+ UNI_CMD_MIB_CNT_S_40BW_CCA_TIME = 92,
+ UNI_CMD_MIB_CNT_S_80BW_CCA_TIME = 93,
+ UNI_CMD_MIB_CNT_S_160BW_CCA_TIME = 94,
+ UNI_CMD_MIB_CNT_S_P20BW_0_ED_TIME = 95,
+ UNI_CMD_MIB_CNT_S_P20BW_1_ED_TIME = 96,
+ UNI_CMD_MIB_CNT_S_P20BW_2_ED_TIME = 97,
+ UNI_CMD_MIB_CNT_S_P20BW_3_ED_TIME = 98,
+ UNI_CMD_MIB_CNT_S_P20BW_4_ED_TIME = 99,
+ UNI_CMD_MIB_CNT_S_P20BW_5_ED_TIME = 100,
+ UNI_CMD_MIB_CNT_S_P20BW_6_ED_TIME = 101,
+ UNI_CMD_MIB_CNT_S_P20BW_7_ED_TIME = 102,
+ UNI_CMD_MIB_CNT_S_P20BW_8_ED_TIME = 103,
+ UNI_CMD_MIB_CNT_S_P20BW_9_ED_TIME = 104,
+ UNI_CMD_MIB_CNT_S_P20BW_10_ED_TIME = 105,
+ UNI_CMD_MIB_CNT_S_P20BW_11_ED_TIME = 106,
+ UNI_CMD_MIB_CNT_S_P20BW_12_ED_TIME = 107,
+ UNI_CMD_MIB_CNT_S_P20BW_13_ED_TIME = 108,
+ UNI_CMD_MIB_CNT_S_P20BW_14_ED_TIME = 109,
+ UNI_CMD_MIB_CNT_S_P20BW_15_ED_TIME = 110,
+ UNI_CMD_MIB_CNT_TX_BW_320MHZ = 111,
+ UNI_CMD_MIB_CNT_TX_DDLMT_RNG0 = 112, /* Rng0~Rng4 112~116 */
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_3 = 117, /* 117~120 */
+ UNI_CMD_MIB_CNT_BSS0_TX = 121, /* 121~124 */
+ UNI_CMD_MIB_CNT_BSS0_TX_DATA = 125, /* 125~128 */
+ UNI_CMD_MIB_CNT_BSS0_TX_BYTE = 129, /* 129~132 */
+ UNI_CMD_MIB_CNT_RX_OK_BSS0 = 133, /* 133~136 */
+ UNI_CMD_MIB_CNT_RX_BYTE_BSS0 = 137, /* 137~140 */
+ UNI_CMD_MIB_CNT_RX_DATA_BSS0 = 141, /* 141~144 */
+ UNI_CMD_MIB_CNT_MBSS0_TX_OK = 145, /* 145~160 */
+ UNI_CMD_MIB_CNT_MBSS0_TX_BYTE = 161, /* 161~176 */
+ UNI_CMD_MIB_CNT_RX_OK_MBSS0 = 177, /* 177~192 */
+ UNI_CMD_MIB_CNT_RX_BYTE_MBSS0 = 193, /* 193~208 */
+ UNI_CMD_MIB_CNT_AMPDU = 209,
+ UNI_CMD_MIB_CNT_AMPDU_MPDU = 210,
+ UNI_CMD_MIB_CNT_AMPDU_ACKED = 211,
+ UNI_CMD_MIB_CNT_MAX_NUM
+};
+
+enum ENUM_UNI_CMD_GET_STATISTICS_TAG {
+ UNI_CMD_GET_STATISTICS_TAG_BASIC = 0,
+ UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1,
+ UNI_CMD_GET_STATISTICS_TAG_STA = 2,
+ UNI_CMD_GET_STATISTICS_TAG_BUG_REPORT = 3,
+ UNI_CMD_GET_STATISTICS_TAG_EML_STATS = 6,
+ UNI_CMD_GET_STATISTICS_TAG_REGULAR_STATS = 7,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_LINK_QUALITY = 8,
+ UNI_CMD_GET_STATISTICS_TAG_GET_ML_CHNL_COND = 9,
+ UNI_CMD_GET_STATISTICS_TAG_STAT_WTBL = 10,
+ UNI_CMD_GET_STATISTICS_TAG_LINK_LAYER_STATS = 0x80,
+ UNI_CMD_GET_STATISTICS_TAG_PPDU_LATENCY,
+ UNI_CMD_GET_STATISTICS_TAG_CURRENT_TX_RATE,
+ UNI_CMD_GET_STATISTICS_TAG_BEACON_REPORT = 0x86,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_CURRENT_TX_RATE = 0x87,
+ UNI_CMD_GET_STATISTICS_TAG_BSS_PRED_TX_BITRATE = 0x88,
+};
+
#define UNI_WOW_DETECT_TYPE_MAGIC BIT(0)
#define UNI_WOW_DETECT_TYPE_ANY BIT(1)
#define UNI_WOW_DETECT_TYPE_DISCONNECT BIT(2)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index f9dcc0bba393..3863a89b8974 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
obj-$(CONFIG_MT7925E) += mt7925e.o
obj-$(CONFIG_MT7925U) += mt7925u.o
-mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o
+mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o stats.o
mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7925e-y := pci.o pci_mac.o pci_mcu.o
mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index d01ff49de47a..7a40d5ac47da 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -1,8 +1,11 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
+#include <linux/math64.h>
+
#include "mt7925.h"
#include "mcu.h"
+#include "stats.h"
static int
mt7925_reg_set(void *data, u64 val)
@@ -286,6 +289,346 @@ static int mt7925_chip_reset(void *data, u64 val)
DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
+static const u32 stats_query_mib_counters[] = {
+ UNI_CMD_MIB_CNT_RX_FCS_ERR,
+ UNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW,
+ UNI_CMD_MIB_CNT_RX_MPDU,
+ UNI_CMD_MIB_CNT_AMPDU_RX_COUNT,
+ UNI_CMD_MIB_CNT_PF_DROP,
+ UNI_CMD_MIB_CNT_LEN_MISMATCH,
+ UNI_CMD_MIB_CNT_CHANNEL_IDLE,
+ UNI_CMD_MIB_CNT_CCA_NAV_TX_TIME,
+ UNI_CMD_MIB_CNT_MDRDY,
+ UNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,
+ UNI_CMD_MIB_CNT_P_CCA_TIME,
+ UNI_CMD_MIB_CNT_S_CCA_TIME,
+ UNI_CMD_MIB_CNT_P_ED_TIME,
+ UNI_CMD_MIB_CNT_BCN_TX,
+ UNI_CMD_MIB_CNT_TX_BW_40MHZ,
+ UNI_CMD_MIB_CNT_TX_BW_80MHZ,
+ UNI_CMD_MIB_CNT_TX_BW_160MHZ,
+ UNI_CMD_MIB_CNT_BSS0_BA_MISS,
+ UNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY,
+ UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2,
+ UNI_CMD_MIB_CNT_BSS0_RTS_RETRY,
+ UNI_CMD_MIB_CNT_BSS0_ACK_FAIL,
+ UNI_CMD_MIB_CNT_AMPDU,
+ UNI_CMD_MIB_CNT_AMPDU_MPDU,
+ UNI_CMD_MIB_CNT_AMPDU_ACKED,
+};
+
+/* Dump the whole firmware auto-rate table and mark the entry currently in use
+ * with "-->"
+ */
+static void
+mt7925_dump_auto_rate_table(struct seq_file *s,
+ const struct mt7925_wtbl_rate *wr)
+{
+ u8 fcap = wr->fcap;
+ u8 rate_idx = wr->rate_idx;
+ u8 cbrn = wr->cbrn;
+ int i;
+
+ seq_printf(s, "%-22s Cur Rate Index = %u\n", " ", rate_idx);
+
+ for (i = 0; i < MT7925_AUTO_RATE_NUM; i++) {
+ u16 rate_code = le16_to_cpu(wr->rate_code[i]);
+ u8 txmode = MT7925_HW_RATE_TO_MODE(rate_code);
+ u8 rate = MT7925_HW_RATE_TO_MCS(rate_code);
+ u8 nsts = MT7925_HW_RATE_TO_NSS(rate_code) + 1;
+ u8 stbc = MT7925_HW_RATE_TO_STBC(rate_code);
+ u8 dcm = MT7925_HW_RATE_TO_DCM(rate_code);
+ u8 ersu106t = MT7925_HW_RATE_TO_106T(rate_code);
+ u8 sgi = mt7925_wtbl_get_sgi(wr, txmode, fcap);
+ u8 ldpc = mt7925_wtbl_get_ldpc(wr, txmode);
+ const char *mode_str = "N/A";
+
+ if (dcm)
+ rate = MT7925_HW_RATE_UNMASK_DCM(rate);
+ if (ersu106t)
+ rate = MT7925_HW_RATE_UNMASK_106T(rate);
+
+ if (txmode < ARRAY_SIZE(mt7925_tx_mode_str))
+ mode_str = mt7925_tx_mode_str[txmode];
+
+ seq_printf(s, "Rate index[%d] %s", i,
+ rate_idx == i ? "--> " : " ");
+
+ /* rate/MCS field */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ",
+ mt7925_tx_rate_cck_str[rate & 0x3]);
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_hw_rate_ofdm_str(rate));
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF)
+ seq_printf(s, "MCS%d, ", rate);
+ else
+ seq_printf(s, "%s%d_MCS%d, ",
+ stbc ? "NSTS" : "NSS", nsts, rate);
+
+ /* bandwidth: mirror the CCK/OFDM -> BW20 and cbrn handling */
+ if (txmode == MT7925_TX_RATE_MODE_CCK ||
+ txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_tx_rate_bw_str[0]);
+ else if (i > cbrn)
+ seq_printf(s, "%s, ",
+ fcap < 5 ?
+ (fcap > MT7925_BW_20 ?
+ mt7925_tx_rate_bw_str[fcap - 1] :
+ mt7925_tx_rate_bw_str[fcap]) :
+ mt7925_tx_rate_bw_str[5]);
+ else
+ seq_printf(s, "%s, ",
+ fcap < 5 ? mt7925_tx_rate_bw_str[fcap] :
+ mt7925_tx_rate_bw_str[5]);
+
+ /* GI / preamble */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ", rate < 4 ? "LP" : "SP");
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ ; /* OFDM has no GI/preamble to print */
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF ||
+ txmode == MT7925_TX_RATE_MODE_VHT ||
+ txmode == MT7925_TX_RATE_MODE_PLR)
+ seq_printf(s, "%s, ", sgi == 0 ? "LGI" : "SGI");
+ else
+ seq_printf(s, "%s, ",
+ sgi == 0 ? "SGI" :
+ (sgi == 1 ? "MGI" : "LGI"));
+
+ /* mode, DCM, 106t, STBC, coding */
+ seq_printf(s, "%s%s%s%s%s\n",
+ mode_str,
+ dcm ? ", DCM" : "", ersu106t ? ", 106t" : "",
+ stbc ? ", STBC, " : ", ",
+ (ldpc == 0 ||
+ txmode == MT7925_TX_RATE_MODE_CCK ||
+ txmode == MT7925_TX_RATE_MODE_OFDM) ?
+ "BCC" : "LDPC");
+ }
+}
+
+static int mt792x_stats(struct seq_file *s, void *data)
+{
+ s8 rssi = 0;
+ u8 band_idx = 0;
+ bool wtbl_rate_valid = false;
+ int ret = 0;
+ int i = 0;
+ u32 stats_value = 0;
+ u32 tx_link_speed_kbps = 0;
+ u64 tx_total = 0;
+ u64 tx_fail = 0;
+ u64 tx_per = 0;
+ u32 tx_per_rem = 0;
+ /* UNI_CMD_MIB_DATA */
+ u64 mib_values[ARRAY_SIZE(stats_query_mib_counters)];
+ struct mt792x_dev *dev = dev_get_drvdata(s->private);
+ struct mt7925_wtbl_rate wtbl_rate;
+ struct mt7925_sta_stats *stats = NULL;
+
+ band_idx = dev->mphy.band_idx;
+
+ stats = kzalloc(sizeof(*stats), GFP_KERNEL);
+ if (!stats)
+ return -ENOMEM;
+
+ /* Get STA_STATS */
+ mt792x_mutex_acquire(dev);
+ ret = mt792x_mcu_get_stat(dev, stats);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get MIB_STATS */
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_mcu_get_mib_info(dev, band_idx, stats_query_mib_counters,
+ ARRAY_SIZE(stats_query_mib_counters), mib_values);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get Link_Speed and RSSI */
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_mcu_get_link_quality(dev, stats->bss_idx, &rssi, &tx_link_speed_kbps);
+ mt792x_mutex_release(dev);
+ if (ret)
+ goto out;
+
+ /* Get the firmware auto-rate table (best effort; do not abort the
+ * whole stats dump if the WTBL query is not available).
+ */
+ mt792x_mutex_acquire(dev);
+ if (!mt792x_mcu_get_wtbl_rate(dev, stats->wtbl_idx, &wtbl_rate))
+ wtbl_rate_valid = true;
+ mt792x_mutex_release(dev);
+
+ /* Print STA_STATS info */
+ seq_printf(s, "(STA) connected AP MAC Address = %pM\n", stats->mac_addr);
+ seq_printf(s, "%-22s BssIdx = [%u]\n", " ", stats->bss_idx);
+ seq_printf(s, "%-22s StaRecIdx = [%u]\n",
+ " ", stats->sta_idx);
+ seq_printf(s, "%-22s RSSI = %d\n", " ", rssi);
+ seq_printf(s, "%-22s Link_Speed = %u (kbit/s)\n", " ", tx_link_speed_kbps);
+
+ seq_printf(s, "%-22s temperature = %u\n",
+ " ", stats->temperature);
+
+ stats_value = le32_to_cpu(stats->transmit_count) - le32_to_cpu(stats->transmit_fail_count);
+ seq_printf(s, "%-22s Tx success = %u\n",
+ " ", stats_value);
+
+ stats_value = le32_to_cpu(stats->tx_fail_count) - le32_to_cpu(stats->tx_life_timeout_count);
+ seq_printf(s, "%-22s Tx fail to Rcv ACK after retry = %u\n",
+ " ", stats_value);
+
+ seq_printf(s, "%-22s Rx Mpdu = %u\n",
+ " ", le32_to_cpu(stats->mib[band_idx].rx_mpdu_cnt));
+
+ seq_printf(s, "%-22s Rx Fcs Error = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].fcs_error));
+ seq_printf(s, "%-22s Rx FIFO full = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].rx_fifo_full));
+
+ for (i = 0; i < STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM; i++)
+ seq_printf(s, "%-22s TRX_AGG_RANGE[%d] = %u (PPDU)\n",
+ " ", i, le32_to_cpu(stats->mib[band_idx].tx_range_ampdu_cnt[i]));
+
+ seq_printf(s, "%-22s MPDUs in AMPDUs transmitted = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].ampdu_tx_sf_cnt));
+ seq_printf(s, "%-22s MPDUs in AMPDUs transmitted with ACK reply = %u (MPDU)\n",
+ " ", le32_to_cpu(stats->mib[band_idx].ampdu_tx_ack_sf_cnt));
+ seq_printf(s, "%-22s rate1_tx_cnt = %u\n",
+ " ", le32_to_cpu(stats->rate1_tx_cnt));
+ seq_printf(s, "%-22s rate1_fail_cnt = %u\n",
+ " ", le32_to_cpu(stats->rate1_fail_cnt));
+ seq_printf(s, "%-22s train_up = %u\n",
+ " ", le16_to_cpu(stats->train_up));
+ seq_printf(s, "%-22s train_down = %u\n",
+ " ", le16_to_cpu(stats->train_down));
+ seq_printf(s, "%-22s is_force_tx_stream = %u\n",
+ " ", stats->is_force_tx_stream);
+ seq_printf(s, "%-22s is_force_se_off = %u\n",
+ " ", stats->is_force_se_off);
+ seq_printf(s, "%-22s max_ampdu_factor = %u\n",
+ " ", stats->max_ampdu_factor);
+ seq_printf(s, "%-22s tx_rate_up_penalty = %u\n",
+ " ", stats->tx_rate_up_penalty);
+ seq_printf(s, "%-22s low_traffic_mode = %u\n",
+ " ", stats->low_traffic_mode);
+ seq_printf(s, "%-22s low_traffic_count = %u\n",
+ " ", stats->low_traffic_count);
+ seq_printf(s, "%-22s low_traffic_dashboard = %u\n",
+ " ", stats->low_traffic_dashboard);
+ seq_printf(s, "%-22s dynamic_sgi_state = %u\n",
+ " ", stats->dynamic_sgi_state);
+ seq_printf(s, "%-22s dynamic_sgi_score = %u\n",
+ " ", stats->dynamic_sgi_score);
+ seq_printf(s, "%-22s dynamic_bw_state = %u\n",
+ " ", stats->dynamic_bw_state);
+ seq_printf(s, "%-22s dynamic_gband_256qam_state = %u\n",
+ " ", stats->dynamic_gband_256qam_state);
+ seq_printf(s, "%-22s vht_non_sp_rate_state = %u\n",
+ " ", stats->vht_non_sp_rate_state);
+
+ /* Decode the TX Vector BBP latch to show the current TX MCS rate */
+ if (band_idx < MT7925_STA_STATS_BAND_NUM)
+ mt7925_print_last_tx_rate(s, stats->tx_vector[band_idx].txv);
+
+ /* Dump the whole firmware auto-rate table and mark the entry currently */
+ if (wtbl_rate_valid)
+ mt7925_dump_auto_rate_table(s, &wtbl_rate);
+
+ seq_puts(s, "\nmib state:\n");
+ /* ===Rx Related Counters=== */
+ seq_puts(s, "=== Rx Related Counters ===\n");
+ seq_printf(s, "%-22s Rx with CRC = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_FCS_ERR]);
+ seq_printf(s, "%-22s Rx drop due to out of resource = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_FIFO_OVERFLOW]);
+ seq_printf(s, "%-22s Rx Mpdu = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_MPDU]);
+ seq_printf(s, "%-22s Rx AMpdu = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_RX_COUNT]);
+ seq_printf(s, "%-22s Rx PF Drop = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_PF_DROP]);
+ seq_printf(s, "%-22s Rx Len Mismatch = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_LEN_MISMATCH]);
+
+ /* ===Phy/Timing Related Counters=== */
+ seq_puts(s, "\n=== Phy/Timing Related Counters ===\n");
+ seq_printf(s, "%-22s ChannelIdleCnt = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_CHANNEL_IDLE]);
+ seq_printf(s, "%-22s CCA_NAV_Tx_Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_CCA_NAV_TX_TIME]);
+ seq_printf(s, "%-22s Rx_MDRDY_CNT = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_MDRDY]);
+ seq_printf(s, "%-22s CCK_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_CCK_MDRDY_TIME]);
+ seq_printf(s, "%-22s OFDM_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME]);
+ seq_printf(s, "%-22s OFDM_GREEN_MDRDY = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME]);
+ seq_printf(s, "%-22s Prim CCA Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_P_CCA_TIME]);
+ seq_printf(s, "%-22s Sec CCA Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_S_CCA_TIME]);
+ seq_printf(s, "%-22s Prim ED Time = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_P_ED_TIME]);
+
+ /* ===Tx Related Counters(Generic)=== */
+ seq_puts(s, "\n=== Tx Related Counters(Generic) ===\n");
+ seq_printf(s, "%-22s BeaconTxCnt = %llu\n",
+ " ", mib_values[QUERY_MIB_CNT_BCN_TX]);
+ seq_printf(s, "%-22s Tx 40MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_40MHZ]);
+ seq_printf(s, "%-22s Tx 80MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_80MHZ]);
+ seq_printf(s, "%-22s Tx 160MHz Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_TX_BW_160MHZ]);
+
+ /* ===BSSID[0] Related Counters=== */
+ seq_puts(s, "\n=== BSSID[0] Related Counters ===\n");
+ seq_printf(s, "%-22s BA Miss Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_BA_MISS]);
+ seq_printf(s, "%-22s RTS Tx Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_RTS_TX_CNT]);
+ seq_printf(s, "%-22s Frame Retry Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY]);
+ seq_printf(s, "%-22s Frame Retry 2 Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY_2]);
+ seq_printf(s, "%-22s RTS Retry Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_RTS_RETRY]);
+ seq_printf(s, "%-22s Ack Failed Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_BSS0_ACK_FAIL]);
+
+ /* ===AMPDU Related Counters=== */
+ seq_puts(s, "\n=== AMPDU Related Counters ===\n");
+ seq_printf(s, "%-22s Tx AMPDU_Pkt_Cnt = %llu (PPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU]);
+ seq_printf(s, "%-22s Tx AMPDU_MPDU_Pkt_Cnt = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_MPDU]);
+ seq_printf(s, "%-22s AMPDU Tx success = %llu (MPDU)\n",
+ " ", mib_values[QUERY_MIB_CNT_AMPDU_ACKED]);
+
+ tx_total = mib_values[QUERY_MIB_CNT_AMPDU_MPDU];
+ tx_fail = mib_values[QUERY_MIB_CNT_AMPDU_MPDU] - mib_values[QUERY_MIB_CNT_AMPDU_ACKED];
+ tx_per = tx_total == 0 ? 0 : div64_u64(1000 * tx_fail, tx_total);
+ seq_printf(s, "%-22s AMPDU Tx fail count = %llu (MPDU), PER=%llu.%1llu%%\n",
+ " ",
+ tx_fail,
+ div_u64_rem(tx_per, 10, &tx_per_rem), (u64)tx_per_rem);
+
+out:
+ kfree(stats);
+ return ret;
+}
+
int mt7925_init_debugfs(struct mt792x_dev *dev)
{
struct dentry *dir;
@@ -313,5 +656,8 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
mt792x_pm_stats);
debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
+ debugfs_create_devm_seqfile(dev->mt76.dev, "stats", dir,
+ mt792x_stats);
+
return 0;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.c b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c
new file mode 100644
index 000000000000..9a47953062bc
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include "mt7925.h"
+#include "mcu.h"
+#include "mac.h"
+#include "stats.h"
+
+#define MT7925_STA_STATS_VERSION 1
+#define MT7925_STA_STATS_VALID 1U
+
+#define MT7925_MIB_MAX_CNT 32
+#define MT7925_MIB_CNT_MAX_NUM 512 /* firmware UNI_CMD_MIB_CNT_MAX_NUM upper bound */
+
+#define MT7925_LQ_BAND_NUM 4 /* Link quality band */
+
+const char * const mt7925_tx_mode_str[] = {
+ "CCK", "OFDM", "MM", "GF", "VHT", "PLR",
+ "PLRP", "ALR", "HE_SU", "HE_ER", "HE_TRIG", "HE_MU",
+ "EHT_ER", "TOF", "EHT_TRIG", "EHT_MU"
+};
+
+const char * const mt7925_tx_rate_cck_str[] = {
+ "1M", "2M", "5.5M", "11M", "N/A"
+};
+
+const char * const mt7925_tx_rate_bw_str[] = {
+ "BW20", "BW40", "BW80", "BW160/BW8080", "BW320", "N/A"
+};
+
+struct mt7925_sta_stats_req {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ u8 sta_rec_idx;
+ u8 rsv1;
+ u8 lls_read_clear;
+ u8 reset_counter;
+} __packed;
+
+struct mt7925_sta_stats_event {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ struct mt7925_sta_stats stats;
+} __packed;
+
+/* WTBL auto-rate request TLV, mirror Gen4m struct UNI_CMD_STAT_WTBL.
+ * The RA tag reuses UNI_CMD_ID_STATISTICS_WTBL (10).
+ */
+struct mt7925_wtbl_rate_req {
+ u8 rsv[4];
+ __le16 tag;
+ __le16 len;
+ __le16 wlan_idx;
+ __le16 rsv1;
+} __packed;
+
+/* WTBL auto-rate event TLV. Firmware answers cmd tag 10 with event tag 12. */
+struct mt7925_wtbl_rate_event {
+ u8 rsv[4];
+ struct mt7925_wtbl_rate rate;
+} __packed;
+
+/* fixed field, mirror UNI_(CMD|EVENT)_MIB_INFO */
+struct mt7925_mib_hdr {
+ u8 band_idx;
+ u8 rsv[3];
+} __packed;
+
+/* One requested/returned MIB counter, mirror UNI_(CMD|EVENT)_MIB_DATA_T. */
+struct mt7925_mib_data {
+ __le16 tag; /* UNI_(CMD|EVENT)_MIB_DATA = 0 */
+ __le16 len;
+ __le32 counter; /* MIB counter ID (enum mt7925_mib_counter) */
+ __le64 data; /* only valid on event; ignored on cmd */
+} __packed;
+
+/* per-band link quality entry, mirror UNI_LINK_QUALITY */
+struct mt7925_link_quality {
+ s8 rssi; /* moving-average beacon RSSI of connected AP */
+ s8 link_quality; /* always 0 */
+ __le16 link_speed; /* TX rate1, unit 500 Kb/s */
+ u8 medium_busy_pct; /* always 0 */
+ u8 is_ready; /* 0: fields invalid, 1: valid */
+ u8 rsv[2];
+} __packed;
+
+/* request TLV, mirror UNI_CMD_LINK_QUALITY (header + tag/len only) */
+struct mt7925_lq_req {
+ u8 rsv[4]; /* UNI_CMD_GET_STATISTICS fixed field */
+ __le16 tag; /* UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1 */
+ __le16 len;
+} __packed;
+
+/* event TLV, mirror UNI_EVENT_LINK_QUALITY */
+struct mt7925_lq_event {
+ u8 rsv[4]; /* UNI_EVENT_STATISTICS fixed field */
+ __le16 tag; /* UNI_EVENT_ID_STATISTICS_LINK_QUALITY = 1 */
+ __le16 len;
+ struct mt7925_link_quality lq[MT7925_LQ_BAND_NUM];
+} __packed;
+
+static void
+mt7925_get_ap_sta_rec_idx_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mt7925_sta_rec_lookup *lookup = data;
+ struct mt792x_vif *mvif;
+ struct mt792x_sta *msta;
+
+ if (lookup->sta_rec_idx >= 0)
+ return;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return;
+
+ mvif = (struct mt792x_vif *)vif->drv_priv;
+ msta = mvif->wep_sta;
+ if (!msta)
+ return;
+
+ if (!msta->deflink.wcid.sta)
+ return;
+
+ lookup->sta_rec_idx = msta->deflink.wcid.idx;
+}
+
+int
+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx)
+{
+ struct mt7925_sta_rec_lookup lookup = {
+ .sta_rec_idx = -1,
+ };
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+ ieee80211_iterate_active_interfaces(dev->mphy.hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_get_ap_sta_rec_idx_iter,
+ &lookup);
+
+ if (lookup.sta_rec_idx < 0)
+ return -ENOTCONN;
+
+ if (lookup.sta_rec_idx > U8_MAX)
+ return -ERANGE;
+
+ *sta_rec_idx = lookup.sta_rec_idx;
+
+ return 0;
+}
+
+/* Decode the TX Vector BBP latch and print the last TX rate */
+void
+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3])
+{
+ const char *mode_str = "N/A";
+ u8 rate, txmode, frmode, sgi, ldpc, nsts, stbc, spe, dcm, ersu106t;
+ s8 txpwr, pos_txpwr;
+ u32 txv[3];
+
+ txv[0] = le32_to_cpu(le_txv[0]);
+ txv[1] = le32_to_cpu(le_txv[1]);
+ txv[2] = le32_to_cpu(le_txv[2]);
+
+ if (txv[0] == 0xffffffff) {
+ seq_printf(s, "%-22s %s = %s\n", " ", "Last TX Rate", "N/A");
+ seq_printf(s, "%-22s %s = %s\n",
+ " ", "Chip Out TX Power", "N/A");
+ return;
+ }
+
+ rate = MT7925_TXV_GET_TX_RATE(txv);
+ txmode = MT7925_TXV_GET_TX_MODE(txv);
+ frmode = MT7925_TXV_GET_TX_FRMODE(txv);
+ nsts = MT7925_TXV_GET_TX_NSTS(txv) + 1;
+ sgi = MT7925_TXV_GET_TX_SGI(txv);
+ ldpc = MT7925_TXV_GET_TX_LDPC(txv);
+ stbc = MT7925_TXV_GET_TX_STBC(txv);
+ txpwr = MT7925_TXV_GET_TX_PWR(txv);
+ spe = MT7925_TXV_GET_TX_SPE_IDX(txv);
+ dcm = MT7925_TXV_GET_TX_DCM(txv);
+ ersu106t = MT7925_TXV_GET_TX_106T(txv);
+
+ if (dcm)
+ rate = MT7925_TXV_RATE_UNMASK_DCM(rate);
+ if (ersu106t)
+ rate = MT7925_TXV_RATE_UNMASK_106T(rate);
+
+ if (txmode < ARRAY_SIZE(mt7925_tx_mode_str))
+ mode_str = mt7925_tx_mode_str[txmode];
+
+ seq_printf(s, "%-22s %s = ", " ", "Last TX Rate");
+
+ /* rate/MCS field */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ",
+ rate < 4 ? mt7925_tx_rate_cck_str[rate] :
+ (((rate >= 5) && (rate <= 7)) ?
+ mt7925_tx_rate_cck_str[rate - 4] :
+ mt7925_tx_rate_cck_str[4]));
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ seq_printf(s, "%s, ", mt7925_hw_rate_ofdm_str(rate));
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF)
+ seq_printf(s, "MCS%d, ", rate);
+ else
+ seq_printf(s, "%s%d_MCS%d, ",
+ stbc ? "NSTS" : "NSS", nsts, rate);
+
+ /* bandwidth */
+ seq_printf(s, "%s, ",
+ frmode < 5 ? mt7925_tx_rate_bw_str[frmode] :
+ mt7925_tx_rate_bw_str[5]);
+
+ /* GI / preamble */
+ if (txmode == MT7925_TX_RATE_MODE_CCK)
+ seq_printf(s, "%s, ", rate < 4 ? "LP" : "SP");
+ else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+ ; /* OFDM has no GI/preamble to print */
+ else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+ txmode == MT7925_TX_RATE_MODE_HTGF ||
+ txmode == MT7925_TX_RATE_MODE_VHT ||
+ txmode == MT7925_TX_RATE_MODE_PLR)
+ seq_printf(s, "%s, ", sgi == 0 ? "LGI" : "SGI");
+ else
+ seq_printf(s, "%s, ",
+ sgi == 0 ? "SGI" : (sgi == 1 ? "MGI" : "LGI"));
+
+ /* mode, DCM, 106t, STBC, coding, SPE */
+ seq_printf(s, "%s%s%s%s%s%s%d\n",
+ mode_str,
+ dcm ? ", DCM" : "", ersu106t ? ", 106t" : "",
+ stbc ? ", STBC, " : ", ", ldpc == 0 ? "BCC" : "LDPC",
+ ", SPE", spe);
+
+ /* chip out TX power (unit: 0.5 dBm) */
+ pos_txpwr = (txpwr < 0) ? (~txpwr + 1) : txpwr;
+ seq_printf(s, "%-22s %s = %c%d.%1d dBm\n",
+ " ", "Chip Out TX Power",
+ (txpwr < 0) ? '-' : '+',
+ (pos_txpwr / 2), 5 * (pos_txpwr % 2));
+}
+
+/* Guard interval used for the auto-rate entries, mirror Gen4m
+ * connac3x_get_sgi_info_from_fw(): pick the per-mode/per-BW flag.
+ */
+u8
+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap)
+{
+ bool he = (txmode == MT7925_TX_RATE_MODE_HE_SU ||
+ txmode == MT7925_TX_RATE_MODE_HE_ER ||
+ txmode == MT7925_TX_RATE_MODE_HE_TRIG ||
+ txmode == MT7925_TX_RATE_MODE_HE_MU);
+ bool eht = (txmode == MT7925_TX_RATE_MODE_EHT_ER ||
+ txmode == MT7925_TX_RATE_MODE_EHT_TRIG ||
+ txmode == MT7925_TX_RATE_MODE_EHT_MU);
+
+ if (eht)
+ return wr->gi_eht;
+
+ switch (fcap) {
+ case MT7925_BW_20:
+ return he ? wr->g2_he : wr->g2;
+ case MT7925_BW_40:
+ return he ? wr->g4_he : wr->g4;
+ case MT7925_BW_80:
+ return he ? wr->g8_he : wr->g8;
+ case MT7925_BW_160:
+ return he ? wr->g16_he : wr->g16;
+ default:
+ return 0;
+ }
+}
+
+/* Coding used for the auto-rate entries, mirror Gen4m
+ * connac3x_wtbl_get_ldpc_info_from_fw(): 0 = BCC, 1 = LDPC.
+ */
+u8
+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode)
+{
+ switch (txmode) {
+ case MT7925_TX_RATE_MODE_HTMIX:
+ case MT7925_TX_RATE_MODE_HTGF:
+ return wr->ht_ldpc;
+ case MT7925_TX_RATE_MODE_VHT:
+ return wr->vht_ldpc;
+ case MT7925_TX_RATE_MODE_HE_SU:
+ case MT7925_TX_RATE_MODE_HE_ER:
+ case MT7925_TX_RATE_MODE_HE_TRIG:
+ case MT7925_TX_RATE_MODE_HE_MU:
+ return wr->he_ldpc;
+ case MT7925_TX_RATE_MODE_EHT_ER:
+ case MT7925_TX_RATE_MODE_EHT_TRIG:
+ case MT7925_TX_RATE_MODE_EHT_MU:
+ return wr->eht_ldpc;
+ default:
+ return 0;
+ }
+}
+
+int mt792x_mcu_get_stat(struct mt792x_dev *dev,
+ struct mt7925_sta_stats *stats)
+{
+ u16 len = 0;
+ int ret = 0;
+ struct mt7925_sta_stats_event *event = NULL;
+ struct sk_buff *skb = NULL;
+ struct mt7925_sta_stats_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_STA),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ .lls_read_clear = false,
+ .reset_counter = false,
+ };
+
+ ret = mt7925_get_ap_sta_rec_idx(dev, &req.sta_rec_idx);
+ if (ret)
+ return ret;
+
+ if (!stats)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO),
+ &req, sizeof(req), true, &skb);
+
+ if (ret)
+ return ret;
+
+ event = (struct mt7925_sta_stats_event *)skb->data;
+
+ if (le16_to_cpu(event->tag) != UNI_CMD_ID_STATISTICS_STA) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ len = le16_to_cpu(event->len);
+ if (len < sizeof(*event) - sizeof(event->rsv) ||
+ len > skb->len - sizeof(event->rsv) || !IS_ALIGNED(len, 4)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ if (event->stats.version != MT7925_STA_STATS_VERSION) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ if (!(le32_to_cpu(event->stats.flags) & MT7925_STA_STATS_VALID)) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ memcpy(stats, &event->stats, sizeof(*stats));
+
+out:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,
+ const u32 *counters, u8 counter_num,
+ u64 *values)
+{
+ u8 *req;
+ u8 *tlv, *tlv_end;
+ u16 req_len;
+ int i, ret;
+ struct mt7925_mib_hdr *req_hdr;
+ struct mt7925_mib_data *req_tlv;
+ struct sk_buff *skb;
+
+ if (!counters || !values || !counter_num ||
+ counter_num > MT7925_MIB_MAX_CNT)
+ return -EINVAL;
+
+ /* clear output; counters not returned by firmware stay 0 */
+ memset(values, 0, counter_num * sizeof(*values));
+
+ req_len = sizeof(struct mt7925_mib_hdr) +
+ counter_num * sizeof(struct mt7925_mib_data);
+
+ req = kzalloc(req_len, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req_hdr = (struct mt7925_mib_hdr *)req;
+ req_hdr->band_idx = band_idx;
+
+ req_tlv = (struct mt7925_mib_data *)(req + sizeof(*req_hdr));
+ for (i = 0; i < counter_num; i++) {
+ req_tlv[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA);
+ req_tlv[i].len = cpu_to_le16(sizeof(struct mt7925_mib_data));
+ req_tlv[i].counter = cpu_to_le32(counters[i]);
+ }
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_MIB_INFO), req,
+ req_len, true, &skb);
+ kfree(req);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(struct mt7925_mib_hdr)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ /* walk the returned TLVs, match each counter ID to the request */
+ tlv = skb->data + sizeof(struct mt7925_mib_hdr);
+ tlv_end = skb->data + skb->len;
+
+ while (tlv + sizeof(struct mt7925_mib_data) <= tlv_end) {
+ struct mt7925_mib_data *evt_tlv =
+ (struct mt7925_mib_data *)tlv;
+ u16 tag = le16_to_cpu(evt_tlv->tag);
+ u16 len = le16_to_cpu(evt_tlv->len);
+ u32 counter;
+
+ if (len < sizeof(struct mt7925_mib_data) ||
+ tlv + len > tlv_end)
+ break;
+
+ if (tag == UNI_CMD_MIB_DATA) {
+ counter = le32_to_cpu(evt_tlv->counter);
+
+ if (counter < MT7925_MIB_CNT_MAX_NUM) {
+ for (i = 0; i < counter_num; i++) {
+ if (counters[i] == counter) {
+ values[i] =
+ le64_to_cpu(evt_tlv->data);
+ break;
+ }
+ }
+ }
+ }
+
+ tlv += len;
+ }
+
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+
+ return ret;
+}
+
+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx)
+{
+ char * const ofdm_str[] = {
+ "6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M", "N/A"
+ };
+
+ switch (ofdm_idx) {
+ case 11: return ofdm_str[0]; /* 6M */
+ case 15: return ofdm_str[1]; /* 9M */
+ case 10: return ofdm_str[2]; /* 12M */
+ case 14: return ofdm_str[3]; /* 18M */
+ case 9: return ofdm_str[4]; /* 24M */
+ case 13: return ofdm_str[5]; /* 36M */
+ case 8: return ofdm_str[6]; /* 48M */
+ case 12: return ofdm_str[7]; /* 54M */
+ default: return ofdm_str[8];
+ }
+}
+
+/* mt792x_mcu_get_wtbl_rate - fetch the firmware auto-rate table for a WCID
+ * @dev: device
+ * @wlan_idx: HW WTBL / WCID index (see mt7925_sta_stats.wtbl_idx)
+ * @rate: out, filled with the auto-rate table snapshot
+ *
+ * Mirror of Gen4m wlanoidGetStatWtbl(): send UNI_CMD_GET_STAT_INFO with the
+ * WTBL rate TLV (cmd tag 10) and parse the reply (event tag 12) into @rate.
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,
+ struct mt7925_wtbl_rate *rate)
+{
+ int ret;
+ struct mt7925_wtbl_rate_event *event = NULL;
+ struct sk_buff *skb = NULL;
+ struct mt7925_wtbl_rate_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_WTBL),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ .wlan_idx = cpu_to_le16(wlan_idx),
+ };
+
+ if (!rate)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO),
+ &req, sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(*event)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ event = (struct mt7925_wtbl_rate_event *)skb->data;
+
+ if (le16_to_cpu(event->rate.tag) != MT7925_UNI_EVENT_STATISTICS_WTBL) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ memcpy(rate, &event->rate, sizeof(*rate));
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+/**
+ * mt7925_mcu_get_link_quality - query RSSI and TX link speed for a band
+ * @dev: device
+ * @band_idx: band/BSS index (0..3)
+ * @rssi: out, moving-average beacon RSSI in dBm (may be NULL)
+ * @tx_link_speed_kbps: out, TX rate in kbit/s (may be NULL)
+ *
+ * Return: 0 on success, -ENOENT if the band's link quality is not ready.
+ *
+ * Only RSSI and TX rate come from firmware here; for RX rate read wcid->rate.
+ */
+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,
+ s8 *rssi, u32 *tx_link_speed_kbps)
+{
+ int ret;
+ struct mt7925_lq_event *event;
+ struct mt7925_link_quality *lq;
+ struct sk_buff *skb;
+ struct mt7925_lq_req req = {
+ .tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_LINK_QUALITY),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+ };
+
+ if (band_idx >= MT7925_LQ_BAND_NUM)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_UNI_QUERY(GET_STAT_INFO), &req,
+ sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ if (skb->len < sizeof(*event)) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ event = (struct mt7925_lq_event *)skb->data;
+
+ if (le16_to_cpu(event->tag) != UNI_CMD_ID_STATISTICS_LINK_QUALITY) {
+ ret = -EPROTO;
+ goto out;
+ }
+
+ lq = &event->lq[band_idx];
+ if (!lq->is_ready) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (rssi)
+ *rssi = lq->rssi;
+
+ /* firmware reports TX rate in 500 Kb/s units -> kbit/s */
+ if (tx_link_speed_kbps)
+ *tx_link_speed_kbps = le16_to_cpu(lq->link_speed) * 500;
+
+ ret = 0;
+
+out:
+ dev_kfree_skb(skb);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.h b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h
new file mode 100644
index 000000000000..a70b9eb3aa80
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h
@@ -0,0 +1,309 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#ifndef __MT7925_STATS_H
+#define __MT7925_STATS_H
+
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include "../mt76_connac_mcu.h"
+
+#define MT7925_STA_STATS_AC_NUM 4
+#define MT7925_STA_STATS_BAND_NUM 2
+#define MT7925_STA_STATS_AGG_RANGE_NUM 16
+#define MT7925_STA_STATS_TX_QUALITY_NUM 4
+#define MT7925_TX_MODE_NUM 16
+#define MT7925_TX_RATE_CCK_NUM 5
+#define MT7925_TX_RATE_BW_NUM 6
+
+/* HW rate code (16-bit) field extract, mirror Gen4m CONNAC3X_HW_TX_RATE_TO_*.
+ * The auto-rate table stores one rate code per entry.
+ */
+#define MT7925_HW_RATE_TO_MCS(_x) (((_x) & GENMASK(5, 0)) >> 0)
+#define MT7925_HW_RATE_TO_MODE(_x) (((_x) & GENMASK(9, 6)) >> 6)
+#define MT7925_HW_RATE_TO_NSS(_x) (((_x) & GENMASK(13, 10)) >> 10)
+#define MT7925_HW_RATE_TO_STBC(_x) (((_x) & BIT(14)) >> 14)
+#define MT7925_HW_RATE_TO_DCM(_x) (((_x) & BIT(4)) >> 4)
+#define MT7925_HW_RATE_TO_106T(_x) (((_x) & BIT(5)) >> 5)
+#define MT7925_HW_RATE_UNMASK_DCM(_r) ((u8)(_r) & 0xef)
+#define MT7925_HW_RATE_UNMASK_106T(_r) ((u8)(_r) & 0xdf)
+
+/* Number of entries in the firmware auto-rate table (mirror Gen4m
+ * AUTO_RATE_NUM). RA cycles through these and reports which one is in use
+ * via wtbl->rate_idx.
+ */
+#define MT7925_AUTO_RATE_NUM 8
+
+/* Firmware EVENT_STATISTICS sub-event tags. The WTBL rate request uses cmd tag
+ * UNI_CMD_ID_STATISTICS_WTBL (10) but the firmware replies with event tag 12.
+ */
+#define MT7925_UNI_EVENT_STATISTICS_WTBL 12
+
+/* CONNAC3X TX Vector BBP latch bit fields (mirror cmm_asic_connac3x.h).
+ * These operate on the 3-DWORD tx_vector[band].txv[3] array.
+ */
+#define MT7925_TXV_GET_TX_RATE(_txv) ((_txv)[2] & 0x7f)
+#define MT7925_TXV_GET_TX_LDPC(_txv) (((_txv)[2] & (0x1 << 7)) >> 7)
+#define MT7925_TXV_GET_TX_STBC(_txv) (((_txv)[0] & (0x3 << 6)) >> 6)
+#define MT7925_TXV_GET_TX_FRMODE(_txv) (((_txv)[0] & (0x7 << 8)) >> 8)
+#define MT7925_TXV_GET_TX_MODE(_txv) (((_txv)[0] & (0xf << 12)) >> 12)
+#define MT7925_TXV_GET_TX_NSTS(_txv) (((_txv)[2] & (0xfU << 28)) >> 28)
+#define MT7925_TXV_GET_TX_PWR(_txv) (((_txv)[0] & (0xff << 16)) >> 16)
+#define MT7925_TXV_GET_TX_SGI(_txv) (((_txv)[1] & (0x3 << 26)) >> 26)
+#define MT7925_TXV_GET_TX_SPE_IDX(_txv) (((_txv)[0] & (0x1f << 0)) >> 0)
+#define MT7925_TXV_GET_TX_DCM(_txv) (((_txv)[2] & (0x1 << 4)) >> 4)
+#define MT7925_TXV_GET_TX_106T(_txv) (((_txv)[2] & (0x1 << 5)) >> 5)
+#define MT7925_TXV_RATE_UNMASK_DCM(_r) ((u8)(_r) & 0xef)
+#define MT7925_TXV_RATE_UNMASK_106T(_r) ((u8)(_r) & 0xdf)
+
+extern const char * const mt7925_tx_mode_str[MT7925_TX_MODE_NUM];
+extern const char * const mt7925_tx_rate_cck_str[MT7925_TX_RATE_CCK_NUM];
+extern const char * const mt7925_tx_rate_bw_str[MT7925_TX_RATE_BW_NUM];
+
+/* TX rate mode, mirror Gen4m TX_RATE_MODE_* (see nic_rate.h). Used to decode
+ * both the TX Vector BBP latch and the firmware auto-rate table rate codes.
+ */
+enum mt7925_tx_rate_mode {
+ MT7925_TX_RATE_MODE_CCK = 0,
+ MT7925_TX_RATE_MODE_OFDM = 1,
+ MT7925_TX_RATE_MODE_HTMIX = 2,
+ MT7925_TX_RATE_MODE_HTGF = 3,
+ MT7925_TX_RATE_MODE_VHT = 4,
+ MT7925_TX_RATE_MODE_PLR = 5,
+ MT7925_TX_RATE_MODE_HE_SU = 8,
+ MT7925_TX_RATE_MODE_HE_ER = 9,
+ MT7925_TX_RATE_MODE_HE_TRIG = 10,
+ MT7925_TX_RATE_MODE_HE_MU = 11,
+ MT7925_TX_RATE_MODE_EHT_ER = 13,
+ MT7925_TX_RATE_MODE_EHT_TRIG = 14,
+ MT7925_TX_RATE_MODE_EHT_MU = 15,
+};
+
+enum {
+ UNI_CMD_MIB_DATA,
+};
+
+enum stats_query_mib_array_index {
+ QUERY_MIB_CNT_RX_FCS_ERR = 0,
+ QUERY_MIB_CNT_RX_FIFO_OVERFLOW,
+ QUERY_MIB_CNT_RX_MPDU,
+ QUERY_MIB_CNT_AMPDU_RX_COUNT,
+ QUERY_MIB_CNT_PF_DROP,
+ QUERY_MIB_CNT_LEN_MISMATCH,
+ QUERY_MIB_CNT_CHANNEL_IDLE,
+ QUERY_MIB_CNT_CCA_NAV_TX_TIME,
+ QUERY_MIB_CNT_MDRDY,
+ QUERY_MIB_CNT_RX_CCK_MDRDY_TIME,
+ QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,
+ QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,
+ QUERY_MIB_CNT_P_CCA_TIME,
+ QUERY_MIB_CNT_S_CCA_TIME,
+ QUERY_MIB_CNT_P_ED_TIME,
+ QUERY_MIB_CNT_BCN_TX,
+ QUERY_MIB_CNT_TX_BW_40MHZ,
+ QUERY_MIB_CNT_TX_BW_80MHZ,
+ QUERY_MIB_CNT_TX_BW_160MHZ,
+ QUERY_MIB_CNT_BSS0_BA_MISS,
+ QUERY_MIB_CNT_BSS0_RTS_TX_CNT,
+ QUERY_MIB_CNT_BSS0_FRAME_RETRY,
+ QUERY_MIB_CNT_BSS0_FRAME_RETRY_2,
+ QUERY_MIB_CNT_BSS0_RTS_RETRY,
+ QUERY_MIB_CNT_BSS0_ACK_FAIL,
+ QUERY_MIB_CNT_AMPDU,
+ QUERY_MIB_CNT_AMPDU_MPDU,
+ QUERY_MIB_CNT_AMPDU_ACKED,
+ QUERY_MIB_CNT_MAX_NUM,
+};
+
+enum stats_trx_agg_range {
+ STAT_MIB_CNT_TRX_AGG_RANGE0_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE1_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE2_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE3_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE4_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE5_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE6_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE7_IDX,
+ STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM
+};
+
+enum {
+ MT7925_BW_20 = 0,
+ MT7925_BW_40,
+ MT7925_BW_80,
+ MT7925_BW_160,
+};
+
+struct mt7925_sta_stats_ac {
+ __le32 tx_fail;
+ __le32 tx_retry;
+} __packed;
+
+struct mt7925_sta_stats_tx_vector {
+ __le32 txv[3];
+} __packed;
+
+struct mt7925_sta_stats_mib {
+ __le32 rx_mpdu_cnt;
+ __le32 fcs_error;
+ __le32 rx_fifo_full;
+ __le32 ampdu_tx_sf_cnt;
+ __le32 ampdu_tx_ack_sf_cnt;
+ __le32 tx_range_ampdu_cnt[MT7925_STA_STATS_AGG_RANGE_NUM];
+} __packed;
+
+/* Firmware EVENT_STA_STATISTICS body. */
+struct mt7925_sta_stats {
+ u8 version;
+ u8 rsv[3];
+ __le32 flags;
+
+ u8 sta_idx;
+ u8 bss_idx;
+ u8 wtbl_idx;
+ u8 rsv1;
+
+ u8 mac_addr[ETH_ALEN];
+ u8 per;
+ u8 rcpi;
+
+ __le32 phy_mode;
+ __le16 link_speed; /* 0.5 Mbit/s units */
+ u8 link_quality;
+ u8 link_reserved;
+
+ __le32 tx_count;
+ __le32 tx_fail_count;
+ __le32 tx_life_timeout_count;
+ __le32 tx_done_air_time;
+ __le32 transmit_count;
+ __le32 transmit_fail_count;
+
+ struct mt7925_sta_stats_ac ac[MT7925_STA_STATS_AC_NUM];
+
+ u8 temperature;
+ u8 skip_ar;
+ u8 ar_table_idx;
+ u8 rate_entry_idx;
+ u8 rate_entry_idx_prev;
+ u8 tx_sgi_detect_pass_cnt;
+ u8 ave_per;
+ u8 rsv2;
+
+ __le32 agg_range_ctrl[2];
+ u8 range_mode;
+ u8 rsv3[3];
+ __le32 agg_range_ctrl_ext[6];
+
+ u8 ar_state_curr;
+ u8 ar_state_prev;
+ u8 ar_action_type;
+ u8 highest_rate_cnt;
+ u8 lowest_rate_cnt;
+ u8 rsv4;
+ __le16 train_up;
+ __le16 train_down;
+ u8 rsv5[2];
+ __le32 rate1_tx_cnt;
+ __le32 rate1_fail_cnt;
+
+ struct mt7925_sta_stats_tx_vector tx_vector[MT7925_STA_STATS_BAND_NUM];
+ struct mt7925_sta_stats_mib mib[MT7925_STA_STATS_BAND_NUM];
+
+ u8 is_force_tx_stream;
+ u8 is_force_se_off;
+ __le16 ra_running_cnt;
+ u8 ra_status;
+ u8 max_ampdu_factor;
+ u8 tx_quality[MT7925_STA_STATS_TX_QUALITY_NUM];
+ u8 tx_rate_up_penalty;
+ u8 low_traffic_mode;
+ u8 low_traffic_count;
+ u8 low_traffic_dashboard;
+ u8 dynamic_sgi_state;
+ u8 dynamic_sgi_score;
+ u8 dynamic_bw_state;
+ u8 dynamic_gband_256qam_state;
+ u8 vht_non_sp_rate_state;
+ u8 rsv6[3];
+ u8 rsv7[2];
+} __packed;
+
+struct mt7925_sta_rec_lookup {
+ int sta_rec_idx;
+};
+
+/* Firmware auto-rate table snapshot, mirror Gen4m struct UNI_EVENT_STAT_WTBL.
+ *
+ * The firmware RA maintains up to MT7925_AUTO_RATE_NUM candidate rates in
+ * rate_code[]. rate_idx tells which entry is currently in use, so a dump can
+ * point at it with "-->". fcap is the bandwidth capability and the ldpc/sgi
+ * flags describe the per-mode coding/guard-interval used for those rates.
+ */
+struct mt7925_wtbl_rate {
+ __le16 tag;
+ __le16 len;
+ u8 rx_rcpi0;
+ u8 rx_rcpi1;
+ u8 cbrn;
+ u8 fcap;
+ u8 spe_idx;
+ u8 g2; /* SGI enabled, non-HE, BW20 */
+ u8 g4; /* SGI enabled, non-HE, BW40 */
+ u8 g8; /* SGI enabled, non-HE, BW80 */
+ u8 g16; /* SGI enabled, non-HE, BW160 */
+ u8 g2_he; /* GI, HE, BW20 */
+ u8 g4_he; /* GI, HE, BW40 */
+ u8 g8_he; /* GI, HE, BW80 */
+ u8 g16_he; /* GI, HE, BW160 */
+ u8 gi_eht; /* GI, EHT */
+ u8 ht_ldpc;
+ u8 vht_ldpc;
+ u8 he_ldpc;
+ u8 eht_ldpc;
+ u8 af;
+ u8 rate_idx; /* index into rate_code[] currently in use */
+ __le16 rate_code[MT7925_AUTO_RATE_NUM];
+ __le32 rsv[4];
+} __packed;
+
+int
+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx);
+
+void
+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3]);
+
+u8
+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap);
+
+u8
+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode);
+
+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx);
+
+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,
+ struct mt7925_wtbl_rate *rate);
+
+int mt792x_mcu_get_stat(struct mt792x_dev *dev,
+ struct mt7925_sta_stats *stats);
+
+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,
+ const u32 *counters, u8 counter_num,
+ u64 *values);
+
+/* GET_STAT_INFO (UNI_CMD_ID_GET_STATISTICS = 0x23),
+ * sub-command UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1.
+ *
+ * @band_idx: band/BSS index (0..3)
+ * @rssi: out, moving-average beacon RSSI (dBm), may be NULL
+ * @tx_link_speed_kbps: out, TX link speed in kbit/s, may be NULL
+ *
+ * Returns 0 on success, -ENOENT if the band's link quality is not ready.
+ *
+ * NOTE: firmware provides only RSSI and TX rate here. RX rate/bandwidth are
+ * not in this event; read wcid->rate for the RX rate.
+ */
+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,
+ s8 *rssi, u32 *tx_link_speed_kbps);
+
+#endif
--
2.45.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-27 8:44 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-24 3:07 [PATCH v1] wifi: mt76: mt7928: Add debugfs for stats to provide debug purpose Eason Lai
2026-08-25 4:00 ` kernel test robot
2026-08-25 7:55 ` kernel test robot
2026-08-27 5:54 ` [PATCH v2] " Eason Lai
2026-08-27 8:44 ` [PATCH v3] " Eason Lai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).