Merge tag 'mt76-for-kvalo-2023-09-30' of https://github.com/nbd168/wireless
authorKalle Valo <kvalo@kernel.org>
Mon, 9 Oct 2023 06:35:41 +0000 (09:35 +0300)
committerKalle Valo <kvalo@kernel.org>
Mon, 9 Oct 2023 06:35:41 +0000 (09:35 +0300)
mt76 patches for 6.7

* mt7603/mt7628 stability improvements
* fixes
* new driver for mt7925

72 files changed:
drivers/net/wireless/mediatek/mt76/Kconfig
drivers/net/wireless/mediatek/mt76/Makefile
drivers/net/wireless/mediatek/mt76/debugfs.c
drivers/net/wireless/mediatek/mt76/dma.c
drivers/net/wireless/mediatek/mt76/eeprom.c
drivers/net/wireless/mediatek/mt76/mac80211.c
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
drivers/net/wireless/mediatek/mt76/mt7603/core.c
drivers/net/wireless/mediatek/mt76/mt7603/init.c
drivers/net/wireless/mediatek/mt76/mt7603/mac.c
drivers/net/wireless/mediatek/mt76/mt7603/main.c
drivers/net/wireless/mediatek/mt76/mt7603/regs.h
drivers/net/wireless/mediatek/mt76/mt7615/init.c
drivers/net/wireless/mediatek/mt76/mt7615/main.c
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
drivers/net/wireless/mediatek/mt76/mt76_connac.h
drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
drivers/net/wireless/mediatek/mt76/mt76x02_util.c
drivers/net/wireless/mediatek/mt76/mt7915/init.c
drivers/net/wireless/mediatek/mt76/mt7915/mac.c
drivers/net/wireless/mediatek/mt76/mt7915/main.c
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
drivers/net/wireless/mediatek/mt76/mt7915/regs.h
drivers/net/wireless/mediatek/mt76/mt7915/soc.c
drivers/net/wireless/mediatek/mt76/mt7921/init.c
drivers/net/wireless/mediatek/mt76/mt7921/mac.c
drivers/net/wireless/mediatek/mt76/mt7921/main.c
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
drivers/net/wireless/mediatek/mt76/mt7921/mcu.h
drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
drivers/net/wireless/mediatek/mt76/mt7921/pci.c
drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c
drivers/net/wireless/mediatek/mt76/mt7921/usb.c
drivers/net/wireless/mediatek/mt76/mt7925/Kconfig [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/Makefile [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/init.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/mac.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/mac.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/main.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/mcu.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/pci.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/regs.h [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt7925/usb.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt792x.h
drivers/net/wireless/mediatek/mt76/mt792x_core.c
drivers/net/wireless/mediatek/mt76/mt792x_dma.c
drivers/net/wireless/mediatek/mt76/mt792x_usb.c
drivers/net/wireless/mediatek/mt76/mt7996/init.c
drivers/net/wireless/mediatek/mt76/mt7996/mac.c
drivers/net/wireless/mediatek/mt76/mt7996/main.c
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
drivers/net/wireless/mediatek/mt76/mt7996/regs.h
drivers/net/wireless/mediatek/mt76/tx.c
include/linux/pci_ids.h

index 7eb1b0b..a86f800 100644 (file)
@@ -44,3 +44,4 @@ source "drivers/net/wireless/mediatek/mt76/mt7615/Kconfig"
 source "drivers/net/wireless/mediatek/mt76/mt7915/Kconfig"
 source "drivers/net/wireless/mediatek/mt76/mt7921/Kconfig"
 source "drivers/net/wireless/mediatek/mt76/mt7996/Kconfig"
+source "drivers/net/wireless/mediatek/mt76/mt7925/Kconfig"
index 85c4799..d6575fe 100644 (file)
@@ -44,3 +44,4 @@ obj-$(CONFIG_MT7615_COMMON) += mt7615/
 obj-$(CONFIG_MT7915E) += mt7915/
 obj-$(CONFIG_MT7921_COMMON) += mt7921/
 obj-$(CONFIG_MT7996E) += mt7996/
+obj-$(CONFIG_MT7925_COMMON) += mt7925/
index 57fbcc8..ae83be5 100644 (file)
@@ -109,8 +109,6 @@ mt76_register_debugfs_fops(struct mt76_phy *phy,
        struct dentry *dir;
 
        dir = debugfs_create_dir("mt76", phy->hw->wiphy->debugfsdir);
-       if (!dir)
-               return NULL;
 
        debugfs_create_u8("led_pin", 0600, dir, &phy->leds.pin);
        debugfs_create_u32("regidx", 0600, dir, &dev->debugfs_reg);
index dc8f4e1..511fe7e 100644 (file)
@@ -53,6 +53,11 @@ mt76_alloc_txwi(struct mt76_dev *dev)
 
        addr = dma_map_single(dev->dma_dev, txwi, dev->drv->txwi_size,
                              DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
+               kfree(txwi);
+               return NULL;
+       }
+
        t = (struct mt76_txwi_cache *)(txwi + dev->drv->txwi_size);
        t->dma_addr = addr;
 
@@ -330,9 +335,6 @@ mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx,
        if (e->txwi == DMA_DUMMY_DATA)
                e->txwi = NULL;
 
-       if (e->skb == DMA_DUMMY_DATA)
-               e->skb = NULL;
-
        *prev_e = *e;
        memset(e, 0, sizeof(*e));
 }
@@ -737,16 +739,18 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
        if (!q->ndesc)
                return;
 
-       spin_lock_bh(&q->lock);
-
        do {
+               spin_lock_bh(&q->lock);
                buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more, NULL);
+               spin_unlock_bh(&q->lock);
+
                if (!buf)
                        break;
 
                mt76_put_page_pool_buf(buf, false);
        } while (1);
 
+       spin_lock_bh(&q->lock);
        if (q->rx_head) {
                dev_kfree_skb(q->rx_head);
                q->rx_head = NULL;
index 3656493..7725dd6 100644 (file)
@@ -188,7 +188,7 @@ static bool mt76_string_prop_find(struct property *prop, const char *str)
        return false;
 }
 
-static struct device_node *
+struct device_node *
 mt76_find_power_limits_node(struct mt76_dev *dev)
 {
        struct device_node *np = dev->dev->of_node;
@@ -227,6 +227,7 @@ mt76_find_power_limits_node(struct mt76_dev *dev)
        of_node_put(np);
        return fallback;
 }
+EXPORT_SYMBOL_GPL(mt76_find_power_limits_node);
 
 static const __be32 *
 mt76_get_of_array(struct device_node *np, char *name, size_t *len, int min)
@@ -241,7 +242,7 @@ mt76_get_of_array(struct device_node *np, char *name, size_t *len, int min)
        return prop->value;
 }
 
-static struct device_node *
+struct device_node *
 mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan)
 {
        struct device_node *cur;
@@ -265,6 +266,8 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan)
 
        return NULL;
 }
+EXPORT_SYMBOL_GPL(mt76_find_channel_node);
+
 
 static s8
 mt76_get_txs_delta(struct device_node *np, u8 nss)
index d158320..cb76053 100644 (file)
@@ -415,6 +415,9 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
        struct mt76_dev *dev = phy->dev;
        struct wiphy *wiphy = hw->wiphy;
 
+       INIT_LIST_HEAD(&phy->tx_list);
+       spin_lock_init(&phy->tx_lock);
+
        SET_IEEE80211_DEV(hw, dev->dev);
        SET_IEEE80211_PERM_ADDR(hw, phy->macaddr);
 
@@ -452,7 +455,8 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
        ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
        ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
 
-       if (!(dev->drv->drv_flags & MT_DRV_AMSDU_OFFLOAD)) {
+       if (!(dev->drv->drv_flags & MT_DRV_AMSDU_OFFLOAD) &&
+           hw->max_tx_fragments > 1) {
                ieee80211_hw_set(hw, TX_AMSDU);
                ieee80211_hw_set(hw, TX_FRAG_LIST);
        }
@@ -688,6 +692,7 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
        int ret;
 
        dev_set_drvdata(dev->dev, dev);
+       mt76_wcid_init(&dev->global_wcid);
        ret = mt76_phy_init(phy, hw);
        if (ret)
                return ret;
@@ -743,6 +748,7 @@ void mt76_unregister_device(struct mt76_dev *dev)
        if (IS_ENABLED(CONFIG_MT76_LEDS))
                mt76_led_cleanup(&dev->phy);
        mt76_tx_status_check(dev, true);
+       mt76_wcid_cleanup(dev, &dev->global_wcid);
        ieee80211_unregister_hw(hw);
 }
 EXPORT_SYMBOL_GPL(mt76_unregister_device);
@@ -1411,7 +1417,7 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif,
        wcid->phy_idx = phy->band_idx;
        rcu_assign_pointer(dev->wcid[wcid->idx], wcid);
 
-       mt76_packet_id_init(wcid);
+       mt76_wcid_init(wcid);
 out:
        mutex_unlock(&dev->mutex);
 
@@ -1430,7 +1436,7 @@ void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
        if (dev->drv->sta_remove)
                dev->drv->sta_remove(dev, vif, sta);
 
-       mt76_packet_id_flush(dev, wcid);
+       mt76_wcid_cleanup(dev, wcid);
 
        mt76_wcid_mask_clear(dev->wcid_mask, idx);
        mt76_wcid_mask_clear(dev->wcid_phy_mask, idx);
@@ -1486,6 +1492,47 @@ void mt76_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 }
 EXPORT_SYMBOL_GPL(mt76_sta_pre_rcu_remove);
 
+void mt76_wcid_init(struct mt76_wcid *wcid)
+{
+       INIT_LIST_HEAD(&wcid->tx_list);
+       skb_queue_head_init(&wcid->tx_pending);
+
+       INIT_LIST_HEAD(&wcid->list);
+       idr_init(&wcid->pktid);
+}
+EXPORT_SYMBOL_GPL(mt76_wcid_init);
+
+void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid)
+{
+       struct mt76_phy *phy = dev->phys[wcid->phy_idx];
+       struct ieee80211_hw *hw;
+       struct sk_buff_head list;
+       struct sk_buff *skb;
+
+       mt76_tx_status_lock(dev, &list);
+       mt76_tx_status_skb_get(dev, wcid, -1, &list);
+       mt76_tx_status_unlock(dev, &list);
+
+       idr_destroy(&wcid->pktid);
+
+       spin_lock_bh(&phy->tx_lock);
+
+       if (!list_empty(&wcid->tx_list))
+               list_del_init(&wcid->tx_list);
+
+       spin_lock(&wcid->tx_pending.lock);
+       skb_queue_splice_tail_init(&wcid->tx_pending, &list);
+       spin_unlock(&wcid->tx_pending.lock);
+
+       spin_unlock_bh(&phy->tx_lock);
+
+       while ((skb = __skb_dequeue(&list)) != NULL) {
+               hw = mt76_tx_status_get_hw(dev, skb);
+               ieee80211_free_txskb(hw, skb);
+       }
+}
+EXPORT_SYMBOL_GPL(mt76_wcid_cleanup);
+
 int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                     int *dbm)
 {
@@ -1697,11 +1744,16 @@ mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
 }
 EXPORT_SYMBOL_GPL(mt76_init_queue);
 
-u16 mt76_calculate_default_rate(struct mt76_phy *phy, int rateidx)
+u16 mt76_calculate_default_rate(struct mt76_phy *phy,
+                               struct ieee80211_vif *vif, int rateidx)
 {
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct cfg80211_chan_def *chandef = mvif->ctx ?
+                                           &mvif->ctx->def :
+                                           &phy->chandef;
        int offset = 0;
 
-       if (phy->chandef.chan->band != NL80211_BAND_2GHZ)
+       if (chandef->chan->band != NL80211_BAND_2GHZ)
                offset = 4;
 
        /* pick the lowest rate for hidden nodes */
index e875786..ea828ba 100644 (file)
@@ -334,6 +334,9 @@ struct mt76_wcid {
        u32 tx_info;
        bool sw_iv;
 
+       struct list_head tx_list;
+       struct sk_buff_head tx_pending;
+
        struct list_head list;
        struct idr pktid;
 
@@ -376,7 +379,7 @@ struct mt76_rx_tid {
 
        u8 started:1, stopped:1, timer_pending:1;
 
-       struct sk_buff *reorder_buf[];
+       struct sk_buff *reorder_buf[] __counted_by(size);
 };
 
 #define MT_TX_CB_DMA_DONE              BIT(0)
@@ -709,6 +712,7 @@ struct mt76_vif {
        u8 basic_rates_idx;
        u8 mcast_rates_idx;
        u8 beacon_rates_idx;
+       struct ieee80211_chanctx_conf *ctx;
 };
 
 struct mt76_phy {
@@ -719,6 +723,8 @@ struct mt76_phy {
        unsigned long state;
        u8 band_idx;
 
+       spinlock_t tx_lock;
+       struct list_head tx_list;
        struct mt76_queue *q_tx[__MT_TXQ_MAX];
 
        struct cfg80211_chan_def chandef;
@@ -967,6 +973,7 @@ struct mt76_power_limits {
        s8 ofdm[8];
        s8 mcs[4][10];
        s8 ru[7][12];
+       s8 eht[16][16];
 };
 
 struct mt76_ethtool_worker_info {
@@ -1100,7 +1107,8 @@ int mt76_get_of_eeprom(struct mt76_dev *dev, void *data, int offset, int len);
 struct mt76_queue *
 mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
                int ring_base, u32 flags);
-u16 mt76_calculate_default_rate(struct mt76_phy *phy, int rateidx);
+u16 mt76_calculate_default_rate(struct mt76_phy *phy,
+                               struct ieee80211_vif *vif, int rateidx);
 static inline int mt76_init_tx_queue(struct mt76_phy *phy, int qid, int idx,
                                     int n_desc, int ring_base, u32 flags)
 {
@@ -1529,6 +1537,11 @@ mt76_mcu_skb_send_msg(struct mt76_dev *dev, struct sk_buff *skb, int cmd,
 
 void mt76_set_irq_mask(struct mt76_dev *dev, u32 addr, u32 clear, u32 set);
 
+struct device_node *
+mt76_find_power_limits_node(struct mt76_dev *dev);
+struct device_node *
+mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan);
+
 s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
                              struct ieee80211_channel *chan,
                              struct mt76_power_limits *dest,
@@ -1598,22 +1611,7 @@ mt76_token_put(struct mt76_dev *dev, int token)
        return txwi;
 }
 
-static inline void mt76_packet_id_init(struct mt76_wcid *wcid)
-{
-       INIT_LIST_HEAD(&wcid->list);
-       idr_init(&wcid->pktid);
-}
-
-static inline void
-mt76_packet_id_flush(struct mt76_dev *dev, struct mt76_wcid *wcid)
-{
-       struct sk_buff_head list;
-
-       mt76_tx_status_lock(dev, &list);
-       mt76_tx_status_skb_get(dev, wcid, -1, &list);
-       mt76_tx_status_unlock(dev, &list);
-
-       idr_destroy(&wcid->pktid);
-}
+void mt76_wcid_init(struct mt76_wcid *wcid);
+void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid);
 
 #endif
index 8886787..c223f7c 100644 (file)
@@ -9,6 +9,23 @@ struct beacon_bc_data {
        int count[MT7603_MAX_INTERFACES];
 };
 
+static void
+mt7603_mac_stuck_beacon_recovery(struct mt7603_dev *dev)
+{
+       if (dev->beacon_check % 5 != 4)
+               return;
+
+       mt76_clear(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_EN);
+       mt76_set(dev, MT_SCH_4, MT_SCH_4_RESET);
+       mt76_clear(dev, MT_SCH_4, MT_SCH_4_RESET);
+       mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_EN);
+
+       mt76_set(dev, MT_WF_CFG_OFF_WOCCR, MT_WF_CFG_OFF_WOCCR_TMAC_GC_DIS);
+       mt76_set(dev, MT_ARB_SCR, MT_ARB_SCR_TX_DISABLE);
+       mt76_clear(dev, MT_ARB_SCR, MT_ARB_SCR_TX_DISABLE);
+       mt76_clear(dev, MT_WF_CFG_OFF_WOCCR, MT_WF_CFG_OFF_WOCCR_TMAC_GC_DIS);
+}
+
 static void
 mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
 {
@@ -16,6 +33,8 @@ mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
        struct mt76_dev *mdev = &dev->mt76;
        struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;
        struct sk_buff *skb = NULL;
+       u32 om_idx = mvif->idx;
+       u32 val;
 
        if (!(mdev->beacon_mask & BIT(mvif->idx)))
                return;
@@ -24,20 +43,33 @@ mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
        if (!skb)
                return;
 
-       mt76_tx_queue_skb(dev, dev->mphy.q_tx[MT_TXQ_BEACON],
-                         MT_TXQ_BEACON, skb, &mvif->sta.wcid, NULL);
+       if (om_idx)
+               om_idx |= 0x10;
+       val = MT_DMA_FQCR0_BUSY | MT_DMA_FQCR0_MODE |
+               FIELD_PREP(MT_DMA_FQCR0_TARGET_BSS, om_idx) |
+               FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, 3) |
+               FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, 8);
 
        spin_lock_bh(&dev->ps_lock);
-       mt76_wr(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY |
-               FIELD_PREP(MT_DMA_FQCR0_TARGET_WCID, mvif->sta.wcid.idx) |
-               FIELD_PREP(MT_DMA_FQCR0_TARGET_QID,
-                          dev->mphy.q_tx[MT_TXQ_CAB]->hw_idx) |
-               FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, 3) |
-               FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, 8));
 
-       if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000))
+       mt76_wr(dev, MT_DMA_FQCR0, val |
+               FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, MT_TX_HW_QUEUE_BCN));
+       if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000)) {
                dev->beacon_check = MT7603_WATCHDOG_TIMEOUT;
+               goto out;
+       }
+
+       mt76_wr(dev, MT_DMA_FQCR0, val |
+               FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, MT_TX_HW_QUEUE_BMC));
+       if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000)) {
+               dev->beacon_check = MT7603_WATCHDOG_TIMEOUT;
+               goto out;
+       }
 
+       mt76_tx_queue_skb(dev, dev->mphy.q_tx[MT_TXQ_BEACON],
+                         MT_TXQ_BEACON, skb, &mvif->sta.wcid, NULL);
+
+out:
        spin_unlock_bh(&dev->ps_lock);
 }
 
@@ -81,6 +113,18 @@ void mt7603_pre_tbtt_tasklet(struct tasklet_struct *t)
        data.dev = dev;
        __skb_queue_head_init(&data.q);
 
+       /* Flush all previous CAB queue packets and beacons */
+       mt76_wr(dev, MT_WF_ARB_CAB_FLUSH, GENMASK(30, 16) | BIT(0));
+
+       mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_CAB], false);
+       mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BEACON], false);
+
+       if (dev->mphy.q_tx[MT_TXQ_BEACON]->queued > 0)
+               dev->beacon_check++;
+       else
+               dev->beacon_check = 0;
+       mt7603_mac_stuck_beacon_recovery(dev);
+
        q = dev->mphy.q_tx[MT_TXQ_BEACON];
        spin_lock(&q->lock);
        ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
@@ -89,14 +133,9 @@ void mt7603_pre_tbtt_tasklet(struct tasklet_struct *t)
        mt76_queue_kick(dev, q);
        spin_unlock(&q->lock);
 
-       /* Flush all previous CAB queue packets */
-       mt76_wr(dev, MT_WF_ARB_CAB_FLUSH, GENMASK(30, 16) | BIT(0));
-
-       mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_CAB], false);
-
        mt76_csa_check(mdev);
        if (mdev->csa_complete)
-               goto out;
+               return;
 
        q = dev->mphy.q_tx[MT_TXQ_CAB];
        do {
@@ -108,7 +147,7 @@ void mt7603_pre_tbtt_tasklet(struct tasklet_struct *t)
                 skb_queue_len(&data.q) < 8);
 
        if (skb_queue_empty(&data.q))
-               goto out;
+               return;
 
        for (i = 0; i < ARRAY_SIZE(data.tail); i++) {
                if (!data.tail[i])
@@ -136,11 +175,6 @@ void mt7603_pre_tbtt_tasklet(struct tasklet_struct *t)
                MT_WF_ARB_CAB_START_BSSn(0) |
                (MT_WF_ARB_CAB_START_BSS0n(1) *
                 ((1 << (MT7603_MAX_INTERFACES - 1)) - 1)));
-
-out:
-       mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BEACON], false);
-       if (dev->mphy.q_tx[MT_TXQ_BEACON]->queued > hweight8(mdev->beacon_mask))
-               dev->beacon_check++;
 }
 
 void mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval)
index 60a996b..915b834 100644 (file)
@@ -42,11 +42,13 @@ irqreturn_t mt7603_irq_handler(int irq, void *dev_instance)
        }
 
        if (intr & MT_INT_RX_DONE(0)) {
+               dev->rx_pse_check = 0;
                mt7603_irq_disable(dev, MT_INT_RX_DONE(0));
                napi_schedule(&dev->mt76.napi[0]);
        }
 
        if (intr & MT_INT_RX_DONE(1)) {
+               dev->rx_pse_check = 0;
                mt7603_irq_disable(dev, MT_INT_RX_DONE(1));
                napi_schedule(&dev->mt76.napi[1]);
        }
index 0762de3..6c55c72 100644 (file)
@@ -184,6 +184,13 @@ mt7603_mac_init(struct mt7603_dev *dev)
 
        mt76_set(dev, MT_TMAC_TCR, MT_TMAC_TCR_RX_RIFS_MODE);
 
+       if (is_mt7628(dev)) {
+               mt76_set(dev, MT_TMAC_TCR,
+                        MT_TMAC_TCR_TXOP_BURST_STOP | BIT(1) | BIT(0));
+               mt76_set(dev, MT_TXREQ, BIT(27));
+               mt76_set(dev, MT_AGG_TMP, GENMASK(4, 2));
+       }
+
        mt7603_set_tmac_template(dev);
 
        /* Enable RX group to HIF */
@@ -517,6 +524,7 @@ int mt7603_register_device(struct mt7603_dev *dev)
        hw->max_rates = 3;
        hw->max_report_rates = 7;
        hw->max_rate_tries = 11;
+       hw->max_tx_fragments = 1;
 
        hw->radiotap_timestamp.units_pos =
                IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US;
index 99ae080..cf21d06 100644 (file)
@@ -1441,15 +1441,6 @@ static void mt7603_mac_watchdog_reset(struct mt7603_dev *dev)
 
        mt7603_beacon_set_timer(dev, -1, 0);
 
-       if (dev->reset_cause[RESET_CAUSE_RESET_FAILED] ||
-           dev->cur_reset_cause == RESET_CAUSE_RX_PSE_BUSY ||
-           dev->cur_reset_cause == RESET_CAUSE_BEACON_STUCK ||
-           dev->cur_reset_cause == RESET_CAUSE_TX_HANG)
-               mt7603_pse_reset(dev);
-
-       if (dev->reset_cause[RESET_CAUSE_RESET_FAILED])
-               goto skip_dma_reset;
-
        mt7603_mac_stop(dev);
 
        mt76_clear(dev, MT_WPDMA_GLO_CFG,
@@ -1459,28 +1450,32 @@ static void mt7603_mac_watchdog_reset(struct mt7603_dev *dev)
 
        mt7603_irq_disable(dev, mask);
 
-       mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_FORCE_TX_EOF);
-
        mt7603_pse_client_reset(dev);
 
        mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], true);
        for (i = 0; i < __MT_TXQ_MAX; i++)
                mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
 
+       mt7603_dma_sched_reset(dev);
+
+       mt76_tx_status_check(&dev->mt76, true);
+
        mt76_for_each_q_rx(&dev->mt76, i) {
                mt76_queue_rx_reset(dev, i);
        }
 
-       mt76_tx_status_check(&dev->mt76, true);
+       if (dev->reset_cause[RESET_CAUSE_RESET_FAILED] ||
+           dev->cur_reset_cause == RESET_CAUSE_RX_PSE_BUSY)
+               mt7603_pse_reset(dev);
 
-       mt7603_dma_sched_reset(dev);
+       if (!dev->reset_cause[RESET_CAUSE_RESET_FAILED]) {
+               mt7603_mac_dma_start(dev);
 
-       mt7603_mac_dma_start(dev);
+               mt7603_irq_enable(dev, mask);
 
-       mt7603_irq_enable(dev, mask);
+               clear_bit(MT76_RESET, &dev->mphy.state);
+       }
 
-skip_dma_reset:
-       clear_bit(MT76_RESET, &dev->mphy.state);
        mutex_unlock(&dev->mt76.mutex);
 
        mt76_worker_enable(&dev->mt76.tx_worker);
@@ -1570,20 +1565,29 @@ static bool mt7603_rx_pse_busy(struct mt7603_dev *dev)
 {
        u32 addr, val;
 
-       if (mt76_rr(dev, MT_MCU_DEBUG_RESET) & MT_MCU_DEBUG_RESET_QUEUES)
-               return true;
-
        if (mt7603_rx_fifo_busy(dev))
-               return false;
+               goto out;
 
        addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR + MT_CLIENT_STATUS);
        mt76_wr(dev, addr, 3);
        val = mt76_rr(dev, addr) >> 16;
 
-       if (is_mt7628(dev) && (val & 0x4001) == 0x4001)
-               return true;
+       if (!(val & BIT(0)))
+               return false;
 
-       return (val & 0x8001) == 0x8001 || (val & 0xe001) == 0xe001;
+       if (is_mt7628(dev))
+               val &= 0xa000;
+       else
+               val &= 0x8000;
+       if (!val)
+               return false;
+
+out:
+       if (mt76_rr(dev, MT_INT_SOURCE_CSR) &
+           (MT_INT_RX_DONE(0) | MT_INT_RX_DONE(1)))
+               return false;
+
+       return true;
 }
 
 static bool
index c213fd2..89d738d 100644 (file)
@@ -70,7 +70,7 @@ mt7603_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        mvif->sta.wcid.idx = idx;
        mvif->sta.wcid.hw_key_idx = -1;
        mvif->sta.vif = mvif;
-       mt76_packet_id_init(&mvif->sta.wcid);
+       mt76_wcid_init(&mvif->sta.wcid);
 
        eth_broadcast_addr(bc_addr);
        mt7603_wtbl_init(dev, idx, mvif->idx, bc_addr);
@@ -110,7 +110,7 @@ mt7603_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        dev->mt76.vif_mask &= ~BIT_ULL(mvif->idx);
        mutex_unlock(&dev->mt76.mutex);
 
-       mt76_packet_id_flush(&dev->mt76, &mvif->sta.wcid);
+       mt76_wcid_cleanup(&dev->mt76, &mvif->sta.wcid);
 }
 
 void mt7603_init_edcca(struct mt7603_dev *dev)
index a39c9a0..524bceb 100644 (file)
@@ -469,6 +469,11 @@ enum {
 #define MT_WF_SEC_BASE                 0x21a00
 #define MT_WF_SEC(ofs)                 (MT_WF_SEC_BASE + (ofs))
 
+#define MT_WF_CFG_OFF_BASE             0x21e00
+#define MT_WF_CFG_OFF(ofs)             (MT_WF_CFG_OFF_BASE + (ofs))
+#define MT_WF_CFG_OFF_WOCCR            MT_WF_CFG_OFF(0x004)
+#define MT_WF_CFG_OFF_WOCCR_TMAC_GC_DIS        BIT(4)
+
 #define MT_SEC_SCR                     MT_WF_SEC(0x004)
 #define MT_SEC_SCR_MASK_ORDER          GENMASK(1, 0)
 
index 18a50cc..f7722f6 100644 (file)
@@ -58,10 +58,7 @@ int mt7615_thermal_init(struct mt7615_dev *dev)
                              wiphy_name(wiphy));
        hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, dev,
                                                       mt7615_hwmon_groups);
-       if (IS_ERR(hwmon))
-               return PTR_ERR(hwmon);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(hwmon);
 }
 EXPORT_SYMBOL_GPL(mt7615_thermal_init);
 
index 200b175..dab16b5 100644 (file)
@@ -226,7 +226,7 @@ static int mt7615_add_interface(struct ieee80211_hw *hw,
        mvif->sta.wcid.idx = idx;
        mvif->sta.wcid.phy_idx = mvif->mt76.band_idx;
        mvif->sta.wcid.hw_key_idx = -1;
-       mt76_packet_id_init(&mvif->sta.wcid);
+       mt76_wcid_init(&mvif->sta.wcid);
 
        mt7615_mac_wtbl_update(dev, idx,
                               MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -279,7 +279,7 @@ static void mt7615_remove_interface(struct ieee80211_hw *hw,
                list_del_init(&msta->wcid.poll_list);
        spin_unlock_bh(&dev->mt76.sta_poll_lock);
 
-       mt76_packet_id_flush(&dev->mt76, &mvif->sta.wcid);
+       mt76_wcid_cleanup(&dev->mt76, &mvif->sta.wcid);
 }
 
 int mt7615_set_channel(struct mt7615_phy *phy)
index 8d745c9..955974a 100644 (file)
@@ -2147,7 +2147,7 @@ int mt7615_mcu_set_chan_info(struct mt7615_phy *phy, int cmd)
        };
 
        if (cmd == MCU_EXT_CMD(SET_RX_PATH) ||
-           dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
+           phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR)
                req.switch_reason = CH_SWITCH_NORMAL;
        else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
                req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
index 0019890..fbb1181 100644 (file)
@@ -106,7 +106,7 @@ int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
        else
                mt76_connac_write_hw_txp(mdev, tx_info, txp, id);
 
-       tx_info->skb = DMA_DUMMY_DATA;
+       tx_info->skb = NULL;
 
        return 0;
 }
index 22878f0..1f29d8c 100644 (file)
@@ -172,6 +172,11 @@ struct mt76_connac_tx_free {
 
 extern const struct wiphy_wowlan_support mt76_connac_wowlan_support;
 
+static inline bool is_mt7925(struct mt76_dev *dev)
+{
+       return mt76_chip(dev) == 0x7925;
+}
+
 static inline bool is_mt7922(struct mt76_dev *dev)
 {
        return mt76_chip(dev) == 0x7922;
@@ -245,6 +250,7 @@ static inline bool is_mt76_fw_txp(struct mt76_dev *dev)
        switch (mt76_chip(dev)) {
        case 0x7961:
        case 0x7922:
+       case 0x7925:
        case 0x7663:
        case 0x7622:
                return false;
index 68ca084..2250252 100644 (file)
@@ -257,6 +257,8 @@ enum tx_mgnt_type {
 #define MT_TXD7_UDP_TCP_SUM            BIT(15)
 #define MT_TXD7_TX_TIME                        GENMASK(9, 0)
 
+#define MT_TXD9_WLAN_IDX               GENMASK(23, 8)
+
 #define MT_TX_RATE_STBC                        BIT(14)
 #define MT_TX_RATE_NSS                 GENMASK(13, 10)
 #define MT_TX_RATE_MODE                        GENMASK(9, 6)
@@ -269,7 +271,7 @@ enum tx_mgnt_type {
 #define MT_TXFREE0_MSDU_CNT            GENMASK(25, 16)
 #define MT_TXFREE0_RX_BYTE             GENMASK(15, 0)
 
-#define MT_TXFREE1_VER                 GENMASK(18, 16)
+#define MT_TXFREE1_VER                 GENMASK(19, 16)
 
 #define MT_TXFREE_INFO_PAIR            BIT(31)
 #define MT_TXFREE_INFO_HEADER          BIT(30)
@@ -315,6 +317,7 @@ enum tx_mgnt_type {
 
 #define MT_TXS4_TIMESTAMP              GENMASK(31, 0)
 
+/* MPDU based TXS */
 #define MT_TXS5_F0_FINAL_MPDU          BIT(31)
 #define MT_TXS5_F0_QOS                 BIT(30)
 #define MT_TXS5_F0_TX_COUNT            GENMASK(29, 25)
@@ -336,4 +339,17 @@ enum tx_mgnt_type {
 #define MT_TXS7_F1_MPDU_RETRY_COUNT    GENMASK(31, 24)
 #define MT_TXS7_F1_MPDU_RETRY_BYTES    GENMASK(23, 0)
 
+/* PPDU based TXS */
+#define MT_TXS5_MPDU_TX_CNT            GENMASK(30, 20)
+#define MT_TXS5_MPDU_TX_BYTE_SCALE     BIT(15)
+#define MT_TXS5_MPDU_TX_BYTE           GENMASK(14, 0)
+
+#define MT_TXS6_MPDU_FAIL_CNT          GENMASK(30, 20)
+#define MT_TXS6_MPDU_FAIL_BYTE_SCALE   BIT(15)
+#define MT_TXS6_MPDU_FAIL_BYTE         GENMASK(14, 0)
+
+#define MT_TXS7_MPDU_RETRY_CNT         GENMASK(30, 20)
+#define MT_TXS7_MPDU_RETRY_BYTE_SCALE  BIT(15)
+#define MT_TXS7_MPDU_RETRY_BYTE                GENMASK(14, 0)
+
 #endif /* __MT76_CONNAC3_MAC_H */
index ee5177f..93402d2 100644 (file)
@@ -151,23 +151,6 @@ void mt76_connac_tx_complete_skb(struct mt76_dev *mdev,
                return;
        }
 
-       /* error path */
-       if (e->skb == DMA_DUMMY_DATA) {
-               struct mt76_connac_txp_common *txp;
-               struct mt76_txwi_cache *t;
-               u16 token;
-
-               txp = mt76_connac_txwi_to_txp(mdev, e->txwi);
-               if (is_mt76_fw_txp(mdev))
-                       token = le16_to_cpu(txp->fw.token);
-               else
-                       token = le16_to_cpu(txp->hw.msdu_id[0]) &
-                               ~MT_MSDU_ID_VALID;
-
-               t = mt76_token_put(mdev, token);
-               e->skb = t ? t->skb : NULL;
-       }
-
        if (e->skb)
                mt76_tx_complete_skb(mdev, e->wcid, e->skb);
 }
@@ -187,7 +170,7 @@ void mt76_connac_write_hw_txp(struct mt76_dev *dev,
 
        txp->msdu_id[0] = cpu_to_le16(id | MT_MSDU_ID_VALID);
 
-       if (is_mt7663(dev) || is_mt7921(dev))
+       if (is_mt7663(dev) || is_mt7921(dev) || is_mt7925(dev))
                last_mask = MT_TXD_LEN_LAST;
        else
                last_mask = MT_TXD_LEN_AMSDU_LAST |
@@ -231,7 +214,7 @@ mt76_connac_txp_skb_unmap_hw(struct mt76_dev *dev,
        u32 last_mask;
        int i;
 
-       if (is_mt7663(dev) || is_mt7921(dev))
+       if (is_mt7663(dev) || is_mt7921(dev) || is_mt7925(dev))
                last_mask = MT_TXD_LEN_LAST;
        else
                last_mask = MT_TXD_LEN_MSDU_LAST;
@@ -310,7 +293,10 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
                                 struct ieee80211_vif *vif,
                                 bool beacon, bool mcast)
 {
-       u8 nss = 0, mode = 0, band = mphy->chandef.chan->band;
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct cfg80211_chan_def *chandef = mvif->ctx ?
+                                           &mvif->ctx->def : &mphy->chandef;
+       u8 nss = 0, mode = 0, band = chandef->chan->band;
        int rateidx = 0, mcast_rate;
 
        if (!vif)
@@ -343,7 +329,7 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
                rateidx = ffs(vif->bss_conf.basic_rates) - 1;
 
 legacy:
-       rateidx = mt76_calculate_default_rate(mphy, rateidx);
+       rateidx = mt76_calculate_default_rate(mphy, vif, rateidx);
        mode = rateidx >> 8;
        rateidx &= GENMASK(7, 0);
 out:
index 0f0a519..ae6bf3c 100644 (file)
@@ -66,6 +66,7 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len,
 
        if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) ||
            (is_mt7921(dev) && addr == 0x900000) ||
+           (is_mt7925(dev) && addr == 0x900000) ||
            (is_mt7996(dev) && addr == 0x900000))
                cmd = MCU_CMD(PATCH_START_REQ);
        else
@@ -745,7 +746,7 @@ mt76_connac_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
        he->pkt_ext = 2;
 }
 
-static void
+void
 mt76_connac_mcu_sta_he_tlv_v2(struct sk_buff *skb, struct ieee80211_sta *sta)
 {
        struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap;
@@ -777,20 +778,23 @@ mt76_connac_mcu_sta_he_tlv_v2(struct sk_buff *skb, struct ieee80211_sta *sta)
 
        he->pkt_ext = IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US;
 }
+EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_he_tlv_v2);
 
-static u8
+u8
 mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif,
                            enum nl80211_band band, struct ieee80211_sta *sta)
 {
        struct ieee80211_sta_ht_cap *ht_cap;
        struct ieee80211_sta_vht_cap *vht_cap;
        const struct ieee80211_sta_he_cap *he_cap;
+       const struct ieee80211_sta_eht_cap *eht_cap;
        u8 mode = 0;
 
        if (sta) {
                ht_cap = &sta->deflink.ht_cap;
                vht_cap = &sta->deflink.vht_cap;
                he_cap = &sta->deflink.he_cap;
+               eht_cap = &sta->deflink.eht_cap;
        } else {
                struct ieee80211_supported_band *sband;
 
@@ -798,6 +802,7 @@ mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif,
                ht_cap = &sband->ht_cap;
                vht_cap = &sband->vht_cap;
                he_cap = ieee80211_get_he_iftype_cap(sband, vif->type);
+               eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type);
        }
 
        if (band == NL80211_BAND_2GHZ) {
@@ -808,6 +813,9 @@ mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif,
 
                if (he_cap && he_cap->has_he)
                        mode |= PHY_TYPE_BIT_HE;
+
+               if (eht_cap && eht_cap->has_eht)
+                       mode |= PHY_TYPE_BIT_BE;
        } else if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) {
                mode |= PHY_TYPE_BIT_OFDM;
 
@@ -819,17 +827,23 @@ mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif,
 
                if (he_cap && he_cap->has_he)
                        mode |= PHY_TYPE_BIT_HE;
+
+               if (eht_cap && eht_cap->has_eht)
+                       mode |= PHY_TYPE_BIT_BE;
        }
 
        return mode;
 }
+EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode_v2);
 
 void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
                             struct ieee80211_sta *sta,
                             struct ieee80211_vif *vif,
                             u8 rcpi, u8 sta_state)
 {
-       struct cfg80211_chan_def *chandef = &mphy->chandef;
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct cfg80211_chan_def *chandef = mvif->ctx ?
+                                           &mvif->ctx->def : &mphy->chandef;
        enum nl80211_band band = chandef->chan->band;
        struct mt76_dev *dev = mphy->dev;
        struct sta_rec_ra_info *ra_info;
@@ -1369,7 +1383,10 @@ EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode_ext);
 const struct ieee80211_sta_he_cap *
 mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif)
 {
-       enum nl80211_band band = phy->chandef.chan->band;
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct cfg80211_chan_def *chandef = mvif->ctx ?
+                                           &mvif->ctx->def : &phy->chandef;
+       enum nl80211_band band = chandef->chan->band;
        struct ieee80211_supported_band *sband;
 
        sband = phy->hw->wiphy->bands[band];
@@ -1924,126 +1941,6 @@ void mt76_connac_mcu_coredump_event(struct mt76_dev *dev, struct sk_buff *skb,
 }
 EXPORT_SYMBOL_GPL(mt76_connac_mcu_coredump_event);
 
-static void mt76_connac_mcu_parse_tx_resource(struct mt76_dev *dev,
-                                             struct sk_buff *skb)
-{
-       struct mt76_sdio *sdio = &dev->sdio;
-       struct mt76_connac_tx_resource {
-               __le32 version;
-               __le32 pse_data_quota;
-               __le32 pse_mcu_quota;
-               __le32 ple_data_quota;
-               __le32 ple_mcu_quota;
-               __le16 pse_page_size;
-               __le16 ple_page_size;
-               u8 pp_padding;
-               u8 pad[3];
-       } __packed * tx_res;
-
-       tx_res = (struct mt76_connac_tx_resource *)skb->data;
-       sdio->sched.pse_data_quota = le32_to_cpu(tx_res->pse_data_quota);
-       sdio->sched.pse_mcu_quota = le32_to_cpu(tx_res->pse_mcu_quota);
-       sdio->sched.ple_data_quota = le32_to_cpu(tx_res->ple_data_quota);
-       sdio->sched.pse_page_size = le16_to_cpu(tx_res->pse_page_size);
-       sdio->sched.deficit = tx_res->pp_padding;
-}
-
-static void mt76_connac_mcu_parse_phy_cap(struct mt76_dev *dev,
-                                         struct sk_buff *skb)
-{
-       struct mt76_connac_phy_cap {
-               u8 ht;
-               u8 vht;
-               u8 _5g;
-               u8 max_bw;
-               u8 nss;
-               u8 dbdc;
-               u8 tx_ldpc;
-               u8 rx_ldpc;
-               u8 tx_stbc;
-               u8 rx_stbc;
-               u8 hw_path;
-               u8 he;
-       } __packed * cap;
-
-       enum {
-               WF0_24G,
-               WF0_5G
-       };
-
-       cap = (struct mt76_connac_phy_cap *)skb->data;
-
-       dev->phy.antenna_mask = BIT(cap->nss) - 1;
-       dev->phy.chainmask = dev->phy.antenna_mask;
-       dev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G);
-       dev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G);
-}
-
-int mt76_connac_mcu_get_nic_capability(struct mt76_phy *phy)
-{
-       struct mt76_connac_cap_hdr {
-               __le16 n_element;
-               u8 rsv[2];
-       } __packed * hdr;
-       struct sk_buff *skb;
-       int ret, i;
-
-       ret = mt76_mcu_send_and_get_msg(phy->dev, MCU_CE_CMD(GET_NIC_CAPAB),
-                                       NULL, 0, true, &skb);
-       if (ret)
-               return ret;
-
-       hdr = (struct mt76_connac_cap_hdr *)skb->data;
-       if (skb->len < sizeof(*hdr)) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       skb_pull(skb, sizeof(*hdr));
-
-       for (i = 0; i < le16_to_cpu(hdr->n_element); i++) {
-               struct tlv_hdr {
-                       __le32 type;
-                       __le32 len;
-               } __packed * tlv = (struct tlv_hdr *)skb->data;
-               int len;
-
-               if (skb->len < sizeof(*tlv))
-                       break;
-
-               skb_pull(skb, sizeof(*tlv));
-
-               len = le32_to_cpu(tlv->len);
-               if (skb->len < len)
-                       break;
-
-               switch (le32_to_cpu(tlv->type)) {
-               case MT_NIC_CAP_6G:
-                       phy->cap.has_6ghz = skb->data[0];
-                       break;
-               case MT_NIC_CAP_MAC_ADDR:
-                       memcpy(phy->macaddr, (void *)skb->data, ETH_ALEN);
-                       break;
-               case MT_NIC_CAP_PHY:
-                       mt76_connac_mcu_parse_phy_cap(phy->dev, skb);
-                       break;
-               case MT_NIC_CAP_TX_RESOURCE:
-                       if (mt76_is_sdio(phy->dev))
-                               mt76_connac_mcu_parse_tx_resource(phy->dev,
-                                                                 skb);
-                       break;
-               default:
-                       break;
-               }
-               skb_pull(skb, len);
-       }
-out:
-       dev_kfree_skb(skb);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(mt76_connac_mcu_get_nic_capability);
-
 static void
 mt76_connac_mcu_build_sku(struct mt76_dev *dev, s8 *sku,
                          struct mt76_power_limits *limits,
@@ -2087,9 +1984,9 @@ mt76_connac_mcu_build_sku(struct mt76_dev *dev, s8 *sku,
        }
 }
 
-static s8 mt76_connac_get_ch_power(struct mt76_phy *phy,
-                                  struct ieee80211_channel *chan,
-                                  s8 target_power)
+s8 mt76_connac_get_ch_power(struct mt76_phy *phy,
+                           struct ieee80211_channel *chan,
+                           s8 target_power)
 {
        struct mt76_dev *dev = phy->dev;
        struct ieee80211_supported_band *sband;
@@ -2126,6 +2023,7 @@ static s8 mt76_connac_get_ch_power(struct mt76_phy *phy,
 
        return target_power;
 }
+EXPORT_SYMBOL_GPL(mt76_connac_get_ch_power);
 
 static int
 mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
@@ -2144,7 +2042,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
                112, 114, 116, 118, 120, 122, 124,
                126, 128, 132, 134, 136, 138, 140,
                142, 144, 149, 151, 153, 155, 157,
-               159, 161, 165
+               159, 161, 165, 169, 173, 177
        };
        static const u8 chan_list_6ghz[] = {
                  1,   3,   5,   7,   9,  11,  13,
@@ -2164,11 +2062,15 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
                209, 211, 213, 215, 217, 219, 221,
                225, 227, 229, 233
        };
-       int i, n_chan, batch_size, idx = 0, tx_power, last_ch;
+       int i, n_chan, batch_size, idx = 0, tx_power, last_ch, err = 0;
        struct mt76_connac_sku_tlv sku_tlbv;
-       struct mt76_power_limits limits;
+       struct mt76_power_limits *limits;
        const u8 *ch_list;
 
+       limits = devm_kmalloc(dev->dev, sizeof(*limits), GFP_KERNEL);
+       if (!limits)
+               return -ENOMEM;
+
        sku_len = is_mt7921(dev) ? sizeof(sku_tlbv) : sizeof(sku_tlbv) - 92;
        tx_power = 2 * phy->hw->conf.power_level;
        if (!tx_power)
@@ -2195,14 +2097,16 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
 
        for (i = 0; i < batch_size; i++) {
                struct mt76_connac_tx_power_limit_tlv tx_power_tlv = {};
-               int j, err, msg_len, num_ch;
+               int j, msg_len, num_ch;
                struct sk_buff *skb;
 
                num_ch = i == batch_size - 1 ? n_chan % batch_len : batch_len;
                msg_len = sizeof(tx_power_tlv) + num_ch * sizeof(sku_tlbv);
                skb = mt76_mcu_msg_alloc(dev, NULL, msg_len);
-               if (!skb)
-                       return -ENOMEM;
+               if (!skb) {
+                       err = -ENOMEM;
+                       goto out;
+               }
 
                skb_reserve(skb, sizeof(tx_power_tlv));
 
@@ -2233,14 +2137,14 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
                                                             tx_power);
                        sar_power = mt76_get_sar_power(phy, &chan, reg_power);
 
-                       mt76_get_rate_power_limits(phy, &chan, &limits,
+                       mt76_get_rate_power_limits(phy, &chan, limits,
                                                   sar_power);
 
                        tx_power_tlv.last_msg = ch_list[idx] == last_ch;
                        sku_tlbv.channel = ch_list[idx];
 
                        mt76_connac_mcu_build_sku(dev, sku_tlbv.pwr_limit,
-                                                 &limits, band);
+                                                 limits, band);
                        skb_put_data(skb, &sku_tlbv, sku_len);
                }
                __skb_push(skb, sizeof(tx_power_tlv));
@@ -2250,10 +2154,12 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
                                            MCU_CE_CMD(SET_RATE_TX_POWER),
                                            false);
                if (err < 0)
-                       return err;
+                       goto out;
        }
 
-       return 0;
+out:
+       devm_kfree(dev->dev, limits);
+       return err;
 }
 
 int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
@@ -2457,7 +2363,7 @@ mt76_connac_mcu_set_arp_filter(struct mt76_dev *dev, struct ieee80211_vif *vif,
                                 sizeof(req), true);
 }
 
-static int
+int
 mt76_connac_mcu_set_gtk_rekey(struct mt76_dev *dev, struct ieee80211_vif *vif,
                              bool suspend)
 {
@@ -2482,8 +2388,9 @@ mt76_connac_mcu_set_gtk_rekey(struct mt76_dev *dev, struct ieee80211_vif *vif,
        return mt76_mcu_send_msg(dev, MCU_UNI_CMD(OFFLOAD), &req,
                                 sizeof(req), true);
 }
+EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_gtk_rekey);
 
-static int
+int
 mt76_connac_mcu_set_suspend_mode(struct mt76_dev *dev,
                                 struct ieee80211_vif *vif,
                                 bool enable, u8 mdtim,
@@ -2512,6 +2419,7 @@ mt76_connac_mcu_set_suspend_mode(struct mt76_dev *dev,
        return mt76_mcu_send_msg(dev, MCU_UNI_CMD(SUSPEND), &req,
                                 sizeof(req), true);
 }
+EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_suspend_mode);
 
 static int
 mt76_connac_mcu_set_wow_pattern(struct mt76_dev *dev,
@@ -2547,7 +2455,7 @@ mt76_connac_mcu_set_wow_pattern(struct mt76_dev *dev,
        return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SUSPEND), true);
 }
 
-static int
+int
 mt76_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif,
                             bool suspend, struct cfg80211_wowlan *wowlan)
 {
@@ -2599,6 +2507,7 @@ mt76_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif,
        return mt76_mcu_send_msg(dev, MCU_UNI_CMD(SUSPEND), &req,
                                 sizeof(req), true);
 }
+EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_wow_ctrl);
 
 int mt76_connac_mcu_set_hif_suspend(struct mt76_dev *dev, bool suspend)
 {
@@ -3064,7 +2973,7 @@ static u32 mt76_connac2_get_data_mode(struct mt76_dev *dev, u32 info)
 {
        u32 mode = DL_MODE_NEED_RSP;
 
-       if (!is_mt7921(dev) || info == PATCH_SEC_NOT_SUPPORT)
+       if ((!is_mt7921(dev) && !is_mt7925(dev)) || info == PATCH_SEC_NOT_SUPPORT)
                return mode;
 
        switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) {
index 4543e5b..0563b1b 100644 (file)
@@ -191,6 +191,7 @@ struct mt76_connac2_fw_region {
 struct tlv {
        __le16 tag;
        __le16 len;
+       u8 data[];
 } __packed;
 
 struct bss_info_omac {
@@ -795,6 +796,7 @@ enum {
        STA_REC_PHY = 0x15,
        STA_REC_HE_6G = 0x17,
        STA_REC_HE_V2 = 0x19,
+       STA_REC_MLD = 0x20,
        STA_REC_EHT = 0x22,
        STA_REC_HDRT = 0x28,
        STA_REC_HDR_TRANS = 0x2B,
@@ -919,6 +921,7 @@ enum {
        PHY_TYPE_HT_INDEX,
        PHY_TYPE_VHT_INDEX,
        PHY_TYPE_HE_INDEX,
+       PHY_TYPE_BE_INDEX,
        PHY_TYPE_INDEX_NUM
 };
 
@@ -928,6 +931,7 @@ enum {
 #define PHY_TYPE_BIT_HT                                BIT(PHY_TYPE_HT_INDEX)
 #define PHY_TYPE_BIT_VHT                       BIT(PHY_TYPE_VHT_INDEX)
 #define PHY_TYPE_BIT_HE                                BIT(PHY_TYPE_HE_INDEX)
+#define PHY_TYPE_BIT_BE                                BIT(PHY_TYPE_BE_INDEX)
 
 #define MT_WTBL_RATE_TX_MODE                   GENMASK(9, 6)
 #define MT_WTBL_RATE_MCS                       GENMASK(5, 0)
@@ -1009,8 +1013,17 @@ enum {
 enum {
        MCU_UNI_EVENT_RESULT = 0x01,
        MCU_UNI_EVENT_FW_LOG_2_HOST = 0x04,
+       MCU_UNI_EVENT_ACCESS_REG = 0x6,
        MCU_UNI_EVENT_IE_COUNTDOWN = 0x09,
+       MCU_UNI_EVENT_COREDUMP = 0x0a,
+       MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c,
+       MCU_UNI_EVENT_SCAN_DONE = 0x0e,
        MCU_UNI_EVENT_RDD_REPORT = 0x11,
+       MCU_UNI_EVENT_ROC = 0x27,
+       MCU_UNI_EVENT_TX_DONE = 0x2d,
+       MCU_UNI_EVENT_NIC_CAPAB = 0x43,
+       MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+       MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
 };
 
 #define MCU_UNI_CMD_EVENT                      BIT(1)
@@ -1209,12 +1222,17 @@ enum {
        MCU_UNI_CMD_RX_HDR_TRANS = 0x12,
        MCU_UNI_CMD_SER = 0x13,
        MCU_UNI_CMD_TWT = 0x14,
+       MCU_UNI_CMD_SET_DOMAIN_INFO = 0x15,
+       MCU_UNI_CMD_SCAN_REQ = 0x16,
        MCU_UNI_CMD_RDD_CTRL = 0x19,
        MCU_UNI_CMD_GET_MIB_INFO = 0x22,
+       MCU_UNI_CMD_GET_STAT_INFO = 0x23,
        MCU_UNI_CMD_SNIFFER = 0x24,
        MCU_UNI_CMD_SR = 0x25,
        MCU_UNI_CMD_ROC = 0x27,
+       MCU_UNI_CMD_SET_DBDC_PARMS = 0x28,
        MCU_UNI_CMD_TXPOWER = 0x2b,
+       MCU_UNI_CMD_SET_POWER_LIMIT = 0x2c,
        MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
        MCU_UNI_CMD_RA = 0x2f,
        MCU_UNI_CMD_MURU = 0x31,
@@ -1224,6 +1242,8 @@ enum {
        MCU_UNI_CMD_VOW = 0x37,
        MCU_UNI_CMD_RRO = 0x57,
        MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+       MCU_UNI_CMD_PER_STA_INFO = 0x6d,
+       MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
        MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
 };
 
@@ -1279,6 +1299,7 @@ enum {
        UNI_BSS_INFO_RLM = 2,
        UNI_BSS_INFO_BSS_COLOR = 4,
        UNI_BSS_INFO_HE_BASIC = 5,
+       UNI_BSS_INFO_11V_MBSSID = 6,
        UNI_BSS_INFO_BCN_CONTENT = 7,
        UNI_BSS_INFO_BCN_CSA = 8,
        UNI_BSS_INFO_BCN_BCC = 9,
@@ -1293,6 +1314,7 @@ enum {
        UNI_BSS_INFO_IFS_TIME = 23,
        UNI_BSS_INFO_OFFLOAD = 25,
        UNI_BSS_INFO_MLD = 26,
+       UNI_BSS_INFO_PM_DISABLE = 27,
 };
 
 enum {
@@ -1302,6 +1324,17 @@ enum {
        UNI_OFFLOAD_OFFLOAD_BMC_RPY_DETECT,
 };
 
+enum UNI_ALL_STA_INFO_TAG {
+       UNI_ALL_STA_TX_RATE,
+       UNI_ALL_STA_TX_STAT,
+       UNI_ALL_STA_TXRX_ADM_STAT,
+       UNI_ALL_STA_TXRX_AIR_TIME,
+       UNI_ALL_STA_DATA_TX_RETRY_COUNT,
+       UNI_ALL_STA_GI_MODE,
+       UNI_ALL_STA_TXRX_MSDU_COUNT,
+       UNI_ALL_STA_MAX_NUM
+};
+
 enum {
        MT_NIC_CAP_TX_RESOURCE,
        MT_NIC_CAP_TX_EFUSE_ADDR,
@@ -1322,6 +1355,7 @@ enum {
        MT_NIC_CAP_ANTSWP = 0x16,
        MT_NIC_CAP_WFDMA_REALLOC,
        MT_NIC_CAP_6G,
+       MT_NIC_CAP_CHIP_CAP = 0x20,
 };
 
 #define UNI_WOW_DETECT_TYPE_MAGIC              BIT(0)
@@ -1549,6 +1583,15 @@ struct bss_info_uni_he {
        u8 rsv[2];
 } __packed;
 
+struct bss_info_uni_mbssid {
+       __le16 tag;
+       __le16 len;
+       u8 max_indicator;
+       u8 mbss_idx;
+       u8 tx_bss_omac_idx;
+       u8 rsv;
+} __packed;
+
 struct mt76_connac_gtk_rekey_tlv {
        __le16 tag;
        __le16 len;
@@ -1739,7 +1782,7 @@ mt76_connac_mcu_gen_dl_mode(struct mt76_dev *dev, u8 feature_set, bool is_wa)
 
        ret |= feature_set & FW_FEATURE_SET_ENCRYPT ?
               DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV : 0;
-       if (is_mt7921(dev))
+       if (is_mt7921(dev) || is_mt7925(dev))
                ret |= feature_set & FW_FEATURE_ENCRY_MODE ?
                       DL_CONFIG_ENCRY_MODE_SEL : 0;
        ret |= FIELD_PREP(DL_MODE_KEY_IDX,
@@ -1807,6 +1850,9 @@ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb,
 int mt76_connac_mcu_sta_update_hdr_trans(struct mt76_dev *dev,
                                         struct ieee80211_vif *vif,
                                         struct mt76_wcid *wcid, int cmd);
+void mt76_connac_mcu_sta_he_tlv_v2(struct sk_buff *skb, struct ieee80211_sta *sta);
+u8 mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif,
+                              enum nl80211_band band, struct ieee80211_sta *sta);
 int mt76_connac_mcu_wtbl_update_hdr_trans(struct mt76_dev *dev,
                                          struct ieee80211_vif *vif,
                                          struct ieee80211_sta *sta);
@@ -1851,7 +1897,6 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len,
 int mt76_connac_mcu_start_patch(struct mt76_dev *dev);
 int mt76_connac_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get);
 int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option);
-int mt76_connac_mcu_get_nic_capability(struct mt76_phy *phy);
 
 int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
                            struct ieee80211_scan_request *scan_req);
@@ -1866,9 +1911,17 @@ int mt76_connac_mcu_sched_scan_enable(struct mt76_phy *phy,
 int mt76_connac_mcu_update_arp_filter(struct mt76_dev *dev,
                                      struct mt76_vif *vif,
                                      struct ieee80211_bss_conf *info);
+int mt76_connac_mcu_set_gtk_rekey(struct mt76_dev *dev, struct ieee80211_vif *vif,
+                                 bool suspend);
+int mt76_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif,
+                                bool suspend, struct cfg80211_wowlan *wowlan);
 int mt76_connac_mcu_update_gtk_rekey(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif,
                                     struct cfg80211_gtk_rekey_data *key);
+int mt76_connac_mcu_set_suspend_mode(struct mt76_dev *dev,
+                                    struct ieee80211_vif *vif,
+                                    bool enable, u8 mdtim,
+                                    bool wow_suspend);
 int mt76_connac_mcu_set_hif_suspend(struct mt76_dev *dev, bool suspend);
 void mt76_connac_mcu_set_suspend_iter(void *priv, u8 *mac,
                                      struct ieee80211_vif *vif);
@@ -1879,6 +1932,9 @@ int mt76_connac_mcu_chip_config(struct mt76_dev *dev);
 int mt76_connac_mcu_set_deep_sleep(struct mt76_dev *dev, bool enable);
 void mt76_connac_mcu_coredump_event(struct mt76_dev *dev, struct sk_buff *skb,
                                    struct mt76_connac_coredump *coredump);
+s8 mt76_connac_get_ch_power(struct mt76_phy *phy,
+                           struct ieee80211_channel *chan,
+                           s8 target_power);
 int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy);
 int mt76_connac_mcu_set_p2p_oppps(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif);
index ad4dc8e..d570b99 100644 (file)
@@ -136,7 +136,8 @@ EXPORT_SYMBOL_GPL(mt76x02_resync_beacon_timer);
 void
 mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
 {
-       struct mt76x02_dev *dev = (struct mt76x02_dev *)priv;
+       struct beacon_bc_data *data = priv;
+       struct mt76x02_dev *dev = data->dev;
        struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
        struct sk_buff *skb = NULL;
 
@@ -147,7 +148,7 @@ mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
        if (!skb)
                return;
 
-       mt76x02_mac_set_beacon(dev, skb);
+       __skb_queue_tail(&data->q, skb);
 }
 EXPORT_SYMBOL_GPL(mt76x02_update_beacon_iter);
 
@@ -182,9 +183,6 @@ mt76x02_enqueue_buffered_bc(struct mt76x02_dev *dev,
 {
        int i, nframes;
 
-       data->dev = dev;
-       __skb_queue_head_init(&data->q);
-
        do {
                nframes = skb_queue_len(&data->q);
                ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
index e9c5e85..9b5e3fb 100644 (file)
@@ -16,13 +16,17 @@ static void mt76x02_pre_tbtt_tasklet(struct tasklet_struct *t)
        struct mt76x02_dev *dev = from_tasklet(dev, t, mt76.pre_tbtt_tasklet);
        struct mt76_dev *mdev = &dev->mt76;
        struct mt76_queue *q = dev->mphy.q_tx[MT_TXQ_PSD];
-       struct beacon_bc_data data = {};
+       struct beacon_bc_data data = {
+               .dev = dev,
+       };
        struct sk_buff *skb;
        int i;
 
        if (mt76_hw(dev)->conf.flags & IEEE80211_CONF_OFFCHANNEL)
                return;
 
+       __skb_queue_head_init(&data.q);
+
        mt76x02_resync_beacon_timer(dev);
 
        /* Prevent corrupt transmissions during update */
@@ -31,7 +35,10 @@ static void mt76x02_pre_tbtt_tasklet(struct tasklet_struct *t)
 
        ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
                IEEE80211_IFACE_ITER_RESUME_ALL,
-               mt76x02_update_beacon_iter, dev);
+               mt76x02_update_beacon_iter, &data);
+
+       while ((skb = __skb_dequeue(&data.q)) != NULL)
+               mt76x02_mac_set_beacon(dev, skb);
 
        mt76_wr(dev, MT_BCN_BYPASS_MASK,
                0xff00 | ~(0xff00 >> dev->beacon_data_count));
index 2c6c038..85a78de 100644 (file)
@@ -182,7 +182,9 @@ static void mt76x02u_pre_tbtt_work(struct work_struct *work)
 {
        struct mt76x02_dev *dev =
                container_of(work, struct mt76x02_dev, pre_tbtt_work);
-       struct beacon_bc_data data = {};
+       struct beacon_bc_data data = {
+               .dev = dev,
+       };
        struct sk_buff *skb;
        int nbeacons;
 
@@ -192,15 +194,20 @@ static void mt76x02u_pre_tbtt_work(struct work_struct *work)
        if (mt76_hw(dev)->conf.flags & IEEE80211_CONF_OFFCHANNEL)
                return;
 
+       __skb_queue_head_init(&data.q);
+
        mt76x02_resync_beacon_timer(dev);
 
        /* Prevent corrupt transmissions during update */
        mt76_set(dev, MT_BCN_BYPASS_MASK, 0xffff);
        dev->beacon_data_count = 0;
 
-       ieee80211_iterate_active_interfaces(mt76_hw(dev),
+       ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
                IEEE80211_IFACE_ITER_RESUME_ALL,
-               mt76x02_update_beacon_iter, dev);
+               mt76x02_update_beacon_iter, &data);
+
+       while ((skb = __skb_dequeue(&data.q)) != NULL)
+               mt76x02_mac_set_beacon(dev, skb);
 
        mt76_csa_check(&dev->mt76);
 
index dcbb5c6..8a0e812 100644 (file)
@@ -288,7 +288,7 @@ mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif,
        mvif->idx = idx;
        mvif->group_wcid.idx = MT_VIF_WCID(idx);
        mvif->group_wcid.hw_key_idx = -1;
-       mt76_packet_id_init(&mvif->group_wcid);
+       mt76_wcid_init(&mvif->group_wcid);
 
        mtxq = (struct mt76_txq *)vif->txq->drv_priv;
        rcu_assign_pointer(dev->mt76.wcid[MT_VIF_WCID(idx)], &mvif->group_wcid);
@@ -346,7 +346,7 @@ void mt76x02_remove_interface(struct ieee80211_hw *hw,
 
        dev->mt76.vif_mask &= ~BIT_ULL(mvif->idx);
        rcu_assign_pointer(dev->mt76.wcid[mvif->group_wcid.idx], NULL);
-       mt76_packet_id_flush(&dev->mt76, &mvif->group_wcid);
+       mt76_wcid_cleanup(&dev->mt76, &mvif->group_wcid);
 }
 EXPORT_SYMBOL_GPL(mt76x02_remove_interface);
 
index b27d04e..8147828 100644 (file)
@@ -213,10 +213,7 @@ static int mt7915_thermal_init(struct mt7915_phy *phy)
 
        hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy,
                                                       mt7915_hwmon_groups);
-       if (IS_ERR(hwmon))
-               return PTR_ERR(hwmon);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(hwmon);
 }
 
 static void mt7915_led_set_config(struct led_classdev *led_cdev,
@@ -347,6 +344,9 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
        hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE;
        hw->netdev_features = NETIF_F_RXCSUM;
 
+       if (mtk_wed_device_active(&mdev->mmio.wed))
+               hw->netdev_features |= NETIF_F_HW_TC;
+
        hw->radiotap_timestamp.units_pos =
                IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US;
 
@@ -393,8 +393,12 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
                phy->mt76->sband_2g.sband.ht_cap.cap |=
                        IEEE80211_HT_CAP_LDPC_CODING |
                        IEEE80211_HT_CAP_MAX_AMSDU;
-               phy->mt76->sband_2g.sband.ht_cap.ampdu_density =
-                       IEEE80211_HT_MPDU_DENSITY_4;
+               if (is_mt7915(&dev->mt76))
+                       phy->mt76->sband_2g.sband.ht_cap.ampdu_density =
+                               IEEE80211_HT_MPDU_DENSITY_4;
+               else
+                       phy->mt76->sband_2g.sband.ht_cap.ampdu_density =
+                               IEEE80211_HT_MPDU_DENSITY_2;
        }
 
        if (phy->mt76->cap.has_5ghz) {
@@ -404,10 +408,11 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
                phy->mt76->sband_5g.sband.ht_cap.cap |=
                        IEEE80211_HT_CAP_LDPC_CODING |
                        IEEE80211_HT_CAP_MAX_AMSDU;
-               phy->mt76->sband_5g.sband.ht_cap.ampdu_density =
-                       IEEE80211_HT_MPDU_DENSITY_4;
 
                if (is_mt7915(&dev->mt76)) {
+                       phy->mt76->sband_5g.sband.ht_cap.ampdu_density =
+                               IEEE80211_HT_MPDU_DENSITY_4;
+
                        vht_cap->cap |=
                                IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 |
                                IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
@@ -417,6 +422,9 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
                                        IEEE80211_VHT_CAP_SHORT_GI_160 |
                                        FIELD_PREP(IEEE80211_VHT_CAP_EXT_NSS_BW_MASK, 1);
                } else {
+                       phy->mt76->sband_5g.sband.ht_cap.ampdu_density =
+                               IEEE80211_HT_MPDU_DENSITY_2;
+
                        vht_cap->cap |=
                                IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
                                IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
index b8b0c0f..2222fb9 100644 (file)
@@ -809,7 +809,7 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
                txp->rept_wds_wcid = cpu_to_le16(wcid->idx);
        else
                txp->rept_wds_wcid = cpu_to_le16(0x3ff);
-       tx_info->skb = DMA_DUMMY_DATA;
+       tx_info->skb = NULL;
 
        /* pass partial skb header to fw */
        tx_info->buf[1].len = MT_CT_PARSE_LEN;
index 8ebbf18..a3fd54c 100644 (file)
@@ -253,7 +253,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
        mvif->sta.wcid.phy_idx = ext_phy;
        mvif->sta.wcid.hw_key_idx = -1;
        mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
-       mt76_packet_id_init(&mvif->sta.wcid);
+       mt76_wcid_init(&mvif->sta.wcid);
 
        mt7915_mac_wtbl_update(dev, idx,
                               MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -314,7 +314,7 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw,
                list_del_init(&msta->wcid.poll_list);
        spin_unlock_bh(&dev->mt76.sta_poll_lock);
 
-       mt76_packet_id_flush(&dev->mt76, &msta->wcid);
+       mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
 }
 
 int mt7915_set_channel(struct mt7915_phy *phy)
@@ -483,16 +483,22 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
        if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
                bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
                bool band = phy->mt76->band_idx;
+               u32 rxfilter = phy->rxfilter;
 
-               if (!enabled)
-                       phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC;
-               else
-                       phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;
+               if (!enabled) {
+                       rxfilter |= MT_WF_RFCR_DROP_OTHER_UC;
+                       dev->monitor_mask &= ~BIT(band);
+               } else {
+                       rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;
+                       dev->monitor_mask |= BIT(band);
+               }
 
                mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN,
                               enabled);
+               mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_MDP_DCR0_RX_HDR_TRANS_EN,
+                              !dev->monitor_mask);
                mt76_testmode_reset(phy->mt76, true);
-               mt76_wr(dev, MT_WF_RFCR(band), phy->rxfilter);
+               mt76_wr(dev, MT_WF_RFCR(band), rxfilter);
        }
 
        mutex_unlock(&dev->mt76.mutex);
@@ -527,6 +533,7 @@ static void mt7915_configure_filter(struct ieee80211_hw *hw,
                        MT_WF_RFCR1_DROP_BA |
                        MT_WF_RFCR1_DROP_CFEND |
                        MT_WF_RFCR1_DROP_CFACK;
+       u32 rxfilter;
        u32 flags = 0;
 
 #define MT76_FILTER(_flag, _hw) do {                                   \
@@ -561,7 +568,12 @@ static void mt7915_configure_filter(struct ieee80211_hw *hw,
                             MT_WF_RFCR_DROP_NDPA);
 
        *total_flags = flags;
-       mt76_wr(dev, MT_WF_RFCR(band), phy->rxfilter);
+       rxfilter = phy->rxfilter;
+       if (hw->conf.flags & IEEE80211_CONF_MONITOR)
+               rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;
+       else
+               rxfilter |= MT_WF_RFCR_DROP_OTHER_UC;
+       mt76_wr(dev, MT_WF_RFCR(band), rxfilter);
 
        if (*total_flags & FIF_CONTROL)
                mt76_clear(dev, MT_WF_RFCR1(band), ctl_flags);
@@ -646,11 +658,13 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
                mt7915_update_bss_color(hw, vif, &info->he_bss_color);
 
        if (changed & (BSS_CHANGED_BEACON |
-                      BSS_CHANGED_BEACON_ENABLED |
-                      BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
-                      BSS_CHANGED_FILS_DISCOVERY))
+                      BSS_CHANGED_BEACON_ENABLED))
                mt7915_mcu_add_beacon(hw, vif, info->enable_beacon, changed);
 
+       if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+                      BSS_CHANGED_FILS_DISCOVERY))
+               mt7915_mcu_add_inband_discov(dev, vif, changed);
+
        if (set_bss_info == 0)
                mt7915_mcu_add_bss_info(phy, vif, false);
        if (set_sta == 0)
@@ -1386,7 +1400,7 @@ void mt7915_get_et_strings(struct ieee80211_hw *hw,
        if (sset != ETH_SS_STATS)
                return;
 
-       memcpy(data, *mt7915_gstrings_stats, sizeof(mt7915_gstrings_stats));
+       memcpy(data, mt7915_gstrings_stats, sizeof(mt7915_gstrings_stats));
        data += sizeof(mt7915_gstrings_stats);
        page_pool_ethtool_stats_get_strings(data);
 }
@@ -1639,6 +1653,20 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
 
        return 0;
 }
+
+static int
+mt7915_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                   struct net_device *netdev, enum tc_setup_type type,
+                   void *type_data)
+{
+       struct mt7915_dev *dev = mt7915_hw_dev(hw);
+       struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+
+       if (!mtk_wed_device_active(wed))
+               return -EOPNOTSUPP;
+
+       return mtk_wed_device_setup_tc(wed, netdev, type, type_data);
+}
 #endif
 
 const struct ieee80211_ops mt7915_ops = {
@@ -1693,5 +1721,6 @@ const struct ieee80211_ops mt7915_ops = {
        .set_radar_background = mt7915_set_radar_background,
 #ifdef CONFIG_NET_MEDIATEK_SOC_WED
        .net_fill_forward_path = mt7915_net_fill_forward_path,
+       .net_setup_tc = mt7915_net_setup_tc,
 #endif
 };
index 50ae7bf..b22f06d 100644 (file)
@@ -225,8 +225,10 @@ int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
 static void
 mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
 {
-       if (vif->bss_conf.csa_active)
-               ieee80211_csa_finish(vif);
+       if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
+               return;
+
+       ieee80211_csa_finish(vif);
 }
 
 static void
@@ -326,7 +328,7 @@ mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb)
 static void
 mt7915_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
 {
-       if (!vif->bss_conf.color_change_active)
+       if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION)
                return;
 
        ieee80211_color_change_finish(vif);
@@ -906,6 +908,8 @@ mt7915_mcu_sta_muru_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
                HE_MAC(CAP2_MU_CASCADING, elem->mac_cap_info[2]);
        muru->ofdma_ul.uo_ra =
                HE_MAC(CAP3_OFDMA_RA, elem->mac_cap_info[3]);
+       muru->ofdma_ul.rx_ctrl_frame_to_mbss =
+               HE_MAC(CAP3_RX_CTRL_FRAME_TO_MULTIBSS, elem->mac_cap_info[3]);
 }
 
 static void
@@ -1015,13 +1019,13 @@ mt7915_is_ebf_supported(struct mt7915_phy *phy, struct ieee80211_vif *vif,
                        struct ieee80211_sta *sta, bool bfee)
 {
        struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
-       int tx_ant = hweight8(phy->mt76->chainmask) - 1;
+       int sts = hweight16(phy->mt76->chainmask);
 
        if (vif->type != NL80211_IFTYPE_STATION &&
            vif->type != NL80211_IFTYPE_AP)
                return false;
 
-       if (!bfee && tx_ant < 2)
+       if (!bfee && sts < 2)
                return false;
 
        if (sta->deflink.he_cap.has_he) {
@@ -1882,10 +1886,9 @@ mt7915_mcu_beacon_cont(struct mt7915_dev *dev, struct ieee80211_vif *vif,
        memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
 }
 
-static void
-mt7915_mcu_beacon_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif,
-                               struct sk_buff *rskb, struct bss_info_bcn *bcn,
-                               u32 changed)
+int
+mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+                            u32 changed)
 {
 #define OFFLOAD_TX_MODE_SU     BIT(0)
 #define OFFLOAD_TX_MODE_MU     BIT(1)
@@ -1895,14 +1898,27 @@ mt7915_mcu_beacon_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vi
        struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
        enum nl80211_band band = chandef->chan->band;
        struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+       struct bss_info_bcn *bcn;
        struct bss_info_inband_discovery *discov;
        struct ieee80211_tx_info *info;
-       struct sk_buff *skb = NULL;
-       struct tlv *tlv;
+       struct sk_buff *rskb, *skb = NULL;
+       struct tlv *tlv, *sub_tlv;
        bool ext_phy = phy != &dev->phy;
        u8 *buf, interval;
        int len;
 
+       if (vif->bss_conf.nontransmitted)
+               return 0;
+
+       rskb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL,
+                                              MT7915_MAX_BSS_OFFLOAD_SIZE);
+       if (IS_ERR(rskb))
+               return PTR_ERR(rskb);
+
+       tlv = mt76_connac_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof(*bcn));
+       bcn = (struct bss_info_bcn *)tlv;
+       bcn->enable = true;
+
        if (changed & BSS_CHANGED_FILS_DISCOVERY &&
            vif->bss_conf.fils_discovery.max_interval) {
                interval = vif->bss_conf.fils_discovery.max_interval;
@@ -1913,27 +1929,29 @@ mt7915_mcu_beacon_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vi
                skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
        }
 
-       if (!skb)
-               return;
+       if (!skb) {
+               dev_kfree_skb(rskb);
+               return -EINVAL;
+       }
 
        info = IEEE80211_SKB_CB(skb);
        info->control.vif = vif;
        info->band = band;
-
        info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, ext_phy);
 
        len = sizeof(*discov) + MT_TXD_SIZE + skb->len;
        len = (len & 0x3) ? ((len | 0x3) + 1) : len;
 
-       if (len > (MT7915_MAX_BSS_OFFLOAD_SIZE - rskb->len)) {
+       if (skb->len > MT7915_MAX_BEACON_SIZE) {
                dev_err(dev->mt76.dev, "inband discovery size limit exceed\n");
+               dev_kfree_skb(rskb);
                dev_kfree_skb(skb);
-               return;
+               return -EINVAL;
        }
 
-       tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_DISCOV,
-                                          len, &bcn->sub_ntlv, &bcn->len);
-       discov = (struct bss_info_inband_discovery *)tlv;
+       sub_tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_DISCOV,
+                                              len, &bcn->sub_ntlv, &bcn->len);
+       discov = (struct bss_info_inband_discovery *)sub_tlv;
        discov->tx_mode = OFFLOAD_TX_MODE_SU;
        /* 0: UNSOL PROBE RESP, 1: FILS DISCOV */
        discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY);
@@ -1941,13 +1959,16 @@ mt7915_mcu_beacon_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vi
        discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
        discov->enable = true;
 
-       buf = (u8 *)tlv + sizeof(*discov);
+       buf = (u8 *)sub_tlv + sizeof(*discov);
 
        mt7915_mac_write_txwi(&dev->mt76, (__le32 *)buf, skb, wcid, 0, NULL,
                              0, changed);
        memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
 
        dev_kfree_skb(skb);
+
+       return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+                                    MCU_EXT_CMD(BSS_INFO_UPDATE), true);
 }
 
 int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
@@ -1980,11 +2001,14 @@ int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                goto out;
 
        skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
-       if (!skb)
+       if (!skb) {
+               dev_kfree_skb(rskb);
                return -EINVAL;
+       }
 
-       if (skb->len > MT7915_MAX_BEACON_SIZE - MT_TXD_SIZE) {
+       if (skb->len > MT7915_MAX_BEACON_SIZE) {
                dev_err(dev->mt76.dev, "Bcn size limit exceed\n");
+               dev_kfree_skb(rskb);
                dev_kfree_skb(skb);
                return -EINVAL;
        }
@@ -1997,11 +2021,6 @@ int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        mt7915_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
        dev_kfree_skb(skb);
 
-       if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP ||
-           changed & BSS_CHANGED_FILS_DISCOVERY)
-               mt7915_mcu_beacon_inband_discov(dev, vif, rskb,
-                                               bcn, changed);
-
 out:
        return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
                                     MCU_EXT_CMD(BSS_INFO_UPDATE), true);
@@ -2725,10 +2744,10 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
        if (mt76_connac_spe_idx(phy->mt76->antenna_mask))
                req.tx_path_num = fls(phy->mt76->antenna_mask);
 
-       if (cmd == MCU_EXT_CMD(SET_RX_PATH) ||
-           dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
+       if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR)
                req.switch_reason = CH_SWITCH_NORMAL;
-       else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+       else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL ||
+                phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE)
                req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
        else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
                                          NL80211_IFTYPE_AP))
index b9ea297..1592b5d 100644 (file)
@@ -495,10 +495,14 @@ enum {
        SER_RECOVER
 };
 
-#define MT7915_MAX_BEACON_SIZE         512
-#define MT7915_MAX_INBAND_FRAME_SIZE   256
-#define MT7915_MAX_BSS_OFFLOAD_SIZE    (MT7915_MAX_BEACON_SIZE +         \
-                                        MT7915_MAX_INBAND_FRAME_SIZE +   \
+#define MT7915_MAX_BEACON_SIZE         1308
+#define MT7915_BEACON_UPDATE_SIZE      (sizeof(struct sta_req_hdr) +   \
+                                        sizeof(struct bss_info_bcn) +  \
+                                        sizeof(struct bss_info_bcn_cntdwn) +   \
+                                        sizeof(struct bss_info_bcn_mbss) +     \
+                                        MT_TXD_SIZE +  \
+                                        sizeof(struct bss_info_bcn_cont))
+#define MT7915_MAX_BSS_OFFLOAD_SIZE    (MT7915_MAX_BEACON_SIZE +       \
                                         MT7915_BEACON_UPDATE_SIZE)
 
 #define MT7915_BSS_UPDATE_MAX_SIZE     (sizeof(struct sta_req_hdr) +   \
@@ -511,12 +515,6 @@ enum {
                                         sizeof(struct bss_info_bmc_rate) +\
                                         sizeof(struct bss_info_ext_bss))
 
-#define MT7915_BEACON_UPDATE_SIZE      (sizeof(struct sta_req_hdr) +   \
-                                        sizeof(struct bss_info_bcn_cntdwn) + \
-                                        sizeof(struct bss_info_bcn_mbss) + \
-                                        sizeof(struct bss_info_bcn_cont) + \
-                                        sizeof(struct bss_info_inband_discovery))
-
 static inline s8
 mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower)
 {
index 0456e56..d317c52 100644 (file)
@@ -295,6 +295,8 @@ struct mt7915_dev {
        bool muru_debug;
        bool ibf;
 
+       u8 monitor_mask;
+
        struct dentry *debugfs_dir;
        struct rchan *relay_fwlog;
 
@@ -447,6 +449,8 @@ int mt7915_mcu_add_rx_ba(struct mt7915_dev *dev,
                         bool add);
 int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vif,
                                struct cfg80211_he_bss_color *he_bss_color);
+int mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+                                u32 changed);
 int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                          int enable, u32 changed);
 int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif,
index 588cd87..89ac8e6 100644 (file)
@@ -172,6 +172,7 @@ enum offs_rev {
 
 #define MT_MDP_DCR0                    MT_MDP(0x000)
 #define MT_MDP_DCR0_DAMSDU_EN          BIT(15)
+#define MT_MDP_DCR0_RX_HDR_TRANS_EN    BIT(19)
 
 #define MT_MDP_DCR1                    MT_MDP(0x004)
 #define MT_MDP_DCR1_MAX_RX_LEN         GENMASK(15, 3)
index 37348b2..06e3d9d 100644 (file)
@@ -1219,10 +1219,7 @@ static int mt798x_wmac_init(struct mt7915_dev *dev)
                return PTR_ERR(dev->sku);
 
        dev->rstc = devm_reset_control_get(pdev, "consys");
-       if (IS_ERR(dev->rstc))
-               return PTR_ERR(dev->rstc);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(dev->rstc);
 }
 
 static int mt798x_wmac_probe(struct platform_device *pdev)
index ff63f37..55baac7 100644 (file)
@@ -55,10 +55,56 @@ static int mt7921_thermal_init(struct mt792x_phy *phy)
 
        hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy,
                                                       mt7921_hwmon_groups);
-       if (IS_ERR(hwmon))
-               return PTR_ERR(hwmon);
+       return PTR_ERR_OR_ZERO(hwmon);
+}
 
-       return 0;
+static void
+mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
+{
+#define IS_UNII_INVALID(idx, sfreq, efreq) \
+       (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
+       struct ieee80211_supported_band *sband;
+       struct mt76_dev *mdev = &dev->mt76;
+       struct device_node *np, *band_np;
+       struct ieee80211_channel *ch;
+       int i, cfreq;
+
+       np = mt76_find_power_limits_node(mdev);
+
+       sband = wiphy->bands[NL80211_BAND_5GHZ];
+       band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
+       for (i = 0; i < sband->n_channels; i++) {
+               ch = &sband->channels[i];
+               cfreq = ch->center_freq;
+
+               if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
+                       ch->flags |= IEEE80211_CHAN_DISABLED;
+                       continue;
+               }
+
+               /* UNII-4 */
+               if (IS_UNII_INVALID(0, 5850, 5925))
+                       ch->flags |= IEEE80211_CHAN_DISABLED;
+       }
+
+       sband = wiphy->bands[NL80211_BAND_6GHZ];
+       band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL;
+       for (i = 0; i < sband->n_channels; i++) {
+               ch = &sband->channels[i];
+               cfreq = ch->center_freq;
+
+               if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
+                       ch->flags |= IEEE80211_CHAN_DISABLED;
+                       continue;
+               }
+
+               /* UNII-5/6/7/8 */
+               if (IS_UNII_INVALID(1, 5925, 6425) ||
+                   IS_UNII_INVALID(2, 6425, 6525) ||
+                   IS_UNII_INVALID(3, 6525, 6875) ||
+                   IS_UNII_INVALID(4, 6875, 7125))
+                       ch->flags |= IEEE80211_CHAN_DISABLED;
+       }
 }
 
 static void
@@ -77,6 +123,8 @@ mt7921_regd_notifier(struct wiphy *wiphy,
        mt76_connac_mcu_set_channel_domain(hw->priv);
        mt7921_set_tx_sar_pwr(hw, NULL);
        mt792x_mutex_release(dev);
+
+       mt7921_regd_channel_update(wiphy, dev);
 }
 
 int mt7921_mac_init(struct mt792x_dev *dev)
index 21f9374..867e14f 100644 (file)
@@ -794,7 +794,7 @@ int mt7921_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
        mt7921_usb_sdio_write_txwi(dev, wcid, qid, sta, key, pktid, skb);
 
        type = mt76_is_sdio(mdev) ? MT7921_SDIO_DATA : 0;
-       mt7921_skb_add_usb_sdio_hdr(dev, skb, type);
+       mt792x_skb_add_usb_sdio_hdr(dev, skb, type);
        pad = round_up(skb->len, 4) - skb->len;
        if (mt76_is_usb(mdev))
                pad += 4;
index 62e6da1..510a575 100644 (file)
@@ -259,25 +259,6 @@ static int mt7921_start(struct ieee80211_hw *hw)
        return err;
 }
 
-void mt7921_stop(struct ieee80211_hw *hw)
-{
-       struct mt792x_dev *dev = mt792x_hw_dev(hw);
-       struct mt792x_phy *phy = mt792x_hw_phy(hw);
-
-       cancel_delayed_work_sync(&phy->mt76->mac_work);
-
-       cancel_delayed_work_sync(&dev->pm.ps_work);
-       cancel_work_sync(&dev->pm.wake_work);
-       cancel_work_sync(&dev->reset_work);
-       mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
-
-       mt792x_mutex_acquire(dev);
-       clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-       mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, false, false);
-       mt792x_mutex_release(dev);
-}
-EXPORT_SYMBOL_GPL(mt7921_stop);
-
 static int
 mt7921_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
@@ -315,7 +296,7 @@ mt7921_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        mvif->sta.wcid.phy_idx = mvif->mt76.band_idx;
        mvif->sta.wcid.hw_key_idx = -1;
        mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
-       mt76_packet_id_init(&mvif->sta.wcid);
+       mt76_wcid_init(&mvif->sta.wcid);
 
        mt7921_mac_wtbl_update(dev, idx,
                               MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -701,6 +682,38 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
        mt792x_mutex_release(dev);
 }
 
+static void
+mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_phy *phy = mvif->phy;
+       struct mt792x_dev *dev = phy->dev;
+
+       if (hweight64(dev->mt76.vif_mask) > 1) {
+               phy->power_type = MT_AP_DEFAULT;
+               goto out;
+       }
+
+       switch (vif->bss_conf.power_type) {
+       case IEEE80211_REG_SP_AP:
+               phy->power_type = MT_AP_SP;
+               break;
+       case IEEE80211_REG_VLP_AP:
+               phy->power_type = MT_AP_VLP;
+               break;
+       case IEEE80211_REG_LPI_AP:
+               phy->power_type = MT_AP_LPI;
+               break;
+       case IEEE80211_REG_UNSET_AP:
+       default:
+               phy->power_type = MT_AP_DEFAULT;
+               break;
+       }
+
+out:
+       mt7921_mcu_set_clc(dev, dev->mt76.alpha2, dev->country_ie_env);
+}
+
 int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
                       struct ieee80211_sta *sta)
 {
@@ -736,6 +749,8 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
        if (ret)
                return ret;
 
+       mt7921_regd_set_6ghz_power_type(vif);
+
        mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
 
        return 0;
@@ -753,7 +768,7 @@ void mt7921_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
 
        if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
                mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid,
-                                           true, mvif->ctx);
+                                           true, mvif->mt76.ctx);
 
        ewma_avg_signal_init(&msta->avg_ack_signal);
 
@@ -788,7 +803,7 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
                if (!sta->tdls)
                        mt76_connac_mcu_uni_add_bss(&dev->mphy, vif,
                                                    &mvif->sta.wcid, false,
-                                                   mvif->ctx);
+                                                   mvif->mt76.ctx);
        }
 
        spin_lock_bh(&dev->mt76.sta_poll_lock);
@@ -1205,7 +1220,7 @@ mt7921_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        mt792x_mutex_acquire(dev);
 
        err = mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid,
-                                         true, mvif->ctx);
+                                         true, mvif->mt76.ctx);
        if (err)
                goto out;
 
@@ -1237,7 +1252,7 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                goto out;
 
        mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false,
-                                   mvif->ctx);
+                                   mvif->mt76.ctx);
 
 out:
        mt792x_mutex_release(dev);
@@ -1262,7 +1277,7 @@ static void mt7921_ctx_iter(void *priv, u8 *mac,
        struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
        struct ieee80211_chanctx_conf *ctx = priv;
 
-       if (ctx != mvif->ctx)
+       if (ctx != mvif->mt76.ctx)
                return;
 
        if (vif->type == NL80211_IFTYPE_MONITOR)
@@ -1295,7 +1310,7 @@ static void mt7921_mgd_prepare_tx(struct ieee80211_hw *hw,
                       jiffies_to_msecs(HZ);
 
        mt792x_mutex_acquire(dev);
-       mt7921_set_roc(mvif->phy, mvif, mvif->ctx->def.chan, duration,
+       mt7921_set_roc(mvif->phy, mvif, mvif->mt76.ctx->def.chan, duration,
                       MT7921_ROC_REQ_JOIN);
        mt792x_mutex_release(dev);
 }
@@ -1312,7 +1327,7 @@ static void mt7921_mgd_complete_tx(struct ieee80211_hw *hw,
 const struct ieee80211_ops mt7921_ops = {
        .tx = mt792x_tx,
        .start = mt7921_start,
-       .stop = mt7921_stop,
+       .stop = mt792x_stop,
        .add_interface = mt7921_add_interface,
        .remove_interface = mt792x_remove_interface,
        .config = mt7921_config,
index 90c9397..63f3d4a 100644 (file)
@@ -448,6 +448,129 @@ out:
        return ret;
 }
 
+static void mt7921_mcu_parse_tx_resource(struct mt76_dev *dev,
+                                        struct sk_buff *skb)
+{
+       struct mt76_sdio *sdio = &dev->sdio;
+       struct mt7921_tx_resource {
+               __le32 version;
+               __le32 pse_data_quota;
+               __le32 pse_mcu_quota;
+               __le32 ple_data_quota;
+               __le32 ple_mcu_quota;
+               __le16 pse_page_size;
+               __le16 ple_page_size;
+               u8 pp_padding;
+               u8 pad[3];
+       } __packed * tx_res;
+
+       tx_res = (struct mt7921_tx_resource *)skb->data;
+       sdio->sched.pse_data_quota = le32_to_cpu(tx_res->pse_data_quota);
+       sdio->sched.pse_mcu_quota = le32_to_cpu(tx_res->pse_mcu_quota);
+       sdio->sched.ple_data_quota = le32_to_cpu(tx_res->ple_data_quota);
+       sdio->sched.pse_page_size = le16_to_cpu(tx_res->pse_page_size);
+       sdio->sched.deficit = tx_res->pp_padding;
+}
+
+static void mt7921_mcu_parse_phy_cap(struct mt76_dev *dev,
+                                    struct sk_buff *skb)
+{
+       struct mt7921_phy_cap {
+               u8 ht;
+               u8 vht;
+               u8 _5g;
+               u8 max_bw;
+               u8 nss;
+               u8 dbdc;
+               u8 tx_ldpc;
+               u8 rx_ldpc;
+               u8 tx_stbc;
+               u8 rx_stbc;
+               u8 hw_path;
+               u8 he;
+       } __packed * cap;
+
+       enum {
+               WF0_24G,
+               WF0_5G
+       };
+
+       cap = (struct mt7921_phy_cap *)skb->data;
+
+       dev->phy.antenna_mask = BIT(cap->nss) - 1;
+       dev->phy.chainmask = dev->phy.antenna_mask;
+       dev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G);
+       dev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G);
+}
+
+static int mt7921_mcu_get_nic_capability(struct mt792x_phy *mphy)
+{
+       struct mt76_connac_cap_hdr {
+               __le16 n_element;
+               u8 rsv[2];
+       } __packed * hdr;
+       struct sk_buff *skb;
+       struct mt76_phy *phy = mphy->mt76;
+       int ret, i;
+
+       ret = mt76_mcu_send_and_get_msg(phy->dev, MCU_CE_CMD(GET_NIC_CAPAB),
+                                       NULL, 0, true, &skb);
+       if (ret)
+               return ret;
+
+       hdr = (struct mt76_connac_cap_hdr *)skb->data;
+       if (skb->len < sizeof(*hdr)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       skb_pull(skb, sizeof(*hdr));
+
+       for (i = 0; i < le16_to_cpu(hdr->n_element); i++) {
+               struct tlv_hdr {
+                       __le32 type;
+                       __le32 len;
+               } __packed * tlv = (struct tlv_hdr *)skb->data;
+               int len;
+
+               if (skb->len < sizeof(*tlv))
+                       break;
+
+               skb_pull(skb, sizeof(*tlv));
+
+               len = le32_to_cpu(tlv->len);
+               if (skb->len < len)
+                       break;
+
+               switch (le32_to_cpu(tlv->type)) {
+               case MT_NIC_CAP_6G:
+                       phy->cap.has_6ghz = skb->data[0];
+                       break;
+               case MT_NIC_CAP_MAC_ADDR:
+                       memcpy(phy->macaddr, (void *)skb->data, ETH_ALEN);
+                       break;
+               case MT_NIC_CAP_PHY:
+                       mt7921_mcu_parse_phy_cap(phy->dev, skb);
+                       break;
+               case MT_NIC_CAP_TX_RESOURCE:
+                       if (mt76_is_sdio(phy->dev))
+                               mt7921_mcu_parse_tx_resource(phy->dev,
+                                                            skb);
+                       break;
+               case MT_NIC_CAP_CHIP_CAP:
+                       memcpy(&mphy->chip_cap, (void *)skb->data, sizeof(u64));
+                       break;
+               default:
+                       break;
+               }
+               skb_pull(skb, len);
+       }
+out:
+       dev_kfree_skb(skb);
+
+       return ret;
+}
+
 int mt7921_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl)
 {
        struct {
@@ -469,7 +592,7 @@ int mt7921_run_firmware(struct mt792x_dev *dev)
        if (err)
                return err;
 
-       err = mt76_connac_mcu_get_nic_capability(&dev->mphy);
+       err = mt7921_mcu_get_nic_capability(&dev->phy);
        if (err)
                return err;
 
@@ -1123,7 +1246,9 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
                         struct mt7921_clc *clc,
                         u8 idx)
 {
-       struct sk_buff *skb;
+#define CLC_CAP_EVT_EN BIT(0)
+#define CLC_CAP_DTS_EN BIT(1)
+       struct sk_buff *skb, *ret_skb = NULL;
        struct {
                u8 ver;
                u8 pad0;
@@ -1131,13 +1256,15 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
                u8 idx;
                u8 env;
                u8 acpi_conf;
-               u8 pad1;
+               u8 cap;
                u8 alpha2[2];
                u8 type[2];
-               u8 rsvd[64];
+               u8 env_6g;
+               u8 rsvd[63];
        } __packed req = {
                .idx = idx,
                .env = env_cap,
+               .env_6g = dev->phy.power_type,
                .acpi_conf = mt792x_acpi_get_flags(&dev->phy),
        };
        int ret, valid_cnt = 0;
@@ -1146,6 +1273,11 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
        if (!clc)
                return 0;
 
+       if (dev->phy.chip_cap & MT792x_CHIP_CAP_CLC_EVT_EN)
+               req.cap |= CLC_CAP_EVT_EN;
+       if (mt76_find_power_limits_node(&dev->mt76))
+               req.cap |= CLC_CAP_DTS_EN;
+
        pos = clc->data;
        for (i = 0; i < clc->nr_country; i++) {
                struct mt7921_clc_rule *rule = (struct mt7921_clc_rule *)pos;
@@ -1167,10 +1299,21 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
                        return -ENOMEM;
                skb_put_data(skb, rule->data, len);
 
-               ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
-                                           MCU_CE_CMD(SET_CLC), false);
+               ret = mt76_mcu_skb_send_and_get_msg(&dev->mt76, skb,
+                                                   MCU_CE_CMD(SET_CLC),
+                                                   !!(req.cap & CLC_CAP_EVT_EN),
+                                                   &ret_skb);
                if (ret < 0)
                        return ret;
+
+               if (ret_skb) {
+                       struct mt7921_clc_info_tlv *info;
+
+                       info = (struct mt7921_clc_info_tlv *)(ret_skb->data + 4);
+                       dev->phy.clc_chan_conf = info->chan_conf;
+                       dev_kfree_skb(ret_skb);
+               }
+
                valid_cnt++;
        }
 
index 9b0aa3b..f9a259e 100644 (file)
@@ -99,4 +99,17 @@ struct mt7921_rftest_evt {
        __le32 param0;
        __le32 param1;
 } __packed;
+
+struct mt7921_clc_info_tlv {
+       __le16 tag;
+       __le16 len;
+
+       u8 chan_conf; /* BIT(0) : Enable UNII-4
+                      * BIT(1) : Enable UNII-5
+                      * BIT(2) : Enable UNII-6
+                      * BIT(3) : Enable UNII-7
+                      * BIT(4) : Enable UNII-8
+                      */
+       u8 rsv[63];
+} __packed;
 #endif
index 87dd068..f286211 100644 (file)
 #define MT7921_SKU_MAX_DELTA_IDX       MT7921_SKU_RATE_NUM
 #define MT7921_SKU_TABLE_SIZE          (MT7921_SKU_RATE_NUM + 1)
 
-#define MT7921_SDIO_HDR_TX_BYTES       GENMASK(15, 0)
-#define MT7921_SDIO_HDR_PKT_TYPE       GENMASK(17, 16)
-
 #define MCU_UNI_EVENT_ROC  0x27
+#define MCU_UNI_EVENT_CLC  0x80
 
 enum {
        UNI_ROC_ACQUIRE,
@@ -235,20 +233,6 @@ mt7921_l1_rmw(struct mt792x_dev *dev, u32 addr, u32 mask, u32 val)
 #define mt7921_l1_set(dev, addr, val)  mt7921_l1_rmw(dev, addr, 0, val)
 #define mt7921_l1_clear(dev, addr, val)        mt7921_l1_rmw(dev, addr, val, 0)
 
-static inline void
-mt7921_skb_add_usb_sdio_hdr(struct mt792x_dev *dev, struct sk_buff *skb,
-                           int type)
-{
-       u32 hdr, len;
-
-       len = mt76_is_usb(&dev->mt76) ? skb->len : skb->len + sizeof(hdr);
-       hdr = FIELD_PREP(MT7921_SDIO_HDR_TX_BYTES, len) |
-             FIELD_PREP(MT7921_SDIO_HDR_PKT_TYPE, type);
-
-       put_unaligned_le32(hdr, skb_push(skb, sizeof(hdr)));
-}
-
-void mt7921_stop(struct ieee80211_hw *hw);
 int mt7921_mac_init(struct mt792x_dev *dev);
 bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask);
 int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
index 3dda84a..f04e709 100644 (file)
@@ -17,6 +17,8 @@ static const struct pci_device_id mt7921_pci_device_table[] = {
                .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
        { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922),
                .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM },
+       { PCI_DEVICE(PCI_VENDOR_ID_ITTIM, 0x7922),
+               .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM },
        { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608),
                .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
        { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616),
index e7a995e..c866144 100644 (file)
@@ -48,7 +48,7 @@ int mt7921e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
        memset(txp, 0, sizeof(struct mt76_connac_hw_txp));
        mt76_connac_write_hw_txp(mdev, tx_info, txp, id);
 
-       tx_info->skb = DMA_DUMMY_DATA;
+       tx_info->skb = NULL;
 
        return 0;
 }
index 310eeca..5e4501d 100644 (file)
@@ -38,7 +38,7 @@ mt7921s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
        if (cmd == MCU_CMD(FW_SCATTER))
                type = MT7921_SDIO_FWDL;
 
-       mt7921_skb_add_usb_sdio_hdr(dev, skb, type);
+       mt792x_skb_add_usb_sdio_hdr(dev, skb, type);
        pad = round_up(skb->len, 4) - skb->len;
        __skb_put_zero(skb, pad);
 
index 59cd3d9..e5258c7 100644 (file)
@@ -43,7 +43,7 @@ mt7921u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
        else
                ep = MT_EP_OUT_AC_BE;
 
-       mt7921_skb_add_usb_sdio_hdr(dev, skb, 0);
+       mt792x_skb_add_usb_sdio_hdr(dev, skb, 0);
        pad = round_up(skb->len, 4) + 4 - skb->len;
        __skb_put_zero(skb, pad);
 
@@ -135,14 +135,6 @@ out:
        return err;
 }
 
-static void mt7921u_stop(struct ieee80211_hw *hw)
-{
-       struct mt792x_dev *dev = mt792x_hw_dev(hw);
-
-       mt76u_stop_tx(&dev->mt76);
-       mt7921_stop(hw);
-}
-
 static int mt7921u_probe(struct usb_interface *usb_intf,
                         const struct usb_device_id *id)
 {
@@ -189,7 +181,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf,
        if (!ops)
                return -ENOMEM;
 
-       ops->stop = mt7921u_stop;
+       ops->stop = mt792xu_stop;
        mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops);
        if (!mdev)
                return -ENOMEM;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig
new file mode 100644 (file)
index 0000000..5854e95
--- /dev/null
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: ISC
+config MT7925_COMMON
+       tristate
+       select MT792x_LIB
+       select WANT_DEV_COREDUMP
+
+config MT7925E
+       tristate "MediaTek MT7925E (PCIe) support"
+       select MT7925_COMMON
+       depends on MAC80211
+       depends on PCI
+       help
+         This adds support for MT7925-based wireless PCIe devices,
+         which support operation at 6GHz, 5GHz, and 2.4GHz IEEE 802.11be
+         2x2:2SS 4096-QAM, 160MHz channels.
+
+         To compile this driver as a module, choose M here.
+
+config MT7925U
+       tristate "MediaTek MT7925U (USB) support"
+       select MT792x_USB
+       select MT7925_COMMON
+       depends on MAC80211
+       depends on USB
+       help
+         This adds support for MT7925-based wireless USB devices,
+         which support operation at 6GHz, 5GHz, and 2.4GHz IEEE 802.11be
+         2x2:2SS 4096-QAM, 160MHz channels.
+
+         To compile this driver as a module, choose M here.
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
new file mode 100644 (file)
index 0000000..d321e4e
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: ISC
+
+obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
+obj-$(CONFIG_MT7925E) += mt7925e.o
+obj-$(CONFIG_MT7925U) += mt7925u.o
+
+mt7925-common-y := mac.o mcu.o main.o init.o debugfs.o
+mt7925e-y := pci.o pci_mac.o pci_mcu.o
+mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
new file mode 100644 (file)
index 0000000..1e2fc65
--- /dev/null
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include "mt7925.h"
+#include "mcu.h"
+
+static int
+mt7925_reg_set(void *data, u64 val)
+{
+       struct mt792x_dev *dev = data;
+       u32 regval = val;
+
+       mt792x_mutex_acquire(dev);
+       mt7925_mcu_regval(dev, dev->mt76.debugfs_reg, &regval, true);
+       mt792x_mutex_release(dev);
+
+       return 0;
+}
+
+static int
+mt7925_reg_get(void *data, u64 *val)
+{
+       struct mt792x_dev *dev = data;
+       u32 regval;
+       int ret;
+
+       mt792x_mutex_acquire(dev);
+       ret = mt7925_mcu_regval(dev, dev->mt76.debugfs_reg, &regval, false);
+       mt792x_mutex_release(dev);
+       if (!ret)
+               *val = regval;
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt7925_reg_get, mt7925_reg_set,
+                        "0x%08llx\n");
+static int
+mt7925_fw_debug_set(void *data, u64 val)
+{
+       struct mt792x_dev *dev = data;
+
+       mt792x_mutex_acquire(dev);
+
+       dev->fw_debug = (u8)val;
+       mt7925_mcu_fw_log_2_host(dev, dev->fw_debug);
+
+       mt792x_mutex_release(dev);
+
+       return 0;
+}
+
+static int
+mt7925_fw_debug_get(void *data, u64 *val)
+{
+       struct mt792x_dev *dev = data;
+
+       *val = dev->fw_debug;
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug, mt7925_fw_debug_get,
+                        mt7925_fw_debug_set, "%lld\n");
+
+DEFINE_SHOW_ATTRIBUTE(mt792x_tx_stats);
+
+static void
+mt7925_seq_puts_array(struct seq_file *file, const char *str,
+                     s8 val[][2], int len, u8 band_idx)
+{
+       int i;
+
+       seq_printf(file, "%-22s:", str);
+       for (i = 0; i < len; i++)
+               if (val[i][band_idx] == 127)
+                       seq_printf(file, " %6s", "N.A");
+               else
+                       seq_printf(file, " %6d", val[i][band_idx]);
+       seq_puts(file, "\n");
+}
+
+#define mt7925_print_txpwr_entry(prefix, rate, idx)    \
+({                                                     \
+       mt7925_seq_puts_array(s, #prefix " (tmac)",     \
+                             txpwr->rate,              \
+                             ARRAY_SIZE(txpwr->rate),  \
+                             idx);                     \
+})
+
+static inline void
+mt7925_eht_txpwr(struct seq_file *s, struct mt7925_txpwr *txpwr, u8 band_idx)
+{
+       seq_printf(s, "%-22s  %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+                  " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
+                  "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11",
+                  "mcs12", "mcs13", "mcs14", "mcs15");
+       mt7925_print_txpwr_entry(EHT26, eht26, band_idx);
+       mt7925_print_txpwr_entry(EHT52, eht52, band_idx);
+       mt7925_print_txpwr_entry(EHT106, eht106, band_idx);
+       mt7925_print_txpwr_entry(EHT242, eht242, band_idx);
+       mt7925_print_txpwr_entry(EHT484, eht484, band_idx);
+
+       mt7925_print_txpwr_entry(EHT996, eht996, band_idx);
+       mt7925_print_txpwr_entry(EHT996x2, eht996x2, band_idx);
+       mt7925_print_txpwr_entry(EHT996x4, eht996x4, band_idx);
+       mt7925_print_txpwr_entry(EHT26_52, eht26_52, band_idx);
+       mt7925_print_txpwr_entry(EHT26_106, eht26_106, band_idx);
+       mt7925_print_txpwr_entry(EHT484_242, eht484_242, band_idx);
+       mt7925_print_txpwr_entry(EHT996_484, eht996_484, band_idx);
+       mt7925_print_txpwr_entry(EHT996_484_242, eht996_484_242, band_idx);
+       mt7925_print_txpwr_entry(EHT996x2_484, eht996x2_484, band_idx);
+       mt7925_print_txpwr_entry(EHT996x3, eht996x3, band_idx);
+       mt7925_print_txpwr_entry(EHT996x3_484, eht996x3_484, band_idx);
+}
+
+static int
+mt7925_txpwr(struct seq_file *s, void *data)
+{
+       struct mt792x_dev *dev = dev_get_drvdata(s->private);
+       struct mt7925_txpwr *txpwr = NULL;
+       u8 band_idx = dev->mphy.band_idx;
+       int ret = 0;
+
+       txpwr = devm_kmalloc(dev->mt76.dev, sizeof(*txpwr), GFP_KERNEL);
+
+       if (!txpwr)
+               return -ENOMEM;
+
+       mt792x_mutex_acquire(dev);
+       ret = mt7925_get_txpwr_info(dev, band_idx, txpwr);
+       mt792x_mutex_release(dev);
+
+       if (ret)
+               goto out;
+
+       seq_printf(s, "%-22s  %6s %6s %6s %6s\n",
+                  " ", "1m", "2m", "5m", "11m");
+       mt7925_print_txpwr_entry(CCK, cck, band_idx);
+
+       seq_printf(s, "%-22s  %6s %6s %6s %6s %6s %6s %6s %6s\n",
+                  " ", "6m", "9m", "12m", "18m", "24m", "36m",
+                  "48m", "54m");
+       mt7925_print_txpwr_entry(OFDM, ofdm, band_idx);
+
+       seq_printf(s, "%-22s  %6s %6s %6s %6s %6s %6s %6s %6s\n",
+                  " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
+                  "mcs6", "mcs7");
+       mt7925_print_txpwr_entry(HT20, ht20, band_idx);
+
+       seq_printf(s, "%-22s  %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+                  " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
+                  "mcs6", "mcs7", "mcs32");
+       mt7925_print_txpwr_entry(HT40, ht40, band_idx);
+
+       seq_printf(s, "%-22s  %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+                  " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
+                  "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
+       mt7925_print_txpwr_entry(VHT20, vht20, band_idx);
+       mt7925_print_txpwr_entry(VHT40, vht40, band_idx);
+
+       mt7925_print_txpwr_entry(VHT80, vht80, band_idx);
+       mt7925_print_txpwr_entry(VHT160, vht160, band_idx);
+
+       mt7925_print_txpwr_entry(HE26, he26, band_idx);
+       mt7925_print_txpwr_entry(HE52, he52, band_idx);
+       mt7925_print_txpwr_entry(HE106, he106, band_idx);
+       mt7925_print_txpwr_entry(HE242, he242, band_idx);
+       mt7925_print_txpwr_entry(HE484, he484, band_idx);
+
+       mt7925_print_txpwr_entry(HE996, he996, band_idx);
+       mt7925_print_txpwr_entry(HE996x2, he996x2, band_idx);
+
+       mt7925_eht_txpwr(s, txpwr, band_idx);
+
+out:
+       devm_kfree(dev->mt76.dev, txpwr);
+       return ret;
+}
+
+static int
+mt7925_pm_set(void *data, u64 val)
+{
+       struct mt792x_dev *dev = data;
+       struct mt76_connac_pm *pm = &dev->pm;
+
+       if (mt76_is_usb(&dev->mt76))
+               return -EOPNOTSUPP;
+
+       mutex_lock(&dev->mt76.mutex);
+
+       if (val == pm->enable_user)
+               goto out;
+
+       if (!pm->enable_user) {
+               pm->stats.last_wake_event = jiffies;
+               pm->stats.last_doze_event = jiffies;
+       }
+       /* make sure the chip is awake here and ps_work is scheduled
+        * just at end of the this routine.
+        */
+       pm->enable = false;
+       mt76_connac_pm_wake(&dev->mphy, pm);
+
+       pm->enable_user = val;
+       mt7925_set_runtime_pm(dev);
+       mt76_connac_power_save_sched(&dev->mphy, pm);
+out:
+       mutex_unlock(&dev->mt76.mutex);
+
+       return 0;
+}
+
+static int
+mt7925_pm_get(void *data, u64 *val)
+{
+       struct mt792x_dev *dev = data;
+
+       *val = dev->pm.enable_user;
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_pm, mt7925_pm_get, mt7925_pm_set, "%lld\n");
+
+static int
+mt7925_deep_sleep_set(void *data, u64 val)
+{
+       struct mt792x_dev *dev = data;
+       struct mt76_connac_pm *pm = &dev->pm;
+       bool monitor = !!(dev->mphy.hw->conf.flags & IEEE80211_CONF_MONITOR);
+       bool enable = !!val;
+
+       if (mt76_is_usb(&dev->mt76))
+               return -EOPNOTSUPP;
+
+       mt792x_mutex_acquire(dev);
+       if (pm->ds_enable_user == enable)
+               goto out;
+
+       pm->ds_enable_user = enable;
+       pm->ds_enable = enable && !monitor;
+       mt7925_mcu_set_deep_sleep(dev, pm->ds_enable);
+out:
+       mt792x_mutex_release(dev);
+
+       return 0;
+}
+
+static int
+mt7925_deep_sleep_get(void *data, u64 *val)
+{
+       struct mt792x_dev *dev = data;
+
+       *val = dev->pm.ds_enable_user;
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_ds, mt7925_deep_sleep_get,
+                        mt7925_deep_sleep_set, "%lld\n");
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_pm_idle_timeout, mt792x_pm_idle_timeout_get,
+                        mt792x_pm_idle_timeout_set, "%lld\n");
+
+static int mt7925_chip_reset(void *data, u64 val)
+{
+       struct mt792x_dev *dev = data;
+       int ret = 0;
+
+       switch (val) {
+       case 1:
+               /* Reset wifisys directly. */
+               mt792x_reset(&dev->mt76);
+               break;
+       default:
+               /* Collect the core dump before reset wifisys. */
+               mt792x_mutex_acquire(dev);
+               ret = mt7925_mcu_chip_config(dev, "assert");
+               mt792x_mutex_release(dev);
+               break;
+       }
+
+       return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
+
+int mt7925_init_debugfs(struct mt792x_dev *dev)
+{
+       struct dentry *dir;
+
+       dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval);
+       if (!dir)
+               return -ENOMEM;
+
+       if (mt76_is_mmio(&dev->mt76))
+               debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues",
+                                           dir, mt792x_queues_read);
+       else
+               debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues",
+                                           dir, mt76_queues_read);
+
+       debugfs_create_devm_seqfile(dev->mt76.dev, "acq", dir,
+                                   mt792x_queues_acq);
+       debugfs_create_devm_seqfile(dev->mt76.dev, "txpower_sku", dir,
+                                   mt7925_txpwr);
+       debugfs_create_file("tx_stats", 0400, dir, dev, &mt792x_tx_stats_fops);
+       debugfs_create_file("fw_debug", 0600, dir, dev, &fops_fw_debug);
+       debugfs_create_file("runtime-pm", 0600, dir, dev, &fops_pm);
+       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, "runtime_pm_stats", dir,
+                                   mt792x_pm_stats);
+       debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
new file mode 100644 (file)
index 0000000..8f9b7a2
--- /dev/null
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include <linux/etherdevice.h>
+#include <linux/firmware.h>
+#include "mt7925.h"
+#include "mac.h"
+#include "mcu.h"
+
+static void
+mt7925_regd_notifier(struct wiphy *wiphy,
+                    struct regulatory_request *req)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt76_dev *mdev = &dev->mt76;
+
+       /* allow world regdom at the first boot only */
+       if (!memcmp(req->alpha2, "00", 2) &&
+           mdev->alpha2[0] && mdev->alpha2[1])
+               return;
+
+       /* do not need to update the same country twice */
+       if (!memcmp(req->alpha2, mdev->alpha2, 2) &&
+           dev->country_ie_env == req->country_ie_env)
+               return;
+
+       memcpy(mdev->alpha2, req->alpha2, 2);
+       mdev->region = req->dfs_region;
+       dev->country_ie_env = req->country_ie_env;
+
+       mt792x_mutex_acquire(dev);
+       mt7925_mcu_set_clc(dev, req->alpha2, req->country_ie_env);
+       mt7925_mcu_set_channel_domain(hw->priv);
+       mt7925_set_tx_sar_pwr(hw, NULL);
+       mt792x_mutex_release(dev);
+}
+
+static void mt7925_mac_init_basic_rates(struct mt792x_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mt76_rates); i++) {
+               u16 rate = mt76_rates[i].hw_value;
+               u16 idx = MT792x_BASIC_RATES_TBL + i;
+
+               rate = FIELD_PREP(MT_TX_RATE_MODE, rate >> 8) |
+                      FIELD_PREP(MT_TX_RATE_IDX, rate & GENMASK(7, 0));
+               mt7925_mac_set_fixed_rate_table(dev, idx, rate);
+       }
+}
+
+int mt7925_mac_init(struct mt792x_dev *dev)
+{
+       int i;
+
+       mt76_rmw_field(dev, MT_MDP_DCR1, MT_MDP_DCR1_MAX_RX_LEN, 1536);
+       /* enable hardware de-agg */
+       mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN);
+
+       for (i = 0; i < MT792x_WTBL_SIZE; i++)
+               mt7925_mac_wtbl_update(dev, i,
+                                      MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+       for (i = 0; i < 2; i++)
+               mt792x_mac_init_band(dev, i);
+
+       mt7925_mac_init_basic_rates(dev);
+
+       memzero_explicit(&dev->mt76.alpha2, sizeof(dev->mt76.alpha2));
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt7925_mac_init);
+
+static int __mt7925_init_hardware(struct mt792x_dev *dev)
+{
+       int ret;
+
+       ret = mt792x_mcu_init(dev);
+       if (ret)
+               goto out;
+
+       mt76_eeprom_override(&dev->mphy);
+
+       ret = mt7925_mcu_set_eeprom(dev);
+       if (ret)
+               goto out;
+
+       ret = mt7925_mac_init(dev);
+       if (ret)
+               goto out;
+
+out:
+       return ret;
+}
+
+static int mt7925_init_hardware(struct mt792x_dev *dev)
+{
+       int ret, i;
+
+       set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
+
+       for (i = 0; i < MT792x_MCU_INIT_RETRY_COUNT; i++) {
+               ret = __mt7925_init_hardware(dev);
+               if (!ret)
+                       break;
+
+               mt792x_init_reset(dev);
+       }
+
+       if (i == MT792x_MCU_INIT_RETRY_COUNT) {
+               dev_err(dev->mt76.dev, "hardware init failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void mt7925_init_work(struct work_struct *work)
+{
+       struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
+                                             init_work);
+       int ret;
+
+       ret = mt7925_init_hardware(dev);
+       if (ret)
+               return;
+
+       mt76_set_stream_caps(&dev->mphy, true);
+       mt7925_set_stream_he_eht_caps(&dev->phy);
+
+       ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+                                  ARRAY_SIZE(mt76_rates));
+       if (ret) {
+               dev_err(dev->mt76.dev, "register device failed\n");
+               return;
+       }
+
+       ret = mt7925_init_debugfs(dev);
+       if (ret) {
+               dev_err(dev->mt76.dev, "register debugfs failed\n");
+               return;
+       }
+
+       /* we support chip reset now */
+       dev->hw_init_done = true;
+
+       mt7925_mcu_set_deep_sleep(dev, dev->pm.ds_enable);
+}
+
+int mt7925_register_device(struct mt792x_dev *dev)
+{
+       struct ieee80211_hw *hw = mt76_hw(dev);
+       int ret;
+
+       dev->phy.dev = dev;
+       dev->phy.mt76 = &dev->mt76.phy;
+       dev->mt76.phy.priv = &dev->phy;
+       dev->mt76.tx_worker.fn = mt792x_tx_worker;
+
+       INIT_DELAYED_WORK(&dev->pm.ps_work, mt792x_pm_power_save_work);
+       INIT_WORK(&dev->pm.wake_work, mt792x_pm_wake_work);
+       spin_lock_init(&dev->pm.wake.lock);
+       mutex_init(&dev->pm.mutex);
+       init_waitqueue_head(&dev->pm.wait);
+       spin_lock_init(&dev->pm.txq_lock);
+       INIT_DELAYED_WORK(&dev->mphy.mac_work, mt792x_mac_work);
+       INIT_DELAYED_WORK(&dev->phy.scan_work, mt7925_scan_work);
+       INIT_DELAYED_WORK(&dev->coredump.work, mt7925_coredump_work);
+#if IS_ENABLED(CONFIG_IPV6)
+       INIT_WORK(&dev->ipv6_ns_work, mt7925_set_ipv6_ns_work);
+       skb_queue_head_init(&dev->ipv6_ns_list);
+#endif
+       skb_queue_head_init(&dev->phy.scan_event_list);
+       skb_queue_head_init(&dev->coredump.msg_list);
+
+       INIT_WORK(&dev->reset_work, mt7925_mac_reset_work);
+       INIT_WORK(&dev->init_work, mt7925_init_work);
+
+       INIT_WORK(&dev->phy.roc_work, mt7925_roc_work);
+       timer_setup(&dev->phy.roc_timer, mt792x_roc_timer, 0);
+       init_waitqueue_head(&dev->phy.roc_wait);
+
+       dev->pm.idle_timeout = MT792x_PM_TIMEOUT;
+       dev->pm.stats.last_wake_event = jiffies;
+       dev->pm.stats.last_doze_event = jiffies;
+       if (!mt76_is_usb(&dev->mt76)) {
+               dev->pm.enable_user = true;
+               dev->pm.enable = true;
+               dev->pm.ds_enable_user = true;
+               dev->pm.ds_enable = true;
+       }
+
+       if (!mt76_is_mmio(&dev->mt76))
+               hw->extra_tx_headroom += MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE;
+
+       mt792x_init_acpi_sar(dev);
+
+       ret = mt792x_init_wcid(dev);
+       if (ret)
+               return ret;
+
+       ret = mt792x_init_wiphy(hw);
+       if (ret)
+               return ret;
+
+       hw->wiphy->reg_notifier = mt7925_regd_notifier;
+       dev->mphy.sband_2g.sband.ht_cap.cap |=
+                       IEEE80211_HT_CAP_LDPC_CODING |
+                       IEEE80211_HT_CAP_MAX_AMSDU;
+       dev->mphy.sband_2g.sband.ht_cap.ampdu_density =
+                       IEEE80211_HT_MPDU_DENSITY_2;
+       dev->mphy.sband_5g.sband.ht_cap.cap |=
+                       IEEE80211_HT_CAP_LDPC_CODING |
+                       IEEE80211_HT_CAP_MAX_AMSDU;
+       dev->mphy.sband_2g.sband.ht_cap.ampdu_density =
+                       IEEE80211_HT_MPDU_DENSITY_1;
+       dev->mphy.sband_5g.sband.vht_cap.cap |=
+                       IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
+                       IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+                       IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
+                       (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+       dev->mphy.sband_5g.sband.vht_cap.cap |=
+                       IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+                       IEEE80211_VHT_CAP_SHORT_GI_160;
+
+       dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask;
+       dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask;
+
+       queue_work(system_wq, &dev->init_work);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt7925_register_device);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
new file mode 100644 (file)
index 0000000..1b9fbd9
--- /dev/null
@@ -0,0 +1,1452 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include <linux/devcoredump.h>
+#include <linux/etherdevice.h>
+#include <linux/timekeeping.h>
+#include "mt7925.h"
+#include "../dma.h"
+#include "mac.h"
+#include "mcu.h"
+
+bool mt7925_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask)
+{
+       mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX,
+                FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask);
+
+       return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY,
+                        0, 5000);
+}
+
+static void mt7925_mac_sta_poll(struct mt792x_dev *dev)
+{
+       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 mt792x_sta *msta;
+       u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
+       LIST_HEAD(sta_poll_list);
+       struct rate_info *rate;
+       s8 rssi[4];
+       int i;
+
+       spin_lock_bh(&dev->mt76.sta_poll_lock);
+       list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list);
+       spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+       while (true) {
+               bool clear = false;
+               u32 addr, val;
+               u16 idx;
+               u8 bw;
+
+               if (list_empty(&sta_poll_list))
+                       break;
+               msta = list_first_entry(&sta_poll_list,
+                                       struct mt792x_sta, wcid.poll_list);
+               spin_lock_bh(&dev->mt76.sta_poll_lock);
+               list_del_init(&msta->wcid.poll_list);
+               spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+               idx = msta->wcid.idx;
+               addr = mt7925_mac_wtbl_lmac_addr(dev, idx, MT_WTBL_AC0_CTT_OFFSET);
+
+               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] = 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;
+
+                       addr += 8;
+               }
+
+               if (clear) {
+                       mt7925_mac_wtbl_update(dev, 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 < IEEE80211_NUM_ACS; i++) {
+                       u8 q = mt76_connac_lmac_mapping(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);
+               }
+
+               /* We don't support reading GI info from txs packets.
+                * For accurate tx status reporting and AQL improvement,
+                * we need to make sure that flags match so polling GI
+                * from per-sta counters directly.
+                */
+               rate = &msta->wcid.rate;
+
+               switch (rate->bw) {
+               case RATE_INFO_BW_160:
+                       bw = IEEE80211_STA_RX_BW_160;
+                       break;
+               case RATE_INFO_BW_80:
+                       bw = IEEE80211_STA_RX_BW_80;
+                       break;
+               case RATE_INFO_BW_40:
+                       bw = IEEE80211_STA_RX_BW_40;
+                       break;
+               default:
+                       bw = IEEE80211_STA_RX_BW_20;
+                       break;
+               }
+
+               addr = mt7925_mac_wtbl_lmac_addr(dev, idx, 6);
+               val = mt76_rr(dev, addr);
+               if (rate->flags & RATE_INFO_FLAGS_EHT_MCS) {
+                       addr = mt7925_mac_wtbl_lmac_addr(dev, idx, 5);
+                       val = mt76_rr(dev, addr);
+                       rate->eht_gi = FIELD_GET(GENMASK(25, 24), val);
+               } else if (rate->flags & RATE_INFO_FLAGS_HE_MCS) {
+                       u8 offs = MT_WTBL_TXRX_RATE_G2_HE + 2 * bw;
+
+                       rate->he_gi = (val & (0x3 << offs)) >> offs;
+               } else if (rate->flags &
+                          (RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) {
+                       if (val & BIT(MT_WTBL_TXRX_RATE_G2 + bw))
+                               rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+                       else
+                               rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI;
+               }
+
+               /* get signal strength of resp frames (CTS/BA/ACK) */
+               addr = mt7925_mac_wtbl_lmac_addr(dev, idx, 34);
+               val = mt76_rr(dev, addr);
+
+               rssi[0] = to_rssi(GENMASK(7, 0), val);
+               rssi[1] = to_rssi(GENMASK(15, 8), val);
+               rssi[2] = to_rssi(GENMASK(23, 16), val);
+               rssi[3] = to_rssi(GENMASK(31, 14), val);
+
+               msta->ack_signal =
+                       mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi);
+
+               ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
+       }
+}
+
+void mt7925_mac_set_fixed_rate_table(struct mt792x_dev *dev,
+                                    u8 tbl_idx, u16 rate_idx)
+{
+       u32 ctrl = MT_WTBL_ITCR_WR | MT_WTBL_ITCR_EXEC | tbl_idx;
+
+       mt76_wr(dev, MT_WTBL_ITDR0, rate_idx);
+       /* use wtbl spe idx */
+       mt76_wr(dev, MT_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL);
+       mt76_wr(dev, MT_WTBL_ITCR, ctrl);
+}
+
+/* The HW does not translate the mac header to 802.3 for mesh point */
+static int mt7925_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
+{
+       struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+       struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap);
+       struct mt792x_sta *msta = (struct mt792x_sta *)status->wcid;
+       __le32 *rxd = (__le32 *)skb->data;
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif;
+       struct ieee80211_hdr hdr;
+       u16 frame_control;
+
+       if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) !=
+           MT_RXD3_NORMAL_U2M)
+               return -EINVAL;
+
+       if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4))
+               return -EINVAL;
+
+       if (!msta || !msta->vif)
+               return -EINVAL;
+
+       sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+       vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
+
+       /* store the info from RXD and ethhdr to avoid being overridden */
+       frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL);
+       hdr.frame_control = cpu_to_le16(frame_control);
+       hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_SEQ_CTRL));
+       hdr.duration_id = 0;
+
+       ether_addr_copy(hdr.addr1, vif->addr);
+       ether_addr_copy(hdr.addr2, sta->addr);
+       switch (frame_control & (IEEE80211_FCTL_TODS |
+                                IEEE80211_FCTL_FROMDS)) {
+       case 0:
+               ether_addr_copy(hdr.addr3, vif->bss_conf.bssid);
+               break;
+       case IEEE80211_FCTL_FROMDS:
+               ether_addr_copy(hdr.addr3, eth_hdr->h_source);
+               break;
+       case IEEE80211_FCTL_TODS:
+               ether_addr_copy(hdr.addr3, eth_hdr->h_dest);
+               break;
+       case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS:
+               ether_addr_copy(hdr.addr3, eth_hdr->h_dest);
+               ether_addr_copy(hdr.addr4, eth_hdr->h_source);
+               break;
+       default:
+               break;
+       }
+
+       skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2);
+       if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) ||
+           eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX))
+               ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header);
+       else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN)
+               ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header);
+       else
+               skb_pull(skb, 2);
+
+       if (ieee80211_has_order(hdr.frame_control))
+               memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[11],
+                      IEEE80211_HT_CTL_LEN);
+       if (ieee80211_is_data_qos(hdr.frame_control)) {
+               __le16 qos_ctrl;
+
+               qos_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_QOS_CTL));
+               memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl,
+                      IEEE80211_QOS_CTL_LEN);
+       }
+
+       if (ieee80211_has_a4(hdr.frame_control))
+               memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr));
+       else
+               memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6);
+
+       return 0;
+}
+
+static int
+mt7925_mac_fill_rx_rate(struct mt792x_dev *dev,
+                       struct mt76_rx_status *status,
+                       struct ieee80211_supported_band *sband,
+                       __le32 *rxv, u8 *mode)
+{
+       u32 v0, v2;
+       u8 stbc, gi, bw, dcm, nss;
+       int i, idx;
+       bool cck = false;
+
+       v0 = le32_to_cpu(rxv[0]);
+       v2 = le32_to_cpu(rxv[2]);
+
+       idx = FIELD_GET(MT_PRXV_TX_RATE, v0);
+       i = idx;
+       nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1;
+
+       stbc = FIELD_GET(MT_PRXV_HT_STBC, v2);
+       gi = FIELD_GET(MT_PRXV_HT_SHORT_GI, v2);
+       *mode = FIELD_GET(MT_PRXV_TX_MODE, v2);
+       dcm = FIELD_GET(MT_PRXV_DCM, v2);
+       bw = FIELD_GET(MT_PRXV_FRAME_MODE, v2);
+
+       switch (*mode) {
+       case MT_PHY_TYPE_CCK:
+               cck = true;
+               fallthrough;
+       case MT_PHY_TYPE_OFDM:
+               i = mt76_get_rate(&dev->mt76, sband, i, cck);
+               break;
+       case MT_PHY_TYPE_HT_GF:
+       case MT_PHY_TYPE_HT:
+               status->encoding = RX_ENC_HT;
+               if (gi)
+                       status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+               if (i > 31)
+                       return -EINVAL;
+               break;
+       case MT_PHY_TYPE_VHT:
+               status->nss = nss;
+               status->encoding = RX_ENC_VHT;
+               if (gi)
+                       status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+               if (i > 11)
+                       return -EINVAL;
+               break;
+       case MT_PHY_TYPE_HE_MU:
+       case MT_PHY_TYPE_HE_SU:
+       case MT_PHY_TYPE_HE_EXT_SU:
+       case MT_PHY_TYPE_HE_TB:
+               status->nss = nss;
+               status->encoding = RX_ENC_HE;
+               i &= GENMASK(3, 0);
+
+               if (gi <= NL80211_RATE_INFO_HE_GI_3_2)
+                       status->he_gi = gi;
+
+               status->he_dcm = dcm;
+               break;
+       case MT_PHY_TYPE_EHT_SU:
+       case MT_PHY_TYPE_EHT_TRIG:
+       case MT_PHY_TYPE_EHT_MU:
+               status->nss = nss;
+               status->encoding = RX_ENC_EHT;
+               i &= GENMASK(3, 0);
+
+               if (gi <= NL80211_RATE_INFO_EHT_GI_3_2)
+                       status->eht.gi = gi;
+               break;
+       default:
+               return -EINVAL;
+       }
+       status->rate_idx = i;
+
+       switch (bw) {
+       case IEEE80211_STA_RX_BW_20:
+               break;
+       case IEEE80211_STA_RX_BW_40:
+               if (*mode & MT_PHY_TYPE_HE_EXT_SU &&
+                   (idx & MT_PRXV_TX_ER_SU_106T)) {
+                       status->bw = RATE_INFO_BW_HE_RU;
+                       status->he_ru =
+                               NL80211_RATE_INFO_HE_RU_ALLOC_106;
+               } else {
+                       status->bw = RATE_INFO_BW_40;
+               }
+               break;
+       case IEEE80211_STA_RX_BW_80:
+               status->bw = RATE_INFO_BW_80;
+               break;
+       case IEEE80211_STA_RX_BW_160:
+               status->bw = RATE_INFO_BW_160;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc;
+       if (*mode < MT_PHY_TYPE_HE_SU && gi)
+               status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+
+       return 0;
+}
+
+static int
+mt7925_mac_fill_rx(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+       u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM;
+       struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+       bool hdr_trans, unicast, insert_ccmp_hdr = false;
+       u8 chfreq, qos_ctl = 0, remove_pad, amsdu_info;
+       u16 hdr_gap;
+       __le32 *rxv = NULL, *rxd = (__le32 *)skb->data;
+       struct mt76_phy *mphy = &dev->mt76.phy;
+       struct mt792x_phy *phy = &dev->phy;
+       struct ieee80211_supported_band *sband;
+       u32 csum_status = *(u32 *)skb->cb;
+       u32 rxd0 = le32_to_cpu(rxd[0]);
+       u32 rxd1 = le32_to_cpu(rxd[1]);
+       u32 rxd2 = le32_to_cpu(rxd[2]);
+       u32 rxd3 = le32_to_cpu(rxd[3]);
+       u32 rxd4 = le32_to_cpu(rxd[4]);
+       struct mt792x_sta *msta = NULL;
+       u8 mode = 0; /* , band_idx; */
+       u16 seq_ctrl = 0;
+       __le16 fc = 0;
+       int idx;
+
+       memset(status, 0, sizeof(*status));
+
+       if (!test_bit(MT76_STATE_RUNNING, &mphy->state))
+               return -EINVAL;
+
+       if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR)
+               return -EINVAL;
+
+       hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS;
+       if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM))
+               return -EINVAL;
+
+       /* ICV error or CCMP/BIP/WPI MIC error */
+       if (rxd1 & MT_RXD1_NORMAL_ICV_ERR)
+               status->flag |= RX_FLAG_ONLY_MONITOR;
+
+       chfreq = FIELD_GET(MT_RXD3_NORMAL_CH_FREQ, rxd3);
+       unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M;
+       idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1);
+       status->wcid = mt792x_rx_get_wcid(dev, idx, unicast);
+
+       if (status->wcid) {
+               msta = container_of(status->wcid, struct mt792x_sta, wcid);
+               spin_lock_bh(&dev->mt76.sta_poll_lock);
+               if (list_empty(&msta->wcid.poll_list))
+                       list_add_tail(&msta->wcid.poll_list,
+                                     &dev->mt76.sta_poll_list);
+               spin_unlock_bh(&dev->mt76.sta_poll_lock);
+       }
+
+       mt792x_get_status_freq_info(status, chfreq);
+
+       switch (status->band) {
+       case NL80211_BAND_5GHZ:
+               sband = &mphy->sband_5g.sband;
+               break;
+       case NL80211_BAND_6GHZ:
+               sband = &mphy->sband_6g.sband;
+               break;
+       default:
+               sband = &mphy->sband_2g.sband;
+               break;
+       }
+
+       if (!sband->channels)
+               return -EINVAL;
+
+       if (mt76_is_mmio(&dev->mt76) && (rxd0 & csum_mask) == csum_mask &&
+           !(csum_status & (BIT(0) | BIT(2) | BIT(3))))
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       if (rxd3 & MT_RXD3_NORMAL_FCS_ERR)
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+       if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR)
+               status->flag |= RX_FLAG_MMIC_ERROR;
+
+       if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 &&
+           !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) {
+               status->flag |= RX_FLAG_DECRYPTED;
+               status->flag |= RX_FLAG_IV_STRIPPED;
+               status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED;
+       }
+
+       remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2);
+
+       if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR)
+               return -EINVAL;
+
+       rxd += 8;
+       if (rxd1 & MT_RXD1_NORMAL_GROUP_4) {
+               u32 v0 = le32_to_cpu(rxd[0]);
+               u32 v2 = le32_to_cpu(rxd[2]);
+
+               /* TODO: need to map rxd address */
+               fc = cpu_to_le16(FIELD_GET(MT_RXD8_FRAME_CONTROL, v0));
+               seq_ctrl = FIELD_GET(MT_RXD10_SEQ_CTRL, v2);
+               qos_ctl = FIELD_GET(MT_RXD10_QOS_CTL, v2);
+
+               rxd += 4;
+               if ((u8 *)rxd - skb->data >= skb->len)
+                       return -EINVAL;
+       }
+
+       if (rxd1 & MT_RXD1_NORMAL_GROUP_1) {
+               u8 *data = (u8 *)rxd;
+
+               if (status->flag & RX_FLAG_DECRYPTED) {
+                       switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) {
+                       case MT_CIPHER_AES_CCMP:
+                       case MT_CIPHER_CCMP_CCX:
+                       case MT_CIPHER_CCMP_256:
+                               insert_ccmp_hdr =
+                                       FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2);
+                               fallthrough;
+                       case MT_CIPHER_TKIP:
+                       case MT_CIPHER_TKIP_NO_MIC:
+                       case MT_CIPHER_GCMP:
+                       case MT_CIPHER_GCMP_256:
+                               status->iv[0] = data[5];
+                               status->iv[1] = data[4];
+                               status->iv[2] = data[3];
+                               status->iv[3] = data[2];
+                               status->iv[4] = data[1];
+                               status->iv[5] = data[0];
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               rxd += 4;
+               if ((u8 *)rxd - skb->data >= skb->len)
+                       return -EINVAL;
+       }
+
+       if (rxd1 & MT_RXD1_NORMAL_GROUP_2) {
+               status->timestamp = le32_to_cpu(rxd[0]);
+               status->flag |= RX_FLAG_MACTIME_START;
+
+               if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) {
+                       status->flag |= RX_FLAG_AMPDU_DETAILS;
+
+                       /* all subframes of an A-MPDU have the same timestamp */
+                       if (phy->rx_ampdu_ts != status->timestamp) {
+                               if (!++phy->ampdu_ref)
+                                       phy->ampdu_ref++;
+                       }
+                       phy->rx_ampdu_ts = status->timestamp;
+
+                       status->ampdu_ref = phy->ampdu_ref;
+               }
+
+               rxd += 4;
+               if ((u8 *)rxd - skb->data >= skb->len)
+                       return -EINVAL;
+       }
+
+       /* RXD Group 3 - P-RXV */
+       if (rxd1 & MT_RXD1_NORMAL_GROUP_3) {
+               u32 v3;
+               int ret;
+
+               rxv = rxd;
+               rxd += 4;
+               if ((u8 *)rxd - skb->data >= skb->len)
+                       return -EINVAL;
+
+               v3 = le32_to_cpu(rxv[3]);
+
+               status->chains = mphy->antenna_mask;
+               status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v3);
+               status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v3);
+               status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v3);
+               status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v3);
+
+               /* RXD Group 5 - C-RXV */
+               if (rxd1 & MT_RXD1_NORMAL_GROUP_5) {
+                       rxd += 24;
+                       if ((u8 *)rxd - skb->data >= skb->len)
+                               return -EINVAL;
+               }
+
+               ret = mt7925_mac_fill_rx_rate(dev, status, sband, rxv, &mode);
+               if (ret < 0)
+                       return ret;
+       }
+
+       amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4);
+       status->amsdu = !!amsdu_info;
+       if (status->amsdu) {
+               status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME;
+               status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME;
+       }
+
+       hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad;
+       if (hdr_trans && ieee80211_has_morefrags(fc)) {
+               if (mt7925_reverse_frag0_hdr_trans(skb, hdr_gap))
+                       return -EINVAL;
+               hdr_trans = false;
+       } else {
+               int pad_start = 0;
+
+               skb_pull(skb, hdr_gap);
+               if (!hdr_trans && status->amsdu) {
+                       pad_start = ieee80211_get_hdrlen_from_skb(skb);
+               } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) {
+                       /* When header translation failure is indicated,
+                        * the hardware will insert an extra 2-byte field
+                        * containing the data length after the protocol
+                        * type field.
+                        */
+                       pad_start = 12;
+                       if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q)
+                               pad_start += 4;
+                       else
+                               pad_start = 0;
+               }
+
+               if (pad_start) {
+                       memmove(skb->data + 2, skb->data, pad_start);
+                       skb_pull(skb, 2);
+               }
+       }
+
+       if (!hdr_trans) {
+               struct ieee80211_hdr *hdr;
+
+               if (insert_ccmp_hdr) {
+                       u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1);
+
+                       mt76_insert_ccmp_hdr(skb, key_id);
+               }
+
+               hdr = mt76_skb_get_hdr(skb);
+               fc = hdr->frame_control;
+               if (ieee80211_is_data_qos(fc)) {
+                       seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
+                       qos_ctl = *ieee80211_get_qos_ctl(hdr);
+               }
+       } else {
+               status->flag |= RX_FLAG_8023;
+       }
+
+       mt792x_mac_assoc_rssi(dev, skb);
+
+       if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
+               mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode);
+
+       if (!status->wcid || !ieee80211_is_data_qos(fc))
+               return 0;
+
+       status->aggr = unicast && !ieee80211_is_qos_nullfunc(fc);
+       status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl);
+       status->qos_ctl = qos_ctl;
+
+       return 0;
+}
+
+static void
+mt7925_mac_write_txwi_8023(__le32 *txwi, struct sk_buff *skb,
+                          struct mt76_wcid *wcid)
+{
+       u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+       u8 fc_type, fc_stype;
+       u16 ethertype;
+       bool wmm = false;
+       u32 val;
+
+       if (wcid->sta) {
+               struct ieee80211_sta *sta;
+
+               sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
+               wmm = sta->wme;
+       }
+
+       val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3) |
+             FIELD_PREP(MT_TXD1_TID, tid);
+
+       ethertype = get_unaligned_be16(&skb->data[12]);
+       if (ethertype >= ETH_P_802_3_MIN)
+               val |= MT_TXD1_ETH_802_3;
+
+       txwi[1] |= cpu_to_le32(val);
+
+       fc_type = IEEE80211_FTYPE_DATA >> 2;
+       fc_stype = wmm ? IEEE80211_STYPE_QOS_DATA >> 4 : 0;
+
+       val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) |
+             FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype);
+
+       txwi[2] |= cpu_to_le32(val);
+}
+
+static void
+mt7925_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi,
+                           struct sk_buff *skb,
+                           struct ieee80211_key_conf *key)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       bool multicast = is_multicast_ether_addr(hdr->addr1);
+       u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+       __le16 fc = hdr->frame_control;
+       u8 fc_type, fc_stype;
+       u32 val;
+
+       if (ieee80211_is_action(fc) &&
+           mgmt->u.action.category == WLAN_CATEGORY_BACK &&
+           mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ)
+               tid = MT_TX_ADDBA;
+       else if (ieee80211_is_mgmt(hdr->frame_control))
+               tid = MT_TX_NORMAL;
+
+       val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) |
+             FIELD_PREP(MT_TXD1_HDR_INFO,
+                        ieee80211_get_hdrlen_from_skb(skb) / 2) |
+             FIELD_PREP(MT_TXD1_TID, tid);
+
+       if (!ieee80211_is_data(fc) || multicast ||
+           info->flags & IEEE80211_TX_CTL_USE_MINRATE)
+               val |= MT_TXD1_FIXED_RATE;
+
+       if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) &&
+           key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
+               val |= MT_TXD1_BIP;
+               txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME);
+       }
+
+       txwi[1] |= cpu_to_le32(val);
+
+       fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2;
+       fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4;
+
+       val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) |
+             FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype);
+
+       txwi[2] |= cpu_to_le32(val);
+
+       txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast));
+       if (ieee80211_is_beacon(fc))
+               txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT);
+
+       if (info->flags & IEEE80211_TX_CTL_INJECTED) {
+               u16 seqno = le16_to_cpu(hdr->seq_ctrl);
+
+               if (ieee80211_is_back_req(hdr->frame_control)) {
+                       struct ieee80211_bar *bar;
+
+                       bar = (struct ieee80211_bar *)skb->data;
+                       seqno = le16_to_cpu(bar->start_seq_num);
+               }
+
+               val = MT_TXD3_SN_VALID |
+                     FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno));
+               txwi[3] |= cpu_to_le32(val);
+               txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
+       }
+}
+
+void
+mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
+                     struct sk_buff *skb, struct mt76_wcid *wcid,
+                     struct ieee80211_key_conf *key, int pid,
+                     enum mt76_txq_id qid, u32 changed)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_vif *vif = info->control.vif;
+       u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0, band_idx = 0;
+       u32 val, sz_txd = mt76_is_mmio(dev) ? MT_TXD_SIZE : MT_SDIO_TXD_SIZE;
+       bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+       struct mt76_vif *mvif;
+       bool beacon = !!(changed & (BSS_CHANGED_BEACON |
+                                   BSS_CHANGED_BEACON_ENABLED));
+       bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+                                        BSS_CHANGED_FILS_DISCOVERY));
+
+       mvif = vif ? (struct mt76_vif *)vif->drv_priv : NULL;
+       if (mvif) {
+               omac_idx = mvif->omac_idx;
+               wmm_idx = mvif->wmm_idx;
+               band_idx = mvif->band_idx;
+       }
+
+       if (inband_disc) {
+               p_fmt = MT_TX_TYPE_FW;
+               q_idx = MT_LMAC_ALTX0;
+       } else if (beacon) {
+               p_fmt = MT_TX_TYPE_FW;
+               q_idx = MT_LMAC_BCN0;
+       } else if (qid >= MT_TXQ_PSD) {
+               p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT : MT_TX_TYPE_SF;
+               q_idx = MT_LMAC_ALTX0;
+       } else {
+               p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT : MT_TX_TYPE_SF;
+               q_idx = wmm_idx * MT76_CONNAC_MAX_WMM_SETS +
+                       mt76_connac_lmac_mapping(skb_get_queue_mapping(skb));
+
+               /* counting non-offloading skbs */
+               wcid->stats.tx_bytes += skb->len;
+               wcid->stats.tx_packets++;
+       }
+
+       val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) |
+             FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) |
+             FIELD_PREP(MT_TXD0_Q_IDX, q_idx);
+       txwi[0] = cpu_to_le32(val);
+
+       val = FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) |
+             FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx);
+
+       if (band_idx)
+               val |= FIELD_PREP(MT_TXD1_TGID, band_idx);
+
+       txwi[1] = cpu_to_le32(val);
+       txwi[2] = 0;
+
+       val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, 15);
+
+       if (key)
+               val |= MT_TXD3_PROTECT_FRAME;
+       if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+               val |= MT_TXD3_NO_ACK;
+       if (wcid->amsdu)
+               val |= MT_TXD3_HW_AMSDU;
+
+       txwi[3] = cpu_to_le32(val);
+       txwi[4] = 0;
+
+       val = FIELD_PREP(MT_TXD5_PID, pid);
+       if (pid >= MT_PACKET_ID_FIRST) {
+               val |= MT_TXD5_TX_STATUS_HOST;
+               txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
+               txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
+       }
+
+       txwi[5] = cpu_to_le32(val);
+
+       val = MT_TXD6_DIS_MAT | MT_TXD6_DAS |
+             FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
+       txwi[6] = cpu_to_le32(val);
+       txwi[7] = 0;
+
+       if (is_8023)
+               mt7925_mac_write_txwi_8023(txwi, skb, wcid);
+       else
+               mt7925_mac_write_txwi_80211(dev, txwi, skb, key);
+
+       if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) {
+               struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+               bool mcast = ieee80211_is_data(hdr->frame_control) &&
+                            is_multicast_ether_addr(hdr->addr1);
+               u8 idx = MT792x_BASIC_RATES_TBL;
+
+               if (mvif) {
+                       if (mcast && mvif->mcast_rates_idx)
+                               idx = mvif->mcast_rates_idx;
+                       else if (beacon && mvif->beacon_rates_idx)
+                               idx = mvif->beacon_rates_idx;
+                       else
+                               idx = mvif->basic_rates_idx;
+               }
+
+               txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_TX_RATE, idx));
+               txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
+       }
+}
+EXPORT_SYMBOL_GPL(mt7925_mac_write_txwi);
+
+static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+{
+       struct mt792x_sta *msta;
+       u16 fc, tid;
+       u32 val;
+
+       if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+               return;
+
+       tid = le32_get_bits(txwi[1], MT_TXD1_TID);
+       if (tid >= 6) /* skip VO queue */
+               return;
+
+       val = le32_to_cpu(txwi[2]);
+       fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 |
+            FIELD_GET(MT_TXD2_SUB_TYPE, val) << 4;
+       if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA)))
+               return;
+
+       msta = (struct mt792x_sta *)sta->drv_priv;
+       if (!test_and_set_bit(tid, &msta->wcid.ampdu_state))
+               ieee80211_start_tx_ba_session(sta, tid, 0);
+}
+
+static bool
+mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid,
+                      int pid, __le32 *txs_data)
+{
+       struct mt76_sta_stats *stats = &wcid->stats;
+       struct ieee80211_supported_band *sband;
+       struct mt76_dev *mdev = &dev->mt76;
+       struct mt76_phy *mphy;
+       struct ieee80211_tx_info *info;
+       struct sk_buff_head list;
+       struct rate_info rate = {};
+       struct sk_buff *skb;
+       bool cck = false;
+       u32 txrate, txs, mode, stbc;
+
+       mt76_tx_status_lock(mdev, &list);
+       skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
+       if (!skb)
+               goto out_no_skb;
+
+       txs = le32_to_cpu(txs_data[0]);
+
+       info = IEEE80211_SKB_CB(skb);
+       if (!(txs & MT_TXS0_ACK_ERROR_MASK))
+               info->flags |= IEEE80211_TX_STAT_ACK;
+
+       info->status.ampdu_len = 1;
+       info->status.ampdu_ack_len = !!(info->flags &
+                                       IEEE80211_TX_STAT_ACK);
+
+       info->status.rates[0].idx = -1;
+
+       txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);
+
+       rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
+       rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1;
+       stbc = le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC);
+
+       if (stbc && rate.nss > 1)
+               rate.nss >>= 1;
+
+       if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss))
+               stats->tx_nss[rate.nss - 1]++;
+       if (rate.mcs < ARRAY_SIZE(stats->tx_mcs))
+               stats->tx_mcs[rate.mcs]++;
+
+       mode = FIELD_GET(MT_TX_RATE_MODE, txrate);
+       switch (mode) {
+       case MT_PHY_TYPE_CCK:
+               cck = true;
+               fallthrough;
+       case MT_PHY_TYPE_OFDM:
+               mphy = mt76_dev_phy(mdev, wcid->phy_idx);
+
+               if (mphy->chandef.chan->band == NL80211_BAND_5GHZ)
+                       sband = &mphy->sband_5g.sband;
+               else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ)
+                       sband = &mphy->sband_6g.sband;
+               else
+                       sband = &mphy->sband_2g.sband;
+
+               rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck);
+               rate.legacy = sband->bitrates[rate.mcs].bitrate;
+               break;
+       case MT_PHY_TYPE_HT:
+       case MT_PHY_TYPE_HT_GF:
+               if (rate.mcs > 31)
+                       goto out;
+
+               rate.flags = RATE_INFO_FLAGS_MCS;
+               if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI)
+                       rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+               break;
+       case MT_PHY_TYPE_VHT:
+               if (rate.mcs > 9)
+                       goto out;
+
+               rate.flags = RATE_INFO_FLAGS_VHT_MCS;
+               break;
+       case MT_PHY_TYPE_HE_SU:
+       case MT_PHY_TYPE_HE_EXT_SU:
+       case MT_PHY_TYPE_HE_TB:
+       case MT_PHY_TYPE_HE_MU:
+               if (rate.mcs > 11)
+                       goto out;
+
+               rate.he_gi = wcid->rate.he_gi;
+               rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate);
+               rate.flags = RATE_INFO_FLAGS_HE_MCS;
+               break;
+       case MT_PHY_TYPE_EHT_SU:
+       case MT_PHY_TYPE_EHT_TRIG:
+       case MT_PHY_TYPE_EHT_MU:
+               if (rate.mcs > 13)
+                       goto out;
+
+               rate.eht_gi = wcid->rate.eht_gi;
+               rate.flags = RATE_INFO_FLAGS_EHT_MCS;
+               break;
+       default:
+               goto out;
+       }
+
+       stats->tx_mode[mode]++;
+
+       switch (FIELD_GET(MT_TXS0_BW, txs)) {
+       case IEEE80211_STA_RX_BW_160:
+               rate.bw = RATE_INFO_BW_160;
+               stats->tx_bw[3]++;
+               break;
+       case IEEE80211_STA_RX_BW_80:
+               rate.bw = RATE_INFO_BW_80;
+               stats->tx_bw[2]++;
+               break;
+       case IEEE80211_STA_RX_BW_40:
+               rate.bw = RATE_INFO_BW_40;
+               stats->tx_bw[1]++;
+               break;
+       default:
+               rate.bw = RATE_INFO_BW_20;
+               stats->tx_bw[0]++;
+               break;
+       }
+       wcid->rate = rate;
+
+out:
+       mt76_tx_status_skb_done(mdev, skb, &list);
+
+out_no_skb:
+       mt76_tx_status_unlock(mdev, &list);
+
+       return !!skb;
+}
+
+void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data)
+{
+       struct mt792x_sta *msta = NULL;
+       struct mt76_wcid *wcid;
+       __le32 *txs_data = data;
+       u16 wcidx;
+       u8 pid;
+
+       if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1)
+               return;
+
+       wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
+       pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
+
+       if (pid < MT_PACKET_ID_FIRST)
+               return;
+
+       if (wcidx >= MT792x_WTBL_SIZE)
+               return;
+
+       rcu_read_lock();
+
+       wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
+       if (!wcid)
+               goto out;
+
+       msta = container_of(wcid, struct mt792x_sta, wcid);
+
+       mt7925_mac_add_txs_skb(dev, wcid, pid, txs_data);
+       if (!wcid->sta)
+               goto out;
+
+       spin_lock_bh(&dev->mt76.sta_poll_lock);
+       if (list_empty(&msta->wcid.poll_list))
+               list_add_tail(&msta->wcid.poll_list, &dev->mt76.sta_poll_list);
+       spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+out:
+       rcu_read_unlock();
+}
+
+void mt7925_txwi_free(struct mt792x_dev *dev, struct mt76_txwi_cache *t,
+                     struct ieee80211_sta *sta, bool clear_status,
+                     struct list_head *free_list)
+{
+       struct mt76_dev *mdev = &dev->mt76;
+       __le32 *txwi;
+       u16 wcid_idx;
+
+       mt76_connac_txp_skb_unmap(mdev, t);
+       if (!t->skb)
+               goto out;
+
+       txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t);
+       if (sta) {
+               struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
+
+               if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
+                       mt7925_tx_check_aggr(sta, txwi);
+
+               wcid_idx = wcid->idx;
+       } else {
+               wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
+       }
+
+       __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list);
+out:
+       t->skb = NULL;
+       mt76_put_txwi(mdev, t);
+}
+EXPORT_SYMBOL_GPL(mt7925_txwi_free);
+
+static void
+mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
+{
+       __le32 *tx_free = (__le32 *)data, *cur_info;
+       struct mt76_dev *mdev = &dev->mt76;
+       struct mt76_txwi_cache *txwi;
+       struct ieee80211_sta *sta = NULL;
+       struct mt76_wcid *wcid = NULL;
+       LIST_HEAD(free_list);
+       struct sk_buff *skb, *tmp;
+       void *end = data + len;
+       bool wake = false;
+       u16 total, count = 0;
+
+       /* clean DMA queues and unmap buffers first */
+       mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false);
+       mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false);
+
+       if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 4))
+               return;
+
+       total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT);
+       for (cur_info = &tx_free[2]; count < total; cur_info++) {
+               u32 msdu, info;
+               u8 i;
+
+               if (WARN_ON_ONCE((void *)cur_info >= end))
+                       return;
+               /* 1'b1: new wcid pair.
+                * 1'b0: msdu_id with the same 'wcid pair' as above.
+                */
+               info = le32_to_cpu(*cur_info);
+               if (info & MT_TXFREE_INFO_PAIR) {
+                       struct mt792x_sta *msta;
+                       u16 idx;
+
+                       idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
+                       wcid = rcu_dereference(dev->mt76.wcid[idx]);
+                       sta = wcid_to_sta(wcid);
+                       if (!sta)
+                               continue;
+
+                       msta = container_of(wcid, struct mt792x_sta, wcid);
+                       spin_lock_bh(&mdev->sta_poll_lock);
+                       if (list_empty(&msta->wcid.poll_list))
+                               list_add_tail(&msta->wcid.poll_list,
+                                             &mdev->sta_poll_list);
+                       spin_unlock_bh(&mdev->sta_poll_lock);
+                       continue;
+               }
+
+               if (info & MT_TXFREE_INFO_HEADER) {
+                       if (wcid) {
+                               wcid->stats.tx_retries +=
+                                       FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1;
+                               wcid->stats.tx_failed +=
+                                       !!FIELD_GET(MT_TXFREE_INFO_STAT, info);
+                       }
+                       continue;
+               }
+
+               for (i = 0; i < 2; i++) {
+                       msdu = (info >> (15 * i)) & MT_TXFREE_INFO_MSDU_ID;
+                       if (msdu == MT_TXFREE_INFO_MSDU_ID)
+                               continue;
+
+                       count++;
+                       txwi = mt76_token_release(mdev, msdu, &wake);
+                       if (!txwi)
+                               continue;
+
+                       mt7925_txwi_free(dev, txwi, sta, 0, &free_list);
+               }
+       }
+
+       mt7925_mac_sta_poll(dev);
+
+       if (wake)
+               mt76_set_tx_blocked(&dev->mt76, false);
+
+       mt76_worker_schedule(&dev->mt76.tx_worker);
+
+       list_for_each_entry_safe(skb, tmp, &free_list, list) {
+               skb_list_del_init(skb);
+               napi_consume_skb(skb, 1);
+       }
+}
+
+bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       __le32 *rxd = (__le32 *)data;
+       __le32 *end = (__le32 *)&rxd[len / 4];
+       enum rx_pkt_type type;
+
+       type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
+       if (type != PKT_TYPE_NORMAL) {
+               u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK);
+
+               if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) ==
+                            MT_RXD0_SW_PKT_TYPE_FRAME))
+                       return true;
+       }
+
+       switch (type) {
+       case PKT_TYPE_TXRX_NOTIFY:
+               /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
+               mt7925_mac_tx_free(dev, data, len); /* mmio */
+               return false;
+       case PKT_TYPE_TXS:
+               for (rxd += 4; rxd + 12 <= end; rxd += 12)
+                       mt7925_mac_add_txs(dev, rxd);
+               return false;
+       default:
+               return true;
+       }
+}
+EXPORT_SYMBOL_GPL(mt7925_rx_check);
+
+void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+                        struct sk_buff *skb, u32 *info)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       __le32 *rxd = (__le32 *)skb->data;
+       __le32 *end = (__le32 *)&skb->data[skb->len];
+       enum rx_pkt_type type;
+       u16 flag;
+
+       type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
+       flag = le32_get_bits(rxd[0], MT_RXD0_PKT_FLAG);
+       if (type != PKT_TYPE_NORMAL) {
+               u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK);
+
+               if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) ==
+                            MT_RXD0_SW_PKT_TYPE_FRAME))
+                       type = PKT_TYPE_NORMAL;
+       }
+
+       if (type == PKT_TYPE_RX_EVENT && flag == 0x1)
+               type = PKT_TYPE_NORMAL_MCU;
+
+       switch (type) {
+       case PKT_TYPE_TXRX_NOTIFY:
+               /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
+               mt7925_mac_tx_free(dev, skb->data, skb->len);
+               napi_consume_skb(skb, 1);
+               break;
+       case PKT_TYPE_RX_EVENT:
+               mt7925_mcu_rx_event(dev, skb);
+               break;
+       case PKT_TYPE_TXS:
+               for (rxd += 2; rxd + 8 <= end; rxd += 8)
+                       mt7925_mac_add_txs(dev, rxd);
+               dev_kfree_skb(skb);
+               break;
+       case PKT_TYPE_NORMAL_MCU:
+       case PKT_TYPE_NORMAL:
+               if (!mt7925_mac_fill_rx(dev, skb)) {
+                       mt76_rx(&dev->mt76, q, skb);
+                       return;
+               }
+               fallthrough;
+       default:
+               dev_kfree_skb(skb);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(mt7925_queue_rx_skb);
+
+static void
+mt7925_vif_connect_iter(void *priv, u8 *mac,
+                       struct ieee80211_vif *vif)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_dev *dev = mvif->phy->dev;
+       struct ieee80211_hw *hw = mt76_hw(dev);
+
+       if (vif->type == NL80211_IFTYPE_STATION)
+               ieee80211_disconnect(vif, true);
+
+       mt76_connac_mcu_uni_add_dev(&dev->mphy, vif, &mvif->sta.wcid, true);
+       mt7925_mcu_set_tx(dev, vif);
+
+       if (vif->type == NL80211_IFTYPE_AP) {
+               mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.wcid,
+                                           true, NULL);
+               mt7925_mcu_sta_update(dev, NULL, vif, true,
+                                     MT76_STA_INFO_STATE_NONE);
+               mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, true);
+       }
+}
+
+/* system error recovery */
+void mt7925_mac_reset_work(struct work_struct *work)
+{
+       struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
+                                             reset_work);
+       struct ieee80211_hw *hw = mt76_hw(dev);
+       struct mt76_connac_pm *pm = &dev->pm;
+       int i, ret;
+
+       dev_dbg(dev->mt76.dev, "chip reset\n");
+       dev->hw_full_reset = true;
+       ieee80211_stop_queues(hw);
+
+       cancel_delayed_work_sync(&dev->mphy.mac_work);
+       cancel_delayed_work_sync(&pm->ps_work);
+       cancel_work_sync(&pm->wake_work);
+
+       for (i = 0; i < 10; i++) {
+               mutex_lock(&dev->mt76.mutex);
+               ret = mt792x_dev_reset(dev);
+               mutex_unlock(&dev->mt76.mutex);
+
+               if (!ret)
+                       break;
+       }
+
+       if (i == 10)
+               dev_err(dev->mt76.dev, "chip reset failed\n");
+
+       if (test_and_clear_bit(MT76_HW_SCANNING, &dev->mphy.state)) {
+               struct cfg80211_scan_info info = {
+                       .aborted = true,
+               };
+
+               ieee80211_scan_completed(dev->mphy.hw, &info);
+       }
+
+       dev->hw_full_reset = false;
+       pm->suspended = false;
+       ieee80211_wake_queues(hw);
+       ieee80211_iterate_active_interfaces(hw,
+                                           IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           mt7925_vif_connect_iter, NULL);
+       mt76_connac_power_save_sched(&dev->mt76.phy, pm);
+}
+
+void mt7925_coredump_work(struct work_struct *work)
+{
+       struct mt792x_dev *dev;
+       char *dump, *data;
+
+       dev = (struct mt792x_dev *)container_of(work, struct mt792x_dev,
+                                               coredump.work.work);
+
+       if (time_is_after_jiffies(dev->coredump.last_activity +
+                                 4 * MT76_CONNAC_COREDUMP_TIMEOUT)) {
+               queue_delayed_work(dev->mt76.wq, &dev->coredump.work,
+                                  MT76_CONNAC_COREDUMP_TIMEOUT);
+               return;
+       }
+
+       dump = vzalloc(MT76_CONNAC_COREDUMP_SZ);
+       data = dump;
+
+       while (true) {
+               struct sk_buff *skb;
+
+               spin_lock_bh(&dev->mt76.lock);
+               skb = __skb_dequeue(&dev->coredump.msg_list);
+               spin_unlock_bh(&dev->mt76.lock);
+
+               if (!skb)
+                       break;
+
+               skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 8);
+               if (!dump || data + skb->len - dump > MT76_CONNAC_COREDUMP_SZ) {
+                       dev_kfree_skb(skb);
+                       continue;
+               }
+
+               memcpy(data, skb->data, skb->len);
+               data += skb->len;
+
+               dev_kfree_skb(skb);
+       }
+
+       if (dump)
+               dev_coredumpv(dev->mt76.dev, dump, MT76_CONNAC_COREDUMP_SZ,
+                             GFP_KERNEL);
+
+       mt792x_reset(&dev->mt76);
+}
+
+/* usb_sdio */
+static void
+mt7925_usb_sdio_write_txwi(struct mt792x_dev *dev, struct mt76_wcid *wcid,
+                          enum mt76_txq_id qid, struct ieee80211_sta *sta,
+                          struct ieee80211_key_conf *key, int pid,
+                          struct sk_buff *skb)
+{
+       __le32 *txwi = (__le32 *)(skb->data - MT_SDIO_TXD_SIZE);
+
+       memset(txwi, 0, MT_SDIO_TXD_SIZE);
+       mt7925_mac_write_txwi(&dev->mt76, txwi, skb, wcid, key, pid, qid, 0);
+       skb_push(skb, MT_SDIO_TXD_SIZE);
+}
+
+int mt7925_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+                                  enum mt76_txq_id qid, struct mt76_wcid *wcid,
+                                  struct ieee80211_sta *sta,
+                                  struct mt76_tx_info *tx_info)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
+       struct ieee80211_key_conf *key = info->control.hw_key;
+       struct sk_buff *skb = tx_info->skb;
+       int err, pad, pktid;
+
+       if (unlikely(tx_info->skb->len <= ETH_HLEN))
+               return -EINVAL;
+
+       if (!wcid)
+               wcid = &dev->mt76.global_wcid;
+
+       if (sta) {
+               struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+
+               if (time_after(jiffies, msta->last_txs + HZ / 4)) {
+                       info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+                       msta->last_txs = jiffies;
+               }
+       }
+
+       pktid = mt76_tx_status_skb_add(&dev->mt76, wcid, skb);
+       mt7925_usb_sdio_write_txwi(dev, wcid, qid, sta, key, pktid, skb);
+
+       mt792x_skb_add_usb_sdio_hdr(dev, skb, 0);
+       pad = round_up(skb->len, 4) - skb->len;
+       if (mt76_is_usb(mdev))
+               pad += 4;
+
+       err = mt76_skb_adjust_pad(skb, pad);
+       if (err)
+               /* Release pktid in case of error. */
+               idr_remove(&wcid->pktid, pktid);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(mt7925_usb_sdio_tx_prepare_skb);
+
+void mt7925_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
+                                    struct mt76_queue_entry *e)
+{
+       __le32 *txwi = (__le32 *)(e->skb->data + MT_SDIO_HDR_SIZE);
+       unsigned int headroom = MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE;
+       struct ieee80211_sta *sta;
+       struct mt76_wcid *wcid;
+       u16 idx;
+
+       idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
+       wcid = rcu_dereference(mdev->wcid[idx]);
+       sta = wcid_to_sta(wcid);
+
+       if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE)))
+               mt7925_tx_check_aggr(sta, txwi);
+
+       skb_pull(e->skb, headroom);
+       mt76_tx_complete_skb(mdev, e->wcid, e->skb);
+}
+EXPORT_SYMBOL_GPL(mt7925_usb_sdio_tx_complete_skb);
+
+bool mt7925_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+
+       mt792x_mutex_acquire(dev);
+       mt7925_mac_sta_poll(dev);
+       mt792x_mutex_release(dev);
+
+       return false;
+}
+EXPORT_SYMBOL_GPL(mt7925_usb_sdio_tx_status_data);
+
+#if IS_ENABLED(CONFIG_IPV6)
+void mt7925_set_ipv6_ns_work(struct work_struct *work)
+{
+       struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
+                                               ipv6_ns_work);
+       struct sk_buff *skb;
+       int ret = 0;
+
+       do {
+               skb = skb_dequeue(&dev->ipv6_ns_list);
+
+               if (!skb)
+                       break;
+
+               mt792x_mutex_acquire(dev);
+               ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+                                           MCU_UNI_CMD(OFFLOAD), true);
+               mt792x_mutex_release(dev);
+
+       } while (!ret);
+
+       if (ret)
+               skb_queue_purge(&dev->ipv6_ns_list);
+}
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.h b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h
new file mode 100644 (file)
index 0000000..b10a993
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: ISC */
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#ifndef __MT7925_MAC_H
+#define __MT7925_MAC_H
+
+#include "../mt76_connac3_mac.h"
+
+#define MT_WTBL_TXRX_CAP_RATE_OFFSET   7
+#define MT_WTBL_TXRX_RATE_G2_HE                24
+#define MT_WTBL_TXRX_RATE_G2           12
+
+#define MT_WTBL_AC0_CTT_OFFSET         20
+
+static inline u32 mt7925_mac_wtbl_lmac_addr(struct mt792x_dev *dev, u16 wcid, u8 dw)
+{
+       mt76_wr(dev, MT_WTBLON_TOP_WDUCR,
+               FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7)));
+
+       return MT_WTBL_LMAC_OFFS(wcid, dw);
+}
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
new file mode 100644 (file)
index 0000000..15c2fb0
--- /dev/null
@@ -0,0 +1,1454 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <net/ipv6.h>
+#include "mt7925.h"
+#include "mcu.h"
+#include "mac.h"
+
+static void
+mt7925_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band,
+                   struct ieee80211_sband_iftype_data *data,
+                       enum nl80211_iftype iftype)
+{
+       struct ieee80211_sta_he_cap *he_cap = &data->he_cap;
+       struct ieee80211_he_cap_elem *he_cap_elem = &he_cap->he_cap_elem;
+       struct ieee80211_he_mcs_nss_supp *he_mcs = &he_cap->he_mcs_nss_supp;
+       int i, nss = hweight8(phy->mt76->antenna_mask);
+       u16 mcs_map = 0;
+
+       for (i = 0; i < 8; i++) {
+               if (i < nss)
+                       mcs_map |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2));
+               else
+                       mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2));
+       }
+
+       he_cap->has_he = true;
+
+       he_cap_elem->mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE;
+       he_cap_elem->mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
+                                      IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3;
+       he_cap_elem->mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU;
+
+       if (band == NL80211_BAND_2GHZ)
+               he_cap_elem->phy_cap_info[0] =
+                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+       else
+               he_cap_elem->phy_cap_info[0] =
+                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+                       IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+       he_cap_elem->phy_cap_info[1] =
+               IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+       he_cap_elem->phy_cap_info[2] =
+               IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+               IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
+               IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
+               IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
+               IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;
+
+       switch (i) {
+       case NL80211_IFTYPE_AP:
+               he_cap_elem->mac_cap_info[2] |=
+                       IEEE80211_HE_MAC_CAP2_BSR;
+               he_cap_elem->mac_cap_info[4] |=
+                       IEEE80211_HE_MAC_CAP4_BQR;
+               he_cap_elem->mac_cap_info[5] |=
+                       IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX;
+               he_cap_elem->phy_cap_info[3] |=
+                       IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK |
+                       IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK;
+               he_cap_elem->phy_cap_info[6] |=
+                       IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE |
+                       IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT;
+               he_cap_elem->phy_cap_info[9] |=
+                       IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU |
+                       IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU;
+               break;
+       case NL80211_IFTYPE_STATION:
+               he_cap_elem->mac_cap_info[1] |=
+                       IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US;
+
+               if (band == NL80211_BAND_2GHZ)
+                       he_cap_elem->phy_cap_info[0] |=
+                               IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G;
+               else
+                       he_cap_elem->phy_cap_info[0] |=
+                               IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G;
+
+               he_cap_elem->phy_cap_info[1] |=
+                       IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
+                       IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+               he_cap_elem->phy_cap_info[3] |=
+                       IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK |
+                       IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK;
+               he_cap_elem->phy_cap_info[4] |=
+                       IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+                       IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
+                       IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
+               he_cap_elem->phy_cap_info[5] |=
+                       IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+                       IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+               he_cap_elem->phy_cap_info[6] |=
+                       IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                       IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+                       IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB |
+                       IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE |
+                       IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT;
+               he_cap_elem->phy_cap_info[7] |=
+                       IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP |
+                       IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+               he_cap_elem->phy_cap_info[8] |=
+                       IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
+                       IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
+                       IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU |
+                       IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484;
+               he_cap_elem->phy_cap_info[9] |=
+                       IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM |
+                       IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK |
+                       IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU |
+                       IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU |
+                       IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+                       IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+               break;
+       default:
+               break;
+       }
+
+       he_mcs->rx_mcs_80 = cpu_to_le16(mcs_map);
+       he_mcs->tx_mcs_80 = cpu_to_le16(mcs_map);
+       he_mcs->rx_mcs_160 = cpu_to_le16(mcs_map);
+       he_mcs->tx_mcs_160 = cpu_to_le16(mcs_map);
+
+       memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres));
+
+       if (he_cap_elem->phy_cap_info[6] &
+           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
+               mt76_connac_gen_ppe_thresh(he_cap->ppe_thres, nss);
+       } else {
+               he_cap_elem->phy_cap_info[9] |=
+                       u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,
+                                      IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
+       }
+
+       if (band == NL80211_BAND_6GHZ) {
+               u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS |
+                         IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS;
+
+               cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_0_5,
+                                      IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) |
+                      u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K,
+                                      IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) |
+                      u16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454,
+                                      IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN);
+
+               data->he_6ghz_capa.capa = cpu_to_le16(cap);
+       }
+}
+
+static void
+mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band,
+                    struct ieee80211_sband_iftype_data *data,
+                    enum nl80211_iftype iftype)
+{
+       struct ieee80211_sta_eht_cap *eht_cap = &data->eht_cap;
+       struct ieee80211_eht_cap_elem_fixed *eht_cap_elem = &eht_cap->eht_cap_elem;
+       struct ieee80211_eht_mcs_nss_supp *eht_nss = &eht_cap->eht_mcs_nss_supp;
+       enum nl80211_chan_width width = phy->mt76->chandef.width;
+       int nss = hweight8(phy->mt76->antenna_mask);
+       int sts = hweight16(phy->mt76->chainmask);
+       u8 val;
+
+       if (!phy->dev->has_eht)
+               return;
+
+       eht_cap->has_eht = true;
+
+       eht_cap_elem->mac_cap_info[0] =
+               IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
+               IEEE80211_EHT_MAC_CAP0_OM_CONTROL;
+
+       eht_cap_elem->phy_cap_info[0] =
+               IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
+               IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
+               IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
+
+       eht_cap_elem->phy_cap_info[0] |=
+               u8_encode_bits(u8_get_bits(sts - 1, BIT(0)),
+                              IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK);
+
+       eht_cap_elem->phy_cap_info[1] =
+               u8_encode_bits(u8_get_bits(sts - 1, GENMASK(2, 1)),
+                              IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) |
+               u8_encode_bits(sts - 1,
+                              IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK);
+
+       eht_cap_elem->phy_cap_info[2] =
+               u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK) |
+               u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK);
+
+       eht_cap_elem->phy_cap_info[3] =
+               IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
+               IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
+               IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
+               IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
+               IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
+               IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
+               IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK;
+
+       eht_cap_elem->phy_cap_info[4] =
+               u8_encode_bits(min_t(int, sts - 1, 2),
+                              IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK);
+
+       eht_cap_elem->phy_cap_info[5] =
+               IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
+               u8_encode_bits(IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US,
+                              IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK) |
+               u8_encode_bits(u8_get_bits(0x11, GENMASK(1, 0)),
+                              IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK);
+
+       val = width == NL80211_CHAN_WIDTH_160 ? 0x7 :
+             width == NL80211_CHAN_WIDTH_80 ? 0x3 : 0x1;
+       eht_cap_elem->phy_cap_info[6] =
+               u8_encode_bits(u8_get_bits(0x11, GENMASK(4, 2)),
+                              IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK) |
+               u8_encode_bits(val, IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK);
+
+       eht_cap_elem->phy_cap_info[7] =
+               IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
+               IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
+               IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
+               IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ;
+
+       val = u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_RX) |
+             u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_TX);
+
+       eht_nss->bw._80.rx_tx_mcs9_max_nss = val;
+       eht_nss->bw._80.rx_tx_mcs11_max_nss = val;
+       eht_nss->bw._80.rx_tx_mcs13_max_nss = val;
+       eht_nss->bw._160.rx_tx_mcs9_max_nss = val;
+       eht_nss->bw._160.rx_tx_mcs11_max_nss = val;
+       eht_nss->bw._160.rx_tx_mcs13_max_nss = val;
+}
+
+static void
+__mt7925_set_stream_he_eht_caps(struct mt792x_phy *phy,
+                               struct ieee80211_supported_band *sband,
+                               enum nl80211_band band)
+{
+       struct ieee80211_sband_iftype_data *data = phy->iftype[band];
+       int i, n = 0;
+
+       for (i = 0; i < NUM_NL80211_IFTYPES; i++) {
+               switch (i) {
+               case NL80211_IFTYPE_STATION:
+               case NL80211_IFTYPE_AP:
+                       break;
+               default:
+                       continue;
+               }
+
+               data[n].types_mask = BIT(i);
+               mt7925_init_he_caps(phy, band, &data[n], i);
+               mt7925_init_eht_caps(phy, band, &data[n], i);
+
+               n++;
+       }
+
+       _ieee80211_set_sband_iftype_data(sband, data, n);
+}
+
+void mt7925_set_stream_he_eht_caps(struct mt792x_phy *phy)
+{
+       if (phy->mt76->cap.has_2ghz)
+               __mt7925_set_stream_he_eht_caps(phy, &phy->mt76->sband_2g.sband,
+                                               NL80211_BAND_2GHZ);
+
+       if (phy->mt76->cap.has_5ghz)
+               __mt7925_set_stream_he_eht_caps(phy, &phy->mt76->sband_5g.sband,
+                                               NL80211_BAND_5GHZ);
+
+       if (phy->mt76->cap.has_6ghz)
+               __mt7925_set_stream_he_eht_caps(phy, &phy->mt76->sband_6g.sband,
+                                               NL80211_BAND_6GHZ);
+}
+
+int __mt7925_start(struct mt792x_phy *phy)
+{
+       struct mt76_phy *mphy = phy->mt76;
+       int err;
+
+       err = mt7925_mcu_set_channel_domain(mphy);
+       if (err)
+               return err;
+
+       err = mt7925_mcu_set_rts_thresh(phy, 0x92b);
+       if (err)
+               return err;
+
+       err = mt7925_set_tx_sar_pwr(mphy->hw, NULL);
+       if (err)
+               return err;
+
+       mt792x_mac_reset_counters(phy);
+       set_bit(MT76_STATE_RUNNING, &mphy->state);
+
+       ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+                                    MT792x_WATCHDOG_TIME);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__mt7925_start);
+
+static int mt7925_start(struct ieee80211_hw *hw)
+{
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+       int err;
+
+       mt792x_mutex_acquire(phy->dev);
+       err = __mt7925_start(phy);
+       mt792x_mutex_release(phy->dev);
+
+       return err;
+}
+
+static int
+mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+       struct mt76_txq *mtxq;
+       int idx, ret = 0;
+
+       mt792x_mutex_acquire(dev);
+
+       mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask);
+       if (mvif->mt76.idx >= MT792x_MAX_INTERFACES) {
+               ret = -ENOSPC;
+               goto out;
+       }
+
+       mvif->mt76.omac_idx = mvif->mt76.idx;
+       mvif->phy = phy;
+       mvif->mt76.band_idx = 0;
+       mvif->mt76.wmm_idx = mvif->mt76.idx % MT76_CONNAC_MAX_WMM_SETS;
+
+       if (phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ)
+               mvif->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL + 4;
+       else
+               mvif->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL;
+
+       ret = mt76_connac_mcu_uni_add_dev(&dev->mphy, vif, &mvif->sta.wcid,
+                                         true);
+       if (ret)
+               goto out;
+
+       dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
+       phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
+
+       idx = MT792x_WTBL_RESERVED - mvif->mt76.idx;
+
+       INIT_LIST_HEAD(&mvif->sta.wcid.poll_list);
+       mvif->sta.wcid.idx = idx;
+       mvif->sta.wcid.phy_idx = mvif->mt76.band_idx;
+       mvif->sta.wcid.hw_key_idx = -1;
+       mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
+       mt76_wcid_init(&mvif->sta.wcid);
+
+       mt7925_mac_wtbl_update(dev, idx,
+                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+       ewma_rssi_init(&mvif->rssi);
+
+       rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
+       if (vif->txq) {
+               mtxq = (struct mt76_txq *)vif->txq->drv_priv;
+               mtxq->wcid = idx;
+       }
+
+       vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+out:
+       mt792x_mutex_release(dev);
+
+       return ret;
+}
+
+static void mt7925_roc_iter(void *priv, u8 *mac,
+                           struct ieee80211_vif *vif)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_phy *phy = priv;
+
+       mt7925_mcu_abort_roc(phy, mvif, phy->roc_token_id);
+}
+
+void mt7925_roc_work(struct work_struct *work)
+{
+       struct mt792x_phy *phy;
+
+       phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy,
+                                               roc_work);
+
+       if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+               return;
+
+       mt792x_mutex_acquire(phy->dev);
+       ieee80211_iterate_active_interfaces(phy->mt76->hw,
+                                           IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           mt7925_roc_iter, phy);
+       mt792x_mutex_release(phy->dev);
+       ieee80211_remain_on_channel_expired(phy->mt76->hw);
+}
+
+static int mt7925_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif)
+{
+       int err = 0;
+
+       del_timer_sync(&phy->roc_timer);
+       cancel_work_sync(&phy->roc_work);
+
+       mt792x_mutex_acquire(phy->dev);
+       if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+               err = mt7925_mcu_abort_roc(phy, vif, phy->roc_token_id);
+       mt792x_mutex_release(phy->dev);
+
+       return err;
+}
+
+static int mt7925_set_roc(struct mt792x_phy *phy,
+                         struct mt792x_vif *vif,
+                         struct ieee80211_channel *chan,
+                         int duration,
+                         enum mt7925_roc_req type)
+{
+       int err;
+
+       if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state))
+               return -EBUSY;
+
+       phy->roc_grant = false;
+
+       err = mt7925_mcu_set_roc(phy, vif, chan, duration, type,
+                                ++phy->roc_token_id);
+       if (err < 0) {
+               clear_bit(MT76_STATE_ROC, &phy->mt76->state);
+               goto out;
+       }
+
+       if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, 4 * HZ)) {
+               mt7925_mcu_abort_roc(phy, vif, phy->roc_token_id);
+               clear_bit(MT76_STATE_ROC, &phy->mt76->state);
+               err = -ETIMEDOUT;
+       }
+
+out:
+       return err;
+}
+
+static int mt7925_remain_on_channel(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_channel *chan,
+                                   int duration,
+                                   enum ieee80211_roc_type type)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+       int err;
+
+       mt792x_mutex_acquire(phy->dev);
+       err = mt7925_set_roc(phy, mvif, chan, duration, MT7925_ROC_REQ_ROC);
+       mt792x_mutex_release(phy->dev);
+
+       return err;
+}
+
+static int mt7925_cancel_remain_on_channel(struct ieee80211_hw *hw,
+                                          struct ieee80211_vif *vif)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+
+       return mt7925_abort_roc(phy, mvif);
+}
+
+static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+                         struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_sta *msta = sta ? (struct mt792x_sta *)sta->drv_priv :
+                                 &mvif->sta;
+       struct mt76_wcid *wcid = &msta->wcid;
+       u8 *wcid_keyidx = &wcid->hw_key_idx;
+       int idx = key->keyidx, err = 0;
+
+       /* The hardware does not support per-STA RX GTK, fallback
+        * to software mode for these.
+        */
+       if ((vif->type == NL80211_IFTYPE_ADHOC ||
+            vif->type == NL80211_IFTYPE_MESH_POINT) &&
+           (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
+            key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
+           !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+               return -EOPNOTSUPP;
+
+       /* fall back to sw encryption for unsupported ciphers */
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+               wcid_keyidx = &wcid->hw_key_idx2;
+               break;
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               if (!mvif->wep_sta)
+                       return -EOPNOTSUPP;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+       case WLAN_CIPHER_SUITE_SMS4:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       mt792x_mutex_acquire(dev);
+
+       if (cmd == SET_KEY && !mvif->mt76.cipher) {
+               struct mt792x_phy *phy = mt792x_hw_phy(hw);
+
+               mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
+               mt7925_mcu_add_bss_info(phy, mvif->mt76.ctx, vif, sta, true);
+       }
+
+       if (cmd == SET_KEY)
+               *wcid_keyidx = idx;
+       else if (idx == *wcid_keyidx)
+               *wcid_keyidx = -1;
+       else
+               goto out;
+
+       mt76_wcid_key_setup(&dev->mt76, wcid,
+                           cmd == SET_KEY ? key : NULL);
+
+       err = mt7925_mcu_add_key(&dev->mt76, vif, &msta->bip,
+                                key, MCU_UNI_CMD(STA_REC_UPDATE),
+                                &msta->wcid, cmd);
+
+       if (err)
+               goto out;
+
+       if (key->cipher == WLAN_CIPHER_SUITE_WEP104 ||
+           key->cipher == WLAN_CIPHER_SUITE_WEP40)
+               err = mt7925_mcu_add_key(&dev->mt76, vif, &mvif->wep_sta->bip,
+                                        key, MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+                                        &mvif->wep_sta->wcid, cmd);
+
+out:
+       mt792x_mutex_release(dev);
+
+       return err;
+}
+
+static void
+mt7925_pm_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct mt792x_dev *dev = priv;
+       struct ieee80211_hw *hw = mt76_hw(dev);
+       bool pm_enable = dev->pm.enable;
+       int err;
+
+       err = mt7925_mcu_set_beacon_filter(dev, vif, pm_enable);
+       if (err < 0)
+               return;
+
+       if (pm_enable) {
+               vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+               ieee80211_hw_set(hw, CONNECTION_MONITOR);
+       } else {
+               vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER;
+               __clear_bit(IEEE80211_HW_CONNECTION_MONITOR, hw->flags);
+       }
+}
+
+static void
+mt7925_sniffer_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct mt792x_dev *dev = priv;
+       struct ieee80211_hw *hw = mt76_hw(dev);
+       struct mt76_connac_pm *pm = &dev->pm;
+       bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
+
+       mt7925_mcu_set_sniffer(dev, vif, monitor);
+       pm->enable = pm->enable_user && !monitor;
+       pm->ds_enable = pm->ds_enable_user && !monitor;
+
+       mt7925_mcu_set_deep_sleep(dev, pm->ds_enable);
+
+       if (monitor)
+               mt7925_mcu_set_beacon_filter(dev, vif, false);
+}
+
+void mt7925_set_runtime_pm(struct mt792x_dev *dev)
+{
+       struct ieee80211_hw *hw = mt76_hw(dev);
+       struct mt76_connac_pm *pm = &dev->pm;
+       bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
+
+       pm->enable = pm->enable_user && !monitor;
+       ieee80211_iterate_active_interfaces(hw,
+                                           IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           mt7925_pm_interface_iter, dev);
+       pm->ds_enable = pm->ds_enable_user && !monitor;
+       mt7925_mcu_set_deep_sleep(dev, pm->ds_enable);
+}
+
+static int mt7925_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       int ret = 0;
+
+       mt792x_mutex_acquire(dev);
+
+       if (changed & IEEE80211_CONF_CHANGE_POWER) {
+               ret = mt7925_set_tx_sar_pwr(hw, NULL);
+               if (ret)
+                       goto out;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+               ieee80211_iterate_active_interfaces(hw,
+                                                   IEEE80211_IFACE_ITER_RESUME_ALL,
+                                                   mt7925_sniffer_interface_iter, dev);
+       }
+
+out:
+       mt792x_mutex_release(dev);
+
+       return ret;
+}
+
+static void mt7925_configure_filter(struct ieee80211_hw *hw,
+                                   unsigned int changed_flags,
+                                   unsigned int *total_flags,
+                                   u64 multicast)
+{
+#define MT7925_FILTER_FCSFAIL    BIT(2)
+#define MT7925_FILTER_CONTROL    BIT(5)
+#define MT7925_FILTER_OTHER_BSS  BIT(6)
+#define MT7925_FILTER_ENABLE     BIT(31)
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       u32 flags = MT7925_FILTER_ENABLE;
+
+#define MT7925_FILTER(_fif, _type) do {                        \
+               if (*total_flags & (_fif))              \
+                       flags |= MT7925_FILTER_##_type; \
+       } while (0)
+
+       MT7925_FILTER(FIF_FCSFAIL, FCSFAIL);
+       MT7925_FILTER(FIF_CONTROL, CONTROL);
+       MT7925_FILTER(FIF_OTHER_BSS, OTHER_BSS);
+
+       mt792x_mutex_acquire(dev);
+       mt7925_mcu_set_rxfilter(dev, flags, 0, 0);
+       mt792x_mutex_release(dev);
+
+       *total_flags &= (FIF_OTHER_BSS | FIF_FCSFAIL | FIF_CONTROL);
+}
+
+static u8
+mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                      bool beacon, bool mcast)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct mt76_phy *mphy = hw->priv;
+       u16 rate;
+       u8 i, idx, ht;
+
+       rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon, mcast);
+       ht = FIELD_GET(MT_TX_RATE_MODE, rate) > MT_PHY_TYPE_OFDM;
+
+       if (beacon && ht) {
+               struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+               /* must odd index */
+               idx = MT7925_BEACON_RATES_TBL + 2 * (mvif->idx % 20);
+               mt7925_mac_set_fixed_rate_table(dev, idx, rate);
+               return idx;
+       }
+
+       idx = FIELD_GET(MT_TX_RATE_IDX, rate);
+       for (i = 0; i < ARRAY_SIZE(mt76_rates); i++)
+               if ((mt76_rates[i].hw_value & GENMASK(7, 0)) == idx)
+                       return MT792x_BASIC_RATES_TBL + i;
+
+       return mvif->basic_rates_idx;
+}
+
+static void mt7925_bss_info_changed(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_bss_conf *info,
+                                   u64 changed)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+       mt792x_mutex_acquire(dev);
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               int slottime = info->use_short_slot ? 9 : 20;
+
+               if (slottime != phy->slottime) {
+                       phy->slottime = slottime;
+                       mt792x_mac_set_timeing(phy);
+               }
+       }
+
+       if (changed & BSS_CHANGED_MCAST_RATE)
+               mvif->mcast_rates_idx =
+                               mt7925_get_rates_table(hw, vif, false, true);
+
+       if (changed & BSS_CHANGED_BASIC_RATES)
+               mvif->basic_rates_idx =
+                               mt7925_get_rates_table(hw, vif, false, false);
+
+       if (changed & (BSS_CHANGED_BEACON |
+                      BSS_CHANGED_BEACON_ENABLED)) {
+               mvif->beacon_rates_idx =
+                               mt7925_get_rates_table(hw, vif, true, false);
+
+               mt7925_mcu_uni_add_beacon_offload(dev, hw, vif,
+                                                 info->enable_beacon);
+       }
+
+       /* ensure that enable txcmd_mode after bss_info */
+       if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
+               mt7925_mcu_set_tx(dev, vif);
+
+       if (changed & BSS_CHANGED_PS)
+               mt7925_mcu_uni_bss_ps(dev, vif);
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               mt7925_mcu_sta_update(dev, NULL, vif, true,
+                                     MT76_STA_INFO_STATE_ASSOC);
+               mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
+       }
+
+       if (changed & BSS_CHANGED_ARP_FILTER) {
+               struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+               mt7925_mcu_update_arp_filter(&dev->mt76, &mvif->mt76, info);
+       }
+
+       mt792x_mutex_release(dev);
+}
+
+int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                      struct ieee80211_sta *sta)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       int ret, idx;
+
+       idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
+       if (idx < 0)
+               return -ENOSPC;
+
+       INIT_LIST_HEAD(&msta->wcid.poll_list);
+       msta->vif = mvif;
+       msta->wcid.sta = 1;
+       msta->wcid.idx = idx;
+       msta->wcid.phy_idx = mvif->mt76.band_idx;
+       msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+       msta->last_txs = jiffies;
+
+       ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm);
+       if (ret)
+               return ret;
+
+       if (vif->type == NL80211_IFTYPE_STATION)
+               mvif->wep_sta = msta;
+
+       mt7925_mac_wtbl_update(dev, idx,
+                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+       /* should update bss info before STA add */
+       if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
+               mt7925_mcu_add_bss_info(&dev->phy, mvif->mt76.ctx, vif, sta,
+                                       false);
+
+       ret = mt7925_mcu_sta_update(dev, sta, vif, true,
+                                   MT76_STA_INFO_STATE_NONE);
+       if (ret)
+               return ret;
+
+       mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt7925_mac_sta_add);
+
+void mt7925_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+       mt792x_mutex_acquire(dev);
+
+       if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
+               mt7925_mcu_add_bss_info(&dev->phy, mvif->mt76.ctx, vif, sta,
+                                       true);
+
+       ewma_avg_signal_init(&msta->avg_ack_signal);
+
+       mt7925_mac_wtbl_update(dev, msta->wcid.idx,
+                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+       memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
+
+       mt7925_mcu_sta_update(dev, sta, vif, true, MT76_STA_INFO_STATE_ASSOC);
+
+       mt792x_mutex_release(dev);
+}
+EXPORT_SYMBOL_GPL(mt7925_mac_sta_assoc);
+
+void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+
+       mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->wcid);
+       mt76_connac_pm_wake(&dev->mphy, &dev->pm);
+
+       mt7925_mcu_sta_update(dev, sta, vif, false, MT76_STA_INFO_STATE_NONE);
+       mt7925_mac_wtbl_update(dev, msta->wcid.idx,
+                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+               mvif->wep_sta = NULL;
+               ewma_rssi_init(&mvif->rssi);
+               if (!sta->tdls)
+                       mt7925_mcu_add_bss_info(&dev->phy, mvif->mt76.ctx, vif, sta,
+                                               false);
+       }
+
+       spin_lock_bh(&mdev->sta_poll_lock);
+       if (!list_empty(&msta->wcid.poll_list))
+               list_del_init(&msta->wcid.poll_list);
+       spin_unlock_bh(&mdev->sta_poll_lock);
+
+       mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+}
+EXPORT_SYMBOL_GPL(mt7925_mac_sta_remove);
+
+static int mt7925_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+       mt792x_mutex_acquire(dev);
+       mt7925_mcu_set_rts_thresh(&dev->phy, val);
+       mt792x_mutex_release(dev);
+
+       return 0;
+}
+
+static int
+mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                   struct ieee80211_ampdu_params *params)
+{
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct ieee80211_sta *sta = params->sta;
+       struct ieee80211_txq *txq = sta->txq[params->tid];
+       struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+       u16 tid = params->tid;
+       u16 ssn = params->ssn;
+       struct mt76_txq *mtxq;
+       int ret = 0;
+
+       if (!txq)
+               return -EINVAL;
+
+       mtxq = (struct mt76_txq *)txq->drv_priv;
+
+       mt792x_mutex_acquire(dev);
+       switch (action) {
+       case IEEE80211_AMPDU_RX_START:
+               mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn,
+                                  params->buf_size);
+               mt7925_mcu_uni_rx_ba(dev, params, true);
+               break;
+       case IEEE80211_AMPDU_RX_STOP:
+               mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
+               mt7925_mcu_uni_rx_ba(dev, params, false);
+               break;
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
+               mtxq->aggr = true;
+               mtxq->send_bar = false;
+               mt7925_mcu_uni_tx_ba(dev, params, true);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+               mtxq->aggr = false;
+               clear_bit(tid, &msta->wcid.ampdu_state);
+               mt7925_mcu_uni_tx_ba(dev, params, false);
+               break;
+       case IEEE80211_AMPDU_TX_START:
+               set_bit(tid, &msta->wcid.ampdu_state);
+               ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
+               break;
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+               mtxq->aggr = false;
+               clear_bit(tid, &msta->wcid.ampdu_state);
+               mt7925_mcu_uni_tx_ba(dev, params, false);
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+       }
+       mt792x_mutex_release(dev);
+
+       return ret;
+}
+
+static bool is_valid_alpha2(const char *alpha2)
+{
+       if (!alpha2)
+               return false;
+
+       if (alpha2[0] == '0' && alpha2[1] == '0')
+               return true;
+
+       if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
+               return true;
+
+       return false;
+}
+
+void mt7925_scan_work(struct work_struct *work)
+{
+       struct mt792x_phy *phy;
+
+       phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy,
+                                               scan_work.work);
+
+       while (true) {
+               struct mt76_dev *mdev = &phy->dev->mt76;
+               struct sk_buff *skb;
+               struct tlv *tlv;
+               int tlv_len;
+
+               spin_lock_bh(&phy->dev->mt76.lock);
+               skb = __skb_dequeue(&phy->scan_event_list);
+               spin_unlock_bh(&phy->dev->mt76.lock);
+
+               if (!skb)
+                       break;
+
+               skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4);
+               tlv = (struct tlv *)skb->data;
+               tlv_len = skb->len;
+
+               while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) {
+                       struct mt7925_mcu_scan_chinfo_event *evt;
+
+                       switch (le16_to_cpu(tlv->tag)) {
+                       case UNI_EVENT_SCAN_DONE_BASIC:
+                               if (test_and_clear_bit(MT76_HW_SCANNING, &phy->mt76->state)) {
+                                       struct cfg80211_scan_info info = {
+                                               .aborted = false,
+                                       };
+                                       ieee80211_scan_completed(phy->mt76->hw, &info);
+                               }
+                               break;
+                       case UNI_EVENT_SCAN_DONE_CHNLINFO:
+                               evt = (struct mt7925_mcu_scan_chinfo_event *)tlv->data;
+
+                               if (!is_valid_alpha2(evt->alpha2))
+                                       break;
+
+                               if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
+                                       break;
+
+                               mt7925_mcu_set_clc(phy->dev, evt->alpha2, ENVIRON_INDOOR);
+
+                               break;
+                       case UNI_EVENT_SCAN_DONE_NLO:
+                               ieee80211_sched_scan_results(phy->mt76->hw);
+                               break;
+                       default:
+                               break;
+                       }
+
+                       tlv_len -= le16_to_cpu(tlv->len);
+                       tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
+               }
+
+               dev_kfree_skb(skb);
+       }
+}
+
+static int
+mt7925_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+              struct ieee80211_scan_request *req)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt76_phy *mphy = hw->priv;
+       int err;
+
+       mt792x_mutex_acquire(dev);
+       err = mt7925_mcu_hw_scan(mphy, vif, req);
+       mt792x_mutex_release(dev);
+
+       return err;
+}
+
+static void
+mt7925_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt76_phy *mphy = hw->priv;
+
+       mt792x_mutex_acquire(dev);
+       mt7925_mcu_cancel_hw_scan(mphy, vif);
+       mt792x_mutex_release(dev);
+}
+
+static int
+mt7925_start_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                       struct cfg80211_sched_scan_request *req,
+                       struct ieee80211_scan_ies *ies)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt76_phy *mphy = hw->priv;
+       int err;
+
+       mt792x_mutex_acquire(dev);
+
+       err = mt7925_mcu_sched_scan_req(mphy, vif, req);
+       if (err < 0)
+               goto out;
+
+       err = mt7925_mcu_sched_scan_enable(mphy, vif, true);
+out:
+       mt792x_mutex_release(dev);
+
+       return err;
+}
+
+static int
+mt7925_stop_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt76_phy *mphy = hw->priv;
+       int err;
+
+       mt792x_mutex_acquire(dev);
+       err = mt7925_mcu_sched_scan_enable(mphy, vif, false);
+       mt792x_mutex_release(dev);
+
+       return err;
+}
+
+static int
+mt7925_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+       int max_nss = hweight8(hw->wiphy->available_antennas_tx);
+
+       if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss)
+               return -EINVAL;
+
+       if ((BIT(hweight8(tx_ant)) - 1) != tx_ant)
+               tx_ant = BIT(ffs(tx_ant) - 1) - 1;
+
+       mt792x_mutex_acquire(dev);
+
+       phy->mt76->antenna_mask = tx_ant;
+       phy->mt76->chainmask = tx_ant;
+
+       mt76_set_stream_caps(phy->mt76, true);
+       mt7925_set_stream_he_eht_caps(phy);
+
+       /* TODO: update bmc_wtbl spe_idx when antenna changes */
+       mt792x_mutex_release(dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int mt7925_suspend(struct ieee80211_hw *hw,
+                         struct cfg80211_wowlan *wowlan)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+
+       cancel_delayed_work_sync(&phy->scan_work);
+       cancel_delayed_work_sync(&phy->mt76->mac_work);
+
+       cancel_delayed_work_sync(&dev->pm.ps_work);
+       mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
+
+       mt792x_mutex_acquire(dev);
+
+       clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+       ieee80211_iterate_active_interfaces(hw,
+                                           IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           mt7925_mcu_set_suspend_iter,
+                                           &dev->mphy);
+
+       mt792x_mutex_release(dev);
+
+       return 0;
+}
+
+static int mt7925_resume(struct ieee80211_hw *hw)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+
+       mt792x_mutex_acquire(dev);
+
+       set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+       ieee80211_iterate_active_interfaces(hw,
+                                           IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           mt7925_mcu_set_suspend_iter,
+                                           &dev->mphy);
+
+       ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+                                    MT792x_WATCHDOG_TIME);
+
+       mt792x_mutex_release(dev);
+
+       return 0;
+}
+
+static void mt7925_set_rekey_data(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif,
+                                 struct cfg80211_gtk_rekey_data *data)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+       mt792x_mutex_acquire(dev);
+       mt76_connac_mcu_update_gtk_rekey(hw, vif, data);
+       mt792x_mutex_release(dev);
+}
+#endif /* CONFIG_PM */
+
+static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        struct ieee80211_sta *sta,
+                                        bool enabled)
+{
+       struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+       mt792x_mutex_acquire(dev);
+
+       if (enabled)
+               set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+       else
+               clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+
+       mt7925_mcu_wtbl_update_hdr_trans(dev, vif, sta);
+
+       mt792x_mutex_release(dev);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static void mt7925_ipv6_addr_change(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct inet6_dev *idev)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_dev *dev = mvif->phy->dev;
+       struct inet6_ifaddr *ifa;
+       struct sk_buff *skb;
+       u8 idx = 0;
+
+       struct {
+               struct {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct mt7925_arpns_tlv arpns;
+               struct in6_addr ns_addrs[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
+       } req_hdr = {
+               .hdr = {
+                       .bss_idx = mvif->mt76.idx,
+               },
+               .arpns = {
+                       .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ND),
+                       .len = cpu_to_le16(sizeof(req_hdr) - 4),
+                       .enable = true,
+               },
+       };
+
+       read_lock_bh(&idev->lock);
+       list_for_each_entry(ifa, &idev->addr_list, if_list) {
+               if (ifa->flags & IFA_F_TENTATIVE)
+                       continue;
+               req_hdr.ns_addrs[idx] = ifa->addr;
+               if (++idx >= IEEE80211_BSS_ARP_ADDR_LIST_LEN)
+                       break;
+       }
+       read_unlock_bh(&idev->lock);
+
+       if (!idx)
+               return;
+
+       req_hdr.arpns.ips_num = idx;
+
+       skb = __mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req_hdr),
+                                  0, GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       skb_put_data(skb, &req_hdr, sizeof(req_hdr));
+
+       skb_queue_tail(&dev->ipv6_ns_list, skb);
+
+       ieee80211_queue_work(dev->mt76.hw, &dev->ipv6_ns_work);
+}
+#endif
+
+int mt7925_set_tx_sar_pwr(struct ieee80211_hw *hw,
+                         const struct cfg80211_sar_specs *sar)
+{
+       struct mt76_phy *mphy = hw->priv;
+
+       if (sar) {
+               int err = mt76_init_sar_power(hw, sar);
+
+               if (err)
+                       return err;
+       }
+       mt792x_init_acpi_sar_power(mt792x_hw_phy(hw), !sar);
+
+       return mt7925_mcu_set_rate_txpower(mphy);
+}
+
+static int mt7925_set_sar_specs(struct ieee80211_hw *hw,
+                               const struct cfg80211_sar_specs *sar)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       int err;
+
+       mt792x_mutex_acquire(dev);
+       err = mt7925_mcu_set_clc(dev, dev->mt76.alpha2,
+                                dev->country_ie_env);
+       if (err < 0)
+               goto out;
+
+       err = mt7925_set_tx_sar_pwr(hw, sar);
+out:
+       mt792x_mutex_release(dev);
+
+       return err;
+}
+
+static void
+mt7925_channel_switch_beacon(struct ieee80211_hw *hw,
+                            struct ieee80211_vif *vif,
+                            struct cfg80211_chan_def *chandef)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+       mt792x_mutex_acquire(dev);
+       mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, true);
+       mt792x_mutex_release(dev);
+}
+
+static int
+mt7925_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+               struct ieee80211_bss_conf *link_conf)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       int err;
+
+       mt792x_mutex_acquire(dev);
+
+       err = mt7925_mcu_add_bss_info(&dev->phy, mvif->mt76.ctx, vif, NULL,
+                                     true);
+       if (err)
+               goto out;
+
+       err = mt7925_mcu_set_bss_pm(dev, vif, true);
+       if (err)
+               goto out;
+
+       err = mt7925_mcu_sta_update(dev, NULL, vif, true,
+                                   MT76_STA_INFO_STATE_NONE);
+out:
+       mt792x_mutex_release(dev);
+
+       return err;
+}
+
+static void
+mt7925_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+              struct ieee80211_bss_conf *link_conf)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       int err;
+
+       mt792x_mutex_acquire(dev);
+
+       err = mt7925_mcu_set_bss_pm(dev, vif, false);
+       if (err)
+               goto out;
+
+       mt7925_mcu_add_bss_info(&dev->phy, mvif->mt76.ctx, vif, NULL,
+                               false);
+
+out:
+       mt792x_mutex_release(dev);
+}
+
+static int
+mt7925_add_chanctx(struct ieee80211_hw *hw,
+                  struct ieee80211_chanctx_conf *ctx)
+{
+       return 0;
+}
+
+static void
+mt7925_remove_chanctx(struct ieee80211_hw *hw,
+                     struct ieee80211_chanctx_conf *ctx)
+{
+}
+
+static void mt7925_ctx_iter(void *priv, u8 *mac,
+                           struct ieee80211_vif *vif)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct ieee80211_chanctx_conf *ctx = priv;
+
+       if (ctx != mvif->mt76.ctx)
+               return;
+
+       if (vif->type == NL80211_IFTYPE_MONITOR) {
+               mt7925_mcu_set_sniffer(mvif->phy->dev, vif, true);
+               mt7925_mcu_config_sniffer(mvif, ctx);
+       } else {
+               mt7925_mcu_set_chctx(mvif->phy->mt76, &mvif->mt76, ctx);
+       }
+}
+
+static void
+mt7925_change_chanctx(struct ieee80211_hw *hw,
+                     struct ieee80211_chanctx_conf *ctx,
+                     u32 changed)
+{
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+
+       mt792x_mutex_acquire(phy->dev);
+       ieee80211_iterate_active_interfaces(phy->mt76->hw,
+                                           IEEE80211_IFACE_ITER_ACTIVE,
+                                           mt7925_ctx_iter, ctx);
+       mt792x_mutex_release(phy->dev);
+}
+
+static void mt7925_mgd_prepare_tx(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_prep_tx_info *info)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       u16 duration = info->duration ? info->duration :
+                      jiffies_to_msecs(HZ);
+
+       mt792x_mutex_acquire(dev);
+       mt7925_set_roc(mvif->phy, mvif, mvif->mt76.ctx->def.chan, duration,
+                      MT7925_ROC_REQ_JOIN);
+       mt792x_mutex_release(dev);
+}
+
+static void mt7925_mgd_complete_tx(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_prep_tx_info *info)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+       mt7925_abort_roc(mvif->phy, mvif);
+}
+
+const struct ieee80211_ops mt7925_ops = {
+       .tx = mt792x_tx,
+       .start = mt7925_start,
+       .stop = mt792x_stop,
+       .add_interface = mt7925_add_interface,
+       .remove_interface = mt792x_remove_interface,
+       .config = mt7925_config,
+       .conf_tx = mt792x_conf_tx,
+       .configure_filter = mt7925_configure_filter,
+       .bss_info_changed = mt7925_bss_info_changed,
+       .start_ap = mt7925_start_ap,
+       .stop_ap = mt7925_stop_ap,
+       .sta_state = mt76_sta_state,
+       .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+       .set_key = mt7925_set_key,
+       .sta_set_decap_offload = mt7925_sta_set_decap_offload,
+#if IS_ENABLED(CONFIG_IPV6)
+       .ipv6_addr_change = mt7925_ipv6_addr_change,
+#endif /* CONFIG_IPV6 */
+       .ampdu_action = mt7925_ampdu_action,
+       .set_rts_threshold = mt7925_set_rts_threshold,
+       .wake_tx_queue = mt76_wake_tx_queue,
+       .release_buffered_frames = mt76_release_buffered_frames,
+       .channel_switch_beacon = mt7925_channel_switch_beacon,
+       .get_txpower = mt76_get_txpower,
+       .get_stats = mt792x_get_stats,
+       .get_et_sset_count = mt792x_get_et_sset_count,
+       .get_et_strings = mt792x_get_et_strings,
+       .get_et_stats = mt792x_get_et_stats,
+       .get_tsf = mt792x_get_tsf,
+       .set_tsf = mt792x_set_tsf,
+       .get_survey = mt76_get_survey,
+       .get_antenna = mt76_get_antenna,
+       .set_antenna = mt7925_set_antenna,
+       .set_coverage_class = mt792x_set_coverage_class,
+       .hw_scan = mt7925_hw_scan,
+       .cancel_hw_scan = mt7925_cancel_hw_scan,
+       .sta_statistics = mt792x_sta_statistics,
+       .sched_scan_start = mt7925_start_sched_scan,
+       .sched_scan_stop = mt7925_stop_sched_scan,
+#ifdef CONFIG_PM
+       .suspend = mt7925_suspend,
+       .resume = mt7925_resume,
+       .set_wakeup = mt792x_set_wakeup,
+       .set_rekey_data = mt7925_set_rekey_data,
+#endif /* CONFIG_PM */
+       .flush = mt792x_flush,
+       .set_sar_specs = mt7925_set_sar_specs,
+       .remain_on_channel = mt7925_remain_on_channel,
+       .cancel_remain_on_channel = mt7925_cancel_remain_on_channel,
+       .add_chanctx = mt7925_add_chanctx,
+       .remove_chanctx = mt7925_remove_chanctx,
+       .change_chanctx = mt7925_change_chanctx,
+       .assign_vif_chanctx = mt792x_assign_vif_chanctx,
+       .unassign_vif_chanctx = mt792x_unassign_vif_chanctx,
+       .mgd_prepare_tx = mt7925_mgd_prepare_tx,
+       .mgd_complete_tx = mt7925_mgd_complete_tx,
+};
+EXPORT_SYMBOL_GPL(mt7925_ops);
+
+MODULE_AUTHOR("Deren Wu <deren.wu@mediatek.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
new file mode 100644 (file)
index 0000000..9c0e397
--- /dev/null
@@ -0,0 +1,3174 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include "mt7925.h"
+#include "mcu.h"
+#include "mac.h"
+
+#define MT_STA_BFER                    BIT(0)
+#define MT_STA_BFEE                    BIT(1)
+
+static bool mt7925_disable_clc;
+module_param_named(disable_clc, mt7925_disable_clc, bool, 0644);
+MODULE_PARM_DESC(disable_clc, "disable CLC support");
+
+int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd,
+                             struct sk_buff *skb, int seq)
+{
+       int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd);
+       struct mt7925_mcu_rxd *rxd;
+       int ret = 0;
+
+       if (!skb) {
+               dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", cmd, seq);
+               mt792x_reset(mdev);
+
+               return -ETIMEDOUT;
+       }
+
+       rxd = (struct mt7925_mcu_rxd *)skb->data;
+       if (seq != rxd->seq)
+               return -EAGAIN;
+
+       if (cmd == MCU_CMD(PATCH_SEM_CONTROL) ||
+           cmd == MCU_CMD(PATCH_FINISH_REQ)) {
+               skb_pull(skb, sizeof(*rxd) - 4);
+               ret = *skb->data;
+       } else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) ||
+                  cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) ||
+                  cmd == MCU_UNI_CMD(STA_REC_UPDATE) ||
+                  cmd == MCU_UNI_CMD(HIF_CTRL) ||
+                  cmd == MCU_UNI_CMD(OFFLOAD) ||
+                  cmd == MCU_UNI_CMD(SUSPEND)) {
+               struct mt7925_mcu_uni_event *event;
+
+               skb_pull(skb, sizeof(*rxd));
+               event = (struct mt7925_mcu_uni_event *)skb->data;
+               ret = le32_to_cpu(event->status);
+               /* skip invalid event */
+               if (mcu_cmd != event->cid)
+                       ret = -EAGAIN;
+       } else {
+               skb_pull(skb, sizeof(*rxd));
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_parse_response);
+
+int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set)
+{
+#define MT_RF_REG_HDR           GENMASK(31, 24)
+#define MT_RF_REG_ANT           GENMASK(23, 16)
+#define RF_REG_PREFIX           0x99
+       struct {
+               u8 __rsv[4];
+               union {
+                       struct uni_cmd_access_reg_basic {
+                               __le16 tag;
+                               __le16 len;
+                               __le32 idx;
+                               __le32 data;
+                       } __packed reg;
+                       struct uni_cmd_access_rf_reg_basic {
+                               __le16 tag;
+                               __le16 len;
+                               __le16 ant;
+                               u8 __rsv[2];
+                               __le32 idx;
+                               __le32 data;
+                       } __packed rf_reg;
+               };
+       } __packed * res, req;
+       struct sk_buff *skb;
+       int ret;
+
+       if (u32_get_bits(regidx, MT_RF_REG_HDR) == RF_REG_PREFIX) {
+               req.rf_reg.tag = cpu_to_le16(UNI_CMD_ACCESS_RF_REG_BASIC);
+               req.rf_reg.len = cpu_to_le16(sizeof(req.rf_reg));
+               req.rf_reg.ant = cpu_to_le16(u32_get_bits(regidx, MT_RF_REG_ANT));
+               req.rf_reg.idx = cpu_to_le32(regidx);
+               req.rf_reg.data = set ? cpu_to_le32(*val) : 0;
+       } else {
+               req.reg.tag = cpu_to_le16(UNI_CMD_ACCESS_REG_BASIC);
+               req.reg.len = cpu_to_le16(sizeof(req.reg));
+               req.reg.idx = cpu_to_le32(regidx);
+               req.reg.data = set ? cpu_to_le32(*val) : 0;
+       }
+
+       if (set)
+               return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REG_ACCESS),
+                                        &req, sizeof(req), true);
+
+       ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+                                       MCU_WM_UNI_CMD_QUERY(REG_ACCESS),
+                                       &req, sizeof(req), true, &skb);
+       if (ret)
+               return ret;
+
+       res = (void *)skb->data;
+       if (u32_get_bits(regidx, MT_RF_REG_HDR) == RF_REG_PREFIX)
+               *val = le32_to_cpu(res->rf_reg.data);
+       else
+               *val = le32_to_cpu(res->reg.data);
+
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_regval);
+
+int mt7925_mcu_update_arp_filter(struct mt76_dev *dev,
+                                struct mt76_vif *vif,
+                                struct ieee80211_bss_conf *info)
+{
+       struct ieee80211_vif *mvif = container_of(info, struct ieee80211_vif,
+                                                 bss_conf);
+       struct sk_buff *skb;
+       int i, len = min_t(int, mvif->cfg.arp_addr_cnt,
+                          IEEE80211_BSS_ARP_ADDR_LIST_LEN);
+       struct {
+               struct {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct mt7925_arpns_tlv arp;
+       } req = {
+               .hdr = {
+                       .bss_idx = vif->idx,
+               },
+               .arp = {
+                       .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ARP),
+                       .len = cpu_to_le16(sizeof(req) - 4 + len * 2 * sizeof(__be32)),
+                       .ips_num = len,
+                       .enable = true,
+               },
+       };
+
+       skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(req) + len * 2 * sizeof(__be32));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put_data(skb, &req, sizeof(req));
+       for (i = 0; i < len; i++) {
+               skb_put_data(skb, &mvif->cfg.arp_addr_list[i], sizeof(__be32));
+               skb_put_zero(skb, sizeof(__be32));
+       }
+
+       return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(OFFLOAD), true);
+}
+
+#ifdef CONFIG_PM
+static int
+mt7925_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif,
+                              bool suspend, struct cfg80211_wowlan *wowlan)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct mt76_dev *dev = phy->dev;
+       struct {
+               struct {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct mt76_connac_wow_ctrl_tlv wow_ctrl_tlv;
+               struct mt76_connac_wow_gpio_param_tlv gpio_tlv;
+       } req = {
+               .hdr = {
+                       .bss_idx = mvif->idx,
+               },
+               .wow_ctrl_tlv = {
+                       .tag = cpu_to_le16(UNI_SUSPEND_WOW_CTRL),
+                       .len = cpu_to_le16(sizeof(struct mt76_connac_wow_ctrl_tlv)),
+                       .cmd = suspend ? 1 : 2,
+               },
+               .gpio_tlv = {
+                       .tag = cpu_to_le16(UNI_SUSPEND_WOW_GPIO_PARAM),
+                       .len = cpu_to_le16(sizeof(struct mt76_connac_wow_gpio_param_tlv)),
+                       .gpio_pin = 0xff, /* follow fw about GPIO pin */
+               },
+       };
+
+       if (wowlan->magic_pkt)
+               req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_MAGIC;
+       if (wowlan->disconnect)
+               req.wow_ctrl_tlv.trigger |= (UNI_WOW_DETECT_TYPE_DISCONNECT |
+                                            UNI_WOW_DETECT_TYPE_BCN_LOST);
+       if (wowlan->nd_config) {
+               mt7925_mcu_sched_scan_req(phy, vif, wowlan->nd_config);
+               req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_SCH_SCAN_HIT;
+               mt7925_mcu_sched_scan_enable(phy, vif, suspend);
+       }
+       if (wowlan->n_patterns)
+               req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_BITMAP;
+
+       if (mt76_is_mmio(dev))
+               req.wow_ctrl_tlv.wakeup_hif = WOW_PCIE;
+       else if (mt76_is_usb(dev))
+               req.wow_ctrl_tlv.wakeup_hif = WOW_USB;
+       else if (mt76_is_sdio(dev))
+               req.wow_ctrl_tlv.wakeup_hif = WOW_GPIO;
+
+       return mt76_mcu_send_msg(dev, MCU_UNI_CMD(SUSPEND), &req,
+                                sizeof(req), true);
+}
+
+static int
+mt7925_mcu_set_wow_pattern(struct mt76_dev *dev,
+                          struct ieee80211_vif *vif,
+                          u8 index, bool enable,
+                          struct cfg80211_pkt_pattern *pattern)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct mt7925_wow_pattern_tlv *tlv;
+       struct sk_buff *skb;
+       struct {
+               u8 bss_idx;
+               u8 pad[3];
+       } __packed hdr = {
+               .bss_idx = mvif->idx,
+       };
+
+       skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(hdr) + sizeof(*tlv));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put_data(skb, &hdr, sizeof(hdr));
+       tlv = (struct mt7925_wow_pattern_tlv *)skb_put(skb, sizeof(*tlv));
+       tlv->tag = cpu_to_le16(UNI_SUSPEND_WOW_PATTERN);
+       tlv->len = cpu_to_le16(sizeof(*tlv));
+       tlv->bss_idx = 0xF;
+       tlv->data_len = pattern->pattern_len;
+       tlv->enable = enable;
+       tlv->index = index;
+       tlv->offset = 0;
+
+       memcpy(tlv->pattern, pattern->pattern, pattern->pattern_len);
+       memcpy(tlv->mask, pattern->mask, DIV_ROUND_UP(pattern->pattern_len, 8));
+
+       return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SUSPEND), true);
+}
+
+void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac,
+                                struct ieee80211_vif *vif)
+{
+       struct mt76_phy *phy = priv;
+       bool suspend = !test_bit(MT76_STATE_RUNNING, &phy->state);
+       struct ieee80211_hw *hw = phy->hw;
+       struct cfg80211_wowlan *wowlan = hw->wiphy->wowlan_config;
+       int i;
+
+       mt76_connac_mcu_set_gtk_rekey(phy->dev, vif, suspend);
+
+       mt76_connac_mcu_set_suspend_mode(phy->dev, vif, suspend, 1, true);
+
+       for (i = 0; i < wowlan->n_patterns; i++)
+               mt7925_mcu_set_wow_pattern(phy->dev, vif, i, suspend,
+                                          &wowlan->patterns[i]);
+       mt7925_connac_mcu_set_wow_ctrl(phy, vif, suspend, wowlan);
+}
+
+#endif /* CONFIG_PM */
+
+static void
+mt7925_mcu_connection_loss_iter(void *priv, u8 *mac,
+                               struct ieee80211_vif *vif)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct mt7925_uni_beacon_loss_event *event = priv;
+
+       if (mvif->idx != event->hdr.bss_idx)
+               return;
+
+       if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER) ||
+           vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       ieee80211_connection_loss(vif);
+}
+
+static void
+mt7925_mcu_connection_loss_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+       struct mt7925_uni_beacon_loss_event *event;
+       struct mt76_phy *mphy = &dev->mt76.phy;
+
+       skb_pull(skb, sizeof(struct mt7925_mcu_rxd));
+       event = (struct mt7925_uni_beacon_loss_event *)skb->data;
+
+       ieee80211_iterate_active_interfaces_atomic(mphy->hw,
+                                       IEEE80211_IFACE_ITER_RESUME_ALL,
+                                       mt7925_mcu_connection_loss_iter, event);
+}
+
+static void
+mt7925_mcu_roc_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct mt7925_roc_grant_tlv *grant = priv;
+
+       if (mvif->idx != grant->bss_idx)
+               return;
+
+       mvif->band_idx = grant->dbdcband;
+}
+
+static void
+mt7925_mcu_uni_roc_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+       struct ieee80211_hw *hw = dev->mt76.hw;
+       struct mt7925_roc_grant_tlv *grant;
+       struct mt7925_mcu_rxd *rxd;
+       int duration;
+
+       rxd = (struct mt7925_mcu_rxd *)skb->data;
+       grant = (struct mt7925_roc_grant_tlv *)(rxd->tlv + 4);
+
+       /* should never happen */
+       WARN_ON_ONCE((le16_to_cpu(grant->tag) != UNI_EVENT_ROC_GRANT));
+
+       if (grant->reqtype == MT7925_ROC_REQ_ROC)
+               ieee80211_ready_on_channel(hw);
+       else if (grant->reqtype == MT7925_ROC_REQ_JOIN)
+               ieee80211_iterate_active_interfaces_atomic(hw,
+                                               IEEE80211_IFACE_ITER_RESUME_ALL,
+                                               mt7925_mcu_roc_iter, grant);
+       dev->phy.roc_grant = true;
+       wake_up(&dev->phy.roc_wait);
+       duration = le32_to_cpu(grant->max_interval);
+       mod_timer(&dev->phy.roc_timer,
+                 jiffies + msecs_to_jiffies(duration));
+}
+
+static void
+mt7925_mcu_scan_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+       struct mt76_phy *mphy = &dev->mt76.phy;
+       struct mt792x_phy *phy = (struct mt792x_phy *)mphy->priv;
+
+       spin_lock_bh(&dev->mt76.lock);
+       __skb_queue_tail(&phy->scan_event_list, skb);
+       spin_unlock_bh(&dev->mt76.lock);
+
+       ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work,
+                                    MT792x_HW_SCAN_TIMEOUT);
+}
+
+static void
+mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+#define UNI_EVENT_TX_DONE_MSG 0
+#define UNI_EVENT_TX_DONE_RAW 1
+       struct mt7925_mcu_txs_event {
+               u8 ver;
+               u8 rsv[3];
+               u8 data[0];
+       } __packed * txs;
+       struct tlv *tlv;
+       u32 tlv_len;
+
+       skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4);
+       tlv = (struct tlv *)skb->data;
+       tlv_len = skb->len;
+
+       while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) {
+               switch (le16_to_cpu(tlv->tag)) {
+               case UNI_EVENT_TX_DONE_RAW:
+                       txs = (struct mt7925_mcu_txs_event *)tlv->data;
+                       mt7925_mac_add_txs(dev, txs->data);
+                       break;
+               default:
+                       break;
+               }
+               tlv_len -= le16_to_cpu(tlv->len);
+               tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
+       }
+}
+
+static void
+mt7925_mcu_uni_debug_msg_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+       struct mt7925_uni_debug_msg {
+               __le16 tag;
+               __le16 len;
+               u8 fmt;
+               u8 rsv[3];
+               u8 id;
+               u8 type:3;
+               u8 nr_args:5;
+               union {
+                       struct idxlog {
+                               __le16 rsv;
+                               __le32 ts;
+                               __le32 idx;
+                               u8 data[];
+                       } __packed idx;
+                       struct txtlog {
+                               u8 len;
+                               u8 rsv;
+                               __le32 ts;
+                               u8 data[];
+                       } __packed txt;
+               };
+       } __packed * hdr;
+
+       skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4);
+       hdr = (struct mt7925_uni_debug_msg *)skb->data;
+
+       if (hdr->id == 0x28) {
+               skb_pull(skb, offsetof(struct mt7925_uni_debug_msg, id));
+               wiphy_info(mt76_hw(dev)->wiphy, "%.*s", skb->len, skb->data);
+               return;
+       } else if (hdr->id != 0xa8) {
+               return;
+       }
+
+       if (hdr->type == 0) { /* idx log */
+               int i, ret, len = PAGE_SIZE - 1, nr_val;
+               struct page *page = dev_alloc_pages(get_order(len));
+               __le32 *val;
+               char *buf, *cur;
+
+               if (!page)
+                       return;
+
+               buf = page_address(page);
+               cur = buf;
+
+               nr_val = (le16_to_cpu(hdr->len) - sizeof(*hdr)) / 4;
+               val = (__le32 *)hdr->idx.data;
+               for (i = 0; i < nr_val && len > 0; i++) {
+                       ret = snprintf(cur, len, "0x%x,", le32_to_cpu(val[i]));
+                       if (ret <= 0)
+                               break;
+
+                       cur += ret;
+                       len -= ret;
+               }
+               if (cur > buf)
+                       wiphy_info(mt76_hw(dev)->wiphy, "idx: 0x%X,%d,%s",
+                                  le32_to_cpu(hdr->idx.idx), nr_val, buf);
+               put_page(page);
+       } else if (hdr->type == 2) { /* str log */
+               wiphy_info(mt76_hw(dev)->wiphy, "%.*s", hdr->txt.len, hdr->txt.data);
+       }
+}
+
+static void
+mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev,
+                                   struct sk_buff *skb)
+{
+       struct mt7925_mcu_rxd *rxd;
+
+       rxd = (struct mt7925_mcu_rxd *)skb->data;
+
+       switch (rxd->eid) {
+       case MCU_UNI_EVENT_FW_LOG_2_HOST:
+               mt7925_mcu_uni_debug_msg_event(dev, skb);
+               break;
+       case MCU_UNI_EVENT_ROC:
+               mt7925_mcu_uni_roc_event(dev, skb);
+               break;
+       case MCU_UNI_EVENT_SCAN_DONE:
+               mt7925_mcu_scan_event(dev, skb);
+               return;
+       case MCU_UNI_EVENT_TX_DONE:
+               mt7925_mcu_tx_done_event(dev, skb);
+               break;
+       case MCU_UNI_EVENT_BSS_BEACON_LOSS:
+               mt7925_mcu_connection_loss_event(dev, skb);
+               break;
+       case MCU_UNI_EVENT_COREDUMP:
+               dev->fw_assert = true;
+               mt76_connac_mcu_coredump_event(&dev->mt76, skb, &dev->coredump);
+               return;
+       default:
+               break;
+       }
+       dev_kfree_skb(skb);
+}
+
+void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+       struct mt7925_mcu_rxd *rxd = (struct mt7925_mcu_rxd *)skb->data;
+
+       if (skb_linearize(skb))
+               return;
+
+       if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) {
+               mt7925_mcu_uni_rx_unsolicited_event(dev, skb);
+               return;
+       }
+
+       mt76_mcu_rx_event(&dev->mt76, skb);
+}
+
+static int
+mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
+                 struct ieee80211_ampdu_params *params,
+                 bool enable, bool tx)
+{
+       struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv;
+       struct sta_rec_ba_uni *ba;
+       struct sk_buff *skb;
+       struct tlv *tlv;
+       int len;
+
+       len = sizeof(struct sta_req_hdr) + sizeof(*ba);
+       skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
+                                             len);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba));
+
+       ba = (struct sta_rec_ba_uni *)tlv;
+       ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT;
+       ba->winsize = cpu_to_le16(params->buf_size);
+       ba->ssn = cpu_to_le16(params->ssn);
+       ba->ba_en = enable << params->tid;
+       ba->amsdu = params->amsdu;
+       ba->tid = params->tid;
+
+       return mt76_mcu_skb_send_msg(dev, skb,
+                                    MCU_UNI_CMD(STA_REC_UPDATE), true);
+}
+
+/** starec & wtbl **/
+int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev,
+                        struct ieee80211_ampdu_params *params,
+                        bool enable)
+{
+       struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv;
+       struct mt792x_vif *mvif = msta->vif;
+
+       if (enable && !params->amsdu)
+               msta->wcid.amsdu = false;
+
+       return mt7925_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
+                                enable, true);
+}
+
+int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
+                        struct ieee80211_ampdu_params *params,
+                        bool enable)
+{
+       struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv;
+       struct mt792x_vif *mvif = msta->vif;
+
+       return mt7925_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
+                                enable, false);
+}
+
+static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name)
+{
+       const struct mt76_connac2_fw_trailer *hdr;
+       const struct mt76_connac2_fw_region *region;
+       const struct mt7925_clc *clc;
+       struct mt76_dev *mdev = &dev->mt76;
+       struct mt792x_phy *phy = &dev->phy;
+       const struct firmware *fw;
+       int ret, i, len, offset = 0;
+       u8 *clc_base = NULL;
+
+       if (mt7925_disable_clc ||
+           mt76_is_usb(&dev->mt76))
+               return 0;
+
+       ret = request_firmware(&fw, fw_name, mdev->dev);
+       if (ret)
+               return ret;
+
+       if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+               dev_err(mdev->dev, "Invalid firmware\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
+       for (i = 0; i < hdr->n_region; i++) {
+               region = (const void *)((const u8 *)hdr -
+                                       (hdr->n_region - i) * sizeof(*region));
+               len = le32_to_cpu(region->len);
+
+               /* check if we have valid buffer size */
+               if (offset + len > fw->size) {
+                       dev_err(mdev->dev, "Invalid firmware region\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if ((region->feature_set & FW_FEATURE_NON_DL) &&
+                   region->type == FW_TYPE_CLC) {
+                       clc_base = (u8 *)(fw->data + offset);
+                       break;
+               }
+               offset += len;
+       }
+
+       if (!clc_base)
+               goto out;
+
+       for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) {
+               clc = (const struct mt7925_clc *)(clc_base + offset);
+
+               /* do not init buf again if chip reset triggered */
+               if (phy->clc[clc->idx])
+                       continue;
+
+               phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc,
+                                                 le32_to_cpu(clc->len),
+                                                 GFP_KERNEL);
+
+               if (!phy->clc[clc->idx]) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       ret = mt7925_mcu_set_clc(dev, "00", ENVIRON_INDOOR);
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl)
+{
+       struct {
+               u8 _rsv[4];
+
+               __le16 tag;
+               __le16 len;
+               u8 ctrl;
+               u8 interval;
+               u8 _rsv2[2];
+       } __packed req = {
+               .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_LOG_CTRL),
+               .len = cpu_to_le16(sizeof(req) - 4),
+               .ctrl = ctrl,
+       };
+       int ret;
+
+       ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(WSYS_CONFIG),
+                                       &req, sizeof(req), false, NULL);
+       return ret;
+}
+
+static void
+mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data)
+{
+       struct mt76_phy *mphy = &dev->mt76.phy;
+       struct mt76_dev *mdev = mphy->dev;
+       struct mt7925_mcu_phy_cap {
+               u8 ht;
+               u8 vht;
+               u8 _5g;
+               u8 max_bw;
+               u8 nss;
+               u8 dbdc;
+               u8 tx_ldpc;
+               u8 rx_ldpc;
+               u8 tx_stbc;
+               u8 rx_stbc;
+               u8 hw_path;
+               u8 he;
+               u8 eht;
+       } __packed * cap;
+       enum {
+               WF0_24G,
+               WF0_5G
+       };
+
+       cap = (struct mt7925_mcu_phy_cap *)data;
+
+       mdev->phy.antenna_mask = BIT(cap->nss) - 1;
+       mdev->phy.chainmask = mdev->phy.antenna_mask;
+       mdev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G);
+       mdev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G);
+       dev->has_eht = cap->eht;
+}
+
+static int
+mt7925_mcu_get_nic_capability(struct mt792x_dev *dev)
+{
+       struct mt76_phy *mphy = &dev->mt76.phy;
+       struct {
+               u8 _rsv[4];
+
+               __le16 tag;
+               __le16 len;
+       } __packed req = {
+               .tag = cpu_to_le16(UNI_CHIP_CONFIG_NIC_CAPA),
+               .len = cpu_to_le16(sizeof(req) - 4),
+       };
+       struct mt76_connac_cap_hdr {
+               __le16 n_element;
+               u8 rsv[2];
+       } __packed * hdr;
+       struct sk_buff *skb;
+       int ret, i;
+
+       ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(CHIP_CONFIG),
+                                       &req, sizeof(req), true, &skb);
+       if (ret)
+               return ret;
+
+       hdr = (struct mt76_connac_cap_hdr *)skb->data;
+       if (skb->len < sizeof(*hdr)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       skb_pull(skb, sizeof(*hdr));
+
+       for (i = 0; i < le16_to_cpu(hdr->n_element); i++) {
+               struct tlv *tlv = (struct tlv *)skb->data;
+               int len;
+
+               if (skb->len < sizeof(*tlv))
+                       break;
+
+               len = le16_to_cpu(tlv->len);
+               if (skb->len < len)
+                       break;
+
+               switch (le16_to_cpu(tlv->tag)) {
+               case MT_NIC_CAP_6G:
+                       mphy->cap.has_6ghz = !!tlv->data[0];
+                       break;
+               case MT_NIC_CAP_MAC_ADDR:
+                       memcpy(mphy->macaddr, (void *)tlv->data, ETH_ALEN);
+                       break;
+               case MT_NIC_CAP_PHY:
+                       mt7925_mcu_parse_phy_cap(dev, tlv->data);
+                       break;
+               default:
+                       break;
+               }
+               skb_pull(skb, len);
+       }
+out:
+       dev_kfree_skb(skb);
+       return ret;
+}
+
+int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd)
+{
+       u16 len = strlen(cmd) + 1;
+       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 = 0,
+                       .data_size = cpu_to_le16(len),
+               },
+       };
+
+       memcpy(req.config.data, cmd, len);
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(CHIP_CONFIG),
+                                &req, sizeof(req), false);
+}
+
+int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable)
+{
+       char cmd[16];
+
+       snprintf(cmd, sizeof(cmd), "KeepFullPwr %d", !enable);
+
+       return mt7925_mcu_chip_config(dev, cmd);
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_set_deep_sleep);
+
+int mt7925_run_firmware(struct mt792x_dev *dev)
+{
+       int err;
+
+       err = mt792x_load_firmware(dev);
+       if (err)
+               return err;
+
+       err = mt7925_mcu_get_nic_capability(dev);
+       if (err)
+               return err;
+
+       set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+       err = mt7925_load_clc(dev, mt792x_ram_name(dev));
+       if (err)
+               return err;
+
+       return mt7925_mcu_fw_log_2_host(dev, 1);
+}
+EXPORT_SYMBOL_GPL(mt7925_run_firmware);
+
+static void
+mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_sta *sta)
+{
+       struct sta_rec_hdr_trans *hdr_trans;
+       struct mt76_wcid *wcid;
+       struct tlv *tlv;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans));
+       hdr_trans = (struct sta_rec_hdr_trans *)tlv;
+       hdr_trans->dis_rx_hdr_tran = true;
+
+       if (vif->type == NL80211_IFTYPE_STATION)
+               hdr_trans->to_ds = true;
+       else
+               hdr_trans->from_ds = true;
+
+       wcid = (struct mt76_wcid *)sta->drv_priv;
+       if (!wcid)
+               return;
+
+       hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
+       if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
+               hdr_trans->to_ds = true;
+               hdr_trans->from_ds = true;
+       }
+}
+
+int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_sta *sta)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_sta *msta;
+       struct sk_buff *skb;
+
+       msta = sta ? (struct mt792x_sta *)sta->drv_priv : &mvif->sta;
+
+       skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
+                                             &msta->wcid,
+                                             MT7925_STA_UPDATE_MAX_SIZE);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       /* starec hdr trans */
+       mt7925_mcu_sta_hdr_trans_tlv(skb, vif, sta);
+       return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+                                    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
+int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_vif *vif)
+{
+#define MCU_EDCA_AC_PARAM      0
+#define WMM_AIFS_SET           BIT(0)
+#define WMM_CW_MIN_SET         BIT(1)
+#define WMM_CW_MAX_SET         BIT(2)
+#define WMM_TXOP_SET           BIT(3)
+#define WMM_PARAM_SET          (WMM_AIFS_SET | WMM_CW_MIN_SET | \
+                                WMM_CW_MAX_SET | WMM_TXOP_SET)
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct {
+               u8 bss_idx;
+               u8 __rsv[3];
+       } __packed hdr = {
+               .bss_idx = mvif->mt76.idx,
+       };
+       struct sk_buff *skb;
+       int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca);
+       int ac;
+
+       skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put_data(skb, &hdr, sizeof(hdr));
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac];
+               struct edca *e;
+               struct tlv *tlv;
+
+               tlv = mt76_connac_mcu_add_tlv(skb, MCU_EDCA_AC_PARAM, sizeof(*e));
+
+               e = (struct edca *)tlv;
+               e->set = WMM_PARAM_SET;
+               e->queue = ac + mvif->mt76.wmm_idx * MT76_CONNAC_MAX_WMM_SETS;
+               e->aifs = q->aifs;
+               e->txop = cpu_to_le16(q->txop);
+
+               if (q->cw_min)
+                       e->cw_min = fls(q->cw_min);
+               else
+                       e->cw_min = 5;
+
+               if (q->cw_max)
+                       e->cw_max = fls(q->cw_max);
+               else
+                       e->cw_max = 10;
+       }
+
+       return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+                                    MCU_UNI_CMD(EDCA_UPDATE), true);
+}
+
+static int
+mt7925_mcu_sta_key_tlv(struct mt76_wcid *wcid,
+                      struct mt76_connac_sta_key_conf *sta_key_conf,
+                      struct sk_buff *skb,
+                      struct ieee80211_key_conf *key,
+                      enum set_key_cmd cmd)
+{
+       struct sta_rec_sec_uni *sec;
+       struct tlv *tlv;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec));
+       sec = (struct sta_rec_sec_uni *)tlv;
+       sec->add = cmd;
+
+       if (cmd == SET_KEY) {
+               struct sec_key_uni *sec_key;
+               u8 cipher;
+
+               cipher = mt76_connac_mcu_get_cipher(key->cipher);
+               if (cipher == MCU_CIPHER_NONE)
+                       return -EOPNOTSUPP;
+
+               sec_key = &sec->key[0];
+               sec_key->cipher_len = sizeof(*sec_key);
+
+               if (cipher == MCU_CIPHER_BIP_CMAC_128) {
+                       sec_key->wlan_idx = cpu_to_le16(wcid->idx);
+                       sec_key->cipher_id = MCU_CIPHER_AES_CCMP;
+                       sec_key->key_id = sta_key_conf->keyidx;
+                       sec_key->key_len = 16;
+                       memcpy(sec_key->key, sta_key_conf->key, 16);
+
+                       sec_key = &sec->key[1];
+                       sec_key->wlan_idx = cpu_to_le16(wcid->idx);
+                       sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128;
+                       sec_key->cipher_len = sizeof(*sec_key);
+                       sec_key->key_len = 16;
+                       memcpy(sec_key->key, key->key, 16);
+                       sec->n_cipher = 2;
+               } else {
+                       sec_key->wlan_idx = cpu_to_le16(wcid->idx);
+                       sec_key->cipher_id = cipher;
+                       sec_key->key_id = key->keyidx;
+                       sec_key->key_len = key->keylen;
+                       memcpy(sec_key->key, key->key, key->keylen);
+
+                       if (cipher == MCU_CIPHER_TKIP) {
+                               /* Rx/Tx MIC keys are swapped */
+                               memcpy(sec_key->key + 16, key->key + 24, 8);
+                               memcpy(sec_key->key + 24, key->key + 16, 8);
+                       }
+
+                       /* store key_conf for BIP batch update */
+                       if (cipher == MCU_CIPHER_AES_CCMP) {
+                               memcpy(sta_key_conf->key, key->key, key->keylen);
+                               sta_key_conf->keyidx = key->keyidx;
+                       }
+
+                       sec->n_cipher = 1;
+               }
+       } else {
+               sec->n_cipher = 0;
+       }
+
+       return 0;
+}
+
+int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+                      struct mt76_connac_sta_key_conf *sta_key_conf,
+                      struct ieee80211_key_conf *key, int mcu_cmd,
+                      struct mt76_wcid *wcid, enum set_key_cmd cmd)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct sk_buff *skb;
+       int ret;
+
+       skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
+                                             MT7925_STA_UPDATE_MAX_SIZE);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       ret = mt7925_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd);
+       if (ret)
+               return ret;
+
+       return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
+}
+
+int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_vif *vif,
+                      struct ieee80211_channel *chan, int duration,
+                      enum mt7925_roc_req type, u8 token_id)
+{
+       int center_ch = ieee80211_frequency_to_channel(chan->center_freq);
+       struct mt792x_dev *dev = phy->dev;
+       struct {
+               struct {
+                       u8 rsv[4];
+               } __packed hdr;
+               struct roc_acquire_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       u8 bss_idx;
+                       u8 tokenid;
+                       u8 control_channel;
+                       u8 sco;
+                       u8 band;
+                       u8 bw;
+                       u8 center_chan;
+                       u8 center_chan2;
+                       u8 bw_from_ap;
+                       u8 center_chan_from_ap;
+                       u8 center_chan2_from_ap;
+                       u8 reqtype;
+                       __le32 maxinterval;
+                       u8 dbdcband;
+                       u8 rsv[3];
+               } __packed roc;
+       } __packed req = {
+               .roc = {
+                       .tag = cpu_to_le16(UNI_ROC_ACQUIRE),
+                       .len = cpu_to_le16(sizeof(struct roc_acquire_tlv)),
+                       .tokenid = token_id,
+                       .reqtype = type,
+                       .maxinterval = cpu_to_le32(duration),
+                       .bss_idx = vif->mt76.idx,
+                       .control_channel = chan->hw_value,
+                       .bw = CMD_CBW_20MHZ,
+                       .bw_from_ap = CMD_CBW_20MHZ,
+                       .center_chan = center_ch,
+                       .center_chan_from_ap = center_ch,
+                       .dbdcband = 0xff, /* auto */
+               },
+       };
+
+       if (chan->hw_value < center_ch)
+               req.roc.sco = 1; /* SCA */
+       else if (chan->hw_value > center_ch)
+               req.roc.sco = 3; /* SCB */
+
+       switch (chan->band) {
+       case NL80211_BAND_6GHZ:
+               req.roc.band = 3;
+               break;
+       case NL80211_BAND_5GHZ:
+               req.roc.band = 2;
+               break;
+       default:
+               req.roc.band = 1;
+               break;
+       }
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC),
+                                &req, sizeof(req), false);
+}
+
+int mt7925_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif,
+                        u8 token_id)
+{
+       struct mt792x_dev *dev = phy->dev;
+       struct {
+               struct {
+                       u8 rsv[4];
+               } __packed hdr;
+               struct roc_abort_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       u8 bss_idx;
+                       u8 tokenid;
+                       u8 dbdcband;
+                       u8 rsv[5];
+               } __packed abort;
+       } __packed req = {
+               .abort = {
+                       .tag = cpu_to_le16(UNI_ROC_ABORT),
+                       .len = cpu_to_le16(sizeof(struct roc_abort_tlv)),
+                       .tokenid = token_id,
+                       .bss_idx = vif->mt76.idx,
+                       .dbdcband = 0xff, /* auto*/
+               },
+       };
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC),
+                                &req, sizeof(req), false);
+}
+
+int mt7925_mcu_set_chan_info(struct mt792x_phy *phy, u16 tag)
+{
+       static const u8 ch_band[] = {
+               [NL80211_BAND_2GHZ] = 0,
+               [NL80211_BAND_5GHZ] = 1,
+               [NL80211_BAND_6GHZ] = 2,
+       };
+       struct mt792x_dev *dev = phy->dev;
+       struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+       int freq1 = chandef->center_freq1;
+       u8 band_idx = chandef->chan->band != NL80211_BAND_2GHZ;
+       struct {
+               /* fixed field */
+               u8 __rsv[4];
+
+               __le16 tag;
+               __le16 len;
+               u8 control_ch;
+               u8 center_ch;
+               u8 bw;
+               u8 tx_path_num;
+               u8 rx_path;     /* mask or num */
+               u8 switch_reason;
+               u8 band_idx;
+               u8 center_ch2;  /* for 80+80 only */
+               __le16 cac_case;
+               u8 channel_band;
+               u8 rsv0;
+               __le32 outband_freq;
+               u8 txpower_drop;
+               u8 ap_bw;
+               u8 ap_center_ch;
+               u8 rsv1[53];
+       } __packed req = {
+               .tag = cpu_to_le16(tag),
+               .len = cpu_to_le16(sizeof(req) - 4),
+               .control_ch = chandef->chan->hw_value,
+               .center_ch = ieee80211_frequency_to_channel(freq1),
+               .bw = mt76_connac_chan_bw(chandef),
+               .tx_path_num = hweight8(phy->mt76->antenna_mask),
+               .rx_path = phy->mt76->antenna_mask,
+               .band_idx = band_idx,
+               .channel_band = ch_band[chandef->chan->band],
+       };
+
+       if (chandef->chan->band == NL80211_BAND_6GHZ)
+               req.channel_band = 2;
+       else
+               req.channel_band = chandef->chan->band;
+
+       if (tag == UNI_CHANNEL_RX_PATH ||
+           dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
+               req.switch_reason = CH_SWITCH_NORMAL;
+       else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+               req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
+       else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
+                                         NL80211_IFTYPE_AP))
+               req.switch_reason = CH_SWITCH_DFS;
+       else
+               req.switch_reason = CH_SWITCH_NORMAL;
+
+       if (tag == UNI_CHANNEL_SWITCH)
+               req.rx_path = hweight8(req.rx_path);
+
+       if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
+               int freq2 = chandef->center_freq2;
+
+               req.center_ch2 = ieee80211_frequency_to_channel(freq2);
+       }
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(CHANNEL_SWITCH),
+                                &req, sizeof(req), true);
+}
+
+int mt7925_mcu_set_eeprom(struct mt792x_dev *dev)
+{
+       struct {
+               u8 _rsv[4];
+
+               __le16 tag;
+               __le16 len;
+               u8 buffer_mode;
+               u8 format;
+               __le16 buf_len;
+       } __packed req = {
+               .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
+               .len = cpu_to_le16(sizeof(req) - 4),
+               .buffer_mode = EE_MODE_EFUSE,
+               .format = EE_FORMAT_WHOLE
+       };
+
+       return mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(EFUSE_CTRL),
+                                        &req, sizeof(req), false, NULL);
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_set_eeprom);
+
+int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_vif *vif)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct {
+               struct {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct ps_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       u8 ps_state; /* 0: device awake
+                                     * 1: static power save
+                                     * 2: dynamic power saving
+                                     * 3: enter TWT power saving
+                                     * 4: leave TWT power saving
+                                     */
+                       u8 pad[3];
+               } __packed ps;
+       } __packed ps_req = {
+               .hdr = {
+                       .bss_idx = mvif->mt76.idx,
+               },
+               .ps = {
+                       .tag = cpu_to_le16(UNI_BSS_INFO_PS),
+                       .len = cpu_to_le16(sizeof(struct ps_tlv)),
+                       .ps_state = vif->cfg.ps ? 2 : 0,
+               },
+       };
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return -EOPNOTSUPP;
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE),
+                                &ps_req, sizeof(ps_req), true);
+}
+
+static int
+mt7925_mcu_uni_bss_bcnft(struct mt792x_dev *dev, struct ieee80211_vif *vif,
+                        bool enable)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct {
+               struct {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct bcnft_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       __le16 bcn_interval;
+                       u8 dtim_period;
+                       u8 bmc_delivered_ac;
+                       u8 bmc_triggered_ac;
+                       u8 pad[3];
+               } __packed bcnft;
+       } __packed bcnft_req = {
+               .hdr = {
+                       .bss_idx = mvif->mt76.idx,
+               },
+               .bcnft = {
+                       .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT),
+                       .len = cpu_to_le16(sizeof(struct bcnft_tlv)),
+                       .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
+                       .dtim_period = vif->bss_conf.dtim_period,
+               },
+       };
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return 0;
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE),
+                                &bcnft_req, sizeof(bcnft_req), true);
+}
+
+int
+mt7925_mcu_set_bss_pm(struct mt792x_dev *dev, struct ieee80211_vif *vif,
+                     bool enable)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct {
+               struct {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct bcnft_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       __le16 bcn_interval;
+                       u8 dtim_period;
+                       u8 bmc_delivered_ac;
+                       u8 bmc_triggered_ac;
+                       u8 pad[3];
+               } __packed enable;
+       } req = {
+               .hdr = {
+                       .bss_idx = mvif->mt76.idx,
+               },
+               .enable = {
+                       .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT),
+                       .len = cpu_to_le16(sizeof(struct bcnft_tlv)),
+                       .dtim_period = vif->bss_conf.dtim_period,
+                       .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
+               },
+       };
+       struct {
+               struct {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct pm_disable {
+                       __le16 tag;
+                       __le16 len;
+               } __packed disable;
+       } req1 = {
+               .hdr = {
+                       .bss_idx = mvif->mt76.idx,
+               },
+               .disable = {
+                       .tag = cpu_to_le16(UNI_BSS_INFO_PM_DISABLE),
+                       .len = cpu_to_le16(sizeof(struct pm_disable))
+               },
+       };
+       int err;
+
+       err = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE),
+                               &req1, sizeof(req1), false);
+       if (err < 0 || !enable)
+               return err;
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE),
+                                &req, sizeof(req), false);
+}
+
+static void
+mt7925_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+       if (!sta->deflink.he_cap.has_he)
+               return;
+
+       mt76_connac_mcu_sta_he_tlv_v2(skb, sta);
+}
+
+static void
+mt7925_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+       struct sta_rec_he_6g_capa *he_6g;
+       struct tlv *tlv;
+
+       if (!sta->deflink.he_6ghz_capa.capa)
+               return;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g));
+
+       he_6g = (struct sta_rec_he_6g_capa *)tlv;
+       he_6g->capa = sta->deflink.he_6ghz_capa.capa;
+}
+
+static void
+mt7925_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+       struct ieee80211_eht_mcs_nss_supp *mcs_map;
+       struct ieee80211_eht_cap_elem_fixed *elem;
+       struct sta_rec_eht *eht;
+       struct tlv *tlv;
+
+       if (!sta->deflink.eht_cap.has_eht)
+               return;
+
+       mcs_map = &sta->deflink.eht_cap.eht_mcs_nss_supp;
+       elem = &sta->deflink.eht_cap.eht_cap_elem;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht));
+
+       eht = (struct sta_rec_eht *)tlv;
+       eht->tid_bitmap = 0xff;
+       eht->mac_cap = cpu_to_le16(*(u16 *)elem->mac_cap_info);
+       eht->phy_cap = cpu_to_le64(*(u64 *)elem->phy_cap_info);
+       eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]);
+
+       if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20)
+               memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, sizeof(eht->mcs_map_bw20));
+       memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80));
+       memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160));
+}
+
+static void
+mt7925_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+       struct sta_rec_ht *ht;
+       struct tlv *tlv;
+
+       if (!sta->deflink.ht_cap.ht_supported)
+               return;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht));
+
+       ht = (struct sta_rec_ht *)tlv;
+       ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
+}
+
+static void
+mt7925_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+       struct sta_rec_vht *vht;
+       struct tlv *tlv;
+
+       /* For 6G band, this tlv is necessary to let hw work normally */
+       if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported)
+               return;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht));
+
+       vht = (struct sta_rec_vht *)tlv;
+       vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
+       vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
+       vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
+}
+
+static void
+mt7925_mcu_sta_amsdu_tlv(struct sk_buff *skb,
+                        struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+       struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+       struct sta_rec_amsdu *amsdu;
+       struct tlv *tlv;
+
+       if (vif->type != NL80211_IFTYPE_STATION &&
+           vif->type != NL80211_IFTYPE_AP)
+               return;
+
+       if (!sta->deflink.agg.max_amsdu_len)
+               return;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu));
+       amsdu = (struct sta_rec_amsdu *)tlv;
+       amsdu->max_amsdu_num = 8;
+       amsdu->amsdu_en = true;
+       msta->wcid.amsdu = true;
+
+       switch (sta->deflink.agg.max_amsdu_len) {
+       case IEEE80211_MAX_MPDU_LEN_VHT_11454:
+               amsdu->max_mpdu_size =
+                       IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
+               return;
+       case IEEE80211_MAX_MPDU_LEN_HT_7935:
+       case IEEE80211_MAX_MPDU_LEN_VHT_7991:
+               amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
+               return;
+       default:
+               amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
+               return;
+       }
+}
+
+static void
+mt7925_mcu_sta_phy_tlv(struct sk_buff *skb,
+                      struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct cfg80211_chan_def *chandef = &mvif->mt76.ctx->def;
+       struct sta_rec_phy *phy;
+       struct tlv *tlv;
+       u8 af = 0, mm = 0;
+
+       if (!sta->deflink.ht_cap.ht_supported && !sta->deflink.he_6ghz_capa.capa)
+               return;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PHY, sizeof(*phy));
+       phy = (struct sta_rec_phy *)tlv;
+       phy->phy_type = mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif, chandef->chan->band, sta);
+       if (sta->deflink.ht_cap.ht_supported) {
+               af = sta->deflink.ht_cap.ampdu_factor;
+               mm = sta->deflink.ht_cap.ampdu_density;
+       }
+
+       if (sta->deflink.vht_cap.vht_supported) {
+               u8 vht_af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+                                     sta->deflink.vht_cap.cap);
+
+               af = max_t(u8, af, vht_af);
+       }
+
+       if (sta->deflink.he_6ghz_capa.capa) {
+               af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
+                                  IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
+               mm = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
+                                  IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START);
+       }
+
+       phy->ampdu = FIELD_PREP(IEEE80211_HT_AMPDU_PARM_FACTOR, af) |
+                    FIELD_PREP(IEEE80211_HT_AMPDU_PARM_DENSITY, mm);
+       phy->max_ampdu_len = af;
+}
+
+static void
+mt7925_mcu_sta_state_v2_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+                           struct ieee80211_sta *sta,
+                           struct ieee80211_vif *vif,
+                           u8 rcpi, u8 sta_state)
+{
+       struct sta_rec_state_v2 {
+               __le16 tag;
+               __le16 len;
+               u8 state;
+               u8 rsv[3];
+               __le32 flags;
+               u8 vht_opmode;
+               u8 action;
+               u8 rsv2[2];
+       } __packed * state;
+       struct tlv *tlv;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_STATE, sizeof(*state));
+       state = (struct sta_rec_state_v2 *)tlv;
+       state->state = sta_state;
+
+       if (sta->deflink.vht_cap.vht_supported) {
+               state->vht_opmode = sta->deflink.bandwidth;
+               state->vht_opmode |= sta->deflink.rx_nss <<
+                       IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
+       }
+}
+
+static void
+mt7925_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb,
+                            struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct cfg80211_chan_def *chandef = &mvif->mt76.ctx->def;
+       enum nl80211_band band = chandef->chan->band;
+       struct sta_rec_ra_info *ra_info;
+       struct tlv *tlv;
+       u16 supp_rates;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra_info));
+       ra_info = (struct sta_rec_ra_info *)tlv;
+
+       supp_rates = sta->deflink.supp_rates[band];
+       if (band == NL80211_BAND_2GHZ)
+               supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates >> 4) |
+                            FIELD_PREP(RA_LEGACY_CCK, supp_rates & 0xf);
+       else
+               supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates);
+
+       ra_info->legacy = cpu_to_le16(supp_rates);
+
+       if (sta->deflink.ht_cap.ht_supported)
+               memcpy(ra_info->rx_mcs_bitmask,
+                      sta->deflink.ht_cap.mcs.rx_mask,
+                      HT_MCS_MASK_NUM);
+}
+
+static void
+mt7925_mcu_sta_mld_tlv(struct sk_buff *skb,
+                      struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+       struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
+       struct sta_rec_mld *mld;
+       struct tlv *tlv;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, sizeof(*mld));
+       mld = (struct sta_rec_mld *)tlv;
+       memcpy(mld->mac_addr, vif->addr, ETH_ALEN);
+       mld->primary_id = cpu_to_le16(wcid->idx);
+       mld->wlan_id = cpu_to_le16(wcid->idx);
+
+       /* TODO: 0 means deflink only, add secondary link(1) later */
+       mld->link_num = !!(hweight8(vif->active_links) > 1);
+       WARN_ON_ONCE(mld->link_num);
+}
+
+static int
+mt7925_mcu_sta_cmd(struct mt76_phy *phy,
+                  struct mt76_sta_cmd_info *info)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)info->vif->drv_priv;
+       struct mt76_dev *dev = phy->dev;
+       struct wtbl_req_hdr *wtbl_hdr;
+       struct tlv *sta_wtbl;
+       struct sk_buff *skb;
+
+       skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, info->wcid,
+                                             MT7925_STA_UPDATE_MAX_SIZE);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       if (info->sta || !info->offload_fw)
+               mt76_connac_mcu_sta_basic_tlv(dev, skb, info->vif, info->sta,
+                                             info->enable, info->newly);
+       if (info->sta && info->enable) {
+               mt7925_mcu_sta_phy_tlv(skb, info->vif, info->sta);
+               mt7925_mcu_sta_ht_tlv(skb, info->sta);
+               mt7925_mcu_sta_vht_tlv(skb, info->sta);
+               mt76_connac_mcu_sta_uapsd(skb, info->vif, info->sta);
+               mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->sta);
+               mt7925_mcu_sta_he_tlv(skb, info->sta);
+               mt7925_mcu_sta_he_6g_tlv(skb, info->sta);
+               mt7925_mcu_sta_eht_tlv(skb, info->sta);
+               mt7925_mcu_sta_rate_ctrl_tlv(skb, info->vif, info->sta);
+               mt7925_mcu_sta_state_v2_tlv(phy, skb, info->sta,
+                                           info->vif, info->rcpi,
+                                           info->state);
+               mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->sta);
+               mt7925_mcu_sta_mld_tlv(skb, info->vif, info->sta);
+       }
+
+       sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL,
+                                          sizeof(struct tlv));
+
+       wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(dev, info->wcid,
+                                                 WTBL_RESET_AND_SET,
+                                                 sta_wtbl, &skb);
+       if (IS_ERR(wtbl_hdr))
+               return PTR_ERR(wtbl_hdr);
+
+       if (info->enable) {
+               mt76_connac_mcu_wtbl_generic_tlv(dev, skb, info->vif,
+                                                info->sta, sta_wtbl,
+                                                wtbl_hdr);
+               mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, info->vif, info->wcid,
+                                                  sta_wtbl, wtbl_hdr);
+               if (info->sta)
+                       mt76_connac_mcu_wtbl_ht_tlv(dev, skb, info->sta,
+                                                   sta_wtbl, wtbl_hdr,
+                                                   true, true);
+       }
+
+       return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true);
+}
+
+int mt7925_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_sta *sta,
+                         struct ieee80211_vif *vif, bool enable,
+                         enum mt76_sta_info_state state)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       int rssi = -ewma_rssi_read(&mvif->rssi);
+       struct mt76_sta_cmd_info info = {
+               .sta = sta,
+               .vif = vif,
+               .enable = enable,
+               .cmd = MCU_UNI_CMD(STA_REC_UPDATE),
+               .state = state,
+               .offload_fw = true,
+               .rcpi = to_rcpi(rssi),
+       };
+       struct mt792x_sta *msta;
+
+       msta = sta ? (struct mt792x_sta *)sta->drv_priv : NULL;
+       info.wcid = msta ? &msta->wcid : &mvif->sta.wcid;
+       info.newly = msta ? state != MT76_STA_INFO_STATE_ASSOC : true;
+
+       return mt7925_mcu_sta_cmd(&dev->mphy, &info);
+}
+
+int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev,
+                                struct ieee80211_vif *vif,
+                                bool enable)
+{
+#define MT7925_FIF_BIT_CLR             BIT(1)
+#define MT7925_FIF_BIT_SET             BIT(0)
+       int err = 0;
+
+       if (enable) {
+               err = mt7925_mcu_uni_bss_bcnft(dev, vif, true);
+               if (err)
+                       return err;
+
+               return mt7925_mcu_set_rxfilter(dev, 0,
+                                              MT7925_FIF_BIT_SET,
+                                              MT_WF_RFCR_DROP_OTHER_BEACON);
+       }
+
+       err = mt7925_mcu_set_bss_pm(dev, vif, false);
+       if (err)
+               return err;
+
+       return mt7925_mcu_set_rxfilter(dev, 0,
+                                      MT7925_FIF_BIT_CLR,
+                                      MT_WF_RFCR_DROP_OTHER_BEACON);
+}
+
+int mt7925_get_txpwr_info(struct mt792x_dev *dev, u8 band_idx, struct mt7925_txpwr *txpwr)
+{
+#define TX_POWER_SHOW_INFO 0x7
+#define TXPOWER_ALL_RATE_POWER_INFO 0x2
+       struct mt7925_txpwr_event *event;
+       struct mt7925_txpwr_req req = {
+               .tag = cpu_to_le16(TX_POWER_SHOW_INFO),
+               .len = cpu_to_le16(sizeof(req) - 4),
+               .catg = TXPOWER_ALL_RATE_POWER_INFO,
+               .band_idx = band_idx,
+       };
+       struct sk_buff *skb;
+       int ret;
+
+       ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(TXPOWER),
+                                       &req, sizeof(req), true, &skb);
+       if (ret)
+               return ret;
+
+       event = (struct mt7925_txpwr_event *)skb->data;
+       memcpy(txpwr, &event->txpwr, sizeof(event->txpwr));
+
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif,
+                          bool enable)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+       struct {
+               struct {
+                       u8 band_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct sniffer_enable_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       u8 enable;
+                       u8 pad[3];
+               } __packed enable;
+       } __packed req = {
+               .hdr = {
+                       .band_idx = mvif->mt76.band_idx,
+               },
+               .enable = {
+                       .tag = cpu_to_le16(UNI_SNIFFER_ENABLE),
+                       .len = cpu_to_le16(sizeof(struct sniffer_enable_tlv)),
+                       .enable = enable,
+               },
+       };
+
+       mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true);
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req),
+                                true);
+}
+
+int mt7925_mcu_config_sniffer(struct mt792x_vif *vif,
+                             struct ieee80211_chanctx_conf *ctx)
+{
+       struct mt76_phy *mphy = vif->phy->mt76;
+       struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &mphy->chandef;
+       int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2;
+
+       const u8 ch_band[] = {
+               [NL80211_BAND_2GHZ] = 1,
+               [NL80211_BAND_5GHZ] = 2,
+               [NL80211_BAND_6GHZ] = 3,
+       };
+       const u8 ch_width[] = {
+               [NL80211_CHAN_WIDTH_20_NOHT] = 0,
+               [NL80211_CHAN_WIDTH_20] = 0,
+               [NL80211_CHAN_WIDTH_40] = 0,
+               [NL80211_CHAN_WIDTH_80] = 1,
+               [NL80211_CHAN_WIDTH_160] = 2,
+               [NL80211_CHAN_WIDTH_80P80] = 3,
+               [NL80211_CHAN_WIDTH_5] = 4,
+               [NL80211_CHAN_WIDTH_10] = 5,
+               [NL80211_CHAN_WIDTH_320] = 6,
+       };
+
+       struct {
+               struct {
+                       u8 band_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct config_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       u16 aid;
+                       u8 ch_band;
+                       u8 bw;
+                       u8 control_ch;
+                       u8 sco;
+                       u8 center_ch;
+                       u8 center_ch2;
+                       u8 drop_err;
+                       u8 pad[3];
+               } __packed tlv;
+       } __packed req = {
+               .hdr = {
+                       .band_idx = vif->mt76.band_idx,
+               },
+               .tlv = {
+                       .tag = cpu_to_le16(UNI_SNIFFER_CONFIG),
+                       .len = cpu_to_le16(sizeof(req.tlv)),
+                       .control_ch = chandef->chan->hw_value,
+                       .center_ch = ieee80211_frequency_to_channel(freq1),
+                       .drop_err = 1,
+               },
+       };
+
+       if (chandef->chan->band < ARRAY_SIZE(ch_band))
+               req.tlv.ch_band = ch_band[chandef->chan->band];
+       if (chandef->width < ARRAY_SIZE(ch_width))
+               req.tlv.bw = ch_width[chandef->width];
+
+       if (freq2)
+               req.tlv.center_ch2 = ieee80211_frequency_to_channel(freq2);
+
+       if (req.tlv.control_ch < req.tlv.center_ch)
+               req.tlv.sco = 1; /* SCA */
+       else if (req.tlv.control_ch > req.tlv.center_ch)
+               req.tlv.sco = 3; /* SCB */
+
+       return mt76_mcu_send_msg(mphy->dev, MCU_UNI_CMD(SNIFFER),
+                                &req, sizeof(req), true);
+}
+
+int
+mt7925_mcu_uni_add_beacon_offload(struct mt792x_dev *dev,
+                                 struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif,
+                                 bool enable)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct ieee80211_mutable_offsets offs;
+       struct {
+               struct req_hdr {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct bcn_content_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       __le16 tim_ie_pos;
+                       __le16 csa_ie_pos;
+                       __le16 bcc_ie_pos;
+                       /* 0: disable beacon offload
+                        * 1: enable beacon offload
+                        * 2: update probe respond offload
+                        */
+                       u8 enable;
+                       /* 0: legacy format (TXD + payload)
+                        * 1: only cap field IE
+                        */
+                       u8 type;
+                       __le16 pkt_len;
+                       u8 pkt[512];
+               } __packed beacon_tlv;
+       } req = {
+               .hdr = {
+                       .bss_idx = mvif->mt76.idx,
+               },
+               .beacon_tlv = {
+                       .tag = cpu_to_le16(UNI_BSS_INFO_BCN_CONTENT),
+                       .len = cpu_to_le16(sizeof(struct bcn_content_tlv)),
+                       .enable = enable,
+                       .type = 1,
+               },
+       };
+       struct sk_buff *skb;
+       u8 cap_offs;
+
+       /* support enable/update process only
+        * disable flow would be handled in bss stop handler automatically
+        */
+       if (!enable)
+               return -EOPNOTSUPP;
+
+       skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0);
+       if (!skb)
+               return -EINVAL;
+
+       cap_offs = offsetof(struct ieee80211_mgmt, u.beacon.capab_info);
+       if (!skb_pull(skb, cap_offs)) {
+               dev_err(dev->mt76.dev, "beacon format err\n");
+               dev_kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       if (skb->len > 512) {
+               dev_err(dev->mt76.dev, "beacon size limit exceed\n");
+               dev_kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       memcpy(req.beacon_tlv.pkt, skb->data, skb->len);
+       req.beacon_tlv.pkt_len = cpu_to_le16(skb->len);
+       offs.tim_offset -= cap_offs;
+       req.beacon_tlv.tim_ie_pos = cpu_to_le16(offs.tim_offset);
+
+       if (offs.cntdwn_counter_offs[0]) {
+               u16 csa_offs;
+
+               csa_offs = offs.cntdwn_counter_offs[0] - cap_offs - 4;
+               req.beacon_tlv.csa_ie_pos = cpu_to_le16(csa_offs);
+       }
+       dev_kfree_skb(skb);
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE),
+                                &req, sizeof(req), true);
+}
+
+int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif *mvif,
+                        struct ieee80211_chanctx_conf *ctx)
+{
+       struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef;
+       int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2;
+       enum nl80211_band band = chandef->chan->band;
+       struct mt76_dev *mdev = phy->dev;
+       struct {
+               struct {
+                       u8 bss_idx;
+                       u8 pad[3];
+               } __packed hdr;
+               struct rlm_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       u8 control_channel;
+                       u8 center_chan;
+                       u8 center_chan2;
+                       u8 bw;
+                       u8 tx_streams;
+                       u8 rx_streams;
+                       u8 ht_op_info;
+                       u8 sco;
+                       u8 band;
+                       u8 pad[3];
+               } __packed rlm;
+       } __packed rlm_req = {
+               .hdr = {
+                       .bss_idx = mvif->idx,
+               },
+               .rlm = {
+                       .tag = cpu_to_le16(UNI_BSS_INFO_RLM),
+                       .len = cpu_to_le16(sizeof(struct rlm_tlv)),
+                       .control_channel = chandef->chan->hw_value,
+                       .center_chan = ieee80211_frequency_to_channel(freq1),
+                       .center_chan2 = ieee80211_frequency_to_channel(freq2),
+                       .tx_streams = hweight8(phy->antenna_mask),
+                       .ht_op_info = 4, /* set HT 40M allowed */
+                       .rx_streams = hweight8(phy->antenna_mask),
+                       .band = band,
+               },
+       };
+
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_40:
+               rlm_req.rlm.bw = CMD_CBW_40MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+               rlm_req.rlm.bw = CMD_CBW_80MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_80P80:
+               rlm_req.rlm.bw = CMD_CBW_8080MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_160:
+               rlm_req.rlm.bw = CMD_CBW_160MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_5:
+               rlm_req.rlm.bw = CMD_CBW_5MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_10:
+               rlm_req.rlm.bw = CMD_CBW_10MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_20:
+       default:
+               rlm_req.rlm.bw = CMD_CBW_20MHZ;
+               rlm_req.rlm.ht_op_info = 0;
+               break;
+       }
+
+       if (rlm_req.rlm.control_channel < rlm_req.rlm.center_chan)
+               rlm_req.rlm.sco = 1; /* SCA */
+       else if (rlm_req.rlm.control_channel > rlm_req.rlm.center_chan)
+               rlm_req.rlm.sco = 3; /* SCB */
+
+       return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &rlm_req,
+                                sizeof(rlm_req), true);
+}
+
+static struct sk_buff *
+__mt7925_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len)
+{
+       struct bss_req_hdr hdr = {
+               .bss_idx = mvif->idx,
+       };
+       struct sk_buff *skb;
+
+       skb = mt76_mcu_msg_alloc(dev, NULL, len);
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
+
+       skb_put_data(skb, &hdr, sizeof(hdr));
+
+       return skb;
+}
+
+static u8
+mt7925_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif,
+                       enum nl80211_band band, struct ieee80211_sta *sta)
+{
+       struct ieee80211_he_6ghz_capa *he_6ghz_capa;
+       const struct ieee80211_sta_eht_cap *eht_cap;
+       __le16 capa = 0;
+       u8 mode = 0;
+
+       if (sta) {
+               he_6ghz_capa = &sta->deflink.he_6ghz_capa;
+               eht_cap = &sta->deflink.eht_cap;
+       } else {
+               struct ieee80211_supported_band *sband;
+
+               sband = phy->hw->wiphy->bands[band];
+               capa = ieee80211_get_he_6ghz_capa(sband, vif->type);
+               he_6ghz_capa = (struct ieee80211_he_6ghz_capa *)&capa;
+
+               eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type);
+       }
+
+       switch (band) {
+       case NL80211_BAND_2GHZ:
+               if (eht_cap && eht_cap->has_eht)
+                       mode |= PHY_MODE_BE_24G;
+               break;
+       case NL80211_BAND_5GHZ:
+               if (eht_cap && eht_cap->has_eht)
+                       mode |= PHY_MODE_BE_5G;
+               break;
+       case NL80211_BAND_6GHZ:
+               if (he_6ghz_capa && he_6ghz_capa->capa)
+                       mode |= PHY_MODE_AX_6G;
+
+               if (eht_cap && eht_cap->has_eht)
+                       mode |= PHY_MODE_BE_6G;
+               break;
+       default:
+               break;
+       }
+
+       return mode;
+}
+
+static void
+mt7925_mcu_bss_basic_tlv(struct sk_buff *skb,
+                        struct ieee80211_vif *vif,
+                        struct ieee80211_sta *sta,
+                        struct ieee80211_chanctx_conf *ctx,
+                        struct mt76_phy *phy, u16 wlan_idx,
+                        bool enable)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_sta *msta = sta ? (struct mt792x_sta *)sta->drv_priv :
+                                 &mvif->sta;
+       struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef;
+       enum nl80211_band band = chandef->chan->band;
+       struct mt76_connac_bss_basic_tlv *basic_req;
+       u8 idx, basic_phy;
+       struct tlv *tlv;
+       int conn_type;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*basic_req));
+       basic_req = (struct mt76_connac_bss_basic_tlv *)tlv;
+
+       idx = mvif->mt76.omac_idx > EXT_BSSID_START ? HW_BSSID_0 :
+                                                     mvif->mt76.omac_idx;
+       basic_req->hw_bss_idx = idx;
+
+       basic_req->phymode_ext = mt7925_get_phy_mode_ext(phy, vif, band, sta);
+
+       basic_phy = mt76_connac_get_phy_mode_v2(phy, vif, band, sta);
+       basic_req->nonht_basic_phy = cpu_to_le16(basic_phy);
+
+       memcpy(basic_req->bssid, vif->bss_conf.bssid, ETH_ALEN);
+       basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, sta);
+       basic_req->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+       basic_req->dtim_period = vif->bss_conf.dtim_period;
+       basic_req->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
+       basic_req->sta_idx = cpu_to_le16(msta->wcid.idx);
+       basic_req->omac_idx = mvif->mt76.omac_idx;
+       basic_req->band_idx = mvif->mt76.band_idx;
+       basic_req->wmm_idx = mvif->mt76.wmm_idx;
+       basic_req->conn_state = !enable;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_MESH_POINT:
+       case NL80211_IFTYPE_AP:
+               if (vif->p2p)
+                       conn_type = CONNECTION_P2P_GO;
+               else
+                       conn_type = CONNECTION_INFRA_AP;
+               basic_req->conn_type = cpu_to_le32(conn_type);
+               basic_req->active = enable;
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (vif->p2p)
+                       conn_type = CONNECTION_P2P_GC;
+               else
+                       conn_type = CONNECTION_INFRA_STA;
+               basic_req->conn_type = cpu_to_le32(conn_type);
+               basic_req->active = true;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               basic_req->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC);
+               basic_req->active = true;
+               break;
+       default:
+               WARN_ON(1);
+               break;
+       }
+}
+
+static void
+mt7925_mcu_bss_sec_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct bss_sec_tlv {
+               __le16 tag;
+               __le16 len;
+               u8 mode;
+               u8 status;
+               u8 cipher;
+               u8 __rsv;
+       } __packed * sec;
+       struct tlv *tlv;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec));
+       sec = (struct bss_sec_tlv *)tlv;
+
+       switch (mvif->cipher) {
+       case MCU_CIPHER_GCMP_256:
+       case MCU_CIPHER_GCMP:
+               sec->mode = MODE_WPA3_SAE;
+               sec->status = 8;
+               break;
+       case MCU_CIPHER_AES_CCMP:
+               sec->mode = MODE_WPA2_PSK;
+               sec->status = 6;
+               break;
+       case MCU_CIPHER_TKIP:
+               sec->mode = MODE_WPA2_PSK;
+               sec->status = 4;
+               break;
+       case MCU_CIPHER_WEP104:
+       case MCU_CIPHER_WEP40:
+               sec->mode = MODE_SHARED;
+               sec->status = 0;
+               break;
+       default:
+               sec->mode = MODE_OPEN;
+               sec->status = 1;
+               break;
+       }
+
+       sec->cipher = mvif->cipher;
+}
+
+static void
+mt7925_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt792x_phy *phy,
+                      struct ieee80211_chanctx_conf *ctx,
+                          struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta)
+{
+       struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->mt76->chandef;
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       enum nl80211_band band = chandef->chan->band;
+       struct bss_rate_tlv *bmc;
+       struct tlv *tlv;
+       u8 idx = mvif->mcast_rates_idx ?
+                mvif->mcast_rates_idx : mvif->basic_rates_idx;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc));
+
+       bmc = (struct bss_rate_tlv *)tlv;
+
+       bmc->short_preamble = (band == NL80211_BAND_2GHZ);
+       bmc->bc_fixed_rate = idx;
+       bmc->mc_fixed_rate = idx;
+}
+
+static void
+mt7925_mcu_bss_mld_tlv(struct sk_buff *skb,
+                      struct ieee80211_vif *vif,
+                      struct ieee80211_sta *sta)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       bool is_mld = ieee80211_vif_is_mld(vif);
+       struct bss_mld_tlv *mld;
+       struct tlv *tlv;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld));
+       mld = (struct bss_mld_tlv *)tlv;
+
+       mld->link_id = sta ? (is_mld ? vif->bss_conf.link_id : 0) : 0xff;
+       mld->group_mld_id = is_mld ? mvif->mt76.idx : 0xff;
+       mld->own_mld_id = mvif->mt76.idx + 32;
+       mld->remap_idx = 0xff;
+
+       if (sta)
+               memcpy(mld->mac_addr, sta->addr, ETH_ALEN);
+}
+
+static void
+mt7925_mcu_bss_qos_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+{
+       struct mt76_connac_bss_qos_tlv *qos;
+       struct tlv *tlv;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_QBSS, sizeof(*qos));
+       qos = (struct mt76_connac_bss_qos_tlv *)tlv;
+       qos->qos = vif->bss_conf.qos;
+}
+
+static void
+mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+                     struct mt792x_phy *phy)
+{
+#define DEFAULT_HE_PE_DURATION         4
+#define DEFAULT_HE_DURATION_RTS_THRES  1023
+       const struct ieee80211_sta_he_cap *cap;
+       struct bss_info_uni_he *he;
+       struct tlv *tlv;
+
+       cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));
+
+       he = (struct bss_info_uni_he *)tlv;
+       he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;
+       if (!he->he_pe_duration)
+               he->he_pe_duration = DEFAULT_HE_PE_DURATION;
+
+       he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th);
+       if (!he->he_rts_thres)
+               he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES);
+
+       he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80;
+       he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160;
+       he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80;
+}
+
+static void
+mt7925_mcu_bss_color_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+                        bool enable)
+{
+       struct bss_info_uni_bss_color *color;
+       struct tlv *tlv;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BSS_COLOR, sizeof(*color));
+       color = (struct bss_info_uni_bss_color *)tlv;
+
+       color->enable = enable ?
+               vif->bss_conf.he_bss_color.enabled : 0;
+       color->bss_color = enable ?
+               vif->bss_conf.he_bss_color.color : 0;
+}
+
+int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
+                           struct ieee80211_chanctx_conf *ctx,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta,
+                           int enable)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       struct mt792x_dev *dev = phy->dev;
+       struct sk_buff *skb;
+       int err;
+
+       skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+                                        MT7925_BSS_UPDATE_MAX_SIZE);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       /* bss_basic must be first */
+       mt7925_mcu_bss_basic_tlv(skb, vif, sta, ctx, phy->mt76,
+                                mvif->sta.wcid.idx, enable);
+       mt7925_mcu_bss_sec_tlv(skb, vif);
+
+       mt7925_mcu_bss_bmc_tlv(skb, phy, ctx, vif, sta);
+       mt7925_mcu_bss_qos_tlv(skb, vif);
+       mt7925_mcu_bss_mld_tlv(skb, vif, sta);
+
+       if (vif->bss_conf.he_support) {
+               mt7925_mcu_bss_he_tlv(skb, vif, phy);
+               mt7925_mcu_bss_color_tlv(skb, vif, enable);
+       }
+
+       err = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+                                   MCU_UNI_CMD(BSS_INFO_UPDATE), true);
+       if (err < 0)
+               return err;
+
+       return mt7925_mcu_set_chctx(phy->mt76, &mvif->mt76, ctx);
+}
+
+int mt7925_mcu_set_dbdc(struct mt76_phy *phy)
+{
+       struct mt76_dev *mdev = phy->dev;
+
+       struct mbmc_conf_tlv *conf;
+       struct mbmc_set_req *hdr;
+       struct sk_buff *skb;
+       struct tlv *tlv;
+       int max_len, err;
+
+       max_len = sizeof(*hdr) + sizeof(*conf);
+       skb = mt76_mcu_msg_alloc(mdev, NULL, max_len);
+       if (!skb)
+               return -ENOMEM;
+
+       hdr = (struct mbmc_set_req *)skb_put(skb, sizeof(*hdr));
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_MBMC_SETTING, sizeof(*conf));
+       conf = (struct mbmc_conf_tlv *)tlv;
+
+       conf->mbmc_en = 1;
+       conf->band = 0; /* unused */
+
+       err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SET_DBDC_PARMS),
+                                   false);
+
+       return err;
+}
+
+#define MT76_CONNAC_SCAN_CHANNEL_TIME          60
+
+int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
+                      struct ieee80211_scan_request *scan_req)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct cfg80211_scan_request *sreq = &scan_req->req;
+       int n_ssids = 0, err, i, duration;
+       struct ieee80211_channel **scan_list = sreq->channels;
+       struct mt76_dev *mdev = phy->dev;
+       struct mt76_connac_mcu_scan_channel *chan;
+       struct sk_buff *skb;
+
+       struct scan_hdr_tlv *hdr;
+       struct scan_req_tlv *req;
+       struct scan_ssid_tlv *ssid;
+       struct scan_bssid_tlv *bssid;
+       struct scan_chan_info_tlv *chan_info;
+       struct scan_ie_tlv *ie;
+       struct scan_misc_tlv *misc;
+       struct tlv *tlv;
+       int max_len;
+
+       max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) +
+                               sizeof(*bssid) + sizeof(*chan_info) +
+                               sizeof(*misc) + sizeof(*ie);
+
+       skb = mt76_mcu_msg_alloc(mdev, NULL, max_len);
+       if (!skb)
+               return -ENOMEM;
+
+       set_bit(MT76_HW_SCANNING, &phy->state);
+       mvif->scan_seq_num = (mvif->scan_seq_num + 1) & 0x7f;
+
+       hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr));
+       hdr->seq_num = mvif->scan_seq_num | mvif->band_idx << 7;
+       hdr->bss_idx = mvif->idx;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_REQ, sizeof(*req));
+       req = (struct scan_req_tlv *)tlv;
+       req->scan_type = sreq->n_ssids ? 1 : 0;
+       req->probe_req_num = sreq->n_ssids ? 2 : 0;
+
+       duration = MT76_CONNAC_SCAN_CHANNEL_TIME;
+       /* increase channel time for passive scan */
+       if (!sreq->n_ssids)
+               duration *= 2;
+       req->timeout_value = cpu_to_le16(sreq->n_channels * duration);
+       req->channel_min_dwell_time = cpu_to_le16(duration);
+       req->channel_dwell_time = cpu_to_le16(duration);
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID, sizeof(*ssid));
+       ssid = (struct scan_ssid_tlv *)tlv;
+       for (i = 0; i < sreq->n_ssids; i++) {
+               if (!sreq->ssids[i].ssid_len)
+                       continue;
+
+               ssid->ssids[i].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len);
+               memcpy(ssid->ssids[i].ssid, sreq->ssids[i].ssid,
+                      sreq->ssids[i].ssid_len);
+               n_ssids++;
+       }
+       ssid->ssid_type = n_ssids ? BIT(2) : BIT(0);
+       ssid->ssids_num = n_ssids;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, sizeof(*bssid));
+       bssid = (struct scan_bssid_tlv *)tlv;
+
+       memcpy(bssid->bssid, sreq->bssid, ETH_ALEN);
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_CHANNEL, sizeof(*chan_info));
+       chan_info = (struct scan_chan_info_tlv *)tlv;
+       chan_info->channels_num = min_t(u8, sreq->n_channels,
+                                       ARRAY_SIZE(chan_info->channels));
+       for (i = 0; i < chan_info->channels_num; i++) {
+               chan = &chan_info->channels[i];
+
+               switch (scan_list[i]->band) {
+               case NL80211_BAND_2GHZ:
+                       chan->band = 1;
+                       break;
+               case NL80211_BAND_6GHZ:
+                       chan->band = 3;
+                       break;
+               default:
+                       chan->band = 2;
+                       break;
+               }
+               chan->channel_num = scan_list[i]->hw_value;
+       }
+       chan_info->channel_type = sreq->n_channels ? 4 : 0;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, sizeof(*ie));
+       ie = (struct scan_ie_tlv *)tlv;
+       if (sreq->ie_len > 0) {
+               memcpy(ie->ies, sreq->ie, sreq->ie_len);
+               ie->ies_len = cpu_to_le16(sreq->ie_len);
+       }
+
+       req->scan_func |= SCAN_FUNC_SPLIT_SCAN;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_MISC, sizeof(*misc));
+       misc = (struct scan_misc_tlv *)tlv;
+       if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+               get_random_mask_addr(misc->random_mac, sreq->mac_addr,
+                                    sreq->mac_addr_mask);
+               req->scan_func |= SCAN_FUNC_RANDOM_MAC;
+       }
+
+       err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ),
+                                   false);
+       if (err < 0)
+               clear_bit(MT76_HW_SCANNING, &phy->state);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_hw_scan);
+
+int mt7925_mcu_sched_scan_req(struct mt76_phy *phy,
+                             struct ieee80211_vif *vif,
+                             struct cfg80211_sched_scan_request *sreq)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct ieee80211_channel **scan_list = sreq->channels;
+       struct mt76_connac_mcu_scan_channel *chan;
+       struct mt76_dev *mdev = phy->dev;
+       struct cfg80211_match_set *cfg_match;
+       struct cfg80211_ssid *cfg_ssid;
+
+       struct scan_hdr_tlv *hdr;
+       struct scan_sched_req *req;
+       struct scan_ssid_tlv *ssid;
+       struct scan_chan_info_tlv *chan_info;
+       struct scan_ie_tlv *ie;
+       struct scan_sched_ssid_match_sets *match;
+       struct sk_buff *skb;
+       struct tlv *tlv;
+       int i, max_len;
+
+       max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) +
+                 sizeof(*chan_info) + sizeof(*ie) +
+                 sizeof(*match);
+
+       skb = mt76_mcu_msg_alloc(mdev, NULL, max_len);
+       if (!skb)
+               return -ENOMEM;
+
+       mvif->scan_seq_num = (mvif->scan_seq_num + 1) & 0x7f;
+
+       hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr));
+       hdr->seq_num = mvif->scan_seq_num | mvif->band_idx << 7;
+       hdr->bss_idx = mvif->idx;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SCHED_REQ, sizeof(*req));
+       req = (struct scan_sched_req *)tlv;
+       req->version = 1;
+
+       if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
+               req->scan_func |= SCAN_FUNC_RANDOM_MAC;
+
+       req->intervals_num = sreq->n_scan_plans;
+       for (i = 0; i < req->intervals_num; i++)
+               req->intervals[i] = cpu_to_le16(sreq->scan_plans[i].interval);
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID, sizeof(*ssid));
+       ssid = (struct scan_ssid_tlv *)tlv;
+
+       ssid->ssids_num = sreq->n_ssids;
+       ssid->ssid_type = BIT(2);
+       for (i = 0; i < ssid->ssids_num; i++) {
+               cfg_ssid = &sreq->ssids[i];
+               memcpy(ssid->ssids[i].ssid, cfg_ssid->ssid, cfg_ssid->ssid_len);
+               ssid->ssids[i].ssid_len = cpu_to_le32(cfg_ssid->ssid_len);
+       }
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID_MATCH_SETS, sizeof(*match));
+       match = (struct scan_sched_ssid_match_sets *)tlv;
+       match->match_num = sreq->n_match_sets;
+       for (i = 0; i < match->match_num; i++) {
+               cfg_match = &sreq->match_sets[i];
+               memcpy(match->match[i].ssid, cfg_match->ssid.ssid,
+                      cfg_match->ssid.ssid_len);
+               match->match[i].rssi_th = cpu_to_le32(cfg_match->rssi_thold);
+               match->match[i].ssid_len = cfg_match->ssid.ssid_len;
+       }
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_CHANNEL, sizeof(*chan_info));
+       chan_info = (struct scan_chan_info_tlv *)tlv;
+       chan_info->channels_num = min_t(u8, sreq->n_channels,
+                                       ARRAY_SIZE(chan_info->channels));
+       for (i = 0; i < chan_info->channels_num; i++) {
+               chan = &chan_info->channels[i];
+
+               switch (scan_list[i]->band) {
+               case NL80211_BAND_2GHZ:
+                       chan->band = 1;
+                       break;
+               case NL80211_BAND_6GHZ:
+                       chan->band = 3;
+                       break;
+               default:
+                       chan->band = 2;
+                       break;
+               }
+               chan->channel_num = scan_list[i]->hw_value;
+       }
+       chan_info->channel_type = sreq->n_channels ? 4 : 0;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, sizeof(*ie));
+       ie = (struct scan_ie_tlv *)tlv;
+       if (sreq->ie_len > 0) {
+               memcpy(ie->ies, sreq->ie, sreq->ie_len);
+               ie->ies_len = cpu_to_le16(sreq->ie_len);
+       }
+
+       return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ),
+                                    false);
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_sched_scan_req);
+
+int
+mt7925_mcu_sched_scan_enable(struct mt76_phy *phy,
+                            struct ieee80211_vif *vif,
+                            bool enable)
+{
+       struct mt76_dev *mdev = phy->dev;
+       struct scan_sched_enable *req;
+       struct scan_hdr_tlv *hdr;
+       struct sk_buff *skb;
+       struct tlv *tlv;
+       int max_len;
+
+       max_len = sizeof(*hdr) + sizeof(*req);
+
+       skb = mt76_mcu_msg_alloc(mdev, NULL, max_len);
+       if (!skb)
+               return -ENOMEM;
+
+       hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr));
+       hdr->seq_num = 0;
+       hdr->bss_idx = 0;
+
+       tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SCHED_ENABLE, sizeof(*req));
+       req = (struct scan_sched_enable *)tlv;
+       req->active = !enable;
+
+       if (enable)
+               set_bit(MT76_HW_SCHED_SCANNING, &phy->state);
+       else
+               clear_bit(MT76_HW_SCHED_SCANNING, &phy->state);
+
+       return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ),
+                                    false);
+}
+
+int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy,
+                             struct ieee80211_vif *vif)
+{
+       struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+       struct {
+               struct scan_hdr {
+                       u8 seq_num;
+                       u8 bss_idx;
+                       u8 pad[2];
+               } __packed hdr;
+               struct scan_cancel_tlv {
+                       __le16 tag;
+                       __le16 len;
+                       u8 is_ext_channel;
+                       u8 rsv[3];
+               } __packed cancel;
+       } req = {
+               .hdr = {
+                       .seq_num = mvif->scan_seq_num,
+                       .bss_idx = mvif->idx,
+               },
+               .cancel = {
+                       .tag = cpu_to_le16(UNI_SCAN_CANCEL),
+                       .len = cpu_to_le16(sizeof(struct scan_cancel_tlv)),
+               },
+       };
+
+       if (test_and_clear_bit(MT76_HW_SCANNING, &phy->state)) {
+               struct cfg80211_scan_info info = {
+                       .aborted = true,
+               };
+
+               ieee80211_scan_completed(phy->hw, &info);
+       }
+
+       return mt76_mcu_send_msg(phy->dev, MCU_UNI_CMD(SCAN_REQ),
+                                &req, sizeof(req), false);
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_cancel_hw_scan);
+
+int mt7925_mcu_set_channel_domain(struct mt76_phy *phy)
+{
+       int len, i, n_max_channels, n_2ch = 0, n_5ch = 0, n_6ch = 0;
+       struct {
+               struct {
+                       u8 alpha2[4]; /* regulatory_request.alpha2 */
+                       u8 bw_2g; /* BW_20_40M          0
+                                  * BW_20M             1
+                                  * BW_20_40_80M       2
+                                  * BW_20_40_80_160M   3
+                                  * BW_20_40_80_8080M  4
+                                  */
+                       u8 bw_5g;
+                       u8 bw_6g;
+                       u8 pad;
+               } __packed hdr;
+               struct n_chan {
+                       __le16 tag;
+                       __le16 len;
+                       u8 n_2ch;
+                       u8 n_5ch;
+                       u8 n_6ch;
+                       u8 pad;
+               } __packed n_ch;
+       } req = {
+               .hdr = {
+                       .bw_2g = 0,
+                       .bw_5g = 3, /* BW_20_40_80_160M */
+                       .bw_6g = 3,
+               },
+               .n_ch = {
+                       .tag = cpu_to_le16(2),
+               },
+       };
+       struct mt76_connac_mcu_chan {
+               __le16 hw_value;
+               __le16 pad;
+               __le32 flags;
+       } __packed channel;
+       struct mt76_dev *dev = phy->dev;
+       struct ieee80211_channel *chan;
+       struct sk_buff *skb;
+
+       n_max_channels = phy->sband_2g.sband.n_channels +
+                        phy->sband_5g.sband.n_channels +
+                        phy->sband_6g.sband.n_channels;
+       len = sizeof(req) + n_max_channels * sizeof(channel);
+
+       skb = mt76_mcu_msg_alloc(dev, NULL, len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_reserve(skb, sizeof(req));
+
+       for (i = 0; i < phy->sband_2g.sband.n_channels; i++) {
+               chan = &phy->sband_2g.sband.channels[i];
+               if (chan->flags & IEEE80211_CHAN_DISABLED)
+                       continue;
+
+               channel.hw_value = cpu_to_le16(chan->hw_value);
+               channel.flags = cpu_to_le32(chan->flags);
+               channel.pad = 0;
+
+               skb_put_data(skb, &channel, sizeof(channel));
+               n_2ch++;
+       }
+       for (i = 0; i < phy->sband_5g.sband.n_channels; i++) {
+               chan = &phy->sband_5g.sband.channels[i];
+               if (chan->flags & IEEE80211_CHAN_DISABLED)
+                       continue;
+
+               channel.hw_value = cpu_to_le16(chan->hw_value);
+               channel.flags = cpu_to_le32(chan->flags);
+               channel.pad = 0;
+
+               skb_put_data(skb, &channel, sizeof(channel));
+               n_5ch++;
+       }
+       for (i = 0; i < phy->sband_6g.sband.n_channels; i++) {
+               chan = &phy->sband_6g.sband.channels[i];
+               if (chan->flags & IEEE80211_CHAN_DISABLED)
+                       continue;
+
+               channel.hw_value = cpu_to_le16(chan->hw_value);
+               channel.flags = cpu_to_le32(chan->flags);
+               channel.pad = 0;
+
+               skb_put_data(skb, &channel, sizeof(channel));
+               n_6ch++;
+       }
+
+       BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(req.hdr.alpha2));
+       memcpy(req.hdr.alpha2, dev->alpha2, sizeof(dev->alpha2));
+       req.n_ch.n_2ch = n_2ch;
+       req.n_ch.n_5ch = n_5ch;
+       req.n_ch.n_6ch = n_6ch;
+       len = sizeof(struct n_chan) + (n_2ch + n_5ch + n_6ch) * sizeof(channel);
+       req.n_ch.len = cpu_to_le16(len);
+       memcpy(__skb_push(skb, sizeof(req)), &req, sizeof(req));
+
+       return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SET_DOMAIN_INFO),
+                                    false);
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_set_channel_domain);
+
+static int
+__mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
+                    enum environment_cap env_cap,
+                    struct mt7925_clc *clc, u8 idx)
+{
+       struct mt7925_clc_segment *seg;
+       struct sk_buff *skb;
+       struct {
+               u8 rsv[4];
+               __le16 tag;
+               __le16 len;
+
+               u8 ver;
+               u8 pad0;
+               __le16 size;
+               u8 idx;
+               u8 env;
+               u8 acpi_conf;
+               u8 pad1;
+               u8 alpha2[2];
+               u8 type[2];
+               u8 rsvd[64];
+       } __packed req = {
+               .tag = cpu_to_le16(0x3),
+               .len = cpu_to_le16(sizeof(req) - 4),
+
+               .idx = idx,
+               .env = env_cap,
+               .acpi_conf = mt792x_acpi_get_flags(&dev->phy),
+       };
+       int ret, valid_cnt = 0;
+       u8 i, *pos;
+
+       if (!clc)
+               return 0;
+
+       pos = clc->data + sizeof(*seg) * clc->nr_seg;
+       for (i = 0; i < clc->nr_country; i++) {
+               struct mt7925_clc_rule *rule = (struct mt7925_clc_rule *)pos;
+
+               pos += sizeof(*rule);
+               if (rule->alpha2[0] != alpha2[0] ||
+                   rule->alpha2[1] != alpha2[1])
+                       continue;
+
+               seg = (struct mt7925_clc_segment *)clc->data
+                         + rule->seg_idx - 1;
+
+               memcpy(req.alpha2, rule->alpha2, 2);
+               memcpy(req.type, rule->type, 2);
+
+               req.size = cpu_to_le16(seg->len);
+               skb = __mt76_mcu_msg_alloc(&dev->mt76, &req,
+                                          le16_to_cpu(req.size) + sizeof(req),
+                                          sizeof(req), GFP_KERNEL);
+               if (!skb)
+                       return -ENOMEM;
+               skb_put_data(skb, clc->data + seg->offset, seg->len);
+
+               ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+                                           MCU_UNI_CMD(SET_POWER_LIMIT),
+                                           true);
+               if (ret < 0)
+                       return ret;
+               valid_cnt++;
+       }
+
+       if (!valid_cnt)
+               return -ENOENT;
+
+       return 0;
+}
+
+int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
+                      enum environment_cap env_cap)
+{
+       struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy;
+       int i, ret;
+
+       /* submit all clc config */
+       for (i = 0; i < ARRAY_SIZE(phy->clc); i++) {
+               ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap,
+                                          phy->clc[i], i);
+
+               /* If no country found, set "00" as default */
+               if (ret == -ENOENT)
+                       ret = __mt7925_mcu_set_clc(dev, "00",
+                                                  ENVIRON_INDOOR,
+                                                  phy->clc[i], i);
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
+int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb,
+                           int cmd, int *wait_seq)
+{
+       int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd);
+       struct mt76_connac2_mcu_uni_txd *uni_txd;
+       struct mt76_connac2_mcu_txd *mcu_txd;
+       __le32 *txd;
+       u32 val;
+       u8 seq;
+
+       /* TODO: make dynamic based on msg type */
+       mdev->mcu.timeout = 20 * HZ;
+
+       seq = ++mdev->mcu.msg_seq & 0xf;
+       if (!seq)
+               seq = ++mdev->mcu.msg_seq & 0xf;
+
+       if (cmd == MCU_CMD(FW_SCATTER))
+               goto exit;
+
+       txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd);
+       txd = (__le32 *)skb_push(skb, txd_len);
+
+       val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) |
+             FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) |
+             FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0);
+       txd[0] = cpu_to_le32(val);
+
+       val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD);
+       txd[1] = cpu_to_le32(val);
+
+       if (cmd & __MCU_CMD_FIELD_UNI) {
+               uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd;
+               uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd));
+               uni_txd->option = MCU_CMD_UNI_EXT_ACK;
+               uni_txd->cid = cpu_to_le16(mcu_cmd);
+               uni_txd->s2d_index = MCU_S2D_H2N;
+               uni_txd->pkt_type = MCU_PKT_ID;
+               uni_txd->seq = seq;
+
+               goto exit;
+       }
+
+       mcu_txd = (struct mt76_connac2_mcu_txd *)txd;
+       mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd));
+       mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU,
+                                              MT_TX_MCU_PORT_RX_Q0));
+       mcu_txd->pkt_type = MCU_PKT_ID;
+       mcu_txd->seq = seq;
+       mcu_txd->cid = mcu_cmd;
+       mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd);
+
+       if (mcu_txd->ext_cid || (cmd & __MCU_CMD_FIELD_CE)) {
+               if (cmd & __MCU_CMD_FIELD_QUERY)
+                       mcu_txd->set_query = MCU_Q_QUERY;
+               else
+                       mcu_txd->set_query = MCU_Q_SET;
+               mcu_txd->ext_cid_ack = !!mcu_txd->ext_cid;
+       } else {
+               mcu_txd->set_query = MCU_Q_NA;
+       }
+
+       if (cmd & __MCU_CMD_FIELD_WA)
+               mcu_txd->s2d_index = MCU_S2D_H2C;
+       else
+               mcu_txd->s2d_index = MCU_S2D_H2N;
+
+exit:
+       if (wait_seq)
+               *wait_seq = seq;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_fill_message);
+
+int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val)
+{
+       struct {
+               u8 band_idx;
+               u8 _rsv[3];
+
+               __le16 tag;
+               __le16 len;
+               __le32 len_thresh;
+               __le32 pkt_thresh;
+       } __packed req = {
+               .band_idx = phy->mt76->band_idx,
+               .tag = cpu_to_le16(UNI_BAND_CONFIG_RTS_THRESHOLD),
+               .len = cpu_to_le16(sizeof(req) - 4),
+               .len_thresh = cpu_to_le32(val),
+               .pkt_thresh = cpu_to_le32(0x2),
+       };
+
+       return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG),
+                                &req, sizeof(req), true);
+}
+
+int mt7925_mcu_set_radio_en(struct mt792x_phy *phy, bool enable)
+{
+       struct {
+               u8 band_idx;
+               u8 _rsv[3];
+
+               __le16 tag;
+               __le16 len;
+               u8 enable;
+               u8 _rsv2[3];
+       } __packed req = {
+               .band_idx = phy->mt76->band_idx,
+               .tag = cpu_to_le16(UNI_BAND_CONFIG_RADIO_ENABLE),
+               .len = cpu_to_le16(sizeof(req) - 4),
+               .enable = enable,
+       };
+
+       return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG),
+                                &req, sizeof(req), true);
+}
+
+static void
+mt7925_mcu_build_sku(struct mt76_dev *dev, s8 *sku,
+                    struct mt76_power_limits *limits,
+                    enum nl80211_band band)
+{
+       int i, offset = sizeof(limits->cck);
+
+       memset(sku, 127, MT_CONNAC3_SKU_POWER_LIMIT);
+
+       if (band == NL80211_BAND_2GHZ) {
+               /* cck */
+               memcpy(sku, limits->cck, sizeof(limits->cck));
+       }
+
+       /* ofdm */
+       memcpy(&sku[offset], limits->ofdm, sizeof(limits->ofdm));
+       offset += (sizeof(limits->ofdm) * 5);
+
+       /* ht */
+       for (i = 0; i < 2; i++) {
+               memcpy(&sku[offset], limits->mcs[i], 8);
+               offset += 8;
+       }
+       sku[offset++] = limits->mcs[0][0];
+
+       /* vht */
+       for (i = 0; i < ARRAY_SIZE(limits->mcs); i++) {
+               memcpy(&sku[offset], limits->mcs[i],
+                      ARRAY_SIZE(limits->mcs[i]));
+               offset += 12;
+       }
+
+       /* he */
+       for (i = 0; i < ARRAY_SIZE(limits->ru); i++) {
+               memcpy(&sku[offset], limits->ru[i], ARRAY_SIZE(limits->ru[i]));
+               offset += ARRAY_SIZE(limits->ru[i]);
+       }
+
+       /* eht */
+       for (i = 0; i < ARRAY_SIZE(limits->eht); i++) {
+               memcpy(&sku[offset], limits->eht[i], ARRAY_SIZE(limits->eht[i]));
+               offset += ARRAY_SIZE(limits->eht[i]);
+       }
+}
+
+static int
+mt7925_mcu_rate_txpower_band(struct mt76_phy *phy,
+                            enum nl80211_band band)
+{
+       int tx_power, n_chan, last_ch, err = 0, idx = 0;
+       int i, sku_len, batch_size, batch_len = 3;
+       struct mt76_dev *dev = phy->dev;
+       static const u8 chan_list_2ghz[] = {
+               1, 2,  3,  4,  5,  6,  7,
+               8, 9, 10, 11, 12, 13, 14
+       };
+       static const u8 chan_list_5ghz[] = {
+                36,  38,  40,  42,  44,  46,  48,
+                50,  52,  54,  56,  58,  60,  62,
+                64, 100, 102, 104, 106, 108, 110,
+               112, 114, 116, 118, 120, 122, 124,
+               126, 128, 132, 134, 136, 138, 140,
+               142, 144, 149, 151, 153, 155, 157,
+               159, 161, 165, 167
+       };
+       static const u8 chan_list_6ghz[] = {
+                 1,   3,   5,   7,   9,  11,  13,
+                15,  17,  19,  21,  23,  25,  27,
+                29,  33,  35,  37,  39,  41,  43,
+                45,  47,  49,  51,  53,  55,  57,
+                59,  61,  65,  67,  69,  71,  73,
+                75,  77,  79,  81,  83,  85,  87,
+                89,  91,  93,  97,  99, 101, 103,
+               105, 107, 109, 111, 113, 115, 117,
+               119, 121, 123, 125, 129, 131, 133,
+               135, 137, 139, 141, 143, 145, 147,
+               149, 151, 153, 155, 157, 161, 163,
+               165, 167, 169, 171, 173, 175, 177,
+               179, 181, 183, 185, 187, 189, 193,
+               195, 197, 199, 201, 203, 205, 207,
+               209, 211, 213, 215, 217, 219, 221,
+               225, 227, 229, 233
+       };
+       struct mt76_power_limits *limits;
+       struct mt7925_sku_tlv *sku_tlbv;
+       const u8 *ch_list;
+
+       sku_len = sizeof(*sku_tlbv);
+       tx_power = 2 * phy->hw->conf.power_level;
+       if (!tx_power)
+               tx_power = 127;
+
+       if (band == NL80211_BAND_2GHZ) {
+               n_chan = ARRAY_SIZE(chan_list_2ghz);
+               ch_list = chan_list_2ghz;
+               last_ch = chan_list_2ghz[ARRAY_SIZE(chan_list_2ghz) - 1];
+       } else if (band == NL80211_BAND_6GHZ) {
+               n_chan = ARRAY_SIZE(chan_list_6ghz);
+               ch_list = chan_list_6ghz;
+               last_ch = chan_list_6ghz[ARRAY_SIZE(chan_list_6ghz) - 1];
+       } else {
+               n_chan = ARRAY_SIZE(chan_list_5ghz);
+               ch_list = chan_list_5ghz;
+               last_ch = chan_list_5ghz[ARRAY_SIZE(chan_list_5ghz) - 1];
+       }
+       batch_size = DIV_ROUND_UP(n_chan, batch_len);
+
+       limits = devm_kmalloc(dev->dev, sizeof(*limits), GFP_KERNEL);
+       if (!limits)
+               return -ENOMEM;
+
+       sku_tlbv = devm_kmalloc(dev->dev, sku_len, GFP_KERNEL);
+       if (!sku_tlbv) {
+               devm_kfree(dev->dev, limits);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < batch_size; i++) {
+               struct mt7925_tx_power_limit_tlv *tx_power_tlv;
+               int j, msg_len, num_ch;
+               struct sk_buff *skb;
+
+               num_ch = i == batch_size - 1 ? n_chan % batch_len : batch_len;
+               msg_len = sizeof(*tx_power_tlv) + num_ch * sku_len;
+               skb = mt76_mcu_msg_alloc(dev, NULL, msg_len);
+               if (!skb) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+
+               tx_power_tlv = (struct mt7925_tx_power_limit_tlv *)
+                              skb_put(skb, sizeof(*tx_power_tlv));
+
+               BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(tx_power_tlv->alpha2));
+               memcpy(tx_power_tlv->alpha2, dev->alpha2, sizeof(dev->alpha2));
+               tx_power_tlv->n_chan = num_ch;
+               tx_power_tlv->tag = cpu_to_le16(0x1);
+               tx_power_tlv->len = cpu_to_le16(sizeof(*tx_power_tlv));
+
+               switch (band) {
+               case NL80211_BAND_2GHZ:
+                       tx_power_tlv->band = 1;
+                       break;
+               case NL80211_BAND_6GHZ:
+                       tx_power_tlv->band = 3;
+                       break;
+               default:
+                       tx_power_tlv->band = 2;
+                       break;
+               }
+
+               for (j = 0; j < num_ch; j++, idx++) {
+                       struct ieee80211_channel chan = {
+                               .hw_value = ch_list[idx],
+                               .band = band,
+                       };
+                       s8 reg_power, sar_power;
+
+                       reg_power = mt76_connac_get_ch_power(phy, &chan,
+                                                            tx_power);
+                       sar_power = mt76_get_sar_power(phy, &chan, reg_power);
+
+                       mt76_get_rate_power_limits(phy, &chan, limits,
+                                                  sar_power);
+
+                       tx_power_tlv->last_msg = ch_list[idx] == last_ch;
+                       sku_tlbv->channel = ch_list[idx];
+
+                       mt7925_mcu_build_sku(dev, sku_tlbv->pwr_limit,
+                                            limits, band);
+                       skb_put_data(skb, sku_tlbv, sku_len);
+               }
+               err = mt76_mcu_skb_send_msg(dev, skb,
+                                           MCU_UNI_CMD(SET_POWER_LIMIT),
+                                           true);
+               if (err < 0)
+                       goto out;
+       }
+
+out:
+       devm_kfree(dev->dev, sku_tlbv);
+       devm_kfree(dev->dev, limits);
+       return err;
+}
+
+int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy)
+{
+       int err;
+
+       if (phy->cap.has_2ghz) {
+               err = mt7925_mcu_rate_txpower_band(phy,
+                                                  NL80211_BAND_2GHZ);
+               if (err < 0)
+                       return err;
+       }
+
+       if (phy->cap.has_5ghz) {
+               err = mt7925_mcu_rate_txpower_band(phy,
+                                                  NL80211_BAND_5GHZ);
+               if (err < 0)
+                       return err;
+       }
+
+       if (phy->cap.has_6ghz) {
+               err = mt7925_mcu_rate_txpower_band(phy,
+                                                  NL80211_BAND_6GHZ);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
+                           u8 bit_op, u32 bit_map)
+{
+       struct mt792x_phy *phy = &dev->phy;
+       struct {
+               u8 band_idx;
+               u8 rsv1[3];
+
+               __le16 tag;
+               __le16 len;
+               u8 mode;
+               u8 rsv2[3];
+               __le32 fif;
+               __le32 bit_map; /* bit_* for bitmap update */
+               u8 bit_op;
+               u8 pad[51];
+       } __packed req = {
+               .band_idx = phy->mt76->band_idx,
+               .tag = cpu_to_le16(UNI_BAND_CONFIG_SET_MAC80211_RX_FILTER),
+               .len = cpu_to_le16(sizeof(req) - 4),
+
+               .mode = fif ? 0 : 1,
+               .fif = cpu_to_le32(fif),
+               .bit_map = cpu_to_le32(bit_map),
+               .bit_op = bit_op,
+       };
+
+       return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG),
+                                &req, sizeof(req), true);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
new file mode 100644 (file)
index 0000000..3c41e21
--- /dev/null
@@ -0,0 +1,537 @@
+/* SPDX-License-Identifier: ISC */
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#ifndef __MT7925_MCU_H
+#define __MT7925_MCU_H
+
+#include "../mt76_connac_mcu.h"
+
+/* ext event table */
+enum {
+       MCU_EXT_EVENT_RATE_REPORT = 0x87,
+};
+
+struct mt7925_mcu_eeprom_info {
+       __le32 addr;
+       __le32 valid;
+       u8 data[MT7925_EEPROM_BLOCK_SIZE];
+} __packed;
+
+#define MT_RA_RATE_NSS                 GENMASK(8, 6)
+#define MT_RA_RATE_MCS                 GENMASK(3, 0)
+#define MT_RA_RATE_TX_MODE             GENMASK(12, 9)
+#define MT_RA_RATE_DCM_EN              BIT(4)
+#define MT_RA_RATE_BW                  GENMASK(14, 13)
+
+struct mt7925_mcu_rxd {
+       __le32 rxd[8];
+
+       __le16 len;
+       __le16 pkt_type_id;
+
+       u8 eid;
+       u8 seq;
+       u8 option;
+       u8 __rsv;
+
+       u8 ext_eid;
+       u8 __rsv1[2];
+       u8 s2d_index;
+
+       u8 tlv[];
+};
+
+struct mt7925_mcu_uni_event {
+       u8 cid;
+       u8 pad[3];
+       __le32 status; /* 0: success, others: fail */
+} __packed;
+
+enum {
+       MT_EBF = BIT(0),        /* explicit beamforming */
+       MT_IBF = BIT(1)         /* implicit beamforming */
+};
+
+struct mt7925_mcu_reg_event {
+       __le32 reg;
+       __le32 val;
+} __packed;
+
+struct mt7925_mcu_ant_id_config {
+       u8 ant_id[4];
+} __packed;
+
+struct mt7925_txpwr_req {
+       u8 _rsv[4];
+       __le16 tag;
+       __le16 len;
+
+       u8 format_id;
+       u8 catg;
+       u8 band_idx;
+       u8 _rsv1;
+} __packed;
+
+struct mt7925_txpwr_event {
+       u8 rsv[4];
+       __le16 tag;
+       __le16 len;
+
+       u8 catg;
+       u8 band_idx;
+       u8 ch_band;
+       u8 format; /* 0:Legacy, 1:HE */
+
+       /* Rate power info */
+       struct mt7925_txpwr txpwr;
+
+       s8 pwr_max;
+       s8 pwr_min;
+       u8 rsv1;
+} __packed;
+
+enum {
+       TM_SWITCH_MODE,
+       TM_SET_AT_CMD,
+       TM_QUERY_AT_CMD,
+};
+
+enum {
+       MT7925_TM_NORMAL,
+       MT7925_TM_TESTMODE,
+       MT7925_TM_ICAP,
+       MT7925_TM_ICAP_OVERLAP,
+       MT7925_TM_WIFISPECTRUM,
+};
+
+struct mt7925_rftest_cmd {
+       u8 action;
+       u8 rsv[3];
+       __le32 param0;
+       __le32 param1;
+} __packed;
+
+struct mt7925_rftest_evt {
+       __le32 param0;
+       __le32 param1;
+} __packed;
+
+enum {
+       UNI_CHANNEL_SWITCH,
+       UNI_CHANNEL_RX_PATH,
+};
+
+enum {
+       UNI_CHIP_CONFIG_CHIP_CFG = 0x2,
+       UNI_CHIP_CONFIG_NIC_CAPA = 0x3,
+};
+
+enum {
+       UNI_BAND_CONFIG_RADIO_ENABLE,
+       UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
+       UNI_BAND_CONFIG_SET_MAC80211_RX_FILTER = 0x0C,
+};
+
+enum {
+       UNI_WSYS_CONFIG_FW_LOG_CTRL,
+       UNI_WSYS_CONFIG_FW_DBG_CTRL,
+};
+
+enum {
+       UNI_EFUSE_ACCESS = 1,
+       UNI_EFUSE_BUFFER_MODE,
+       UNI_EFUSE_FREE_BLOCK,
+       UNI_EFUSE_BUFFER_RD,
+};
+
+enum {
+       UNI_CMD_ACCESS_REG_BASIC = 0x0,
+       UNI_CMD_ACCESS_RF_REG_BASIC,
+};
+
+enum {
+       UNI_MBMC_SETTING,
+};
+
+enum {
+       UNI_EVENT_SCAN_DONE_BASIC = 0,
+       UNI_EVENT_SCAN_DONE_CHNLINFO = 2,
+       UNI_EVENT_SCAN_DONE_NLO = 3,
+};
+
+struct mt7925_mcu_scan_chinfo_event {
+       u8 nr_chan;
+       u8 alpha2[3];
+} __packed;
+
+enum {
+       UNI_SCAN_REQ = 1,
+       UNI_SCAN_CANCEL = 2,
+       UNI_SCAN_SCHED_REQ = 3,
+       UNI_SCAN_SCHED_ENABLE = 4,
+       UNI_SCAN_SSID = 10,
+       UNI_SCAN_BSSID,
+       UNI_SCAN_CHANNEL,
+       UNI_SCAN_IE,
+       UNI_SCAN_MISC,
+       UNI_SCAN_SSID_MATCH_SETS,
+};
+
+enum {
+       UNI_SNIFFER_ENABLE,
+       UNI_SNIFFER_CONFIG,
+};
+
+struct scan_hdr_tlv {
+       /* fixed field */
+       u8 seq_num;
+       u8 bss_idx;
+       u8 pad[2];
+       /* tlv */
+       u8 data[];
+} __packed;
+
+struct scan_req_tlv {
+       __le16 tag;
+       __le16 len;
+
+       u8 scan_type; /* 0: PASSIVE SCAN
+                      * 1: ACTIVE SCAN
+                      */
+       u8 probe_req_num; /* Number of probe request for each SSID */
+       u8 scan_func; /* BIT(0) Enable random MAC scan
+                      * BIT(1) Disable DBDC scan type 1~3.
+                      * BIT(2) Use DBDC scan type 3 (dedicated one RF to scan).
+                      */
+       u8 src_mask;
+       __le16 channel_min_dwell_time;
+       __le16 channel_dwell_time; /* channel Dwell interval */
+       __le16 timeout_value;
+       __le16 probe_delay_time;
+       u8 func_mask_ext;
+};
+
+struct scan_ssid_tlv {
+       __le16 tag;
+       __le16 len;
+
+       u8 ssid_type; /* BIT(0) wildcard SSID
+                      * BIT(1) P2P wildcard SSID
+                      * BIT(2) specified SSID + wildcard SSID
+                      * BIT(2) + ssid_type_ext BIT(0) specified SSID only
+                      */
+       u8 ssids_num;
+       u8 pad[2];
+       struct mt76_connac_mcu_scan_ssid ssids[4];
+};
+
+struct scan_bssid_tlv {
+       __le16 tag;
+       __le16 len;
+
+       u8 bssid[ETH_ALEN];
+       u8 match_ch;
+       u8 match_ssid_ind;
+       u8 rcpi;
+       u8 pad[3];
+};
+
+struct scan_chan_info_tlv {
+       __le16 tag;
+       __le16 len;
+
+       u8 channel_type; /* 0: Full channels
+                         * 1: Only 2.4GHz channels
+                         * 2: Only 5GHz channels
+                         * 3: P2P social channel only (channel #1, #6 and #11)
+                         * 4: Specified channels
+                         * Others: Reserved
+                         */
+       u8 channels_num; /* valid when channel_type is 4 */
+       u8 pad[2];
+       struct mt76_connac_mcu_scan_channel channels[64];
+};
+
+struct scan_ie_tlv {
+       __le16 tag;
+       __le16 len;
+
+       __le16 ies_len;
+       u8 band;
+       u8 pad;
+       u8 ies[MT76_CONNAC_SCAN_IE_LEN];
+};
+
+struct scan_misc_tlv {
+       __le16 tag;
+       __le16 len;
+
+       u8 random_mac[ETH_ALEN];
+       u8 rsv[2];
+};
+
+struct scan_sched_req {
+       __le16 tag;
+       __le16 len;
+
+       u8 version;
+       u8 stop_on_match;
+       u8 intervals_num;
+       u8 scan_func;
+       __le16 intervals[MT76_CONNAC_MAX_NUM_SCHED_SCAN_INTERVAL];
+};
+
+struct scan_sched_ssid_match_sets {
+       __le16 tag;
+       __le16 len;
+
+       u8 match_num;
+       u8 rsv[3];
+
+       struct mt76_connac_mcu_scan_match match[MT76_CONNAC_MAX_SCAN_MATCH];
+};
+
+struct scan_sched_enable {
+       __le16 tag;
+       __le16 len;
+
+       u8 active;
+       u8 rsv[3];
+};
+
+struct mbmc_set_req {
+       u8 pad[4];
+       u8 data[];
+} __packed;
+
+struct mbmc_conf_tlv {
+       __le16 tag;
+       __le16 len;
+
+       u8 mbmc_en;
+       u8 band;
+       u8 pad[2];
+} __packed;
+
+struct edca {
+       __le16 tag;
+       __le16 len;
+
+       u8 queue;
+       u8 set;
+       u8 cw_min;
+       u8 cw_max;
+       __le16 txop;
+       u8 aifs;
+       u8 __rsv;
+};
+
+struct bss_req_hdr {
+       u8 bss_idx;
+       u8 __rsv[3];
+} __packed;
+
+struct bss_rate_tlv {
+       __le16 tag;
+       __le16 len;
+       u8 __rsv1[4];
+       __le16 bc_trans;
+       __le16 mc_trans;
+       u8 short_preamble;
+       u8 bc_fixed_rate;
+       u8 mc_fixed_rate;
+       u8 __rsv2;
+} __packed;
+
+struct bss_mld_tlv {
+       __le16 tag;
+       __le16 len;
+       u8 group_mld_id;
+       u8 own_mld_id;
+       u8 mac_addr[ETH_ALEN];
+       u8 remap_idx;
+       u8 link_id;
+       u8 __rsv[2];
+} __packed;
+
+struct sta_rec_ba_uni {
+       __le16 tag;
+       __le16 len;
+       u8 tid;
+       u8 ba_type;
+       u8 amsdu;
+       u8 ba_en;
+       __le16 ssn;
+       __le16 winsize;
+       u8 ba_rdd_rro;
+       u8 __rsv[3];
+} __packed;
+
+struct sta_rec_eht {
+       __le16 tag;
+       __le16 len;
+       u8 tid_bitmap;
+       u8 _rsv;
+       __le16 mac_cap;
+       __le64 phy_cap;
+       __le64 phy_cap_ext;
+       u8 mcs_map_bw20[4];
+       u8 mcs_map_bw80[3];
+       u8 mcs_map_bw160[3];
+       u8 mcs_map_bw320[3];
+       u8 _rsv2[3];
+} __packed;
+
+struct sec_key_uni {
+       __le16 wlan_idx;
+       u8 mgmt_prot;
+       u8 cipher_id;
+       u8 cipher_len;
+       u8 key_id;
+       u8 key_len;
+       u8 need_resp;
+       u8 key[32];
+} __packed;
+
+struct sta_rec_sec_uni {
+       __le16 tag;
+       __le16 len;
+       u8 add;
+       u8 n_cipher;
+       u8 rsv[2];
+
+       struct sec_key_uni key[2];
+} __packed;
+
+struct sta_rec_hdr_trans {
+       __le16 tag;
+       __le16 len;
+       u8 from_ds;
+       u8 to_ds;
+       u8 dis_rx_hdr_tran;
+       u8 rsv;
+} __packed;
+
+struct sta_rec_mld {
+       __le16 tag;
+       __le16 len;
+       u8 mac_addr[ETH_ALEN];
+       __le16 primary_id;
+       __le16 secondary_id;
+       __le16 wlan_id;
+       u8 link_num;
+       u8 rsv[3];
+       struct {
+               __le16 wlan_id;
+               u8 bss_idx;
+               u8 rsv;
+       } __packed link[2];
+} __packed;
+
+#define MT7925_STA_UPDATE_MAX_SIZE     (sizeof(struct sta_req_hdr) +           \
+                                        sizeof(struct sta_rec_basic) +         \
+                                        sizeof(struct sta_rec_bf) +            \
+                                        sizeof(struct sta_rec_ht) +            \
+                                        sizeof(struct sta_rec_he_v2) +         \
+                                        sizeof(struct sta_rec_ba_uni) +        \
+                                        sizeof(struct sta_rec_vht) +           \
+                                        sizeof(struct sta_rec_uapsd) +         \
+                                        sizeof(struct sta_rec_amsdu) +         \
+                                        sizeof(struct sta_rec_bfee) +          \
+                                        sizeof(struct sta_rec_phy) +           \
+                                        sizeof(struct sta_rec_ra) +            \
+                                        sizeof(struct sta_rec_sec) +           \
+                                        sizeof(struct sta_rec_ra_fixed) +      \
+                                        sizeof(struct sta_rec_he_6g_capa) +    \
+                                        sizeof(struct sta_rec_eht) +           \
+                                        sizeof(struct sta_rec_hdr_trans) +     \
+                                        sizeof(struct sta_rec_mld) +           \
+                                        sizeof(struct tlv))
+
+#define MT7925_BSS_UPDATE_MAX_SIZE     (sizeof(struct bss_req_hdr) +           \
+                                        sizeof(struct mt76_connac_bss_basic_tlv) +     \
+                                        sizeof(struct mt76_connac_bss_qos_tlv) +       \
+                                        sizeof(struct bss_rate_tlv) +                  \
+                                        sizeof(struct bss_mld_tlv) +                   \
+                                        sizeof(struct bss_info_uni_he) +               \
+                                        sizeof(struct bss_info_uni_bss_color) +        \
+                                        sizeof(struct tlv))
+
+#define MT_CONNAC3_SKU_POWER_LIMIT      449
+struct mt7925_sku_tlv {
+       u8 channel;
+       s8 pwr_limit[MT_CONNAC3_SKU_POWER_LIMIT];
+} __packed;
+
+struct mt7925_tx_power_limit_tlv {
+       u8 rsv[4];
+
+       __le16 tag;
+       __le16 len;
+
+       /* DW0 - common info*/
+       u8 ver;
+       u8 pad0;
+       __le16 rsv1;
+       /* DW1 - cmd hint */
+       u8 n_chan; /* # channel */
+       u8 band; /* 2.4GHz - 5GHz - 6GHz */
+       u8 last_msg;
+       u8 limit_type;
+       /* DW3 */
+       u8 alpha2[4]; /* regulatory_request.alpha2 */
+       u8 pad2[32];
+
+       u8 data[];
+} __packed;
+
+struct mt7925_arpns_tlv {
+       __le16 tag;
+       __le16 len;
+
+       u8 enable;
+       u8 ips_num;
+       u8 rsv[2];
+} __packed;
+
+struct mt7925_wow_pattern_tlv {
+       __le16 tag;
+       __le16 len;
+       u8 bss_idx;
+       u8 index; /* pattern index */
+       u8 enable; /* 0: disable
+                   * 1: enable
+                   */
+       u8 data_len; /* pattern length */
+       u8 offset;
+       u8 mask[MT76_CONNAC_WOW_MASK_MAX_LEN];
+       u8 pattern[MT76_CONNAC_WOW_PATTEN_MAX_LEN];
+       u8 rsv[4];
+} __packed;
+
+int mt7925_mcu_set_dbdc(struct mt76_phy *phy);
+int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
+                      struct ieee80211_scan_request *scan_req);
+int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy,
+                             struct ieee80211_vif *vif);
+int mt7925_mcu_sched_scan_req(struct mt76_phy *phy,
+                             struct ieee80211_vif *vif,
+                             struct cfg80211_sched_scan_request *sreq);
+int mt7925_mcu_sched_scan_enable(struct mt76_phy *phy,
+                                struct ieee80211_vif *vif,
+                                bool enable);
+int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
+                           struct ieee80211_chanctx_conf *ctx,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta,
+                           int enable);
+int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable);
+int mt7925_mcu_set_channel_domain(struct mt76_phy *phy);
+int mt7925_mcu_set_radio_en(struct mt792x_phy *phy, bool enable);
+int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif *mvif,
+                        struct ieee80211_chanctx_conf *ctx);
+int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy);
+int mt7925_mcu_update_arp_filter(struct mt76_dev *dev,
+                                struct mt76_vif *vif,
+                                struct ieee80211_bss_conf *info);
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
new file mode 100644 (file)
index 0000000..33785f5
--- /dev/null
@@ -0,0 +1,309 @@
+/* SPDX-License-Identifier: ISC */
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#ifndef __MT7925_H
+#define __MT7925_H
+
+#include "../mt792x.h"
+#include "regs.h"
+
+#define MT7925_BEACON_RATES_TBL                25
+
+#define MT7925_TX_RING_SIZE            2048
+#define MT7925_TX_MCU_RING_SIZE                256
+#define MT7925_TX_FWDL_RING_SIZE       128
+
+#define MT7925_RX_RING_SIZE            1536
+#define MT7925_RX_MCU_RING_SIZE                512
+
+#define MT7925_EEPROM_SIZE             3584
+#define MT7925_TOKEN_SIZE              8192
+
+#define MT7925_EEPROM_BLOCK_SIZE       16
+
+#define MT7925_SKU_RATE_NUM            161
+#define MT7925_SKU_MAX_DELTA_IDX       MT7925_SKU_RATE_NUM
+#define MT7925_SKU_TABLE_SIZE          (MT7925_SKU_RATE_NUM + 1)
+
+#define MCU_UNI_EVENT_ROC  0x27
+
+enum {
+       UNI_ROC_ACQUIRE,
+       UNI_ROC_ABORT,
+       UNI_ROC_NUM
+};
+
+enum mt7925_roc_req {
+       MT7925_ROC_REQ_JOIN,
+       MT7925_ROC_REQ_ROC,
+       MT7925_ROC_REQ_NUM
+};
+
+enum {
+       UNI_EVENT_ROC_GRANT = 0,
+       UNI_EVENT_ROC_TAG_NUM
+};
+
+struct mt7925_roc_grant_tlv {
+       __le16 tag;
+       __le16 len;
+       u8 bss_idx;
+       u8 tokenid;
+       u8 status;
+       u8 primarychannel;
+       u8 rfsco;
+       u8 rfband;
+       u8 channelwidth;
+       u8 centerfreqseg1;
+       u8 centerfreqseg2;
+       u8 reqtype;
+       u8 dbdcband;
+       u8 rsv[1];
+       __le32 max_interval;
+} __packed;
+
+struct mt7925_beacon_loss_tlv {
+       __le16 tag;
+       __le16 len;
+       u8 reason;
+       u8 nr_btolink;
+       u8 pad[2];
+} __packed;
+
+struct mt7925_uni_beacon_loss_event {
+       struct {
+               u8 bss_idx;
+               u8 pad[3];
+       } __packed hdr;
+       struct mt7925_beacon_loss_tlv beacon_loss;
+} __packed;
+
+#define to_rssi(field, rxv)            ((FIELD_GET(field, rxv) - 220) / 2)
+#define to_rcpi(rssi)                  (2 * (rssi) + 220)
+
+enum mt7925_txq_id {
+       MT7925_TXQ_BAND0,
+       MT7925_TXQ_BAND1,
+       MT7925_TXQ_MCU_WM = 15,
+       MT7925_TXQ_FWDL,
+};
+
+enum mt7925_rxq_id {
+       MT7925_RXQ_BAND0 = 2,
+       MT7925_RXQ_BAND1,
+       MT7925_RXQ_MCU_WM = 0,
+       MT7925_RXQ_MCU_WM2, /* for tx done */
+};
+
+enum {
+       MODE_OPEN = 0,
+       MODE_SHARED = 1,
+       MODE_WPA = 3,
+       MODE_WPA_PSK = 4,
+       MODE_WPA_NONE = 5,
+       MODE_WPA2 = 6,
+       MODE_WPA2_PSK = 7,
+       MODE_WPA3_SAE = 11,
+};
+
+enum {
+       MT7925_CLC_POWER,
+       MT7925_CLC_CHAN,
+       MT7925_CLC_MAX_NUM,
+};
+
+struct mt7925_clc_rule {
+       u8 alpha2[2];
+       u8 type[2];
+       u8 seg_idx;
+       u8 rsv[3];
+} __packed;
+
+struct mt7925_clc_segment {
+       u8 idx;
+       u8 rsv1[3];
+       u32 offset;
+       u32 len;
+       u8 rsv2[4];
+} __packed;
+
+struct mt7925_clc {
+       __le32 len;
+       u8 idx;
+       u8 ver;
+       u8 nr_country;
+       u8 type;
+       u8 nr_seg;
+       u8 rsv[7];
+       u8 data[];
+} __packed;
+
+enum mt7925_eeprom_field {
+       MT_EE_CHIP_ID =         0x000,
+       MT_EE_VERSION =         0x002,
+       MT_EE_MAC_ADDR =        0x004,
+       __MT_EE_MAX =           0x9ff
+};
+
+enum {
+       TXPWR_USER,
+       TXPWR_EEPROM,
+       TXPWR_MAC,
+       TXPWR_MAX_NUM,
+};
+
+struct mt7925_txpwr {
+       s8 cck[4][2];
+       s8 ofdm[8][2];
+       s8 ht20[8][2];
+       s8 ht40[9][2];
+       s8 vht20[12][2];
+       s8 vht40[12][2];
+       s8 vht80[12][2];
+       s8 vht160[12][2];
+       s8 he26[12][2];
+       s8 he52[12][2];
+       s8 he106[12][2];
+       s8 he242[12][2];
+       s8 he484[12][2];
+       s8 he996[12][2];
+       s8 he996x2[12][2];
+       s8 eht26[16][2];
+       s8 eht52[16][2];
+       s8 eht106[16][2];
+       s8 eht242[16][2];
+       s8 eht484[16][2];
+       s8 eht996[16][2];
+       s8 eht996x2[16][2];
+       s8 eht996x4[16][2];
+       s8 eht26_52[16][2];
+       s8 eht26_106[16][2];
+       s8 eht484_242[16][2];
+       s8 eht996_484[16][2];
+       s8 eht996_484_242[16][2];
+       s8 eht996x2_484[16][2];
+       s8 eht996x3[16][2];
+       s8 eht996x3_484[16][2];
+};
+
+extern const struct ieee80211_ops mt7925_ops;
+
+int __mt7925_start(struct mt792x_phy *phy);
+int mt7925_register_device(struct mt792x_dev *dev);
+void mt7925_unregister_device(struct mt792x_dev *dev);
+int mt7925_run_firmware(struct mt792x_dev *dev);
+int mt7925_mcu_set_bss_pm(struct mt792x_dev *dev, struct ieee80211_vif *vif,
+                         bool enable);
+int mt7925_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_sta *sta,
+                         struct ieee80211_vif *vif, bool enable,
+                         enum mt76_sta_info_state state);
+int mt7925_mcu_set_chan_info(struct mt792x_phy *phy, u16 tag);
+int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_vif *vif);
+int mt7925_mcu_set_eeprom(struct mt792x_dev *dev);
+int mt7925_mcu_get_rx_rate(struct mt792x_phy *phy, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta, struct rate_info *rate);
+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_set_rxfilter(struct mt792x_dev *dev, u32 fif,
+                           u8 bit_op, u32 bit_map);
+
+int mt7925_mac_init(struct mt792x_dev *dev);
+int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                      struct ieee80211_sta *sta);
+bool mt7925_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask);
+void mt7925_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta);
+void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta);
+void mt7925_mac_reset_work(struct work_struct *work);
+int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+                          enum mt76_txq_id qid, struct mt76_wcid *wcid,
+                          struct ieee80211_sta *sta,
+                          struct mt76_tx_info *tx_info);
+
+void mt7925_tx_token_put(struct mt792x_dev *dev);
+bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len);
+void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+                        struct sk_buff *skb, u32 *info);
+void mt7925_stats_work(struct work_struct *work);
+void mt7925_set_stream_he_eht_caps(struct mt792x_phy *phy);
+int mt7925_init_debugfs(struct mt792x_dev *dev);
+
+int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev,
+                                struct ieee80211_vif *vif,
+                                bool enable);
+int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev,
+                        struct ieee80211_ampdu_params *params,
+                        bool enable);
+int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
+                        struct ieee80211_ampdu_params *params,
+                        bool enable);
+void mt7925_scan_work(struct work_struct *work);
+void mt7925_roc_work(struct work_struct *work);
+int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_vif *vif);
+void mt7925_coredump_work(struct work_struct *work);
+int mt7925_get_txpwr_info(struct mt792x_dev *dev, u8 band_idx,
+                         struct mt7925_txpwr *txpwr);
+void mt7925_mac_set_fixed_rate_table(struct mt792x_dev *dev,
+                                    u8 tbl_idx, u16 rate_idx);
+void mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
+                          struct sk_buff *skb, struct mt76_wcid *wcid,
+                          struct ieee80211_key_conf *key, int pid,
+                          enum mt76_txq_id qid, u32 changed);
+void mt7925_txwi_free(struct mt792x_dev *dev, struct mt76_txwi_cache *t,
+                     struct ieee80211_sta *sta, bool clear_status,
+                     struct list_head *free_list);
+int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd,
+                             struct sk_buff *skb, int seq);
+
+int mt7925e_mac_reset(struct mt792x_dev *dev);
+int mt7925e_mcu_init(struct mt792x_dev *dev);
+void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data);
+void mt7925_set_runtime_pm(struct mt792x_dev *dev);
+void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac,
+                                struct ieee80211_vif *vif);
+void mt7925_connac_mcu_set_suspend_iter(void *priv, u8 *mac,
+                                       struct ieee80211_vif *vif);
+void mt7925_set_ipv6_ns_work(struct work_struct *work);
+
+int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif,
+                          bool enable);
+int mt7925_mcu_config_sniffer(struct mt792x_vif *vif,
+                             struct ieee80211_chanctx_conf *ctx);
+
+int mt7925_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+                                  enum mt76_txq_id qid, struct mt76_wcid *wcid,
+                                  struct ieee80211_sta *sta,
+                                  struct mt76_tx_info *tx_info);
+void mt7925_usb_sdio_tx_complete_skb(struct mt76_dev *mdev,
+                                    struct mt76_queue_entry *e);
+bool mt7925_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update);
+
+int mt7925_mcu_uni_add_beacon_offload(struct mt792x_dev *dev,
+                                     struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     bool enable);
+int mt7925_set_tx_sar_pwr(struct ieee80211_hw *hw,
+                         const struct cfg80211_sar_specs *sar);
+
+int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set);
+int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
+                      enum environment_cap env_cap);
+int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_vif *vif,
+                      struct ieee80211_channel *chan, int duration,
+                      enum mt7925_roc_req type, u8 token_id);
+int mt7925_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif,
+                        u8 token_id);
+int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb,
+                           int cmd, int *wait_seq);
+int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+                      struct mt76_connac_sta_key_conf *sta_key_conf,
+                      struct ieee80211_key_conf *key, int mcu_cmd,
+                      struct mt76_wcid *wcid, enum set_key_cmd cmd);
+int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val);
+int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_sta *sta);
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
new file mode 100644 (file)
index 0000000..08ef75e
--- /dev/null
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "mt7925.h"
+#include "mac.h"
+#include "mcu.h"
+#include "../dma.h"
+
+static const struct pci_device_id mt7925_pci_device_table[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7925),
+               .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM },
+       { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0717),
+               .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM },
+       { },
+};
+
+static bool mt7925_disable_aspm;
+module_param_named(disable_aspm, mt7925_disable_aspm, bool, 0644);
+MODULE_PARM_DESC(disable_aspm, "disable PCI ASPM support");
+
+static int mt7925e_init_reset(struct mt792x_dev *dev)
+{
+       return mt792x_wpdma_reset(dev, true);
+}
+
+static void mt7925e_unregister_device(struct mt792x_dev *dev)
+{
+       int i;
+       struct mt76_connac_pm *pm = &dev->pm;
+
+       cancel_work_sync(&dev->init_work);
+       mt76_unregister_device(&dev->mt76);
+       mt76_for_each_q_rx(&dev->mt76, i)
+               napi_disable(&dev->mt76.napi[i]);
+       cancel_delayed_work_sync(&pm->ps_work);
+       cancel_work_sync(&pm->wake_work);
+       cancel_work_sync(&dev->reset_work);
+
+       mt7925_tx_token_put(dev);
+       __mt792x_mcu_drv_pmctrl(dev);
+       mt792x_dma_cleanup(dev);
+       mt792x_wfsys_reset(dev);
+       skb_queue_purge(&dev->mt76.mcu.res_q);
+
+       tasklet_disable(&dev->mt76.irq_tasklet);
+}
+
+static void mt7925_reg_remap_restore(struct mt792x_dev *dev)
+{
+       /* remap to ori status */
+       if (unlikely(dev->backup_l1)) {
+               dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L1, dev->backup_l1);
+               dev->backup_l1 = 0;
+       }
+
+       if (dev->backup_l2) {
+               dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, dev->backup_l2);
+               dev->backup_l2 = 0;
+       }
+}
+
+static u32 mt7925_reg_map_l1(struct mt792x_dev *dev, u32 addr)
+{
+       u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr);
+       u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr);
+
+       dev->backup_l1 = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
+
+       dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1,
+                         MT_HIF_REMAP_L1_MASK,
+                         FIELD_PREP(MT_HIF_REMAP_L1_MASK, base));
+
+       /* use read to push write */
+       dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
+
+       return MT_HIF_REMAP_BASE_L1 + offset;
+}
+
+static u32 mt7925_reg_map_l2(struct mt792x_dev *dev, u32 addr)
+{
+       u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, MT_HIF_REMAP_BASE_L2);
+
+       dev->backup_l2 = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
+
+       dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1,
+                         MT_HIF_REMAP_L1_MASK,
+                         FIELD_PREP(MT_HIF_REMAP_L1_MASK, base));
+
+       dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, addr);
+       /* use read to push write */
+       dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
+
+       return MT_HIF_REMAP_BASE_L1;
+}
+
+static u32 __mt7925_reg_addr(struct mt792x_dev *dev, u32 addr)
+{
+       static const struct mt76_connac_reg_map fixed_map[] = {
+               { 0x830c0000, 0x000000, 0x0001000 }, /* WF_MCU_BUS_CR_REMAP */
+               { 0x54000000, 0x002000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA0 */
+               { 0x55000000, 0x003000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA1 */
+               { 0x56000000, 0x004000, 0x0001000 }, /* WFDMA reserved */
+               { 0x57000000, 0x005000, 0x0001000 }, /* WFDMA MCU wrap CR */
+               { 0x58000000, 0x006000, 0x0001000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */
+               { 0x59000000, 0x007000, 0x0001000 }, /* WFDMA PCIE1 MCU DMA1 */
+               { 0x820c0000, 0x008000, 0x0004000 }, /* WF_UMAC_TOP (PLE) */
+               { 0x820c8000, 0x00c000, 0x0002000 }, /* WF_UMAC_TOP (PSE) */
+               { 0x820cc000, 0x00e000, 0x0002000 }, /* WF_UMAC_TOP (PP) */
+               { 0x74030000, 0x010000, 0x0001000 }, /* PCIe MAC */
+               { 0x820e0000, 0x020000, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */
+               { 0x820e1000, 0x020400, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */
+               { 0x820e2000, 0x020800, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */
+               { 0x820e3000, 0x020c00, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */
+               { 0x820e4000, 0x021000, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */
+               { 0x820e5000, 0x021400, 0x0000800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */
+               { 0x820ce000, 0x021c00, 0x0000200 }, /* WF_LMAC_TOP (WF_SEC) */
+               { 0x820e7000, 0x021e00, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */
+               { 0x820cf000, 0x022000, 0x0001000 }, /* WF_LMAC_TOP (WF_PF) */
+               { 0x820e9000, 0x023400, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */
+               { 0x820ea000, 0x024000, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */
+               { 0x820eb000, 0x024200, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */
+               { 0x820ec000, 0x024600, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_INT) */
+               { 0x820ed000, 0x024800, 0x0000800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */
+               { 0x820ca000, 0x026000, 0x0002000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */
+               { 0x820d0000, 0x030000, 0x0010000 }, /* WF_LMAC_TOP (WF_WTBLON) */
+               { 0x40000000, 0x070000, 0x0010000 }, /* WF_UMAC_SYSRAM */
+               { 0x00400000, 0x080000, 0x0010000 }, /* WF_MCU_SYSRAM */
+               { 0x00410000, 0x090000, 0x0010000 }, /* WF_MCU_SYSRAM (configure register) */
+               { 0x820f0000, 0x0a0000, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */
+               { 0x820f1000, 0x0a0600, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */
+               { 0x820f2000, 0x0a0800, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */
+               { 0x820f3000, 0x0a0c00, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */
+               { 0x820f4000, 0x0a1000, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */
+               { 0x820f5000, 0x0a1400, 0x0000800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */
+               { 0x820f7000, 0x0a1e00, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */
+               { 0x820f9000, 0x0a3400, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */
+               { 0x820fa000, 0x0a4000, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */
+               { 0x820fb000, 0x0a4200, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */
+               { 0x820fc000, 0x0a4600, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_INT) */
+               { 0x820fd000, 0x0a4800, 0x0000800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */
+               { 0x820c4000, 0x0a8000, 0x0004000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */
+               { 0x820b0000, 0x0ae000, 0x0001000 }, /* [APB2] WFSYS_ON */
+               { 0x80020000, 0x0b0000, 0x0010000 }, /* WF_TOP_MISC_OFF */
+               { 0x81020000, 0x0c0000, 0x0010000 }, /* WF_TOP_MISC_ON */
+               { 0x7c020000, 0x0d0000, 0x0010000 }, /* CONN_INFRA, wfdma */
+               { 0x7c060000, 0x0e0000, 0x0010000 }, /* CONN_INFRA, conn_host_csr_top */
+               { 0x7c000000, 0x0f0000, 0x0010000 }, /* CONN_INFRA */
+               { 0x70020000, 0x1f0000, 0x0010000 }, /* Reserved for CBTOP, can't switch */
+               { 0x7c500000, 0x060000, 0x2000000 }, /* remap */
+               { 0x0, 0x0, 0x0 } /* End */
+       };
+       int i;
+
+       if (addr < 0x200000)
+               return addr;
+
+       mt7925_reg_remap_restore(dev);
+
+       for (i = 0; i < ARRAY_SIZE(fixed_map); i++) {
+               u32 ofs;
+
+               if (addr < fixed_map[i].phys)
+                       continue;
+
+               ofs = addr - fixed_map[i].phys;
+               if (ofs > fixed_map[i].size)
+                       continue;
+
+               return fixed_map[i].maps + ofs;
+       }
+
+       if ((addr >= 0x18000000 && addr < 0x18c00000) ||
+           (addr >= 0x70000000 && addr < 0x78000000) ||
+           (addr >= 0x7c000000 && addr < 0x7c400000))
+               return mt7925_reg_map_l1(dev, addr);
+
+       return mt7925_reg_map_l2(dev, addr);
+}
+
+static u32 mt7925_rr(struct mt76_dev *mdev, u32 offset)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       u32 addr = __mt7925_reg_addr(dev, offset);
+
+       return dev->bus_ops->rr(mdev, addr);
+}
+
+static void mt7925_wr(struct mt76_dev *mdev, u32 offset, u32 val)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       u32 addr = __mt7925_reg_addr(dev, offset);
+
+       dev->bus_ops->wr(mdev, addr, val);
+}
+
+static u32 mt7925_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       u32 addr = __mt7925_reg_addr(dev, offset);
+
+       return dev->bus_ops->rmw(mdev, addr, mask, val);
+}
+
+static int mt7925_dma_init(struct mt792x_dev *dev)
+{
+       int ret;
+
+       mt76_dma_attach(&dev->mt76);
+
+       ret = mt792x_dma_disable(dev, true);
+       if (ret)
+               return ret;
+
+       /* init tx queue */
+       ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7925_TXQ_BAND0,
+                                        MT7925_TX_RING_SIZE,
+                                        MT_TX_RING_BASE, 0);
+       if (ret)
+               return ret;
+
+       mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4);
+
+       /* command to WM */
+       ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7925_TXQ_MCU_WM,
+                                 MT7925_TX_MCU_RING_SIZE, MT_TX_RING_BASE);
+       if (ret)
+               return ret;
+
+       /* firmware download */
+       ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, MT7925_TXQ_FWDL,
+                                 MT7925_TX_FWDL_RING_SIZE, MT_TX_RING_BASE);
+       if (ret)
+               return ret;
+
+       /* rx event */
+       ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU],
+                              MT7925_RXQ_MCU_WM, MT7925_RX_MCU_RING_SIZE,
+                              MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE);
+       if (ret)
+               return ret;
+
+       /* rx data */
+       ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+                              MT7925_RXQ_BAND0, MT7925_RX_RING_SIZE,
+                              MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE);
+       if (ret)
+               return ret;
+
+       ret = mt76_init_queues(dev, mt792x_poll_rx);
+       if (ret < 0)
+               return ret;
+
+       netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+                         mt792x_poll_tx);
+       napi_enable(&dev->mt76.tx_napi);
+
+       return mt792x_dma_enable(dev);
+}
+
+static int mt7925_pci_probe(struct pci_dev *pdev,
+                           const struct pci_device_id *id)
+{
+       static const struct mt76_driver_ops drv_ops = {
+               /* txwi_size = txd size + txp size */
+               .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_hw_txp),
+               .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ |
+                            MT_DRV_AMSDU_OFFLOAD,
+               .survey_flags = SURVEY_INFO_TIME_TX |
+                               SURVEY_INFO_TIME_RX |
+                               SURVEY_INFO_TIME_BSS_RX,
+               .token_size = MT7925_TOKEN_SIZE,
+               .tx_prepare_skb = mt7925e_tx_prepare_skb,
+               .tx_complete_skb = mt76_connac_tx_complete_skb,
+               .rx_check = mt7925_rx_check,
+               .rx_skb = mt7925_queue_rx_skb,
+               .rx_poll_complete = mt792x_rx_poll_complete,
+               .sta_add = mt7925_mac_sta_add,
+               .sta_assoc = mt7925_mac_sta_assoc,
+               .sta_remove = mt7925_mac_sta_remove,
+               .update_survey = mt792x_update_channel,
+       };
+       static const struct mt792x_hif_ops mt7925_pcie_ops = {
+               .init_reset = mt7925e_init_reset,
+               .reset = mt7925e_mac_reset,
+               .mcu_init = mt7925e_mcu_init,
+               .drv_own = mt792xe_mcu_drv_pmctrl,
+               .fw_own = mt792xe_mcu_fw_pmctrl,
+       };
+       static const struct mt792x_irq_map irq_map = {
+               .host_irq_enable = MT_WFDMA0_HOST_INT_ENA,
+               .tx = {
+                       .all_complete_mask = MT_INT_TX_DONE_ALL,
+                       .mcu_complete_mask = MT_INT_TX_DONE_MCU,
+               },
+               .rx = {
+                       .data_complete_mask = HOST_RX_DONE_INT_ENA2,
+                       .wm_complete_mask = HOST_RX_DONE_INT_ENA0,
+               },
+       };
+       struct ieee80211_ops *ops;
+       struct mt76_bus_ops *bus_ops;
+       struct mt792x_dev *dev;
+       struct mt76_dev *mdev;
+       u8 features;
+       int ret;
+       u16 cmd;
+
+       ret = pcim_enable_device(pdev);
+       if (ret)
+               return ret;
+
+       ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
+       if (ret)
+               return ret;
+
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       if (!(cmd & PCI_COMMAND_MEMORY)) {
+               cmd |= PCI_COMMAND_MEMORY;
+               pci_write_config_word(pdev, PCI_COMMAND, cmd);
+       }
+       pci_set_master(pdev);
+
+       ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+       if (ret < 0)
+               return ret;
+
+       ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+       if (ret)
+               goto err_free_pci_vec;
+
+       if (mt7925_disable_aspm)
+               mt76_pci_disable_aspm(pdev);
+
+       ops = mt792x_get_mac80211_ops(&pdev->dev, &mt7925_ops,
+                                     (void *)id->driver_data, &features);
+       if (!ops) {
+               ret = -ENOMEM;
+               goto err_free_pci_vec;
+       }
+
+       mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops);
+       if (!mdev) {
+               ret = -ENOMEM;
+               goto err_free_pci_vec;
+       }
+
+       pci_set_drvdata(pdev, mdev);
+
+       dev = container_of(mdev, struct mt792x_dev, mt76);
+       dev->fw_features = features;
+       dev->hif_ops = &mt7925_pcie_ops;
+       dev->irq_map = &irq_map;
+       mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]);
+       tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev);
+
+       dev->phy.dev = dev;
+       dev->phy.mt76 = &dev->mt76.phy;
+       dev->mt76.phy.priv = &dev->phy;
+       dev->bus_ops = dev->mt76.bus;
+       bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops),
+                              GFP_KERNEL);
+       if (!bus_ops) {
+               ret = -ENOMEM;
+               goto err_free_dev;
+       }
+
+       bus_ops->rr = mt7925_rr;
+       bus_ops->wr = mt7925_wr;
+       bus_ops->rmw = mt7925_rmw;
+       dev->mt76.bus = bus_ops;
+
+       ret = __mt792x_mcu_fw_pmctrl(dev);
+       if (ret)
+               goto err_free_dev;
+
+       ret = __mt792xe_mcu_drv_pmctrl(dev);
+       if (ret)
+               goto err_free_dev;
+
+       mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |
+                   (mt76_rr(dev, MT_HW_REV) & 0xff);
+
+       dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
+
+       ret = mt792x_wfsys_reset(dev);
+       if (ret)
+               goto err_free_dev;
+
+       mt76_wr(dev, irq_map.host_irq_enable, 0);
+
+       mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
+
+       ret = devm_request_irq(mdev->dev, pdev->irq, mt792x_irq_handler,
+                              IRQF_SHARED, KBUILD_MODNAME, dev);
+       if (ret)
+               goto err_free_dev;
+
+       ret = mt7925_dma_init(dev);
+       if (ret)
+               goto err_free_irq;
+
+       ret = mt7925_register_device(dev);
+       if (ret)
+               goto err_free_irq;
+
+       return 0;
+
+err_free_irq:
+       devm_free_irq(&pdev->dev, pdev->irq, dev);
+err_free_dev:
+       mt76_free_device(&dev->mt76);
+err_free_pci_vec:
+       pci_free_irq_vectors(pdev);
+
+       return ret;
+}
+
+static void mt7925_pci_remove(struct pci_dev *pdev)
+{
+       struct mt76_dev *mdev = pci_get_drvdata(pdev);
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+
+       mt7925e_unregister_device(dev);
+       devm_free_irq(&pdev->dev, pdev->irq, dev);
+       mt76_free_device(&dev->mt76);
+       pci_free_irq_vectors(pdev);
+}
+
+static int mt7925_pci_suspend(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct mt76_dev *mdev = pci_get_drvdata(pdev);
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       struct mt76_connac_pm *pm = &dev->pm;
+       int i, err;
+
+       pm->suspended = true;
+       flush_work(&dev->reset_work);
+       cancel_delayed_work_sync(&pm->ps_work);
+       cancel_work_sync(&pm->wake_work);
+
+       err = mt792x_mcu_drv_pmctrl(dev);
+       if (err < 0)
+               goto restore_suspend;
+
+       /* always enable deep sleep during suspend to reduce
+        * power consumption
+        */
+       mt7925_mcu_set_deep_sleep(dev, true);
+
+       err = mt76_connac_mcu_set_hif_suspend(mdev, true);
+       if (err)
+               goto restore_suspend;
+
+       napi_disable(&mdev->tx_napi);
+       mt76_worker_disable(&mdev->tx_worker);
+
+       mt76_for_each_q_rx(mdev, i) {
+               napi_disable(&mdev->napi[i]);
+       }
+
+       /* wait until dma is idle  */
+       mt76_poll(dev, MT_WFDMA0_GLO_CFG,
+                 MT_WFDMA0_GLO_CFG_TX_DMA_BUSY |
+                 MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000);
+
+       /* put dma disabled */
+       mt76_clear(dev, MT_WFDMA0_GLO_CFG,
+                  MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+
+       /* disable interrupt */
+       mt76_wr(dev, dev->irq_map->host_irq_enable, 0);
+       mt76_wr(dev, MT_WFDMA0_HOST_INT_DIS,
+               dev->irq_map->tx.all_complete_mask |
+               MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD);
+
+       mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
+
+       synchronize_irq(pdev->irq);
+       tasklet_kill(&mdev->irq_tasklet);
+
+       err = mt792x_mcu_fw_pmctrl(dev);
+       if (err)
+               goto restore_napi;
+
+       return 0;
+
+restore_napi:
+       mt76_for_each_q_rx(mdev, i) {
+               napi_enable(&mdev->napi[i]);
+       }
+       napi_enable(&mdev->tx_napi);
+
+       if (!pm->ds_enable)
+               mt7925_mcu_set_deep_sleep(dev, false);
+
+       mt76_connac_mcu_set_hif_suspend(mdev, false);
+
+restore_suspend:
+       pm->suspended = false;
+
+       if (err < 0)
+               mt792x_reset(&dev->mt76);
+
+       return err;
+}
+
+static int mt7925_pci_resume(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct mt76_dev *mdev = pci_get_drvdata(pdev);
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       struct mt76_connac_pm *pm = &dev->pm;
+       int i, err;
+
+       err = mt792x_mcu_drv_pmctrl(dev);
+       if (err < 0)
+               goto failed;
+
+       mt792x_wpdma_reinit_cond(dev);
+
+       /* enable interrupt */
+       mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
+       mt76_connac_irq_enable(&dev->mt76,
+                              dev->irq_map->tx.all_complete_mask |
+                              MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD);
+       mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE);
+
+       /* put dma enabled */
+       mt76_set(dev, MT_WFDMA0_GLO_CFG,
+                MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+
+       mt76_worker_enable(&mdev->tx_worker);
+
+       local_bh_disable();
+       mt76_for_each_q_rx(mdev, i) {
+               napi_enable(&mdev->napi[i]);
+               napi_schedule(&mdev->napi[i]);
+       }
+       napi_enable(&mdev->tx_napi);
+       napi_schedule(&mdev->tx_napi);
+       local_bh_enable();
+
+       err = mt76_connac_mcu_set_hif_suspend(mdev, false);
+
+       /* restore previous ds setting */
+       if (!pm->ds_enable)
+               mt7925_mcu_set_deep_sleep(dev, false);
+
+failed:
+       pm->suspended = false;
+
+       if (err < 0)
+               mt792x_reset(&dev->mt76);
+
+       return err;
+}
+
+static void mt7925_pci_shutdown(struct pci_dev *pdev)
+{
+       mt7925_pci_remove(pdev);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mt7925_pm_ops, mt7925_pci_suspend, mt7925_pci_resume);
+
+static struct pci_driver mt7925_pci_driver = {
+       .name           = KBUILD_MODNAME,
+       .id_table       = mt7925_pci_device_table,
+       .probe          = mt7925_pci_probe,
+       .remove         = mt7925_pci_remove,
+       .shutdown       = mt7925_pci_shutdown,
+       .driver.pm      = pm_sleep_ptr(&mt7925_pm_ops),
+};
+
+module_pci_driver(mt7925_pci_driver);
+
+MODULE_DEVICE_TABLE(pci, mt7925_pci_device_table);
+MODULE_FIRMWARE(MT7925_FIRMWARE_WM);
+MODULE_FIRMWARE(MT7925_ROM_PATCH);
+MODULE_AUTHOR("Deren Wu <deren.wu@mediatek.com>");
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
new file mode 100644 (file)
index 0000000..9fca887
--- /dev/null
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include "mt7925.h"
+#include "../dma.h"
+#include "mac.h"
+
+int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+                          enum mt76_txq_id qid, struct mt76_wcid *wcid,
+                          struct ieee80211_sta *sta,
+                          struct mt76_tx_info *tx_info)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
+       struct ieee80211_key_conf *key = info->control.hw_key;
+       struct mt76_connac_hw_txp *txp;
+       struct mt76_txwi_cache *t;
+       int id, pid;
+       u8 *txwi = (u8 *)txwi_ptr;
+
+       if (unlikely(tx_info->skb->len <= ETH_HLEN))
+               return -EINVAL;
+
+       if (!wcid)
+               wcid = &dev->mt76.global_wcid;
+
+       t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
+       t->skb = tx_info->skb;
+
+       id = mt76_token_consume(mdev, &t);
+       if (id < 0)
+               return id;
+
+       if (sta) {
+               struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+
+               if (time_after(jiffies, msta->last_txs + HZ / 4)) {
+                       info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+                       msta->last_txs = jiffies;
+               }
+       }
+
+       pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
+       mt7925_mac_write_txwi(mdev, txwi_ptr, tx_info->skb, wcid, key,
+                             pid, qid, 0);
+
+       txp = (struct mt76_connac_hw_txp *)(txwi + MT_TXD_SIZE);
+       memset(txp, 0, sizeof(struct mt76_connac_hw_txp));
+       mt76_connac_write_hw_txp(mdev, tx_info, txp, id);
+
+       tx_info->skb = NULL;
+
+       return 0;
+}
+
+void mt7925_tx_token_put(struct mt792x_dev *dev)
+{
+       struct mt76_txwi_cache *txwi;
+       int id;
+
+       spin_lock_bh(&dev->mt76.token_lock);
+       idr_for_each_entry(&dev->mt76.token, txwi, id) {
+               mt7925_txwi_free(dev, txwi, NULL, false, NULL);
+               dev->mt76.token_count--;
+       }
+       spin_unlock_bh(&dev->mt76.token_lock);
+       idr_destroy(&dev->mt76.token);
+}
+
+int mt7925e_mac_reset(struct mt792x_dev *dev)
+{
+       const struct mt792x_irq_map *irq_map = dev->irq_map;
+       int i, err;
+
+       mt792xe_mcu_drv_pmctrl(dev);
+
+       mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
+
+       mt76_wr(dev, dev->irq_map->host_irq_enable, 0);
+       mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
+
+       set_bit(MT76_RESET, &dev->mphy.state);
+       set_bit(MT76_MCU_RESET, &dev->mphy.state);
+       wake_up(&dev->mt76.mcu.wait);
+       skb_queue_purge(&dev->mt76.mcu.res_q);
+
+       mt76_txq_schedule_all(&dev->mphy);
+
+       mt76_worker_disable(&dev->mt76.tx_worker);
+       if (irq_map->rx.data_complete_mask)
+               napi_disable(&dev->mt76.napi[MT_RXQ_MAIN]);
+       if (irq_map->rx.wm_complete_mask)
+               napi_disable(&dev->mt76.napi[MT_RXQ_MCU]);
+       if (irq_map->rx.wm2_complete_mask)
+               napi_disable(&dev->mt76.napi[MT_RXQ_MCU_WA]);
+       if (irq_map->tx.all_complete_mask)
+               napi_disable(&dev->mt76.tx_napi);
+
+       mt7925_tx_token_put(dev);
+       idr_init(&dev->mt76.token);
+
+       mt792x_wpdma_reset(dev, true);
+
+       local_bh_disable();
+       mt76_for_each_q_rx(&dev->mt76, i) {
+               napi_enable(&dev->mt76.napi[i]);
+               napi_schedule(&dev->mt76.napi[i]);
+       }
+       napi_enable(&dev->mt76.tx_napi);
+       napi_schedule(&dev->mt76.tx_napi);
+       local_bh_enable();
+
+       dev->fw_assert = false;
+       clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+
+       mt76_wr(dev, dev->irq_map->host_irq_enable,
+               dev->irq_map->tx.all_complete_mask |
+               MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD);
+       mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
+
+       err = mt792xe_mcu_fw_pmctrl(dev);
+       if (err)
+               return err;
+
+       err = __mt792xe_mcu_drv_pmctrl(dev);
+       if (err)
+               goto out;
+
+       err = mt7925_run_firmware(dev);
+       if (err)
+               goto out;
+
+       err = mt7925_mcu_set_eeprom(dev);
+       if (err)
+               goto out;
+
+       err = mt7925_mac_init(dev);
+       if (err)
+               goto out;
+
+       err = __mt7925_start(&dev->phy);
+out:
+       clear_bit(MT76_RESET, &dev->mphy.state);
+
+       mt76_worker_enable(&dev->mt76.tx_worker);
+
+       return err;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
new file mode 100644 (file)
index 0000000..f95bc5d
--- /dev/null
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include "mt7925.h"
+#include "mcu.h"
+
+static int
+mt7925_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+                       int cmd, int *seq)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       enum mt76_mcuq_id txq = MT_MCUQ_WM;
+       int ret;
+
+       ret = mt7925_mcu_fill_message(mdev, skb, cmd, seq);
+       if (ret)
+               return ret;
+
+       mdev->mcu.timeout = 3 * HZ;
+
+       if (cmd == MCU_CMD(FW_SCATTER))
+               txq = MT_MCUQ_FWDL;
+
+       return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0);
+}
+
+int mt7925e_mcu_init(struct mt792x_dev *dev)
+{
+       static const struct mt76_mcu_ops mt7925_mcu_ops = {
+               .headroom = sizeof(struct mt76_connac2_mcu_txd),
+               .mcu_skb_send_msg = mt7925_mcu_send_message,
+               .mcu_parse_response = mt7925_mcu_parse_response,
+       };
+       int err;
+
+       dev->mt76.mcu_ops = &mt7925_mcu_ops;
+
+       err = mt792xe_mcu_fw_pmctrl(dev);
+       if (err)
+               return err;
+
+       err = __mt792xe_mcu_drv_pmctrl(dev);
+       if (err)
+               return err;
+
+       mt76_rmw_field(dev, MT_PCIE_MAC_PM, MT_PCIE_MAC_PM_L0S_DIS, 1);
+
+       err = mt7925_run_firmware(dev);
+
+       mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false);
+
+       return err;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
new file mode 100644 (file)
index 0000000..985794a
--- /dev/null
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: ISC */
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#ifndef __MT7925_REGS_H
+#define __MT7925_REGS_H
+
+#include "../mt792x_regs.h"
+
+#define MT_MDP_BASE                    0x820cc800
+#define MT_MDP(ofs)                    (MT_MDP_BASE + (ofs))
+
+#define MT_MDP_DCR0                    MT_MDP(0x000)
+#define MT_MDP_DCR0_DAMSDU_EN          BIT(15)
+#define MT_MDP_DCR0_RX_HDR_TRANS_EN    BIT(19)
+
+#define MT_MDP_DCR1                    MT_MDP(0x004)
+#define MT_MDP_DCR1_MAX_RX_LEN         GENMASK(15, 3)
+
+#define MT_MDP_BNRCFR0(_band)          MT_MDP(0x090 + ((_band) << 8))
+#define MT_MDP_RCFR0_MCU_RX_MGMT       GENMASK(5, 4)
+#define MT_MDP_RCFR0_MCU_RX_CTL_NON_BAR        GENMASK(7, 6)
+#define MT_MDP_RCFR0_MCU_RX_CTL_BAR    GENMASK(9, 8)
+
+#define MT_MDP_BNRCFR1(_band)          MT_MDP(0x094 + ((_band) << 8))
+#define MT_MDP_RCFR1_MCU_RX_BYPASS     GENMASK(23, 22)
+#define MT_MDP_RCFR1_RX_DROPPED_UCAST  GENMASK(28, 27)
+#define MT_MDP_RCFR1_RX_DROPPED_MCAST  GENMASK(30, 29)
+#define MT_MDP_TO_HIF                  0
+#define MT_MDP_TO_WM                   1
+
+#define MT_WFDMA0_HOST_INT_ENA         MT_WFDMA0(0x228)
+#define MT_WFDMA0_HOST_INT_DIS         MT_WFDMA0(0x22c)
+#define HOST_RX_DONE_INT_ENA4          BIT(12)
+#define HOST_RX_DONE_INT_ENA5          BIT(13)
+#define HOST_RX_DONE_INT_ENA6          BIT(14)
+#define HOST_RX_DONE_INT_ENA7          BIT(15)
+#define HOST_RX_DONE_INT_ENA8          BIT(16)
+#define HOST_RX_DONE_INT_ENA9          BIT(17)
+#define HOST_RX_DONE_INT_ENA10         BIT(18)
+#define HOST_RX_DONE_INT_ENA11         BIT(19)
+#define HOST_TX_DONE_INT_ENA15         BIT(25)
+#define HOST_TX_DONE_INT_ENA16         BIT(26)
+#define HOST_TX_DONE_INT_ENA17         BIT(27)
+
+/* WFDMA interrupt */
+#define MT_INT_RX_DONE_DATA            HOST_RX_DONE_INT_ENA2
+#define MT_INT_RX_DONE_WM              HOST_RX_DONE_INT_ENA0
+#define MT_INT_RX_DONE_WM2             HOST_RX_DONE_INT_ENA1
+#define MT_INT_RX_DONE_ALL             (MT_INT_RX_DONE_DATA | \
+                                        MT_INT_RX_DONE_WM | \
+                                        MT_INT_RX_DONE_WM2)
+
+#define MT_INT_TX_DONE_MCU_WM          (HOST_TX_DONE_INT_ENA15 | \
+                                        HOST_TX_DONE_INT_ENA17)
+
+#define MT_INT_TX_DONE_FWDL            HOST_TX_DONE_INT_ENA16
+#define MT_INT_TX_DONE_BAND0           HOST_TX_DONE_INT_ENA0
+
+#define MT_INT_TX_DONE_MCU             (MT_INT_TX_DONE_MCU_WM |        \
+                                        MT_INT_TX_DONE_FWDL)
+#define MT_INT_TX_DONE_ALL             (MT_INT_TX_DONE_MCU_WM |        \
+                                        MT_INT_TX_DONE_BAND0 | \
+                                       GENMASK(18, 4))
+
+#define MT_RX_DATA_RING_BASE           MT_WFDMA0(0x500)
+
+#define MT_INFRA_CFG_BASE              0xd1000
+#define MT_INFRA(ofs)                  (MT_INFRA_CFG_BASE + (ofs))
+
+#define MT_HIF_REMAP_L1                        0x155024
+#define MT_HIF_REMAP_L1_MASK           GENMASK(31, 16)
+#define MT_HIF_REMAP_L1_OFFSET         GENMASK(15, 0)
+#define MT_HIF_REMAP_L1_BASE           GENMASK(31, 16)
+#define MT_HIF_REMAP_BASE_L1           0x130000
+
+#define MT_HIF_REMAP_L2                        0x0120
+#if IS_ENABLED(CONFIG_MT76_DEV)
+#define MT_HIF_REMAP_BASE_L2           (0x7c500000 - (0x7c000000 - 0x18000000))
+#else
+#define MT_HIF_REMAP_BASE_L2           0x18500000
+#endif
+
+#define MT_WFSYS_SW_RST_B              0x7c000140
+
+#define MT_WTBLON_TOP_WDUCR            MT_WTBLON_TOP(0x370)
+#define MT_WTBLON_TOP_WDUCR_GROUP      GENMASK(4, 0)
+
+#define MT_WTBL_UPDATE                 MT_WTBLON_TOP(0x380)
+#define MT_WTBL_UPDATE_WLAN_IDX                GENMASK(11, 0)
+#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14)
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
new file mode 100644 (file)
index 0000000..9b885c5
--- /dev/null
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "mt7925.h"
+#include "mcu.h"
+#include "mac.h"
+
+static const struct usb_device_id mt7925u_device_table[] = {
+       { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7925, 0xff, 0xff, 0xff),
+               .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM },
+       { },
+};
+
+static int
+mt7925u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+                        int cmd, int *seq)
+{
+       struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+       u32 pad, ep;
+       int ret;
+
+       ret = mt7925_mcu_fill_message(mdev, skb, cmd, seq);
+       if (ret)
+               return ret;
+
+       mdev->mcu.timeout = 3 * HZ;
+
+       if (cmd != MCU_CMD(FW_SCATTER))
+               ep = MT_EP_OUT_INBAND_CMD;
+       else
+               ep = MT_EP_OUT_AC_BE;
+
+       mt792x_skb_add_usb_sdio_hdr(dev, skb, 0);
+       pad = round_up(skb->len, 4) + 4 - skb->len;
+       __skb_put_zero(skb, pad);
+
+       ret = mt76u_bulk_msg(&dev->mt76, skb->data, skb->len, NULL,
+                            1000, ep);
+       dev_kfree_skb(skb);
+
+       return ret;
+}
+
+static int mt7925u_mcu_init(struct mt792x_dev *dev)
+{
+       static const struct mt76_mcu_ops mcu_ops = {
+               .headroom = MT_SDIO_HDR_SIZE +
+                           sizeof(struct mt76_connac2_mcu_txd),
+               .tailroom = MT_USB_TAIL_SIZE,
+               .mcu_skb_send_msg = mt7925u_mcu_send_message,
+               .mcu_parse_response = mt7925_mcu_parse_response,
+       };
+       int ret;
+
+       dev->mt76.mcu_ops = &mcu_ops;
+
+       mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
+       ret = mt7925_run_firmware(dev);
+       if (ret)
+               return ret;
+
+       set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+       mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
+
+       return 0;
+}
+
+static int mt7925u_mac_reset(struct mt792x_dev *dev)
+{
+       int err;
+
+       mt76_txq_schedule_all(&dev->mphy);
+       mt76_worker_disable(&dev->mt76.tx_worker);
+
+       set_bit(MT76_RESET, &dev->mphy.state);
+       set_bit(MT76_MCU_RESET, &dev->mphy.state);
+
+       wake_up(&dev->mt76.mcu.wait);
+       skb_queue_purge(&dev->mt76.mcu.res_q);
+
+       mt76u_stop_rx(&dev->mt76);
+       mt76u_stop_tx(&dev->mt76);
+
+       mt792xu_wfsys_reset(dev);
+
+       clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+       err = mt76u_resume_rx(&dev->mt76);
+       if (err)
+               goto out;
+
+       err = mt792xu_mcu_power_on(dev);
+       if (err)
+               goto out;
+
+       err = mt792xu_dma_init(dev, false);
+       if (err)
+               goto out;
+
+       mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
+       mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
+
+       err = mt7925_run_firmware(dev);
+       if (err)
+               goto out;
+
+       mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN);
+
+       err = mt7925_mcu_set_eeprom(dev);
+       if (err)
+               goto out;
+
+       err = mt7925_mac_init(dev);
+       if (err)
+               goto out;
+
+       err = __mt7925_start(&dev->phy);
+out:
+       clear_bit(MT76_RESET, &dev->mphy.state);
+
+       mt76_worker_enable(&dev->mt76.tx_worker);
+
+       return err;
+}
+
+static int mt7925u_probe(struct usb_interface *usb_intf,
+                        const struct usb_device_id *id)
+{
+       static const struct mt76_driver_ops drv_ops = {
+               .txwi_size = MT_SDIO_TXD_SIZE,
+               .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ |
+                            MT_DRV_AMSDU_OFFLOAD,
+               .survey_flags = SURVEY_INFO_TIME_TX |
+                               SURVEY_INFO_TIME_RX |
+                               SURVEY_INFO_TIME_BSS_RX,
+               .tx_prepare_skb = mt7925_usb_sdio_tx_prepare_skb,
+               .tx_complete_skb = mt7925_usb_sdio_tx_complete_skb,
+               .tx_status_data = mt7925_usb_sdio_tx_status_data,
+               .rx_skb = mt7925_queue_rx_skb,
+               .rx_check = mt7925_rx_check,
+               .sta_add = mt7925_mac_sta_add,
+               .sta_assoc = mt7925_mac_sta_assoc,
+               .sta_remove = mt7925_mac_sta_remove,
+               .update_survey = mt792x_update_channel,
+       };
+       static const struct mt792x_hif_ops hif_ops = {
+               .mcu_init = mt7925u_mcu_init,
+               .init_reset = mt792xu_init_reset,
+               .reset = mt7925u_mac_reset,
+       };
+       static struct mt76_bus_ops bus_ops = {
+               .rr = mt792xu_rr,
+               .wr = mt792xu_wr,
+               .rmw = mt792xu_rmw,
+               .read_copy = mt76u_read_copy,
+               .write_copy = mt792xu_copy,
+               .type = MT76_BUS_USB,
+       };
+       struct usb_device *udev = interface_to_usbdev(usb_intf);
+       struct ieee80211_ops *ops;
+       struct ieee80211_hw *hw;
+       struct mt792x_dev *dev;
+       struct mt76_dev *mdev;
+       u8 features;
+       int ret;
+
+       ops = mt792x_get_mac80211_ops(&usb_intf->dev, &mt7925_ops,
+                                     (void *)id->driver_info, &features);
+       if (!ops)
+               return -ENOMEM;
+
+       ops->stop = mt792xu_stop;
+
+       mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops);
+       if (!mdev)
+               return -ENOMEM;
+
+       dev = container_of(mdev, struct mt792x_dev, mt76);
+       dev->fw_features = features;
+       dev->hif_ops = &hif_ops;
+
+       udev = usb_get_dev(udev);
+       usb_reset_device(udev);
+
+       usb_set_intfdata(usb_intf, dev);
+
+       ret = __mt76u_init(mdev, usb_intf, &bus_ops);
+       if (ret < 0)
+               goto error;
+
+       mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |
+                   (mt76_rr(dev, MT_HW_REV) & 0xff);
+       dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
+
+       if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) {
+               ret = mt792xu_wfsys_reset(dev);
+               if (ret)
+                       goto error;
+       }
+
+       ret = mt792xu_mcu_power_on(dev);
+       if (ret)
+               goto error;
+
+       ret = mt76u_alloc_mcu_queue(&dev->mt76);
+       if (ret)
+               goto error;
+
+       ret = mt76u_alloc_queues(&dev->mt76);
+       if (ret)
+               goto error;
+
+       ret = mt792xu_dma_init(dev, false);
+       if (ret)
+               goto error;
+
+       hw = mt76_hw(dev);
+       /* check hw sg support in order to enable AMSDU */
+       hw->max_tx_fragments = mdev->usb.sg_en ? MT_HW_TXP_MAX_BUF_NUM : 1;
+
+       ret = mt7925_register_device(dev);
+       if (ret)
+               goto error;
+
+       return 0;
+
+error:
+       mt76u_queues_deinit(&dev->mt76);
+
+       usb_set_intfdata(usb_intf, NULL);
+       usb_put_dev(interface_to_usbdev(usb_intf));
+
+       mt76_free_device(&dev->mt76);
+
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static int mt7925u_suspend(struct usb_interface *intf, pm_message_t state)
+{
+       struct mt792x_dev *dev = usb_get_intfdata(intf);
+       struct mt76_connac_pm *pm = &dev->pm;
+       int err;
+
+       pm->suspended = true;
+       flush_work(&dev->reset_work);
+
+       err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, true);
+       if (err)
+               goto failed;
+
+       mt76u_stop_rx(&dev->mt76);
+       mt76u_stop_tx(&dev->mt76);
+
+       return 0;
+
+failed:
+       pm->suspended = false;
+
+       if (err < 0)
+               mt792x_reset(&dev->mt76);
+
+       return err;
+}
+
+static int mt7925u_resume(struct usb_interface *intf)
+{
+       struct mt792x_dev *dev = usb_get_intfdata(intf);
+       struct mt76_connac_pm *pm = &dev->pm;
+       bool reinit = true;
+       int err, i;
+
+       for (i = 0; i < 10; i++) {
+               u32 val = mt76_rr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT);
+
+               if (!(val & MT_WF_SW_SER_TRIGGER_SUSPEND)) {
+                       reinit = false;
+                       break;
+               }
+               if (val & MT_WF_SW_SER_DONE_SUSPEND) {
+                       mt76_wr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT, 0);
+                       break;
+               }
+
+               msleep(20);
+       }
+
+       if (reinit || mt792x_dma_need_reinit(dev)) {
+               err = mt792xu_dma_init(dev, true);
+               if (err)
+                       goto failed;
+       }
+
+       err = mt76u_resume_rx(&dev->mt76);
+       if (err < 0)
+               goto failed;
+
+       err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, false);
+failed:
+       pm->suspended = false;
+
+       if (err < 0)
+               mt792x_reset(&dev->mt76);
+
+       return err;
+}
+#endif /* CONFIG_PM */
+
+MODULE_DEVICE_TABLE(usb, mt7925u_device_table);
+MODULE_FIRMWARE(MT7925_FIRMWARE_WM);
+MODULE_FIRMWARE(MT7925_ROM_PATCH);
+
+static struct usb_driver mt7925u_driver = {
+       .name           = KBUILD_MODNAME,
+       .id_table       = mt7925u_device_table,
+       .probe          = mt7925u_probe,
+       .disconnect     = mt792xu_disconnect,
+#ifdef CONFIG_PM
+       .suspend        = mt7925u_suspend,
+       .resume         = mt7925u_resume,
+       .reset_resume   = mt7925u_resume,
+#endif /* CONFIG_PM */
+       .soft_unbind    = 1,
+       .disable_hub_initiated_lpm = 1,
+};
+module_usb_driver(mt7925u_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
+MODULE_LICENSE("Dual BSD/GPL");
index 5d5ab86..36fae73 100644 (file)
@@ -25,6 +25,8 @@
 #define MT792x_FW_TAG_FEATURE  4
 #define MT792x_FW_CAP_CNM      BIT(7)
 
+#define MT792x_CHIP_CAP_CLC_EVT_EN BIT(0)
+
 /* NOTE: used to map mt76_rates. idx may change if firmware expands table */
 #define MT792x_BASIC_RATES_TBL 11
 
 
 #define MT7921_FIRMWARE_WM     "mediatek/WIFI_RAM_CODE_MT7961_1.bin"
 #define MT7922_FIRMWARE_WM     "mediatek/WIFI_RAM_CODE_MT7922_1.bin"
+#define MT7925_FIRMWARE_WM     "mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin"
 
 #define MT7921_ROM_PATCH       "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin"
 #define MT7922_ROM_PATCH       "mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin"
+#define MT7925_ROM_PATCH       "mediatek/mt7925/WIFI_MT7925_PATCH_MCU_1_1_hdr.bin"
+
+#define MT792x_SDIO_HDR_TX_BYTES       GENMASK(15, 0)
+#define MT792x_SDIO_HDR_PKT_TYPE       GENMASK(17, 16)
 
 struct mt792x_vif;
 struct mt792x_sta;
@@ -61,6 +68,14 @@ enum {
        MT792x_CLC_MAX_NUM,
 };
 
+enum mt792x_reg_power_type {
+       MT_AP_UNSET = 0,
+       MT_AP_DEFAULT,
+       MT_AP_LPI,
+       MT_AP_SP,
+       MT_AP_VLP,
+};
+
 DECLARE_EWMA(avg_signal, 10, 8)
 
 struct mt792x_sta {
@@ -91,7 +106,6 @@ struct mt792x_vif {
        struct ewma_rssi rssi;
 
        struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
-       struct ieee80211_chanctx_conf *ctx;
 };
 
 struct mt792x_phy {
@@ -113,6 +127,8 @@ struct mt792x_phy {
        struct mt76_mib_stats mib;
 
        u8 sta_work_count;
+       u8 clc_chan_conf;
+       enum mt792x_reg_power_type power_type;
 
        struct sk_buff_head scan_event_list;
        struct delayed_work scan_work;
@@ -120,6 +136,7 @@ struct mt792x_phy {
        void *acpisar;
 #endif
        void *clc[MT792x_CLC_MAX_NUM];
+       u64 chip_cap;
 
        struct work_struct roc_work;
        struct timer_list roc_timer;
@@ -229,6 +246,7 @@ static inline bool mt792x_dma_need_reinit(struct mt792x_dev *dev)
 #define mt792x_mutex_release(dev)      \
        mt76_connac_mutex_release(&(dev)->mt76, &(dev)->pm)
 
+void mt792x_stop(struct ieee80211_hw *hw);
 void mt792x_pm_wake_work(struct work_struct *work);
 void mt792x_pm_power_save_work(struct work_struct *work);
 void mt792x_reset(struct mt76_dev *mdev);
@@ -308,6 +326,8 @@ static inline char *mt792x_ram_name(struct mt792x_dev *dev)
        switch (mt76_chip(&dev->mt76)) {
        case 0x7922:
                return MT7922_FIRMWARE_WM;
+       case 0x7925:
+               return MT7925_FIRMWARE_WM;
        default:
                return MT7921_FIRMWARE_WM;
        }
@@ -318,6 +338,8 @@ static inline char *mt792x_patch_name(struct mt792x_dev *dev)
        switch (mt76_chip(&dev->mt76)) {
        case 0x7922:
                return MT7922_ROM_PATCH;
+       case 0x7925:
+               return MT7925_ROM_PATCH;
        default:
                return MT7921_ROM_PATCH;
        }
@@ -337,6 +359,20 @@ void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val);
 u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val);
 void mt792xu_copy(struct mt76_dev *dev, u32 offset, const void *data, int len);
 void mt792xu_disconnect(struct usb_interface *usb_intf);
+void mt792xu_stop(struct ieee80211_hw *hw);
+
+static inline void
+mt792x_skb_add_usb_sdio_hdr(struct mt792x_dev *dev, struct sk_buff *skb,
+                           int type)
+{
+       u32 hdr, len;
+
+       len = mt76_is_usb(&dev->mt76) ? skb->len : skb->len + sizeof(hdr);
+       hdr = FIELD_PREP(MT792x_SDIO_HDR_TX_BYTES, len) |
+             FIELD_PREP(MT792x_SDIO_HDR_PKT_TYPE, type);
+
+       put_unaligned_le32(hdr, skb_push(skb, sizeof(hdr)));
+}
 
 int __mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev);
 int mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev);
index 46be7f9..502be22 100644 (file)
@@ -91,6 +91,28 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
 }
 EXPORT_SYMBOL_GPL(mt792x_tx);
 
+void mt792x_stop(struct ieee80211_hw *hw)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt792x_phy *phy = mt792x_hw_phy(hw);
+
+       cancel_delayed_work_sync(&phy->mt76->mac_work);
+
+       cancel_delayed_work_sync(&dev->pm.ps_work);
+       cancel_work_sync(&dev->pm.wake_work);
+       cancel_work_sync(&dev->reset_work);
+       mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
+
+       if (is_mt7921(&dev->mt76)) {
+               mt792x_mutex_acquire(dev);
+               mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, false, false);
+               mt792x_mutex_release(dev);
+       }
+
+       clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+}
+EXPORT_SYMBOL_GPL(mt792x_stop);
+
 void mt792x_remove_interface(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif)
 {
@@ -115,7 +137,7 @@ void mt792x_remove_interface(struct ieee80211_hw *hw,
                list_del_init(&msta->wcid.poll_list);
        spin_unlock_bh(&dev->mt76.sta_poll_lock);
 
-       mt76_packet_id_flush(&dev->mt76, &msta->wcid);
+       mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
 }
 EXPORT_SYMBOL_GPL(mt792x_remove_interface);
 
@@ -243,7 +265,7 @@ int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw,
        struct mt792x_dev *dev = mt792x_hw_dev(hw);
 
        mutex_lock(&dev->mt76.mutex);
-       mvif->ctx = ctx;
+       mvif->mt76.ctx = ctx;
        mutex_unlock(&dev->mt76.mutex);
 
        return 0;
@@ -259,7 +281,7 @@ void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw,
        struct mt792x_dev *dev = mt792x_hw_dev(hw);
 
        mutex_lock(&dev->mt76.mutex);
-       mvif->ctx = NULL;
+       mvif->mt76.ctx = NULL;
        mutex_unlock(&dev->mt76.mutex);
 }
 EXPORT_SYMBOL_GPL(mt792x_unassign_vif_chanctx);
@@ -358,7 +380,7 @@ void mt792x_get_et_strings(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        if (sset != ETH_SS_STATS)
                return;
 
-       memcpy(data, *mt792x_gstrings_stats, sizeof(mt792x_gstrings_stats));
+       memcpy(data, mt792x_gstrings_stats, sizeof(mt792x_gstrings_stats));
 
        data += sizeof(mt792x_gstrings_stats);
        page_pool_ethtool_stats_get_strings(data);
index a3dbd38..488326c 100644 (file)
@@ -88,25 +88,44 @@ EXPORT_SYMBOL_GPL(mt792x_rx_poll_complete);
 #define PREFETCH(base, depth)  ((base) << 16 | (depth))
 static void mt792x_dma_prefetch(struct mt792x_dev *dev)
 {
-       mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0, 0x4));
-       mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x40, 0x4));
-       mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x80, 0x4));
-       mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0xc0, 0x4));
-       mt76_wr(dev, MT_WFDMA0_RX_RING5_EXT_CTRL, PREFETCH(0x100, 0x4));
-
-       mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x140, 0x4));
-       mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x180, 0x4));
-       mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x1c0, 0x4));
-       mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x200, 0x4));
-       mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x240, 0x4));
-       mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x280, 0x4));
-       mt76_wr(dev, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x2c0, 0x4));
-       mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x340, 0x4));
-       mt76_wr(dev, MT_WFDMA0_TX_RING17_EXT_CTRL, PREFETCH(0x380, 0x4));
+       if (is_mt7925(&dev->mt76)) {
+               /* rx ring */
+               mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4));
+               mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4));
+               mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4));
+               mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4));
+               /* tx ring */
+               mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x10));
+               mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0200, 0x10));
+               mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0300, 0x10));
+               mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10));
+               mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4));
+       } else {
+               /* rx ring */
+               mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0, 0x4));
+               mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x40, 0x4));
+               mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x80, 0x4));
+               mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0xc0, 0x4));
+               mt76_wr(dev, MT_WFDMA0_RX_RING5_EXT_CTRL, PREFETCH(0x100, 0x4));
+               /* tx ring */
+               mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x140, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x180, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x1c0, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x200, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x240, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x280, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x2c0, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x340, 0x4));
+               mt76_wr(dev, MT_WFDMA0_TX_RING17_EXT_CTRL, PREFETCH(0x380, 0x4));
+       }
 }
 
 int mt792x_dma_enable(struct mt792x_dev *dev)
 {
+       if (is_mt7925(&dev->mt76))
+               mt76_rmw(dev, MT_UWFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28));
+
        /* configure perfetch settings */
        mt792x_dma_prefetch(dev);
 
index 20e7f9c..2dd283c 100644 (file)
@@ -287,6 +287,15 @@ int mt792xu_init_reset(struct mt792x_dev *dev)
 }
 EXPORT_SYMBOL_GPL(mt792xu_init_reset);
 
+void mt792xu_stop(struct ieee80211_hw *hw)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+       mt76u_stop_tx(&dev->mt76);
+       mt792x_stop(hw);
+}
+EXPORT_SYMBOL_GPL(mt792xu_stop);
+
 void mt792xu_disconnect(struct usb_interface *usb_intf)
 {
        struct mt792x_dev *dev = usb_get_intfdata(usb_intf);
index 0d6cc21..55cb177 100644 (file)
@@ -54,23 +54,31 @@ static void mt7996_led_set_config(struct led_classdev *led_cdev,
        dev = container_of(mphy->dev, struct mt7996_dev, mt76);
 
        /* select TX blink mode, 2: only data frames */
-       mt76_rmw_field(dev, MT_TMAC_TCR0(0), MT_TMAC_TCR0_TX_BLINK, 2);
+       mt76_rmw_field(dev, MT_TMAC_TCR0(mphy->band_idx), MT_TMAC_TCR0_TX_BLINK, 2);
 
        /* enable LED */
-       mt76_wr(dev, MT_LED_EN(0), 1);
+       mt76_wr(dev, MT_LED_EN(mphy->band_idx), 1);
 
        /* set LED Tx blink on/off time */
        val = FIELD_PREP(MT_LED_TX_BLINK_ON_MASK, delay_on) |
              FIELD_PREP(MT_LED_TX_BLINK_OFF_MASK, delay_off);
-       mt76_wr(dev, MT_LED_TX_BLINK(0), val);
+       mt76_wr(dev, MT_LED_TX_BLINK(mphy->band_idx), val);
+
+       /* turn LED off */
+       if (delay_off == 0xff && delay_on == 0x0) {
+               val = MT_LED_CTRL_POLARITY | MT_LED_CTRL_KICK;
+       } else {
+               /* control LED */
+               val = MT_LED_CTRL_BLINK_MODE | MT_LED_CTRL_KICK;
+               if (mphy->band_idx == MT_BAND1)
+                       val |= MT_LED_CTRL_BLINK_BAND_SEL;
+       }
 
-       /* control LED */
-       val = MT_LED_CTRL_BLINK_MODE | MT_LED_CTRL_KICK;
        if (mphy->leds.al)
                val |= MT_LED_CTRL_POLARITY;
 
-       mt76_wr(dev, MT_LED_CTRL(0), val);
-       mt76_clear(dev, MT_LED_CTRL(0), MT_LED_CTRL_KICK);
+       mt76_wr(dev, MT_LED_CTRL(mphy->band_idx), val);
+       mt76_clear(dev, MT_LED_CTRL(mphy->band_idx), MT_LED_CTRL_KICK);
 }
 
 static int mt7996_led_set_blink(struct led_classdev *led_cdev,
@@ -173,6 +181,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
        wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
        wiphy->reg_notifier = mt7996_regd_notifier;
        wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+       wiphy->mbssid_max_interfaces = 16;
 
        wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR);
        wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
@@ -196,6 +205,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
        ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
        ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
        ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+       ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
 
        hw->max_tx_fragments = 4;
 
@@ -223,6 +233,12 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
                ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
        }
 
+       /* init led callbacks */
+       if (IS_ENABLED(CONFIG_MT76_LEDS)) {
+               phy->mt76->leds.cdev.brightness_set = mt7996_led_set_brightness;
+               phy->mt76->leds.cdev.blink_set = mt7996_led_set_blink;
+       }
+
        mt76_set_stream_caps(phy->mt76, true);
        mt7996_set_stream_vht_txbf_caps(phy);
        mt7996_set_stream_he_eht_caps(phy);
@@ -258,6 +274,11 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band)
        set = FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_MODE, 0) |
              FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_PARAM, 0x3);
        mt76_rmw(dev, MT_WTBLOFF_RSCR(band), mask, set);
+
+       /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than
+        * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set.
+        */
+       mt76_set(dev, MT_AGG_ACR4(band), MT_AGG_ACR_PPDU_TXS2H);
 }
 
 static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev)
@@ -733,16 +754,17 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
                IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
                IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
 
+       val = max_t(u8, sts - 1, 3);
        eht_cap_elem->phy_cap_info[0] |=
-               u8_encode_bits(u8_get_bits(sts - 1, BIT(0)),
+               u8_encode_bits(u8_get_bits(val, BIT(0)),
                               IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK);
 
        eht_cap_elem->phy_cap_info[1] =
-               u8_encode_bits(u8_get_bits(sts - 1, GENMASK(2, 1)),
+               u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)),
                               IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) |
-               u8_encode_bits(sts - 1,
+               u8_encode_bits(val,
                               IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK) |
-               u8_encode_bits(sts - 1,
+               u8_encode_bits(val,
                               IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
 
        eht_cap_elem->phy_cap_info[2] =
@@ -869,12 +891,6 @@ int mt7996_register_device(struct mt7996_dev *dev)
 
        mt7996_init_wiphy(hw);
 
-       /* init led callbacks */
-       if (IS_ENABLED(CONFIG_MT76_LEDS)) {
-               dev->mphy.leds.cdev.brightness_set = mt7996_led_set_brightness;
-               dev->mphy.leds.cdev.blink_set = mt7996_led_set_blink;
-       }
-
        ret = mt76_register_device(&dev->mt76, true, mt76_rates,
                                   ARRAY_SIZE(mt76_rates));
        if (ret)
index ac8759f..0454083 100644 (file)
@@ -433,7 +433,9 @@ mt7996_mac_fill_rx_rate(struct mt7996_dev *dev,
        case IEEE80211_STA_RX_BW_160:
                status->bw = RATE_INFO_BW_160;
                break;
+       /* rxv reports bw 320-1 and 320-2 separately */
        case IEEE80211_STA_RX_BW_320:
+       case IEEE80211_STA_RX_BW_320 + 1:
                status->bw = RATE_INFO_BW_320;
                break;
        default:
@@ -948,15 +950,6 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
        if (!wcid)
                wcid = &dev->mt76.global_wcid;
 
-       if (sta) {
-               struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
-
-               if (time_after(jiffies, msta->jiffies + HZ / 4)) {
-                       info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
-                       msta->jiffies = jiffies;
-               }
-       }
-
        t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
        t->skb = tx_info->skb;
 
@@ -991,11 +984,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
        }
 
        txp->fw.token = cpu_to_le16(id);
-       if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags))
-               txp->fw.rept_wds_wcid = cpu_to_le16(wcid->idx);
-       else
-               txp->fw.rept_wds_wcid = cpu_to_le16(0xfff);
-       tx_info->skb = DMA_DUMMY_DATA;
+       txp->fw.rept_wds_wcid = cpu_to_le16(sta ? wcid->idx : 0xfff);
+
+       tx_info->skb = NULL;
 
        /* pass partial skb header to fw */
        tx_info->buf[1].len = MT_CT_PARSE_LEN;
@@ -1006,22 +997,35 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
 }
 
 static void
-mt7996_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb)
 {
        struct mt7996_sta *msta;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
        u16 fc, tid;
-       u32 val;
 
        if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
                return;
 
-       tid = le32_get_bits(txwi[1], MT_TXD1_TID);
+       tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
        if (tid >= 6) /* skip VO queue */
                return;
 
-       val = le32_to_cpu(txwi[2]);
-       fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 |
-            FIELD_GET(MT_TXD2_SUB_TYPE, val) << 4;
+       if (is_8023) {
+               fc = IEEE80211_FTYPE_DATA |
+                    (sta->wme ? IEEE80211_STYPE_QOS_DATA : IEEE80211_STYPE_DATA);
+       } else {
+               /* No need to get precise TID for Action/Management Frame,
+                * since it will not meet the following Frame Control
+                * condition anyway.
+                */
+
+               struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+               fc = le16_to_cpu(hdr->frame_control) &
+                    (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
+       }
+
        if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA)))
                return;
 
@@ -1049,9 +1053,9 @@ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
                wcid_idx = wcid->idx;
 
                if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
-                       mt7996_tx_check_aggr(sta, txwi);
+                       mt7996_tx_check_aggr(sta, t->skb);
        } else {
-               wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
+               wcid_idx = le32_get_bits(txwi[9], MT_TXD9_WLAN_IDX);
        }
 
        __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list);
@@ -1070,6 +1074,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
        struct mt76_phy *phy3 = mdev->phys[MT_BAND2];
        struct mt76_txwi_cache *txwi;
        struct ieee80211_sta *sta = NULL;
+       struct mt76_wcid *wcid;
        LIST_HEAD(free_list);
        struct sk_buff *skb, *tmp;
        void *end = data + len;
@@ -1088,7 +1093,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
                mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_BE], false);
        }
 
-       if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 4))
+       if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 5))
                return;
 
        total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT);
@@ -1104,7 +1109,6 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
                info = le32_to_cpu(*cur_info);
                if (info & MT_TXFREE_INFO_PAIR) {
                        struct mt7996_sta *msta;
-                       struct mt76_wcid *wcid;
                        u16 idx;
 
                        idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
@@ -1120,10 +1124,21 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
                                              &mdev->sta_poll_list);
                        spin_unlock_bh(&mdev->sta_poll_lock);
                        continue;
-               }
+               } else if (info & MT_TXFREE_INFO_HEADER) {
+                       u32 tx_retries = 0, tx_failed = 0;
+
+                       if (!wcid)
+                               continue;
 
-               if (info & MT_TXFREE_INFO_HEADER)
+                       tx_retries =
+                               FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1;
+                       tx_failed = tx_retries +
+                               !!FIELD_GET(MT_TXFREE_INFO_STAT, info);
+
+                       wcid->stats.tx_retries += tx_retries;
+                       wcid->stats.tx_failed += tx_failed;
                        continue;
+               }
 
                for (i = 0; i < 2; i++) {
                        msdu = (info >> (15 * i)) & MT_TXFREE_INFO_MSDU_ID;
@@ -1167,22 +1182,31 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
        bool cck = false;
        u32 txrate, txs, mode, stbc;
 
+       txs = le32_to_cpu(txs_data[0]);
+
        mt76_tx_status_lock(mdev, &list);
        skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
-       if (!skb)
-               goto out_no_skb;
 
-       txs = le32_to_cpu(txs_data[0]);
+       if (skb) {
+               info = IEEE80211_SKB_CB(skb);
+               if (!(txs & MT_TXS0_ACK_ERROR_MASK))
+                       info->flags |= IEEE80211_TX_STAT_ACK;
+
+               info->status.ampdu_len = 1;
+               info->status.ampdu_ack_len =
+                       !!(info->flags & IEEE80211_TX_STAT_ACK);
 
-       info = IEEE80211_SKB_CB(skb);
-       if (!(txs & MT_TXS0_ACK_ERROR_MASK))
-               info->flags |= IEEE80211_TX_STAT_ACK;
+               info->status.rates[0].idx = -1;
+       }
 
-       info->status.ampdu_len = 1;
-       info->status.ampdu_ack_len = !!(info->flags &
-                                       IEEE80211_TX_STAT_ACK);
+       if (mtk_wed_device_active(&dev->mt76.mmio.wed) && wcid->sta) {
+               struct ieee80211_sta *sta;
+               u8 tid;
 
-       info->status.rates[0].idx = -1;
+               sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
+               tid = FIELD_GET(MT_TXS0_TID, txs);
+               ieee80211_refresh_tx_agg_session_timer(sta, tid);
+       }
 
        txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);
 
@@ -1282,9 +1306,8 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid,
        wcid->rate = rate;
 
 out:
-       mt76_tx_status_skb_done(mdev, skb, &list);
-
-out_no_skb:
+       if (skb)
+               mt76_tx_status_skb_done(mdev, skb, &list);
        mt76_tx_status_unlock(mdev, &list);
 
        return !!skb;
@@ -1298,13 +1321,10 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
        u16 wcidx;
        u8 pid;
 
-       if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1)
-               return;
-
        wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
        pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
 
-       if (pid < MT_PACKET_ID_FIRST)
+       if (pid < MT_PACKET_ID_NO_SKB)
                return;
 
        if (wcidx >= mt7996_wtbl_size(dev))
@@ -2191,6 +2211,11 @@ void mt7996_mac_work(struct work_struct *work)
                mphy->mac_work_count = 0;
 
                mt7996_mac_update_stats(phy);
+
+               if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
+                       mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_ADM_STAT);
+                       mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_MSDU_COUNT);
+               }
        }
 
        mutex_unlock(&mphy->dev->mutex);
index c3a479d..09c7a28 100644 (file)
@@ -190,7 +190,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
        mvif->mt76.omac_idx = idx;
        mvif->phy = phy;
        mvif->mt76.band_idx = band_idx;
-       mvif->mt76.wmm_idx = band_idx;
+       mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
 
        ret = mt7996_mcu_add_dev_info(phy, vif, true);
        if (ret)
@@ -207,7 +207,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
        mvif->sta.wcid.phy_idx = band_idx;
        mvif->sta.wcid.hw_key_idx = -1;
        mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
-       mt76_packet_id_init(&mvif->sta.wcid);
+       mt76_wcid_init(&mvif->sta.wcid);
 
        mt7996_mac_wtbl_update(dev, idx,
                               MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -248,8 +248,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
        struct mt7996_phy *phy = mt7996_hw_phy(hw);
        int idx = msta->wcid.idx;
 
-       mt7996_mcu_add_bss_info(phy, vif, false);
        mt7996_mcu_add_sta(dev, vif, NULL, false);
+       mt7996_mcu_add_bss_info(phy, vif, false);
 
        if (vif == phy->monitor_vif)
                phy->monitor_vif = NULL;
@@ -268,7 +268,7 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
                list_del_init(&msta->wcid.poll_list);
        spin_unlock_bh(&dev->mt76.sta_poll_lock);
 
-       mt76_packet_id_flush(&dev->mt76, &msta->wcid);
+       mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
 }
 
 int mt7996_set_channel(struct mt7996_phy *phy)
@@ -414,10 +414,16 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
               const struct ieee80211_tx_queue_params *params)
 {
        struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+       const u8 mq_to_aci[] = {
+               [IEEE80211_AC_VO] = 3,
+               [IEEE80211_AC_VI] = 2,
+               [IEEE80211_AC_BE] = 0,
+               [IEEE80211_AC_BK] = 1,
+       };
 
+       /* firmware uses access class index */
+       mvif->queue_params[mq_to_aci[queue]] = *params;
        /* no need to update right away, we'll get BSS_CHANGED_QOS */
-       queue = mt76_connac_lmac_mapping(queue);
-       mvif->queue_params[queue] = *params;
 
        return 0;
 }
@@ -564,17 +570,13 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
        /* station mode uses BSSID to map the wlan entry to a peer,
         * and then peer references bss_info_rfch to set bandwidth cap.
         */
-       if (changed & BSS_CHANGED_BSSID &&
-           vif->type == NL80211_IFTYPE_STATION) {
-               bool join = !is_zero_ether_addr(info->bssid);
-
-               mt7996_mcu_add_bss_info(phy, vif, join);
-               mt7996_mcu_add_sta(dev, vif, NULL, join);
+       if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
+           (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
+           (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
+               mt7996_mcu_add_bss_info(phy, vif, true);
+               mt7996_mcu_add_sta(dev, vif, NULL, true);
        }
 
-       if (changed & BSS_CHANGED_ASSOC)
-               mt7996_mcu_add_bss_info(phy, vif, vif->cfg.assoc);
-
        if (changed & BSS_CHANGED_ERP_CTS_PROT)
                mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot);
 
@@ -595,11 +597,6 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
                mvif->basic_rates_idx =
                        mt7996_get_rates_table(hw, vif, false, false);
 
-       if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) {
-               mt7996_mcu_add_bss_info(phy, vif, true);
-               mt7996_mcu_add_sta(dev, vif, NULL, true);
-       }
-
        /* ensure that enable txcmd_mode after bss_info */
        if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
                mt7996_mcu_set_tx(dev, vif);
@@ -618,8 +615,8 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
                mt7996_mcu_add_beacon(hw, vif, info->enable_beacon);
        }
 
-       if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP ||
-           changed & BSS_CHANGED_FILS_DISCOVERY)
+       if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+                      BSS_CHANGED_FILS_DISCOVERY))
                mt7996_mcu_beacon_inband_discov(dev, vif, changed);
 
        if (changed & BSS_CHANGED_MU_GROUPS)
@@ -660,7 +657,6 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
        msta->wcid.idx = idx;
        msta->wcid.phy_idx = band_idx;
        msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
-       msta->jiffies = jiffies;
 
        ewma_avg_signal_init(&msta->avg_ack_signal);
 
@@ -972,6 +968,7 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
                                  struct ieee80211_sta *sta,
                                  struct station_info *sinfo)
 {
+       struct mt7996_phy *phy = mt7996_hw_phy(hw);
        struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
        struct rate_info *txrate = &msta->wcid.rate;
 
@@ -992,11 +989,31 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
        sinfo->txrate.flags = txrate->flags;
        sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
 
+       sinfo->tx_failed = msta->wcid.stats.tx_failed;
+       sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+
+       sinfo->tx_retries = msta->wcid.stats.tx_retries;
+       sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+
        sinfo->ack_signal = (s8)msta->ack_signal;
        sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
 
        sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal);
        sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
+
+       if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
+               sinfo->tx_bytes = msta->wcid.stats.tx_bytes;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
+
+               sinfo->rx_bytes = msta->wcid.stats.rx_bytes;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
+
+               sinfo->tx_packets = msta->wcid.stats.tx_packets;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
+
+               sinfo->rx_packets = msta->wcid.stats.rx_packets;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
+       }
 }
 
 static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
@@ -1192,7 +1209,7 @@ void mt7996_get_et_strings(struct ieee80211_hw *hw,
                           u32 sset, u8 *data)
 {
        if (sset == ETH_SS_STATS)
-               memcpy(data, *mt7996_gstrings_stats,
+               memcpy(data, mt7996_gstrings_stats,
                       sizeof(mt7996_gstrings_stats));
 }
 
index 4a30db4..bf917be 100644 (file)
@@ -324,8 +324,10 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
 static void
 mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
 {
-       if (vif->bss_conf.csa_active)
-               ieee80211_csa_finish(vif);
+       if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
+               return;
+
+       ieee80211_csa_finish(vif);
 }
 
 static void
@@ -399,7 +401,7 @@ out:
 static void
 mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
 {
-       if (!vif->bss_conf.color_change_active)
+       if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION)
                return;
 
        ieee80211_color_change_finish(vif);
@@ -447,6 +449,54 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
        }
 }
 
+static void
+mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+       struct mt7996_mcu_all_sta_info_event *res;
+       u16 i;
+
+       skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
+
+       res = (struct mt7996_mcu_all_sta_info_event *)skb->data;
+
+       for (i = 0; i < le16_to_cpu(res->sta_num); i++) {
+               u8 ac;
+               u16 wlan_idx;
+               struct mt76_wcid *wcid;
+
+               switch (le16_to_cpu(res->tag)) {
+               case UNI_ALL_STA_TXRX_ADM_STAT:
+                       wlan_idx = le16_to_cpu(res->adm_stat[i].wlan_idx);
+                       wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
+
+                       if (!wcid)
+                               break;
+
+                       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+                               wcid->stats.tx_bytes +=
+                                       le32_to_cpu(res->adm_stat[i].tx_bytes[ac]);
+                               wcid->stats.rx_bytes +=
+                                       le32_to_cpu(res->adm_stat[i].rx_bytes[ac]);
+                       }
+                       break;
+               case UNI_ALL_STA_TXRX_MSDU_COUNT:
+                       wlan_idx = le16_to_cpu(res->msdu_cnt[i].wlan_idx);
+                       wcid = rcu_dereference(dev->mt76.wcid[wlan_idx]);
+
+                       if (!wcid)
+                               break;
+
+                       wcid->stats.tx_packets +=
+                               le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt);
+                       wcid->stats.rx_packets +=
+                               le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
 static void
 mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
 {
@@ -491,6 +541,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
        case MCU_UNI_EVENT_RDD_REPORT:
                mt7996_mcu_rx_radar_detected(dev, skb);
                break;
+       case MCU_UNI_EVENT_ALL_STA_INFO:
+               mt7996_mcu_rx_all_sta_info_event(dev, skb);
+               break;
        default:
                break;
        }
@@ -600,6 +653,24 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
        he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80;
 }
 
+static void
+mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+                         struct mt7996_phy *phy, int enable)
+{
+       struct bss_info_uni_mbssid *mbssid;
+       struct tlv *tlv;
+
+       tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
+
+       mbssid = (struct bss_info_uni_mbssid *)tlv;
+
+       if (enable && vif->bss_conf.bssid_indicator) {
+               mbssid->max_indicator = vif->bss_conf.bssid_indicator;
+               mbssid->mbss_idx = vif->bss_conf.bssid_index;
+               mbssid->tx_bss_omac_idx = 0;
+       }
+}
+
 static void
 mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
                       struct mt7996_phy *phy)
@@ -866,6 +937,9 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
                /* this tag is necessary no matter if the vif is MLD */
                mt7996_mcu_bss_mld_tlv(skb, vif);
        }
+
+       mt7996_mcu_bss_mbssid_tlv(skb, vif, phy, enable);
+
 out:
        return mt76_mcu_skb_send_msg(&dev->mt76, skb,
                                     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
@@ -1152,6 +1226,8 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
                HE_MAC(CAP2_MU_CASCADING, elem->mac_cap_info[2]);
        muru->ofdma_ul.uo_ra =
                HE_MAC(CAP3_OFDMA_RA, elem->mac_cap_info[3]);
+       muru->ofdma_ul.rx_ctrl_frame_to_mbss =
+               HE_MAC(CAP3_RX_CTRL_FRAME_TO_MULTIBSS, elem->mac_cap_info[3]);
 }
 
 static inline bool
@@ -1624,6 +1700,132 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
                                     MCU_WM_UNI_CMD(RA), true);
 }
 
+static int
+mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta, void *data, u32 field)
+{
+       struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+       struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+       struct sta_phy *phy = data;
+       struct sta_rec_ra_fixed *ra;
+       struct sk_buff *skb;
+       struct tlv *tlv;
+
+       skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
+                                             &msta->wcid,
+                                             MT7996_STA_UPDATE_MAX_SIZE);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra));
+       ra = (struct sta_rec_ra_fixed *)tlv;
+
+       switch (field) {
+       case RATE_PARAM_AUTO:
+               break;
+       case RATE_PARAM_FIXED:
+       case RATE_PARAM_FIXED_MCS:
+       case RATE_PARAM_FIXED_GI:
+       case RATE_PARAM_FIXED_HE_LTF:
+               if (phy)
+                       ra->phy = *phy;
+               break;
+       default:
+               break;
+       }
+       ra->field = cpu_to_le32(field);
+
+       return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+                                    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
+static int
+mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+                              struct ieee80211_sta *sta)
+{
+       struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+       struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
+       struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
+       enum nl80211_band band = chandef->chan->band;
+       struct sta_phy phy = {};
+       int ret, nrates = 0;
+
+#define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he)                      \
+       do {                                                                    \
+               u8 i, gi = mask->control[band]._gi;                             \
+               gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI;               \
+               phy.sgi = gi;                                                   \
+               phy.he_ltf = mask->control[band].he_ltf;                        \
+               for (i = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) {    \
+                       if (!mask->control[band]._mcs[i])                       \
+                               continue;                                       \
+                       nrates += hweight16(mask->control[band]._mcs[i]);       \
+                       phy.mcs = ffs(mask->control[band]._mcs[i]) - 1;         \
+                       if (_ht)                                                \
+                               phy.mcs += 8 * i;                               \
+               }                                                               \
+       } while (0)
+
+       if (sta->deflink.he_cap.has_he) {
+               __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1);
+       } else if (sta->deflink.vht_cap.vht_supported) {
+               __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0);
+       } else if (sta->deflink.ht_cap.ht_supported) {
+               __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0);
+       } else {
+               nrates = hweight32(mask->control[band].legacy);
+               phy.mcs = ffs(mask->control[band].legacy) - 1;
+       }
+#undef __sta_phy_bitrate_mask_check
+
+       /* fall back to auto rate control */
+       if (mask->control[band].gi == NL80211_TXRATE_DEFAULT_GI &&
+           mask->control[band].he_gi == GENMASK(7, 0) &&
+           mask->control[band].he_ltf == GENMASK(7, 0) &&
+           nrates != 1)
+               return 0;
+
+       /* fixed single rate */
+       if (nrates == 1) {
+               ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
+                                                RATE_PARAM_FIXED_MCS);
+               if (ret)
+                       return ret;
+       }
+
+       /* fixed GI */
+       if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI ||
+           mask->control[band].he_gi != GENMASK(7, 0)) {
+               struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+               u32 addr;
+
+               /* firmware updates only TXCMD but doesn't take WTBL into
+                * account, so driver should update here to reflect the
+                * actual txrate hardware sends out.
+                */
+               addr = mt7996_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7);
+               if (sta->deflink.he_cap.has_he)
+                       mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi);
+               else
+                       mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
+
+               ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
+                                                RATE_PARAM_FIXED_GI);
+               if (ret)
+                       return ret;
+       }
+
+       /* fixed HE_LTF */
+       if (mask->control[band].he_ltf != GENMASK(7, 0)) {
+               ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy,
+                                                RATE_PARAM_FIXED_HE_LTF);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static void
 mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
                             struct ieee80211_vif *vif, struct ieee80211_sta *sta)
@@ -1733,6 +1935,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
        struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
        struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
        struct sk_buff *skb;
+       int ret;
 
        skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
                                              &msta->wcid,
@@ -1752,8 +1955,12 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
         */
        mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta);
 
-       return mt76_mcu_skb_send_msg(&dev->mt76, skb,
-                                    MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+       ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+                                   MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+       if (ret)
+               return ret;
+
+       return mt7996_mcu_add_rate_ctrl_fixed(dev, vif, sta);
 }
 
 static int
@@ -1995,6 +2202,59 @@ mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
        info->cnt = skb->data[offs->cntdwn_counter_offs[0]];
 }
 
+static void
+mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
+                      struct ieee80211_vif *vif, struct bss_bcn_content_tlv *bcn,
+                      struct ieee80211_mutable_offsets *offs)
+{
+       struct bss_bcn_mbss_tlv *mbss;
+       const struct element *elem;
+       struct tlv *tlv;
+
+       if (!vif->bss_conf.bssid_indicator)
+               return;
+
+       tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_MBSSID, sizeof(*mbss));
+
+       mbss = (struct bss_bcn_mbss_tlv *)tlv;
+       mbss->offset[0] = cpu_to_le16(offs->tim_offset);
+       mbss->bitmap = cpu_to_le32(1);
+
+       for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID,
+                           &skb->data[offs->mbssid_off],
+                           skb->len - offs->mbssid_off) {
+               const struct element *sub_elem;
+
+               if (elem->datalen < 2)
+                       continue;
+
+               for_each_element(sub_elem, elem->data + 1, elem->datalen - 1) {
+                       const struct ieee80211_bssid_index *idx;
+                       const u8 *idx_ie;
+
+                       /* not a valid BSS profile */
+                       if (sub_elem->id || sub_elem->datalen < 4)
+                               continue;
+
+                       /* Find WLAN_EID_MULTI_BSSID_IDX
+                        * in the merged nontransmitted profile
+                        */
+                       idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX,
+                                                 sub_elem->data, sub_elem->datalen);
+                       if (!idx_ie || idx_ie[1] < sizeof(*idx))
+                               continue;
+
+                       idx = (void *)(idx_ie + 2);
+                       if (!idx->bssid_index || idx->bssid_index > 31)
+                               continue;
+
+                       mbss->offset[idx->bssid_index] = cpu_to_le16(idx_ie -
+                                                                    skb->data);
+                       mbss->bitmap |= cpu_to_le32(BIT(idx->bssid_index));
+               }
+       }
+}
+
 static void
 mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
                       struct sk_buff *rskb, struct sk_buff *skb,
@@ -2016,7 +2276,7 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
                        bcn->bcc_ie_pos = cpu_to_le16(offset - 3);
        }
 
-       buf = (u8 *)bcn + sizeof(*bcn) - MAX_BEACON_SIZE;
+       buf = (u8 *)bcn + sizeof(*bcn);
        mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0,
                              BSS_CHANGED_BEACON);
 
@@ -2034,26 +2294,25 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
        struct sk_buff *skb, *rskb;
        struct tlv *tlv;
        struct bss_bcn_content_tlv *bcn;
+       int len;
+
+       if (vif->bss_conf.nontransmitted)
+               return 0;
 
        rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
-                                         MT7996_BEACON_UPDATE_SIZE);
+                                         MT7996_MAX_BSS_OFFLOAD_SIZE);
        if (IS_ERR(rskb))
                return PTR_ERR(rskb);
 
-       tlv = mt7996_mcu_add_uni_tlv(rskb,
-                                    UNI_BSS_INFO_BCN_CONTENT, sizeof(*bcn));
-       bcn = (struct bss_bcn_content_tlv *)tlv;
-       bcn->enable = en;
-
-       if (!en)
-               goto out;
-
        skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
-       if (!skb)
+       if (!skb) {
+               dev_kfree_skb(rskb);
                return -EINVAL;
+       }
 
-       if (skb->len > MAX_BEACON_SIZE - MT_TXD_SIZE) {
+       if (skb->len > MT7996_MAX_BEACON_SIZE) {
                dev_err(dev->mt76.dev, "Bcn size limit exceed\n");
+               dev_kfree_skb(rskb);
                dev_kfree_skb(skb);
                return -EINVAL;
        }
@@ -2061,11 +2320,18 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
        info = IEEE80211_SKB_CB(skb);
        info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
 
+       len = sizeof(*bcn) + MT_TXD_SIZE + skb->len;
+       tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_CONTENT, len);
+       bcn = (struct bss_bcn_content_tlv *)tlv;
+       bcn->enable = en;
+       if (!en)
+               goto out;
+
        mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
-       /* TODO: subtag - 11v MBSSID */
+       mt7996_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs);
        mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs);
-       dev_kfree_skb(skb);
 out:
+       dev_kfree_skb(skb);
        return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
                                     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
 }
@@ -2086,9 +2352,13 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
        struct sk_buff *rskb, *skb = NULL;
        struct tlv *tlv;
        u8 *buf, interval;
+       int len;
+
+       if (vif->bss_conf.nontransmitted)
+               return 0;
 
        rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
-                                         MT7996_INBAND_FRAME_SIZE);
+                                         MT7996_MAX_BSS_OFFLOAD_SIZE);
        if (IS_ERR(rskb))
                return PTR_ERR(rskb);
 
@@ -2102,11 +2372,14 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
                skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
        }
 
-       if (!skb)
+       if (!skb) {
+               dev_kfree_skb(rskb);
                return -EINVAL;
+       }
 
-       if (skb->len > MAX_INBAND_FRAME_SIZE - MT_TXD_SIZE) {
+       if (skb->len > MT7996_MAX_BEACON_SIZE) {
                dev_err(dev->mt76.dev, "inband discovery size limit exceed\n");
+               dev_kfree_skb(rskb);
                dev_kfree_skb(skb);
                return -EINVAL;
        }
@@ -2116,7 +2389,9 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
        info->band = band;
        info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
 
-       tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, sizeof(*discov));
+       len = sizeof(*discov) + MT_TXD_SIZE + skb->len;
+
+       tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, len);
 
        discov = (struct bss_inband_discovery_tlv *)tlv;
        discov->tx_mode = OFFLOAD_TX_MODE_SU;
@@ -2127,7 +2402,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
        discov->enable = true;
        discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED);
 
-       buf = (u8 *)tlv + sizeof(*discov) - MAX_INBAND_FRAME_SIZE;
+       buf = (u8 *)tlv + sizeof(*discov);
 
        mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, changed);
 
@@ -2679,7 +2954,7 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
 
                e = (struct edca *)tlv;
                e->set = WMM_PARAM_SET;
-               e->queue = ac + mvif->mt76.wmm_idx * MT7996_MAX_WMM_SETS;
+               e->queue = ac;
                e->aifs = q->aifs;
                e->txop = cpu_to_le16(q->txop);
 
@@ -2960,10 +3235,10 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
                .channel_band = ch_band[chandef->chan->band],
        };
 
-       if (tag == UNI_CHANNEL_RX_PATH ||
-           dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
+       if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR)
                req.switch_reason = CH_SWITCH_NORMAL;
-       else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+       else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL ||
+                phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE)
                req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
        else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
                                          NL80211_IFTYPE_AP))
@@ -3307,8 +3582,8 @@ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
 
                tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en));
                req_mod_en = (struct bf_mod_en_ctrl *)tlv;
-               req_mod_en->bf_num = 2;
-               req_mod_en->bf_bitmap = GENMASK(0, 0);
+               req_mod_en->bf_num = 3;
+               req_mod_en->bf_bitmap = GENMASK(2, 0);
                break;
        }
        default:
@@ -3548,7 +3823,9 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
                               int cmd)
 {
        struct {
-               u8 _rsv[4];
+               /* fixed field */
+               u8 bss;
+               u8 _rsv[3];
 
                __le16 tag;
                __le16 len;
@@ -3566,7 +3843,7 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
                u8 exponent;
                u8 is_ap;
                u8 agrt_params;
-               u8 __rsv2[135];
+               u8 __rsv2[23];
        } __packed req = {
                .tag = cpu_to_le16(UNI_CMD_TWT_ARGT_UPDATE),
                .len = cpu_to_le16(sizeof(req) - 4),
@@ -3576,6 +3853,7 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
                .flowid = flow->id,
                .peer_id = cpu_to_le16(flow->wcid),
                .duration = flow->duration,
+               .bss = mvif->mt76.idx,
                .bss_idx = mvif->mt76.idx,
                .start_tsf = cpu_to_le64(flow->tsf),
                .mantissa = flow->mantissa,
@@ -3786,3 +4064,20 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val)
        return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req,
                                 sizeof(req), true);
 }
+
+int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
+{
+       struct mt7996_dev *dev = phy->dev;
+       struct {
+               u8 _rsv[4];
+
+               __le16 tag;
+               __le16 len;
+       } __packed req = {
+               .tag = cpu_to_le16(tag),
+               .len = cpu_to_le16(sizeof(req) - 4),
+       };
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO),
+                                &req, sizeof(req), false);
+}
index 078f828..a88f6af 100644 (file)
@@ -153,6 +153,32 @@ struct mt7996_mcu_mib {
        __le64 data;
 } __packed;
 
+struct mt7996_mcu_all_sta_info_event {
+       u8 rsv[4];
+       __le16 tag;
+       __le16 len;
+       u8 more;
+       u8 rsv2;
+       __le16 sta_num;
+       u8 rsv3[2];
+
+       union {
+               struct {
+                       __le16 wlan_idx;
+                       u8 rsv[2];
+                       __le32 tx_bytes[IEEE80211_NUM_ACS];
+                       __le32 rx_bytes[IEEE80211_NUM_ACS];
+               } adm_stat[0];
+
+               struct {
+                       __le16 wlan_idx;
+                       u8 rsv[2];
+                       __le32 tx_msdu_cnt;
+                       __le32 rx_msdu_cnt;
+               } msdu_cnt[0];
+       };
+} __packed;
+
 enum mt7996_chan_mib_offs {
        UNI_MIB_OBSS_AIRTIME = 26,
        UNI_MIB_NON_WIFI_TIME = 27,
@@ -270,8 +296,6 @@ struct bss_inband_discovery_tlv {
        u8 enable;
        __le16 wcid;
        __le16 prob_rsp_len;
-#define MAX_INBAND_FRAME_SIZE 512
-       u8 pkt[MAX_INBAND_FRAME_SIZE];
 } __packed;
 
 struct bss_bcn_content_tlv {
@@ -283,8 +307,6 @@ struct bss_bcn_content_tlv {
        u8 enable;
        u8 type;
        __le16 pkt_len;
-#define MAX_BEACON_SIZE 512
-       u8 pkt[MAX_BEACON_SIZE];
 } __packed;
 
 struct bss_bcn_cntdwn_tlv {
@@ -591,13 +613,14 @@ enum {
                                         sizeof(struct sta_rec_hdr_trans) +     \
                                         sizeof(struct tlv))
 
+#define MT7996_MAX_BEACON_SIZE         1342
 #define MT7996_BEACON_UPDATE_SIZE      (sizeof(struct bss_req_hdr) +           \
                                         sizeof(struct bss_bcn_content_tlv) +   \
+                                        MT_TXD_SIZE +                          \
                                         sizeof(struct bss_bcn_cntdwn_tlv) +    \
                                         sizeof(struct bss_bcn_mbss_tlv))
-
-#define MT7996_INBAND_FRAME_SIZE       (sizeof(struct bss_req_hdr) +           \
-                                        sizeof(struct bss_inband_discovery_tlv))
+#define MT7996_MAX_BSS_OFFLOAD_SIZE    (MT7996_MAX_BEACON_SIZE +               \
+                                        MT7996_BEACON_UPDATE_SIZE)
 
 enum {
        UNI_BAND_CONFIG_RADIO_ENABLE,
index 7354e5c..e53cf6a 100644 (file)
@@ -110,7 +110,6 @@ struct mt7996_sta {
        struct ewma_avg_signal avg_ack_signal;
 
        unsigned long changed;
-       unsigned long jiffies;
 
        struct mt76_connac_sta_key_conf bip;
 
@@ -402,6 +401,7 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
 int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
 void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
 void mt7996_mcu_exit(struct mt7996_dev *dev);
+int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
 
 static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
 {
index 97beab9..0086a78 100644 (file)
@@ -243,6 +243,13 @@ enum base_rev {
                                                 FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \
                                                 FIELD_PREP(MT_WTBL_LMAC_DW, _dw))
 
+/* AGG: band 0(0x820e2000), band 1(0x820f2000), band 2(0x830e2000) */
+#define MT_WF_AGG_BASE(_band)                  __BASE(WF_AGG_BASE, (_band))
+#define MT_WF_AGG(_band, ofs)                  (MT_WF_AGG_BASE(_band) + (ofs))
+
+#define MT_AGG_ACR4(_band)                     MT_WF_AGG(_band, 0x3c)
+#define MT_AGG_ACR_PPDU_TXS2H                  BIT(1)
+
 /* ARB: band 0(0x820e3000), band 1(0x820f3000), band 2(0x830e3000) */
 #define MT_WF_ARB_BASE(_band)                  __BASE(WF_ARB_BASE, (_band))
 #define MT_WF_ARB(_band, ofs)                  (MT_WF_ARB_BASE(_band) + (ofs))
@@ -509,6 +516,7 @@ enum base_rev {
 
 #define MT_LED_CTRL(_n)                                MT_LED_PHYS(0x00 + ((_n) * 4))
 #define MT_LED_CTRL_KICK                       BIT(7)
+#define MT_LED_CTRL_BLINK_BAND_SEL             BIT(4)
 #define MT_LED_CTRL_BLINK_MODE                 BIT(2)
 #define MT_LED_CTRL_POLARITY                   BIT(1)
 
index 6cc26cc..1809b03 100644 (file)
@@ -329,40 +329,32 @@ void
 mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
        struct mt76_wcid *wcid, struct sk_buff *skb)
 {
-       struct mt76_dev *dev = phy->dev;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       struct mt76_queue *q;
-       int qid = skb_get_queue_mapping(skb);
 
        if (mt76_testmode_enabled(phy)) {
                ieee80211_free_txskb(phy->hw, skb);
                return;
        }
 
-       if (WARN_ON(qid >= MT_TXQ_PSD)) {
-               qid = MT_TXQ_BE;
-               skb_set_queue_mapping(skb, qid);
-       }
-
-       if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) &&
-           !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
-           !ieee80211_is_data(hdr->frame_control) &&
-           !ieee80211_is_bufferable_mmpdu(skb)) {
-               qid = MT_TXQ_PSD;
-       }
+       if (WARN_ON(skb_get_queue_mapping(skb) >= MT_TXQ_PSD))
+               skb_set_queue_mapping(skb, MT_TXQ_BE);
 
        if (wcid && !(wcid->tx_info & MT_WCID_TX_INFO_SET))
                ieee80211_get_tx_rates(info->control.vif, sta, skb,
                                       info->control.rates, 1);
 
        info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx);
-       q = phy->q_tx[qid];
 
-       spin_lock_bh(&q->lock);
-       __mt76_tx_queue_skb(phy, qid, skb, wcid, sta, NULL);
-       dev->queue_ops->kick(dev, q);
-       spin_unlock_bh(&q->lock);
+       spin_lock_bh(&wcid->tx_pending.lock);
+       __skb_queue_tail(&wcid->tx_pending, skb);
+       spin_unlock_bh(&wcid->tx_pending.lock);
+
+       spin_lock_bh(&phy->tx_lock);
+       if (list_empty(&wcid->tx_list))
+               list_add_tail(&wcid->tx_list, &phy->tx_list);
+       spin_unlock_bh(&phy->tx_lock);
+
+       mt76_worker_schedule(&phy->dev->tx_worker);
 }
 EXPORT_SYMBOL_GPL(mt76_tx);
 
@@ -593,10 +585,86 @@ void mt76_txq_schedule(struct mt76_phy *phy, enum mt76_txq_id qid)
 }
 EXPORT_SYMBOL_GPL(mt76_txq_schedule);
 
+static int
+mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid)
+{
+       struct mt76_dev *dev = phy->dev;
+       struct ieee80211_sta *sta;
+       struct mt76_queue *q;
+       struct sk_buff *skb;
+       int ret = 0;
+
+       spin_lock(&wcid->tx_pending.lock);
+       while ((skb = skb_peek(&wcid->tx_pending)) != NULL) {
+               struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+               int qid = skb_get_queue_mapping(skb);
+
+               if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) &&
+                   !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
+                   !ieee80211_is_data(hdr->frame_control) &&
+                   !ieee80211_is_bufferable_mmpdu(skb))
+                       qid = MT_TXQ_PSD;
+
+               q = phy->q_tx[qid];
+               if (mt76_txq_stopped(q)) {
+                       ret = -1;
+                       break;
+               }
+
+               __skb_unlink(skb, &wcid->tx_pending);
+               spin_unlock(&wcid->tx_pending.lock);
+
+               sta = wcid_to_sta(wcid);
+               spin_lock(&q->lock);
+               __mt76_tx_queue_skb(phy, qid, skb, wcid, sta, NULL);
+               dev->queue_ops->kick(dev, q);
+               spin_unlock(&q->lock);
+
+               spin_lock(&wcid->tx_pending.lock);
+       }
+       spin_unlock(&wcid->tx_pending.lock);
+
+       return ret;
+}
+
+static void mt76_txq_schedule_pending(struct mt76_phy *phy)
+{
+       if (list_empty(&phy->tx_list))
+               return;
+
+       local_bh_disable();
+       rcu_read_lock();
+
+       spin_lock(&phy->tx_lock);
+       while (!list_empty(&phy->tx_list)) {
+               struct mt76_wcid *wcid = NULL;
+               int ret;
+
+               wcid = list_first_entry(&phy->tx_list, struct mt76_wcid, tx_list);
+               list_del_init(&wcid->tx_list);
+
+               spin_unlock(&phy->tx_lock);
+               ret = mt76_txq_schedule_pending_wcid(phy, wcid);
+               spin_lock(&phy->tx_lock);
+
+               if (ret) {
+                       if (list_empty(&wcid->tx_list))
+                               list_add_tail(&wcid->tx_list, &phy->tx_list);
+                       break;
+               }
+       }
+       spin_unlock(&phy->tx_lock);
+
+       rcu_read_unlock();
+       local_bh_enable();
+}
+
 void mt76_txq_schedule_all(struct mt76_phy *phy)
 {
        int i;
 
+       mt76_txq_schedule_pending(phy);
        for (i = 0; i <= MT_TXQ_BK; i++)
                mt76_txq_schedule(phy, i);
 }
index 5fb3d4c..0328a4c 100644 (file)
 #define PCI_DEVICE_ID_BERKOM_A4T               0xffa4
 #define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO     0xffa8
 
+#define PCI_VENDOR_ID_ITTIM            0x0b48
+
 #define PCI_VENDOR_ID_COMPAQ           0x0e11
 #define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508
 #define PCI_DEVICE_ID_COMPAQ_TACHYON   0xa0fc