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
prev 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.