for (i = 0; i < ARRAY_SIZE(queue_map); i++)
                mt76_queue_tx_cleanup(dev, queue_map[i], false);
 
+       mt7615_mac_sta_poll(dev);
+
        tasklet_schedule(&dev->mt76.tx_tasklet);
 
        return 0;
 
 static void mt7615_mac_init(struct mt7615_dev *dev)
 {
        u32 val, mask, set;
+       int i;
 
        /* enable band 0/1 clk */
        mt76_set(dev, MT_CFG_CCR,
        mt76_rmw(dev, MT_DMA_BN0RCFR0, mask, set);
        mt76_rmw(dev, MT_DMA_BN1RCFR0, mask, set);
 
+       for (i = 0; i < MT7615_WTBL_SIZE; i++)
+               mt7615_mac_wtbl_update(dev, i,
+                                      MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
        mt76_set(dev, MT_WF_RMAC_MIB_TIME0, MT_WF_RMAC_MIB_RXTIME_EN);
+       mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0, MT_WF_RMAC_MIB_RXTIME_EN);
 }
 
 static int mt7615_init_hardware(struct mt7615_dev *dev)
        struct wiphy *wiphy = hw->wiphy;
        int ret;
 
+       INIT_DELAYED_WORK(&dev->mt76.mac_work, mt7615_mac_work);
+       INIT_LIST_HEAD(&dev->sta_poll_list);
+       spin_lock_init(&dev->sta_poll_lock);
+
        ret = mt7615_init_hardware(dev);
        if (ret)
                return ret;
 
-       INIT_DELAYED_WORK(&dev->mt76.mac_work, mt7615_mac_work);
-
        hw->queues = 4;
        hw->max_rates = 3;
        hw->max_report_rates = 7;
 
        mt76_rr(dev, MT_MIB_SDR36(0));
        mt76_rr(dev, MT_MIB_SDR37(0));
        mt76_set(dev, MT_WF_RMAC_MIB_TIME0, MT_WF_RMAC_MIB_RXTIME_CLR);
+       mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0, MT_WF_RMAC_MIB_RXTIME_CLR);
 }
 
 int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb)
        idx = FIELD_GET(MT_RXD2_NORMAL_WLAN_IDX, rxd2);
        status->wcid = mt7615_rx_get_wcid(dev, idx, unicast);
 
+       if (status->wcid) {
+               struct mt7615_sta *msta;
+
+               msta = container_of(status->wcid, struct mt7615_sta, wcid);
+               spin_lock_bh(&dev->sta_poll_lock);
+               if (list_empty(&msta->poll_list))
+                       list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+               spin_unlock_bh(&dev->sta_poll_lock);
+       }
+
        /* TODO: properly support DBDC */
        status->freq = dev->mt76.chandef.chan->center_freq;
        status->band = dev->mt76.chandef.chan->band;
                         0, 5000);
 }
 
+void mt7615_mac_sta_poll(struct mt7615_dev *dev)
+{
+       static const u8 ac_to_tid[4] = {
+               [IEEE80211_AC_BE] = 0,
+               [IEEE80211_AC_BK] = 1,
+               [IEEE80211_AC_VI] = 4,
+               [IEEE80211_AC_VO] = 6
+       };
+       static const u8 hw_queue_map[] = {
+               [IEEE80211_AC_BK] = 0,
+               [IEEE80211_AC_BE] = 1,
+               [IEEE80211_AC_VI] = 2,
+               [IEEE80211_AC_VO] = 3,
+       };
+       struct ieee80211_sta *sta;
+       struct mt7615_sta *msta;
+       u32 addr, tx_time[4], rx_time[4];
+       int i;
+
+       rcu_read_lock();
+
+       while (true) {
+               bool clear = false;
+
+               spin_lock_bh(&dev->sta_poll_lock);
+               if (list_empty(&dev->sta_poll_list)) {
+                       spin_unlock_bh(&dev->sta_poll_lock);
+                       break;
+               }
+               msta = list_first_entry(&dev->sta_poll_list,
+                                       struct mt7615_sta, poll_list);
+               list_del_init(&msta->poll_list);
+               spin_unlock_bh(&dev->sta_poll_lock);
+
+               addr = mt7615_mac_wtbl_addr(msta->wcid.idx) + 19 * 4;
+
+               for (i = 0; i < 4; i++, addr += 8) {
+                       u32 tx_last = msta->airtime_ac[i];
+                       u32 rx_last = msta->airtime_ac[i + 4];
+
+                       msta->airtime_ac[i] = mt76_rr(dev, addr);
+                       msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
+                       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) {
+                       mt7615_mac_wtbl_update(dev, msta->wcid.idx,
+                                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+                       memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
+               }
+
+               if (!msta->wcid.sta)
+                       continue;
+
+               sta = container_of((void *)msta, struct ieee80211_sta,
+                                  drv_priv);
+               for (i = 0; i < 4; i++) {
+                       u32 tx_cur = tx_time[i];
+                       u32 rx_cur = rx_time[hw_queue_map[i]];
+                       u8 tid = ac_to_tid[i];
+
+                       if (!tx_cur && !rx_cur)
+                               continue;
+
+                       ieee80211_sta_register_airtime(sta, tid, tx_cur,
+                                                      rx_cur);
+               }
+       }
+
+       rcu_read_unlock();
+}
+
 void mt7615_mac_set_rates(struct mt7615_dev *dev, struct mt7615_sta *sta,
                          struct ieee80211_tx_rate *probe_rate,
                          struct ieee80211_tx_rate *rates)
        msta = container_of(wcid, struct mt7615_sta, wcid);
        sta = wcid_to_sta(wcid);
 
+       spin_lock_bh(&dev->sta_poll_lock);
+       if (list_empty(&msta->poll_list))
+               list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+       spin_unlock_bh(&dev->sta_poll_lock);
+
        if (mt7615_mac_add_txs_skb(dev, msta, pid, txs_data))
                goto out;
 
 
        dev->vif_mask |= BIT(mvif->idx);
        dev->omac_mask |= BIT(mvif->omac_idx);
        idx = MT7615_WTBL_RESERVED - mvif->idx;
+
+       INIT_LIST_HEAD(&mvif->sta.poll_list);
        mvif->sta.wcid.idx = idx;
        mvif->sta.wcid.hw_key_idx = -1;
+       mt7615_mac_wtbl_update(dev, idx,
+                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
 
        rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
        mtxq = (struct mt76_txq *)vif->txq->drv_priv;
                                    struct ieee80211_vif *vif)
 {
        struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
+       struct mt7615_sta *msta = &mvif->sta;
        struct mt7615_dev *dev = hw->priv;
-       int idx = mvif->sta.wcid.idx;
+       int idx = msta->wcid.idx;
 
        /* TODO: disable beacon for the bss */
 
        dev->vif_mask &= ~BIT(mvif->idx);
        dev->omac_mask &= ~BIT(mvif->omac_idx);
        mutex_unlock(&dev->mt76.mutex);
+
+       spin_lock_bh(&dev->sta_poll_lock);
+       if (!list_empty(&msta->poll_list))
+               list_del_init(&msta->poll_list);
+       spin_unlock_bh(&dev->sta_poll_lock);
 }
 
 static int mt7615_set_channel(struct mt7615_dev *dev)
        if (idx < 0)
                return -ENOSPC;
 
+       INIT_LIST_HEAD(&msta->poll_list);
        msta->vif = mvif;
        msta->wcid.sta = 1;
        msta->wcid.idx = idx;
+       mt7615_mac_wtbl_update(dev, idx,
+                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
 
        mt7615_mcu_add_wtbl(dev, vif, sta);
        mt7615_mcu_set_sta_rec(dev, vif, sta, 1);
                       struct ieee80211_sta *sta)
 {
        struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
+       struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv;
 
        mt7615_mcu_set_sta_rec(dev, vif, sta, 0);
        mt7615_mcu_del_wtbl(dev, sta);
+
+       mt7615_mac_wtbl_update(dev, msta->wcid.idx,
+                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+       spin_lock_bh(&dev->sta_poll_lock);
+       if (!list_empty(&msta->poll_list))
+               list_del_init(&msta->poll_list);
+       spin_unlock_bh(&dev->sta_poll_lock);
 }
 
 static void mt7615_sta_rate_tbl_update(struct ieee80211_hw *hw,
 
 
        struct mt7615_vif *vif;
 
+       struct list_head poll_list;
+       u32 airtime_ac[8];
+
        struct ieee80211_tx_rate rates[4];
 
        struct mt7615_rate_set rateset[2];
 
        __le32 rx_ampdu_ts;
 
+       struct list_head sta_poll_list;
+       spinlock_t sta_poll_lock;
+
        struct {
                u8 n_pulses;
                u32 period;
 void mt7615_mac_reset_counters(struct mt7615_dev *dev);
 void mt7615_mac_cca_stats_reset(struct mt7615_dev *dev);
 void mt7615_mac_set_scs(struct mt7615_dev *dev, bool enable);
+void mt7615_mac_sta_poll(struct mt7615_dev *dev);
 int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi,
                          struct sk_buff *skb, struct mt76_wcid *wcid,
                          struct ieee80211_sta *sta, int pid,
 
 #define MT_WF_RMAC_MIB_RXTIME_CLR      BIT(31)
 #define MT_WF_RMAC_MIB_RXTIME_EN       BIT(30)
 
+#define MT_WF_RMAC_MIB_AIRTIME0                MT_WF_RMAC(0x0380)
+
 #define MT_WF_RMAC_MIB_TIME5           MT_WF_RMAC(0x03d8)
 #define MT_MIB_OBSSTIME_MASK           GENMASK(23, 0)
 
 #define MT_WTBL_UPDATE                 MT_WTBL_OFF(0x030)
 #define MT_WTBL_UPDATE_WLAN_IDX                GENMASK(7, 0)
 #define MT_WTBL_UPDATE_RXINFO_UPDATE   BIT(11)
+#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12)
 #define MT_WTBL_UPDATE_RATE_UPDATE     BIT(13)
 #define MT_WTBL_UPDATE_TX_COUNT_CLEAR  BIT(14)
 #define MT_WTBL_UPDATE_BUSY            BIT(31)