[PATCH] wifi: mt76: mt7925: add coex_info debugfs for WiFi/BT coex

JB Tsai jb.tsai at mediatek.com
Sun Aug 9 19:05:26 PDT 2026


From: Vick Cheng <vick.cheng at mediatek.com>

This is FW MCU CMD API.
Add mt7925_mcu_chip_config_query() which sends the ASCII command as a
UNI query, walks the reply TLVs and copies the payload of the
UNI_CHIP_CONFIG_CHIP_CFG entry back to the caller along with its
response type. Expose it through a read-only 'coex_info' debugfs entry
that queries the firmware on every read, printing the reply as text or
as a hexdump depending on the reported type.

MCU_UNI_QUERY(CHIP_CONFIG) also needs the MCU_CMD_ACK option cleared in
mt7925_mcu_fill_message(), same as the set variant.

This gives a way to read the live WiFi/BT coex state on an upstream
kernel without a vendor tool. The entry is read-only and only issues the
command when read, so it costs nothing when unused, and the
mt7925_mcu_fill_message() change only matches the new query opcode, so
no existing command path changes behaviour.

Signed-off-by: Vick Cheng <vick.cheng at mediatek.com>
---
 .../wireless/mediatek/mt76/mt7925/debugfs.c   | 30 +++++++
 .../net/wireless/mediatek/mt76/mt7925/mcu.c   | 78 ++++++++++++++++++-
 .../net/wireless/mediatek/mt76/mt7925/mcu.h   |  9 +++
 .../wireless/mediatek/mt76/mt7925/mt7925.h    |  2 +
 4 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index d01ff49de47a..9a5ea12b76c7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -286,6 +286,34 @@ static int mt7925_chip_reset(void *data, u64 val)
 
 DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%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;
+}
+
 int mt7925_init_debugfs(struct mt792x_dev *dev)
 {
 	struct dentry *dir;
@@ -309,6 +337,8 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
 	debugfs_create_file("idle-timeout", 0600, dir, dev,
 			    &fops_pm_idle_timeout);
 	debugfs_create_file("chip_reset", 0600, dir, dev, &fops_reset);
+	debugfs_create_devm_seqfile(dev->mt76.dev, "coex_info", dir,
+				    mt7925_coex_info);
 	debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir,
 				    mt792x_pm_stats);
 	debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index fa29c486a455..e6316cea1865 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1160,6 +1160,81 @@ int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd)
 				 &req, sizeof(req), false);
 }
 
+int mt7925_mcu_chip_config_query(struct mt792x_dev *dev, const char *cmd,
+				 u8 *resp_type, void *resp, u16 resp_size)
+{
+	u16 len = strlen(cmd);
+	struct {
+		u8 _rsv[4];
+		__le16 tag;
+		__le16 len;
+		struct mt76_connac_config config;
+	} __packed req = {
+		.tag = cpu_to_le16(UNI_CHIP_CONFIG_CHIP_CFG),
+		.len = cpu_to_le16(sizeof(req) - 4),
+		.config = {
+			.resp_type = 0,
+			.type = CHIP_CONFIG_TYPE_ASCII,
+			.data_size = cpu_to_le16(len),
+		},
+	};
+	const u16 hdr_len = sizeof(struct tlv) +
+			    offsetof(struct mt76_connac_config, data);
+	struct mt76_connac_config *cfg;
+	struct sk_buff *skb;
+	int ret;
+
+	if (!len || len >= sizeof(req.config.data))
+		return -EINVAL;
+
+	memcpy(req.config.data, cmd, len);
+
+	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(CHIP_CONFIG),
+					&req, sizeof(req), true, &skb);
+	if (ret)
+		return ret;
+
+	/* skip the fixed field of the event, the TLVs follow it */
+	if (skb->len < 4) {
+		ret = -EINVAL;
+		goto out;
+	}
+	skb_pull(skb, 4);
+
+	ret = -ENOENT;
+	while (skb->len >= sizeof(struct tlv)) {
+		struct tlv *tlv = (struct tlv *)skb->data;
+		u16 tlv_len = le16_to_cpu(tlv->len);
+		u16 data_size;
+
+		if (tlv_len < sizeof(*tlv) || tlv_len > skb->len)
+			break;
+
+		if (le16_to_cpu(tlv->tag) != UNI_CHIP_CONFIG_CHIP_CFG) {
+			skb_pull(skb, tlv_len);
+			continue;
+		}
+
+		if (tlv_len < hdr_len)
+			break;
+
+		cfg = (struct mt76_connac_config *)tlv->data;
+		data_size = le16_to_cpu(cfg->data_size);
+		if (data_size > tlv_len - hdr_len)
+			break;
+
+		*resp_type = cfg->resp_type;
+		ret = min_t(u16, data_size, resp_size);
+		memcpy(resp, cfg->data, ret);
+		break;
+	}
+
+out:
+	dev_kfree_skb(skb);
+
+	return ret;
+}
+
 int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable)
 {
 	char cmd[16];
@@ -3772,7 +3847,8 @@ int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb,
 			uni_txd->option = MCU_CMD_UNI_EXT_ACK;
 
 		if (cmd == MCU_UNI_CMD(HIF_CTRL) ||
-		    cmd == MCU_UNI_CMD(CHIP_CONFIG))
+		    cmd == MCU_UNI_CMD(CHIP_CONFIG) ||
+		    cmd == MCU_UNI_QUERY(CHIP_CONFIG))
 			uni_txd->option &= ~MCU_CMD_ACK;
 
 		if (mcu_cmd == MCU_UNI_CMD_TESTMODE_CTRL ||
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
index 11f9eac13ffc..2cf3971d9853 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
@@ -119,6 +119,15 @@ enum {
 	UNI_CHIP_CONFIG_NIC_CAPA = 0x3,
 };
 
+/* type/resp_type of struct mt76_connac_config */
+enum {
+	CHIP_CONFIG_TYPE_WO_RESPONSE,
+	CHIP_CONFIG_TYPE_MEM8,
+	CHIP_CONFIG_TYPE_MEM32,
+	CHIP_CONFIG_TYPE_ASCII,
+	CHIP_CONFIG_TYPE_BINARY,
+};
+
 enum {
 	UNI_BAND_CONFIG_RADIO_ENABLE,
 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 321e732347f2..d0db5d8dae48 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -295,6 +295,8 @@ int mt7925_mcu_get_rx_rate(struct mt792x_phy *phy, struct ieee80211_vif *vif,
 int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl);
 void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb);
 int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd);
+int mt7925_mcu_chip_config_query(struct mt792x_dev *dev, const char *cmd,
+				 u8 *resp_type, void *resp, u16 resp_size);
 int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
 			    u8 bit_op, u32 bit_map);
 
-- 
2.45.2




More information about the Linux-mediatek mailing list