diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index 4b56358d..a5f6d25d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -960,6 +960,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
 	if (ret)
 		return ret;
 
+	dev->fw_ver = mt76_rr(dev, MT_SWDEF(0x7c));
+
 	return mt7915_init_debugfs(&dev->phy);
 }
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index 1041d88f..b2e20251 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -1433,7 +1433,8 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
 		mt7915_txwi_free(dev, txwi, sta, &free_list);
 	}
 
-	mt7915_mac_sta_poll(dev);
+	if (!mt7915_firmware_offload(dev))
+		mt7915_mac_sta_poll(dev);
 
 	if (wake)
 		mt76_set_tx_blocked(&dev->mt76, false);
@@ -2195,27 +2196,41 @@ void mt7915_mac_sta_rc_work(struct work_struct *work)
 void mt7915_mac_work(struct work_struct *work)
 {
 	struct mt7915_phy *phy;
+	struct mt7915_dev *dev;
 	struct mt76_phy *mphy;
 
 	mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
 					       mac_work.work);
 	phy = mphy->priv;
+	dev = phy->dev;
 
-	mutex_lock(&mphy->dev->mutex);
+	if (++phy->stats_work_count == 10) {
+		phy->stats_work_count = 0;
+		mutex_lock(&mphy->dev->mutex);
 
-	mt76_update_survey(mphy);
-	if (++mphy->mac_work_count == 5) {
-		mphy->mac_work_count = 0;
+		mt76_update_survey(mphy);
+		if (++mphy->mac_work_count == 5) {
+			mphy->mac_work_count = 0;
 
-		mt7915_mac_update_stats(phy);
-	}
+			mt7915_mac_update_stats(phy);
+		}
 
-	mutex_unlock(&mphy->dev->mutex);
+		mutex_unlock(&mphy->dev->mutex);
 
-	mt76_tx_status_check(mphy->dev, false);
+		mt76_tx_status_check(mphy->dev, false);
+	}
+
+	if (mt7915_firmware_offload(dev)) {
+		mutex_lock(&mphy->dev->mutex);
+		mt7915_mcu_get_all_sta_stats(phy->dev,
+					     MCU_EXT_EVENT_TXRX_AIR_TIME);
+		mt7915_mcu_get_all_sta_stats(phy->dev,
+					     MCU_EXT_EVENT_PHY_GI_MODE);
+		mutex_unlock(&mphy->dev->mutex);
+	}
 
 	ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
-				     MT7915_WATCHDOG_TIME);
+				     MT7915_WATCHDOG_TIME/10);
 }
 
 static void mt7915_dfs_stop_radar_detector(struct mt7915_phy *phy)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index bfc10117..141ef797 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -510,6 +510,126 @@ mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb)
 		   (int)(skb->len - sizeof(*rxd)), data);
 }
 
+static void
+mt7915_mcu_get_sta_airtime(struct mt7915_dev *dev, struct sk_buff *skb)
+{
+	struct mt7915_mcu_sta_stat *at = (struct mt7915_mcu_sta_stat *)skb->data;
+	int idx;
+
+	for (idx = 0; idx < le16_to_cpu(at->sta_num); idx++) {
+		static const u8 ac_to_tid[] = {
+			[IEEE80211_AC_BE] = 0,
+			[IEEE80211_AC_BK] = 1,
+			[IEEE80211_AC_VI] = 4,
+			[IEEE80211_AC_VO] = 6
+		};
+		struct ieee80211_sta *sta;
+		struct mt7915_sta *msta;
+		struct mt76_wcid *wcid;
+		int i;
+		bool clear = false;
+		u16 w_idx;
+		u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
+
+		w_idx = le16_to_cpu(at->airtime[idx].wlan_id);
+		wcid = rcu_dereference(dev->mt76.wcid[w_idx]);
+		sta = wcid_to_sta(wcid);
+		if (!sta)
+			continue;
+
+		msta = container_of(wcid, struct mt7915_sta, wcid);
+
+		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+			u32 tx_last = msta->airtime_ac[i];
+			u32 rx_last = msta->airtime_ac[i + 4];
+
+			msta->airtime_ac[i] =
+				le32_to_cpu(at->airtime[idx].tx_time[i]);
+			msta->airtime_ac[i + 4] =
+				le32_to_cpu(at->airtime[idx].rx_time[i]);
+
+			tx_time[i] = msta->airtime_ac[i] - tx_last;
+			rx_time[i] = msta->airtime_ac[i + 4] - rx_last;
+
+			if ((tx_last | rx_last) & BIT(30))
+				clear = true;
+		}
+
+		if (clear)
+			memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
+
+		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+			u8 q = mt7915_lmac_mapping(dev, i);
+			u32 tx_cur = tx_time[q];
+			u32 rx_cur = rx_time[q];
+			u8 tid = ac_to_tid[i];
+
+			if (!tx_cur && !rx_cur)
+				continue;
+
+			ieee80211_sta_register_airtime(sta, tid, tx_cur,
+				rx_cur);
+		}
+	}
+}
+
+static void
+mt7915_mcu_get_sta_gi(struct mt7915_dev *dev, struct sk_buff *skb)
+{
+	struct mt7915_mcu_sta_stat *mgi = (struct mt7915_mcu_sta_stat *)skb->data;
+	int idx;
+
+	for (idx = 0; idx < le16_to_cpu(mgi->sta_num); idx++) {
+		struct ieee80211_sta *sta;
+		struct mt7915_sta *msta;
+		struct mt76_wcid *wcid;
+		struct rate_info *rate;
+		u8 val;
+		u16 w_idx;
+
+		w_idx = le16_to_cpu(mgi->gi[idx].wlan_id);
+		wcid = rcu_dereference(dev->mt76.wcid[w_idx]);
+		sta = wcid_to_sta(wcid);
+
+		if (!sta)
+			continue;
+
+		msta = container_of(wcid, struct mt7915_sta, wcid);
+		rate = &msta->wcid.rate;
+		val = mgi->gi[idx].gimode;
+
+		if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
+			rate->he_gi = val;
+		else if (rate->flags &
+			(RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) {
+			if (val)
+				rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+			else
+				rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI;
+		}
+	}
+}
+
+static void
+mt7915_mcu_rx_sta_stats(struct mt7915_dev *dev, struct sk_buff *skb)
+{
+	u8 type;
+
+	skb_pull(skb, sizeof(struct mt7915_mcu_rxd));
+	type = *(skb->data);
+
+	switch (type) {
+	case MCU_EXT_EVENT_TXRX_AIR_TIME:
+		mt7915_mcu_get_sta_airtime(dev, skb);
+		break;
+	case MCU_EXT_EVENT_PHY_GI_MODE:
+		mt7915_mcu_get_sta_gi(dev, skb);
+		break;
+	default:
+		break;
+	}
+}
+
 static void
 mt7915_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
 {
@@ -542,6 +662,9 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
 				IEEE80211_IFACE_ITER_RESUME_ALL,
 				mt7915_mcu_cca_finish, dev);
 		break;
+	case MCU_EXT_EVENT_GET_ALL_STA_STATS:
+		mt7915_mcu_rx_sta_stats(dev, skb);
+		break;
 	default:
 		break;
 	}
@@ -571,6 +694,7 @@ void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb)
 	    rxd->ext_eid == MCU_EXT_EVENT_ASSERT_DUMP ||
 	    rxd->ext_eid == MCU_EXT_EVENT_PS_SYNC ||
 	    rxd->ext_eid == MCU_EXT_EVENT_BCC_NOTIFY ||
+	    rxd->ext_eid == MCU_EXT_EVENT_GET_ALL_STA_STATS ||
 	    !rxd->seq)
 		mt7915_mcu_rx_unsolicited_event(dev, skb);
 	else
@@ -2993,6 +3117,20 @@ int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level)
 				 sizeof(data), false);
 }
 
+int mt7915_mcu_get_all_sta_stats(struct mt7915_dev *dev, u8 event)
+{
+
+	struct {
+		u8 event_type;
+		u8 res[3];
+	} req = {
+		.event_type = event
+	};
+
+	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(GET_ALL_STA_STATS),
+		&req, sizeof(req), false);
+}
+
 static int mt7915_mcu_set_mwds(struct mt7915_dev *dev, bool enabled)
 {
 	struct {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index 4636b7dc..a1149224 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -44,6 +44,7 @@ enum {
 	MCU_EXT_EVENT_RDD_REPORT = 0x3a,
 	MCU_EXT_EVENT_CSA_NOTIFY = 0x4f,
 	MCU_EXT_EVENT_BCC_NOTIFY = 0x75,
+	MCU_EXT_EVENT_GET_ALL_STA_STATS = 0xb5,
 };
 
 enum {
@@ -53,6 +54,14 @@ enum {
 	MCU_ATE_CLEAN_TXQUEUE = 0x1c,
 };
 
+enum {
+	MCU_EXT_EVENT_ALL_TX_RX_RATE = 0x1,
+	MCU_EXT_EVENT_TX_STAT_PER_WCID = 0x2,
+	MCU_EXT_EVENT_RX_STAT = 0x03,
+	MCU_EXT_EVENT_TXRX_AIR_TIME = 0x05,
+	MCU_EXT_EVENT_PHY_GI_MODE = 0x07,
+};
+
 struct mt7915_mcu_rxd {
 	__le32 rxd[6];
 
@@ -206,6 +215,24 @@ struct mt7915_mcu_tx {
 	struct edca edca[IEEE80211_NUM_ACS];
 } __packed;
 
+struct mt7915_mcu_sta_stat {
+	u8 event_id;
+	u8 more_event;
+	__le16 sta_num;
+	union {
+		struct {
+			__le16 wlan_id;
+			__le32 tx_time[4];
+			__le32 rx_time[4];
+		} airtime[0];
+		struct {
+			__le16 wlan_id;
+			u8 gimode;
+			u8 rsv;
+		} gi[0];
+	}
+};
+
 #define WMM_AIFS_SET		BIT(0)
 #define WMM_CW_MIN_SET		BIT(1)
 #define WMM_CW_MAX_SET		BIT(2)
@@ -292,6 +319,7 @@ enum {
 	MCU_EXT_CMD_GROUP_PRE_CAL_INFO = 0xab,
 	MCU_EXT_CMD_DPD_PRE_CAL_INFO = 0xac,
 	MCU_EXT_CMD_PHY_STAT_INFO = 0xad,
+	MCU_EXT_CMD_GET_ALL_STA_STATS = 0xb5,
 };
 
 enum {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index c6c846d1..dea83e82 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -47,6 +47,8 @@
 #define MT7915_MAX_TWT_AGRT		16
 #define MT7915_MAX_STA_TWT_AGRT		8
 
+#define MT7915_FIRMWARE_V1		0
+
 struct mt7915_vif;
 struct mt7915_sta;
 struct mt7915_dfs_pulse;
@@ -228,6 +230,8 @@ struct mt7915_phy {
 	struct mib_stats mib;
 	struct mt76_channel_state state_ts;
 
+	u8 stats_work_count;
+
 #ifdef CONFIG_NL80211_TESTMODE
 	struct {
 		u32 *reg_backup;
@@ -276,6 +280,7 @@ struct mt7915_dev {
 	bool ibf;
 	u8 fw_debug_wm;
 	u8 fw_debug_wa;
+	u8 fw_ver;
 
 	void *cal;
 
@@ -457,6 +462,7 @@ int mt7915_mcu_rdd_cmd(struct mt7915_dev *dev, enum mt7915_rdd_cmd cmd,
 int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
 int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl);
 int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level);
+int mt7915_mcu_get_all_sta_stats(struct mt7915_dev *dev, u8 event);
 void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb);
 void mt7915_mcu_exit(struct mt7915_dev *dev);
 
@@ -486,6 +492,11 @@ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask)
 		mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0);
 }
 
+static inline bool mt7915_firmware_offload(struct mt7915_dev *dev)
+{
+	return dev->fw_ver == MT7915_FIRMWARE_V1;
+}
+
 u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw);
 bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask);
 void mt7915_mac_reset_counters(struct mt7915_phy *phy);
