* [PATCH ath-next] wifi: ath12k: Support channel change stats
@ 2026-03-26 5:06 Roopni Devanathan
0 siblings, 0 replies; only message in thread
From: Roopni Devanathan @ 2026-03-26 5:06 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Harish Rachakonda, Roopni Devanathan
From: Harish Rachakonda <quic_rachakon@quicinc.com>
Add support to request channel change stats from the firmware through
HTT stats type 76. These stats give channel switch details like the
channel that the radio changed to, its center frequency, time taken
for the switch, chainmask details, etc.
Sample output:
echo 76 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type
cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats
Channel Change Timings:
|PRIMARY CHANNEL FREQ|BANDWIDTH CENTER FREQ|PHYMODE|TX_CHAINMASK|RX_CHAINMASK|SWITCH TIME(us)|INI(us)|TPC+CTL(us)|CAL(us)|MISC(us)|CTL(us)|SW PROFILE|
| 5200| 5200| 24| 15| 15| 448850| 2410| 10546| 434593| 1071| 1100| 4|
| 5240| 5240| 24| 15| 15| 450730| 4106| 10524| 434528| 1306| 1150| 4|
| 5180| 5210| 26| 15| 15| 467894| 4764| 10438| 451101| 1337| 1508| 4|
| 5200| 5200| 0| 15| 15| 13838| 2692| 1736| 8558| 686| 802| 6|
| 5180| 5180| 0| 15| 15| 13465| 3207| 855| 8579| 578| 760| 6|
| 5200| 5200| 24| 15| 15| 570321| 2441| 10439| 555661| 1574| 949| 4|
Note: WCN7850 firmware does not support HTT stats type 76.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01181-QCAHKSWPL_SILICONZ-1
Signed-off-by: Harish Rachakonda <quic_rachakon@quicinc.com>
Signed-off-by: Roopni Devanathan <roopni.devanathan@oss.qualcomm.com>
---
.../wireless/ath/ath12k/debugfs_htt_stats.c | 72 +++++++++++++++++++
.../wireless/ath/ath12k/debugfs_htt_stats.h | 26 +++++++
2 files changed, 98 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c
index 7f6ca07fb335..b772181a496e 100644
--- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c
+++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c
@@ -5722,6 +5722,75 @@ ath12k_htt_print_tx_hwq_stats_cmn_tlv(const void *tag_buf, u16 tag_len,
stats_req->buf_len = len;
}
+static void
+ath12k_htt_print_chan_switch_stats_tlv(const void *tag_buf, u16 tag_len,
+ struct debug_htt_stats_req *stats_req)
+{
+ const struct ath12k_htt_chan_switch_stats_tlv *sbuf = tag_buf;
+ u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE;
+ u32 switch_freq, switch_profile;
+ u32 len = stats_req->buf_len;
+ u8 *buf = stats_req->buf;
+ u8 i;
+
+ if (tag_len < sizeof(*sbuf))
+ return;
+
+ i = min(le32_to_cpu(sbuf->switch_count), ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN);
+ if (!i)
+ return;
+
+ len += scnprintf(buf + len, buf_len - len, "Channel Change Timings:\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "|%-20s|%-21s|%-7s|%-12s|%-12s|%-15s|",
+ "PRIMARY CHANNEL FREQ", "BANDWIDTH CENTER FREQ", "PHYMODE",
+ "TX_CHAINMASK", "RX_CHAINMASK", "SWITCH TIME(us)");
+ len += scnprintf(buf + len, buf_len - len,
+ "%-7s|%-11s|%-7s|%-8s|%-7s|%-10s|\n",
+ "INI(us)", "TPC+CTL(us)", "CAL(us)", "MISC(us)", "CTL(us)",
+ "SW PROFILE");
+
+ /*
+ * sbuf->switch_count has the number of successful channel changes. The firmware
+ * sends the record of channel change in such a way that sbuf->chan_stats[0] will
+ * point to the channel change that occurred first and the recent channel change
+ * records will be stored in sbuf->chan_stats[9]. As and when new channel change
+ * occurs, sbuf->chan_stats[0] will be replaced by records from the next index,
+ * sbuf->chan_stats[1]. While printing the records, reverse chronological order
+ * is followed, i.e., the most recent channel change records are printed first
+ * and the oldest one, last.
+ */
+ while (i--) {
+ switch_freq = le32_to_cpu(sbuf->chan_stats[i].chan_switch_freq);
+ switch_profile = le32_to_cpu(sbuf->chan_stats[i].chan_switch_profile);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "|%20u|%21u|%7u|%12u|%12u|%15u|",
+ u32_get_bits(switch_freq,
+ ATH12K_HTT_STATS_CHAN_SWITCH_BW_MHZ),
+ u32_get_bits(switch_freq,
+ ATH12K_HTT_STATS_CHAN_SWITCH_BAND_FREQ),
+ u32_get_bits(switch_profile,
+ ATH12K_HTT_STATS_CHAN_SWITCH_PHY_MODE),
+ u32_get_bits(switch_profile,
+ ATH12K_HTT_STATS_CHAN_SWITCH_TX_CHAINMASK),
+ u32_get_bits(switch_profile,
+ ATH12K_HTT_STATS_CHAN_SWITCH_RX_CHAINMASK),
+ le32_to_cpu(sbuf->chan_stats[i].chan_switch_time));
+ len += scnprintf(buf + len, buf_len - len,
+ "%7u|%11u|%7u|%8u|%7u|%10u|\n",
+ le32_to_cpu(sbuf->chan_stats[i].ini_module_time),
+ le32_to_cpu(sbuf->chan_stats[i].tpc_module_time),
+ le32_to_cpu(sbuf->chan_stats[i].cal_module_time),
+ le32_to_cpu(sbuf->chan_stats[i].misc_module_time),
+ le32_to_cpu(sbuf->chan_stats[i].ctl_module_time),
+ u32_get_bits(switch_profile,
+ ATH12K_HTT_STATS_CHAN_SWITCH_SW_PROFILE));
+ }
+
+ stats_req->buf_len = len;
+}
+
static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab,
u16 tag, u16 len, const void *tag_buf,
void *user_data)
@@ -6024,6 +6093,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab,
case HTT_STATS_TX_HWQ_CMN_TAG:
ath12k_htt_print_tx_hwq_stats_cmn_tlv(tag_buf, len, stats_req);
break;
+ case HTT_STATS_CHAN_SWITCH_STATS_TAG:
+ ath12k_htt_print_chan_switch_stats_tlv(tag_buf, len, stats_req);
+ break;
default:
break;
}
diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h
index bfabe6500d44..82ab7b9e4db9 100644
--- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h
+++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h
@@ -164,6 +164,7 @@ enum ath12k_dbg_htt_ext_stats_type {
ATH12K_DBG_HTT_PDEV_MLO_IPC_STATS = 64,
ATH12K_DBG_HTT_EXT_PDEV_RTT_RESP_STATS = 65,
ATH12K_DBG_HTT_EXT_PDEV_RTT_INITIATOR_STATS = 66,
+ ATH12K_DBG_HTT_EXT_CHAN_SWITCH_STATS = 76,
/* keep this last */
ATH12K_DBG_HTT_NUM_EXT_STATS,
@@ -267,6 +268,7 @@ enum ath12k_dbg_htt_tlv_tag {
HTT_STATS_PDEV_RTT_HW_STATS_TAG = 196,
HTT_STATS_PDEV_RTT_TBR_SELFGEN_QUEUED_STATS_TAG = 197,
HTT_STATS_PDEV_RTT_TBR_CMD_RESULT_STATS_TAG = 198,
+ HTT_STATS_CHAN_SWITCH_STATS_TAG = 213,
HTT_STATS_MAX_TAG,
};
@@ -2156,4 +2158,28 @@ struct htt_tx_hwq_stats_cmn_tlv {
__le32 txq_timeout;
} __packed;
+#define ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN 10
+
+#define ATH12K_HTT_STATS_CHAN_SWITCH_BW_MHZ GENMASK(15, 0)
+#define ATH12K_HTT_STATS_CHAN_SWITCH_BAND_FREQ GENMASK(31, 16)
+#define ATH12K_HTT_STATS_CHAN_SWITCH_PHY_MODE GENMASK(7, 0)
+#define ATH12K_HTT_STATS_CHAN_SWITCH_TX_CHAINMASK GENMASK(15, 8)
+#define ATH12K_HTT_STATS_CHAN_SWITCH_RX_CHAINMASK GENMASK(23, 16)
+#define ATH12K_HTT_STATS_CHAN_SWITCH_SW_PROFILE GENMASK(31, 24)
+
+struct ath12k_htt_chan_switch_stats_tlv {
+ struct {
+ __le32 chan_switch_freq;
+ __le32 chan_switch_profile;
+ __le32 chan_switch_time;
+ __le32 cal_module_time;
+ __le32 ini_module_time;
+ __le32 tpc_module_time;
+ __le32 misc_module_time;
+ __le32 ctl_module_time;
+ __le32 reserved;
+ } chan_stats[ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN];
+ __le32 switch_count; /* shows how many channel changes have occurred */
+} __packed;
+
#endif
base-commit: 37538641dac955f6690372f0ebb94e5e14a23418
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-03-26 5:06 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-26 5:06 [PATCH ath-next] wifi: ath12k: Support channel change stats Roopni Devanathan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox