Linux-mediatek Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Anthony Liu <anthony.liu@mediatek.com>
To: <nbd@nbd.name>, <lorenzo@kernel.org>
Cc: <Sean.Wang@mediatek.com>, <Leon.Yen@mediatek.com>,
	<Quan.Zhou@mediatek.com>, <Ryder.lee@mediatek.com>,
	<litien.chang@mediatek.com>, <kun.wu@mediatek.com>,
	<jb.tsai@mediatek.com>, <emery.hsin@mediatek.com>,
	<linux-wireless@vger.kernel.org>,
	<linux-mediatek@lists.infradead.org>,
	Anthony Liu <anthony.liu@mediatek.com>
Subject: [PATCH v1] [PATCH 05/05] wifi: mt76: add PERF_IND for mt7922/mt7928
Date: Thu, 20 Aug 2026 19:12:02 +0800	[thread overview]
Message-ID: <20260820111202.1935373-5-anthony.liu@mediatek.com> (raw)
In-Reply-To: <20260820111202.1935373-1-anthony.liu@mediatek.com>

Update Tx/Rx statistics to F/W for speed tuning.

Change-Id: I76e16739d076515d9a6dc57fe3b38033feb33854
CR-Id: WCNCR00542202
Signed-off-by: Anthony Liu <anthony.liu@mediatek.com>
Reviewed-on: https://gerrit.mediatek.inc/c/neptune/oss/nbd168_wireless/+/12378114
Commit-Check: srv_check_service <srv_check_service@mediatek.com>
Reviewed-by: jb.tsai <jb.tsai@mediatek.com>
Code-Review-Course-Train: srv_check_service <srv_check_service@mediatek.com>
GAI-Code-Review: srv_ai_review001 <srv_ai_review001@mediatek.com>
AutoUT-Review-Label: srv_neptune_adm <srv_neptune_adm@mediatek.com>
---
 .../wireless/mediatek/mt76/mt76_connac_mcu.c  |  15 ++
 .../wireless/mediatek/mt76/mt76_connac_mcu.h  |  53 ++++++
 .../wireless/mediatek/mt76/mt7921/debugfs.c   |  36 ++++
 .../net/wireless/mediatek/mt76/mt7921/mac.c   |   4 +
 .../net/wireless/mediatek/mt76/mt7921/main.c  |   2 +
 .../net/wireless/mediatek/mt76/mt7921/pci.c   |   8 +
 .../wireless/mediatek/mt76/mt7921/pci_mac.c   |   2 +
 .../wireless/mediatek/mt76/mt7925/debugfs.c   |  45 +++++
 .../net/wireless/mediatek/mt76/mt7925/mac.c   |   4 +
 .../net/wireless/mediatek/mt76/mt7925/main.c  |   4 +
 .../net/wireless/mediatek/mt76/mt7925/pci.c   |  14 ++
 .../wireless/mediatek/mt76/mt7925/pci_mac.c   |   2 +
 drivers/net/wireless/mediatek/mt76/mt792x.h   |  49 +++++
 .../net/wireless/mediatek/mt76/mt792x_core.c  | 170 ++++++++++++++++++
 14 files changed, 408 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 015bbd92d458..eb4d02ec3ebe 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -272,6 +272,21 @@ int mt76_connac_mcu_set_rts_thresh(struct mt76_dev *dev, u32 val, u8 band)
 }
 EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_rts_thresh);
 
+int mt76_connac_mcu_uni_set_perf_ind(struct mt76_dev *dev, u32 valid_period,
+				     const u64 *tx_bytes, const u64 *rx_bytes)
+{
+	struct mt76_connac_uni_perf_ind req = {
+		.tag = cpu_to_le16(UNI_PERF_IND_PARM),
+		.len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+	};
+
+	mt76_connac_perf_ind_v1_fill(&req.perf, valid_period, tx_bytes, rx_bytes);
+
+	return mt76_mcu_send_msg(dev, MCU_UNI_CMD(PERF_IND), &req,
+				 sizeof(req), false);
+}
+EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_set_perf_ind);
+
 void mt76_connac_mcu_beacon_loss_iter(void *priv, u8 *mac,
 				      struct ieee80211_vif *vif)
 {
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index d237d56eb1c1..aab101d10186 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1349,6 +1349,7 @@ enum {
 	MCU_UNI_CMD_VOW = 0x37,
 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
 	MCU_UNI_CMD_RSSI_MONITOR = 0x41,
+	MCU_UNI_CMD_PERF_IND = 0x44,
 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
 	MCU_UNI_CMD_NAN	= 0x56,
 	MCU_UNI_CMD_RRO = 0x57,
@@ -1396,6 +1397,7 @@ enum {
 	MCU_CE_CMD_SET_RATE_TX_POWER = 0x5d,
 	MCU_CE_CMD_SCHED_SCAN_ENABLE = 0x61,
 	MCU_CE_CMD_SCHED_SCAN_REQ = 0x62,
+	MCU_CE_CMD_PERF_IND = 0x7e,
 	MCU_CE_CMD_GET_NIC_CAPAB = 0x8a,
 	MCU_CE_CMD_RSSI_MONITOR = 0xa1,
 	MCU_CE_CMD_SET_MU_EDCA_PARMS = 0xb0,
@@ -1463,6 +1465,10 @@ enum {
 	UNI_CHIP_CONFIG_CHIP_CFG = 2,
 };
 
+enum {
+	UNI_PERF_IND_PARM = 0,
+};
+
 enum {
 	MT_NIC_CAP_TX_RESOURCE,
 	MT_NIC_CAP_TX_EFUSE_ADDR,
@@ -1875,6 +1881,51 @@ struct mt76_connac_config {
 	u8 data[320];
 } __packed;
 
+struct mt76_connac_perf_ind {
+	/* DW0 - common info*/
+	u8 cmd_ver;
+	u8 mask; /* per-field valid bitmap */
+	__le16 cmd_len;
+
+	/* DW1 - valid period of the stats */
+	__le32 valid_period;
+
+	/* DW2~15 - stats */
+	__le32 cur_tx_bytes[4];
+	__le32 cur_rx_bytes[4];
+	__le16 cur_rx_rate[4];
+	u8 cur_rx_rcpi0[4];
+	u8 cur_rx_rcpi1[4];
+	u8 cur_rx_nss[4];
+	u8 cur_rx_nss2[4];
+
+	/* DW16-77 padding */
+	__le32 pad[62];
+} __packed;
+
+struct mt76_connac_uni_perf_ind {
+	u8 rsv[4];
+	__le16 tag;
+	__le16 len;
+	struct mt76_connac_perf_ind perf;
+} __packed;
+
+static inline void
+mt76_connac_perf_ind_v1_fill(struct mt76_connac_perf_ind *perf, u32 valid_period,
+			     const u64 *tx_bytes, const u64 *rx_bytes)
+{
+	int i;
+
+	perf->mask = BIT(0) | BIT(1);
+	perf->cmd_len = cpu_to_le16(sizeof(*perf));
+	perf->valid_period = cpu_to_le32(valid_period);
+
+	for (i = 0; i < ARRAY_SIZE(perf->cur_tx_bytes); i++) {
+		perf->cur_tx_bytes[i] = cpu_to_le32(tx_bytes[i]);
+		perf->cur_rx_bytes[i] = cpu_to_le32(rx_bytes[i]);
+	}
+}
+
 struct mt76_connac_mcu_uni_event {
 	u8 cid;
 	u8 pad[3];
@@ -2059,6 +2110,8 @@ int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
 void mt76_connac_mcu_beacon_loss_iter(void *priv, u8 *mac,
 				      struct ieee80211_vif *vif);
 int mt76_connac_mcu_set_rts_thresh(struct mt76_dev *dev, u32 val, u8 band);
+int mt76_connac_mcu_uni_set_perf_ind(struct mt76_dev *dev, u32 valid_period,
+				     const u64 *tx_bytes, const u64 *rx_bytes);
 int mt76_connac_mcu_set_mac_enable(struct mt76_dev *dev, int band, bool enable,
 				   bool hdr_trans);
 int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
index 62bf33347e9e..86055a275ae0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
@@ -247,6 +247,37 @@ static int mt7921_chip_reset(void *data, u64 val)
 
 DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7921_chip_reset, "%lld\n");
 
+/* send one perf_ind carrying the written value in every tx/rx byte field */
+static int mt7921_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, mt7921_perf_ind_set, "%lld\n");
+
+static int mt7921_perf_ind_enable_get(void *data, u64 *val)
+{
+	struct mt792x_dev *dev = data;
+
+	*val = dev->perf.enabled;
+
+	return 0;
+}
+
+static int mt7921_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, mt7921_perf_ind_enable_get,
+			 mt7921_perf_ind_enable_set, "%lld\n");
+
 static int
 mt7921s_sched_quota_read(struct seq_file *s, void *data)
 {
@@ -287,6 +318,11 @@ int mt7921_init_debugfs(struct mt792x_dev *dev)
 	debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir,
 				    mt792x_pm_stats);
 	debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
+	if (mt76_is_mmio(&dev->mt76)) {
+		debugfs_create_file("enable_perf_ind", 0600, dir, dev,
+				    &fops_perf_ind_enable);
+		debugfs_create_file("perf_ind", 0200, dir, dev, &fops_perf_ind);
+	}
 	if (mt76_is_sdio(&dev->mt76))
 		debugfs_create_devm_seqfile(dev->mt76.dev, "sched-quota", dir,
 					    mt7921s_sched_quota_read);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index e69978184f68..3f7e7b41fb3d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -619,6 +619,10 @@ void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
 	case PKT_TYPE_NORMAL_MCU:
 	case PKT_TYPE_NORMAL:
 		if (!mt7921_mac_fill_rx(dev, skb)) {
+			struct mt76_rx_status *status =
+				(struct mt76_rx_status *)skb->cb;
+
+			mt792x_perf_account_rx(dev, status->wcid, skb->len);
 			mt76_rx(&dev->mt76, q, skb);
 			return;
 		}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 68a059504e83..067aa42c4f4c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -730,6 +730,8 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
 		mt7921_mcu_sta_update(dev, NULL, vif, true,
 				      MT76_STA_INFO_STATE_ASSOC);
 		mt7921_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
+
+		mt792x_perf_ind_update(dev, vif, MT792x_PERF_IND_TIME);
 	}
 
 	if (changed & BSS_CHANGED_ARP_FILTER) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index 56a914aaa81e..938b3c3234b4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -51,6 +51,7 @@ static void mt7921e_unregister_device(struct mt792x_dev *dev)
 		wiphy_rfkill_stop_polling(hw->wiphy);
 
 	cancel_work_sync(&dev->init_work);
+	cancel_delayed_work_sync(&dev->perf.work);
 	mt76_unregister_device(&dev->mt76);
 	mt76_for_each_q_rx(&dev->mt76, i)
 		napi_disable(&dev->mt76.napi[i]);
@@ -435,6 +436,9 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
 	if (ret)
 		goto err_free_irq;
 
+	/* the periodic performance indication is started on association */
+	mt792x_perf_ind_init(dev);
+
 	if (of_property_read_bool(dev->mt76.dev->of_node, "wakeup-source"))
 		device_init_wakeup(dev->mt76.dev, true);
 
@@ -475,6 +479,7 @@ static int mt7921_pci_suspend(struct device *device)
 
 	pm->suspended = true;
 	flush_work(&dev->reset_work);
+	cancel_delayed_work_sync(&dev->perf.work);
 	cancel_delayed_work_sync(&pm->ps_work);
 	cancel_work_sync(&pm->wake_work);
 
@@ -598,6 +603,9 @@ static int mt7921_pci_resume(struct device *device)
 
 	mt7921_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env);
 	err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_ON_LED);
+
+	/* re-arm the periodic performance indication if any bss is connected */
+	mt792x_perf_ind_resched(dev);
 failed:
 	pm->suspended = false;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
index 2449ac8ed6a4..0853d56356b0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
@@ -21,6 +21,8 @@ int mt7921e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
 	if (unlikely(tx_info->skb->len <= ETH_HLEN))
 		return -EINVAL;
 
+	mt792x_perf_account_tx(dev, info->control.vif, tx_info->skb->len);
+
 	if (!wcid)
 		wcid = &dev->mt76.global_wcid;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index 69c545986d61..3bb53d8e58b7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -286,6 +286,37 @@ static int mt7925_chip_reset(void *data, u64 val)
 
 DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
 
+/* 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");
+
 int mt7925_init_debugfs(struct mt792x_dev *dev)
 {
 	struct dentry *dir;
@@ -313,5 +344,19 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
 				    mt792x_pm_stats);
 	debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
 
+	/* PERF_IND is supported on MT7928 only within this family */
+	if (is_mt7928(&dev->mt76)) {
+		debugfs_create_file("enable_perf_ind", 0600, dir, dev,
+				    &fops_perf_ind_enable);
+		debugfs_create_file("perf_ind", 0200, dir, dev, &fops_perf_ind);
+	}
+
+	/* PERF_IND is supported on MT7928 only within this family */
+	if (is_mt7928(&dev->mt76)) {
+		debugfs_create_file("enable_perf_ind", 0600, dir, dev,
+				    &fops_perf_ind_enable);
+		debugfs_create_file("perf_ind", 0200, dir, dev, &fops_perf_ind);
+	}
+
 	return 0;
 }
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index 101f571b027f..6e0d2c4c39a5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1458,6 +1458,10 @@ void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
 	case PKT_TYPE_NORMAL_MCU:
 	case PKT_TYPE_NORMAL:
 		if (!mt7925_mac_fill_rx(dev, skb)) {
+			struct mt76_rx_status *status =
+				(struct mt76_rx_status *)skb->cb;
+
+			mt792x_perf_account_rx(dev, status->wcid, skb->len);
 			mt76_rx(&dev->mt76, q, skb);
 			return;
 		}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 84b55f008b3d..1f1f3c1524b8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -2113,6 +2113,10 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,
 
 		if (ieee80211_vif_is_mld(vif))
 			mvif->mlo_pm_state = MT792x_MLO_LINK_ASSOC;
+
+		/* PERF_IND is supported on MT7928 only within this family */
+		if (is_mt7928(&dev->mt76))
+			mt792x_perf_ind_update(dev, vif, MT792x_PERF_IND_TIME);
 	}
 
 	if (changed & BSS_CHANGED_ARP_FILTER) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index 02ef09dd797d..036d9f312a16 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -49,6 +49,8 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)
 
 	cancel_work_sync(&dev->reset_work);
 	cancel_work_sync(&dev->init_work);
+	if (is_mt7928(&dev->mt76))
+		cancel_delayed_work_sync(&dev->perf.work);
 	mt76_unregister_device(&dev->mt76);
 	mt76_for_each_q_rx(&dev->mt76, i)
 		napi_disable(&dev->mt76.napi[i]);
@@ -706,6 +708,12 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
 	if (ret)
 		goto err_free_dma;
 
+	/* PERF_IND is only supported on MT7928 in this family; MT7925 opts out.
+	 * The periodic indication itself is started on association.
+	 */
+	if (is_mt7928(&dev->mt76))
+		mt792x_perf_ind_init(dev);
+
 	return 0;
 
 err_free_dma:
@@ -743,6 +751,8 @@ static int mt7925_pci_suspend(struct device *device)
 	pm->suspended = true;
 	dev->hif_resumed = false;
 	flush_work(&dev->reset_work);
+	if (is_mt7928(&dev->mt76))
+		cancel_delayed_work_sync(&dev->perf.work);
 	cancel_delayed_work_sync(&pm->ps_work);
 	cancel_delayed_work_sync(&dev->mlo_pm_work);
 	cancel_work_sync(&pm->wake_work);
@@ -878,6 +888,10 @@ static int _mt7925_pci_resume(struct device *device, bool restore)
 		mt7925_mcu_set_deep_sleep(dev, false);
 
 	mt7925_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env);
+
+	/* re-arm the periodic performance indication if any bss is connected */
+	if (is_mt7928(&dev->mt76))
+		mt792x_perf_ind_resched(dev);
 failed:
 	pm->suspended = false;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
index 9e4e78910560..4244eb239ccf 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
@@ -22,6 +22,8 @@ int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
 	if (unlikely(tx_info->skb->len <= ETH_HLEN))
 		return -EINVAL;
 
+	mt792x_perf_account_tx(dev, info->control.vif, tx_info->skb->len);
+
 	if (!wcid)
 		wcid = &dev->mt76.global_wcid;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index c1fa3dbba5ca..f8e26fe69cc3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -39,6 +39,9 @@
 
 #define MT792x_WATCHDOG_TIME	(HZ / 4)
 
+#define MT792x_PERF_UPDATE_PERIOD	1000	/* ms */
+#define MT792x_PERF_IND_TIME		(HZ)	/* 1 second */
+
 #define MT792x_DRV_OWN_RETRY_COUNT	10
 #define MT792x_MCU_INIT_RETRY_COUNT	10
 #define MT792x_WFSYS_INIT_RETRY_COUNT	2
@@ -162,6 +165,11 @@ struct mt792x_bss_conf {
 	struct ewma_rssi rssi;
 	struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
 	unsigned int link_id;
+
+	atomic64_t perf_tx_bytes;
+	atomic64_t perf_rx_bytes;
+	u64 perf_last_tx_bytes;
+	u64 perf_last_rx_bytes;
 };
 
 struct mt792x_nan_conf {
@@ -342,8 +350,42 @@ struct mt792x_dev {
 	struct ieee80211_vif *nan_vif;
 	const struct ieee80211_iface_combination *iface_combinations;
 	int n_iface_combinations;
+
+	struct {
+		struct delayed_work work;
+		bool enabled;
+	} perf;
 };
 
+/* accumulate TX bytes for the interface transmitting @len bytes */
+static inline void
+mt792x_perf_account_tx(struct mt792x_dev *dev, struct ieee80211_vif *vif,
+		       u32 len)
+{
+	struct mt792x_vif *mvif;
+
+	if (!vif)
+		return;
+
+	mvif = (struct mt792x_vif *)vif->drv_priv;
+	atomic64_add(len, &mvif->bss_conf.perf_tx_bytes);
+}
+
+/* accumulate RX bytes for the interface owning @wcid */
+static inline void
+mt792x_perf_account_rx(struct mt792x_dev *dev, struct mt76_wcid *wcid, u32 len)
+{
+	struct mt792x_link_sta *mlink;
+	struct mt792x_sta *msta;
+
+	if (!wcid || !wcid->sta)
+		return;
+
+	mlink = container_of(wcid, struct mt792x_link_sta, wcid);
+	msta = container_of(mlink, struct mt792x_sta, deflink);
+	atomic64_add(len, &msta->vif->bss_conf.perf_rx_bytes);
+}
+
 static inline struct mt792x_bss_conf *
 mt792x_vif_to_link(struct mt792x_vif *mvif, u8 link_id)
 {
@@ -533,6 +575,13 @@ void mt792x_mac_link_bss_remove(struct mt792x_dev *dev,
 void mt792x_config_mac_addr_list(struct mt792x_dev *dev);
 int mt792x_mcu_chip_config(struct mt792x_dev *dev, const char *cmd);
 int mt792x_mcu_set_dyn_pcie_gen(struct mt792x_dev *dev);
+int mt792x_mcu_set_perf_ind(struct mt792x_dev *dev, u32 valid_period,
+			    const u64 *tx_bytes, const u64 *rx_bytes);
+void mt792x_perf_ind_update(struct mt792x_dev *dev, struct ieee80211_vif *vif,
+			    unsigned long delay);
+void mt792x_perf_ind_init(struct mt792x_dev *dev);
+void mt792x_perf_ind_resched(struct mt792x_dev *dev);
+int mt792x_perf_ind_trigger(struct mt792x_dev *dev, u64 val);
 
 static inline char *mt792x_ram_name(struct mt792x_dev *dev)
 {
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index e77224e2ca50..e7a5d2f9b742 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -329,6 +329,176 @@ int mt792x_get_stats(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL_GPL(mt792x_get_stats);
 
+int mt792x_mcu_set_perf_ind(struct mt792x_dev *dev, u32 valid_period,
+			    const u64 *tx_bytes, const u64 *rx_bytes)
+{
+	struct mt76_connac_perf_ind req = {};
+
+	mt76_connac_perf_ind_v1_fill(&req, valid_period, tx_bytes, rx_bytes);
+
+	return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(PERF_IND),
+				 &req, sizeof(req), false);
+}
+EXPORT_SYMBOL_GPL(mt792x_mcu_set_perf_ind);
+
+static int mt792x_mcu_perf_ind(struct mt792x_dev *dev, u32 valid_period,
+			       const u64 *tx_bytes, const u64 *rx_bytes)
+{
+	if (is_connac3(&dev->mt76))
+		return mt76_connac_mcu_uni_set_perf_ind(&dev->mt76, valid_period,
+							tx_bytes, rx_bytes);
+
+	return mt792x_mcu_set_perf_ind(dev, valid_period, tx_bytes, rx_bytes);
+}
+
+static bool mt792x_perf_vif_active(struct ieee80211_vif *vif)
+{
+	return vif->cfg.assoc;
+}
+
+struct mt792x_perf_iter_data {
+	u64 tx_delta[MT792x_MAX_INTERFACES];
+	u64 rx_delta[MT792x_MAX_INTERFACES];
+	bool connected;
+};
+
+static void mt792x_perf_collect_iter(void *priv, u8 *mac,
+				     struct ieee80211_vif *vif)
+{
+	struct mt792x_perf_iter_data *data = priv;
+	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+	struct mt792x_bss_conf *mconf = &mvif->bss_conf;
+	u8 idx = mconf->mt76.idx;
+	u64 tx, rx, last_tx, last_rx;
+
+	if (!mt792x_perf_vif_active(vif) || idx >= MT792x_MAX_INTERFACES)
+		return;
+
+	data->connected = true;
+
+	tx = atomic64_read(&mconf->perf_tx_bytes);
+	if (!tx) {
+		data->tx_delta[idx] = 0;
+	} else {
+		last_tx = mconf->perf_last_tx_bytes;
+		data->tx_delta[idx] = tx <= last_tx ? 0 : tx - last_tx;
+		mconf->perf_last_tx_bytes = tx;
+	}
+
+	rx = atomic64_read(&mconf->perf_rx_bytes);
+	if (!rx) {
+		data->rx_delta[idx] = 0;
+	} else {
+		last_rx = mconf->perf_last_rx_bytes;
+		data->rx_delta[idx] = rx <= last_rx ? 0 : rx - last_rx;
+		mconf->perf_last_rx_bytes = rx;
+	}
+}
+
+static void mt792x_perf_active_iter(void *priv, u8 *mac,
+				    struct ieee80211_vif *vif)
+{
+	bool *active = priv;
+
+	if (mt792x_perf_vif_active(vif))
+		*active = true;
+}
+
+static bool mt792x_perf_any_connected(struct mt792x_dev *dev)
+{
+	bool active = false;
+
+	ieee80211_iterate_active_interfaces(dev->mphy.hw,
+					    IEEE80211_IFACE_ITER_NORMAL,
+					    mt792x_perf_active_iter,
+					    &active);
+	return active;
+}
+
+void mt792x_perf_ind_update(struct mt792x_dev *dev, struct ieee80211_vif *vif,
+			    unsigned long delay)
+{
+	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+	/* Reset snapshots so a future re-association starts clean */
+	if (!mt792x_perf_vif_active(vif)) {
+		atomic64_set(&mvif->bss_conf.perf_tx_bytes, 0);
+		atomic64_set(&mvif->bss_conf.perf_rx_bytes, 0);
+		mvif->bss_conf.perf_last_tx_bytes = 0;
+		mvif->bss_conf.perf_last_rx_bytes = 0;
+	}
+
+	if (!mt76_is_mmio(&dev->mt76))
+		return;
+
+	if (mt792x_perf_any_connected(dev))
+		queue_delayed_work(dev->mt76.wq, &dev->perf.work, delay);
+	else
+		cancel_delayed_work(&dev->perf.work);
+}
+EXPORT_SYMBOL_GPL(mt792x_perf_ind_update);
+
+static void mt792x_perf_work(struct work_struct *work)
+{
+	struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
+					      perf.work.work);
+	struct mt792x_perf_iter_data data = {};
+
+	/* not enabled */
+	if (!dev->perf.enabled)
+		return;
+
+	/* skip while asleep */
+	if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm))
+		goto reschedule;
+
+	ieee80211_iterate_active_interfaces(dev->mphy.hw,
+					    IEEE80211_IFACE_ITER_NORMAL,
+					    mt792x_perf_collect_iter, &data);
+
+	if (data.connected)
+		mt792x_mcu_perf_ind(dev, MT792x_PERF_UPDATE_PERIOD,
+				    data.tx_delta, data.rx_delta);
+
+	mt76_connac_pm_unref(&dev->mphy, &dev->pm);
+
+reschedule:
+	if (mt792x_perf_any_connected(dev))
+		queue_delayed_work(dev->mt76.wq, &dev->perf.work,
+				   MT792x_PERF_IND_TIME);
+}
+
+void mt792x_perf_ind_init(struct mt792x_dev *dev)
+{
+	INIT_DELAYED_WORK(&dev->perf.work, mt792x_perf_work);
+}
+EXPORT_SYMBOL_GPL(mt792x_perf_ind_init);
+
+void mt792x_perf_ind_resched(struct mt792x_dev *dev)
+{
+	if (mt792x_perf_any_connected(dev))
+		queue_delayed_work(dev->mt76.wq, &dev->perf.work,
+				   MT792x_PERF_IND_TIME);
+}
+EXPORT_SYMBOL_GPL(mt792x_perf_ind_resched);
+
+int mt792x_perf_ind_trigger(struct mt792x_dev *dev, u64 val)
+{
+	u64 bytes[MT792x_MAX_INTERFACES];
+	int i;
+
+	for (i = 0; i < MT792x_MAX_INTERFACES; i++)
+		bytes[i] = val;
+
+	/* wake the chip and serialize against other MCU commands */
+	mt792x_mutex_acquire(dev);
+	mt792x_mcu_perf_ind(dev, MT792x_PERF_UPDATE_PERIOD, bytes, bytes);
+	mt792x_mutex_release(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mt792x_perf_ind_trigger);
+
 u64 mt792x_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
-- 
2.45.2



      parent reply	other threads:[~2026-08-20 11:12 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-20 11:11 [PATCH v1] [PATCH 01/05] wifi: mt76: refactor chip_config to accept generic commands Anthony Liu
2026-08-20 11:11 ` [PATCH v1] [PATCH 02/05] wifi: mt76: refactor chip_cfg commands to be a generic API Anthony Liu
2026-08-20 11:12 ` [PATCH v1] [PATCH 03/05] wifi: mt76: add ACPI MTFG option for bus configuration Anthony Liu
2026-08-20 11:12 ` [PATCH v1] [PATCH 04/05] wifi: mt76: add PCIe speed tuning for mt7922/mt7928 Anthony Liu
2026-08-20 11:12 ` Anthony Liu [this message]

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20260820111202.1935373-5-anthony.liu@mediatek.com \
    --to=anthony.liu@mediatek.com \
    --cc=Leon.Yen@mediatek.com \
    --cc=Quan.Zhou@mediatek.com \
    --cc=Ryder.lee@mediatek.com \
    --cc=Sean.Wang@mediatek.com \
    --cc=emery.hsin@mediatek.com \
    --cc=jb.tsai@mediatek.com \
    --cc=kun.wu@mediatek.com \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=litien.chang@mediatek.com \
    --cc=lorenzo@kernel.org \
    --cc=nbd@nbd.name \
    /path/to/YOUR_REPLY

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

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