#include "mt76.h"
 #include "dma.h"
 
-#define Q_READ(_dev, _q, _field)               readl(&(_q)->regs->_field)
-#define Q_WRITE(_dev, _q, _field, _val)                writel(_val, &(_q)->regs->_field)
+#if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)
+
+#define Q_READ(_dev, _q, _field) ({                                    \
+       u32 _offset = offsetof(struct mt76_queue_regs, _field);         \
+       u32 _val;                                                       \
+       if ((_q)->flags & MT_QFLAG_WED)                                 \
+               _val = mtk_wed_device_reg_read(&(_dev)->mmio.wed,       \
+                                              ((_q)->wed_regs +        \
+                                               _offset));              \
+       else                                                            \
+               _val = readl(&(_q)->regs->_field);                      \
+       _val;                                                           \
+})
+
+#define Q_WRITE(_dev, _q, _field, _val)        do {                            \
+       u32 _offset = offsetof(struct mt76_queue_regs, _field);         \
+       if ((_q)->flags & MT_QFLAG_WED)                                 \
+               mtk_wed_device_reg_write(&(_dev)->mmio.wed,             \
+                                        ((_q)->wed_regs + _offset),    \
+                                        _val);                         \
+       else                                                            \
+               writel(_val, &(_q)->regs->_field);                      \
+} while (0)
+
+#else
+
+#define Q_READ(_dev, _q, _field)       readl(&(_q)->regs->_field)
+#define Q_WRITE(_dev, _q, _field, _val)        writel(_val, &(_q)->regs->_field)
 
+#endif
 
 static struct mt76_txwi_cache *
 mt76_alloc_txwi(struct mt76_dev *dev)
        mt76_dma_sync_idx(dev, q);
 }
 
-static int
-mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
-                    int idx, int n_desc, int bufsize,
-                    u32 ring_base)
-{
-       int size;
-
-       spin_lock_init(&q->lock);
-       spin_lock_init(&q->cleanup_lock);
-
-       q->regs = dev->mmio.regs + ring_base + idx * MT_RING_SIZE;
-       q->ndesc = n_desc;
-       q->buf_size = bufsize;
-       q->hw_idx = idx;
-
-       size = q->ndesc * sizeof(struct mt76_desc);
-       q->desc = dmam_alloc_coherent(dev->dma_dev, size, &q->desc_dma, GFP_KERNEL);
-       if (!q->desc)
-               return -ENOMEM;
-
-       size = q->ndesc * sizeof(*q->entry);
-       q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
-       if (!q->entry)
-               return -ENOMEM;
-
-       mt76_dma_queue_reset(dev, q);
-
-       return 0;
-}
-
 static int
 mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
                 struct mt76_queue_buf *buf, int nbufs, u32 info,
        return frames;
 }
 
+static int
+mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q)
+{
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+       struct mtk_wed_device *wed = &dev->mmio.wed;
+       int ret, type, ring;
+       u8 flags = q->flags;
+
+       if (!mtk_wed_device_active(wed))
+               q->flags &= ~MT_QFLAG_WED;
+
+       if (!(q->flags & MT_QFLAG_WED))
+               return 0;
+
+       type = FIELD_GET(MT_QFLAG_WED_TYPE, q->flags);
+       ring = FIELD_GET(MT_QFLAG_WED_RING, q->flags);
+
+       switch (type) {
+       case MT76_WED_Q_TX:
+               ret = mtk_wed_device_tx_ring_setup(wed, ring, q->regs);
+               if (!ret)
+                       q->wed_regs = wed->tx_ring[ring].reg_base;
+               break;
+       case MT76_WED_Q_TXFREE:
+               /* WED txfree queue needs ring to be initialized before setup */
+               q->flags = 0;
+               mt76_dma_queue_reset(dev, q);
+               mt76_dma_rx_fill(dev, q);
+               q->flags = flags;
+
+               ret = mtk_wed_device_txfree_ring_setup(wed, q->regs);
+               if (!ret)
+                       q->wed_regs = wed->txfree_ring.reg_base;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+#else
+       return 0;
+#endif
+}
+
+static int
+mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+                    int idx, int n_desc, int bufsize,
+                    u32 ring_base)
+{
+       int ret, size;
+
+       spin_lock_init(&q->lock);
+       spin_lock_init(&q->cleanup_lock);
+
+       q->regs = dev->mmio.regs + ring_base + idx * MT_RING_SIZE;
+       q->ndesc = n_desc;
+       q->buf_size = bufsize;
+       q->hw_idx = idx;
+
+       size = q->ndesc * sizeof(struct mt76_desc);
+       q->desc = dmam_alloc_coherent(dev->dma_dev, size, &q->desc_dma, GFP_KERNEL);
+       if (!q->desc)
+               return -ENOMEM;
+
+       size = q->ndesc * sizeof(*q->entry);
+       q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
+       if (!q->entry)
+               return -ENOMEM;
+
+       ret = mt76_dma_wed_setup(dev, q);
+       if (ret)
+               return ret;
+
+       if (q->flags != MT_WED_Q_TXFREE)
+               mt76_dma_queue_reset(dev, q);
+
+       return 0;
+}
+
 static void
 mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
 {
 static int
 mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
 {
-       int len, data_len, done = 0;
+       int len, data_len, done = 0, dma_idx;
        struct sk_buff *skb;
        unsigned char *data;
+       bool check_ddone = false;
        bool more;
 
+       if (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
+           q->flags == MT_WED_Q_TXFREE) {
+               dma_idx = Q_READ(dev, q, dma_idx);
+               check_ddone = true;
+       }
+
        while (done < budget) {
                u32 info;
 
+               if (check_ddone) {
+                       if (q->tail == dma_idx)
+                               dma_idx = Q_READ(dev, q, dma_idx);
+
+                       if (q->tail == dma_idx)
+                               break;
+               }
+
                data = mt76_dma_dequeue(dev, q, false, &len, &info, &more);
                if (!data)
                        break;
        }
 
        mt76_free_pending_txwi(dev);
+
+       if (mtk_wed_device_active(&dev->mmio.wed))
+               mtk_wed_device_detach(&dev->mmio.wed);
 }
 EXPORT_SYMBOL_GPL(mt76_dma_cleanup);
 
 
 struct mt76_queue *
 mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
-               int ring_base)
+               int ring_base, u32 flags)
 {
        struct mt76_queue *hwq;
        int err;
        if (!hwq)
                return ERR_PTR(-ENOMEM);
 
+       hwq->flags = flags;
+
        err = dev->queue_ops->alloc(dev, hwq, idx, n_desc, 0, ring_base);
        if (err < 0)
                return ERR_PTR(err);
 
        spin_lock_irqsave(&dev->mmio.irq_lock, flags);
        dev->mmio.irqmask &= ~clear;
        dev->mmio.irqmask |= set;
-       if (addr)
-               mt76_mmio_wr(dev, addr, dev->mmio.irqmask);
+       if (addr) {
+               if (mtk_wed_device_active(&dev->mmio.wed))
+                       mtk_wed_device_irq_set_mask(&dev->mmio.wed,
+                                                   dev->mmio.irqmask);
+               else
+                       mt76_mmio_wr(dev, addr, dev->mmio.irqmask);
+       }
        spin_unlock_irqrestore(&dev->mmio.irq_lock, flags);
 }
 EXPORT_SYMBOL_GPL(mt76_set_irq_mask);
 
 #include <linux/leds.h>
 #include <linux/usb.h>
 #include <linux/average.h>
+#include <linux/soc/mediatek/mtk_wed.h>
 #include <net/mac80211.h>
 #include "util.h"
 #include "testmode.h"
 
 #define MT76_TOKEN_FREE_THR    64
 
+#define MT_QFLAG_WED_RING      GENMASK(1, 0)
+#define MT_QFLAG_WED_TYPE      GENMASK(3, 2)
+#define MT_QFLAG_WED           BIT(4)
+
+#define __MT_WED_Q(_type, _n)  (MT_QFLAG_WED | \
+                                FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \
+                                FIELD_PREP(MT_QFLAG_WED_RING, _n))
+#define MT_WED_Q_TX(_n)                __MT_WED_Q(MT76_WED_Q_TX, _n)
+#define MT_WED_Q_TXFREE                __MT_WED_Q(MT76_WED_Q_TXFREE, 0)
+
 struct mt76_dev;
 struct mt76_phy;
 struct mt76_wcid;
        MT76_BUS_SDIO,
 };
 
+enum mt76_wed_type {
+       MT76_WED_Q_TX,
+       MT76_WED_Q_TXFREE,
+};
+
 struct mt76_bus_ops {
        u32 (*rr)(struct mt76_dev *dev, u32 offset);
        void (*wr)(struct mt76_dev *dev, u32 offset, u32 val);
        u8 buf_offset;
        u8 hw_idx;
        u8 qid;
+       u8 flags;
+
+       u32 wed_regs;
 
        dma_addr_t desc_dma;
        struct sk_buff *rx_head;
        void __iomem *regs;
        spinlock_t irq_lock;
        u32 irqmask;
+
+       struct mtk_wed_device wed;
 };
 
 struct mt76_rx_status {
 
        spinlock_t token_lock;
        struct idr token;
+       u16 wed_token_count;
        u16 token_count;
        u16 token_size;
 
 
 struct mt76_queue *
 mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
-               int ring_base);
+               int ring_base, u32 flags);
 u16 mt76_calculate_default_rate(struct mt76_phy *phy, int rateidx);
 static inline int mt76_init_tx_queue(struct mt76_phy *phy, int qid, int idx,
-                                    int n_desc, int ring_base)
+                                    int n_desc, int ring_base, u32 flags)
 {
        struct mt76_queue *q;
 
-       q = mt76_init_queue(phy->dev, qid, idx, n_desc, ring_base);
+       q = mt76_init_queue(phy->dev, qid, idx, n_desc, ring_base, flags);
        if (IS_ERR(q))
                return PTR_ERR(q);
 
 {
        struct mt76_queue *q;
 
-       q = mt76_init_queue(dev, qid, idx, n_desc, ring_base);
+       q = mt76_init_queue(dev, qid, idx, n_desc, ring_base, 0);
        if (IS_ERR(q))
                return PTR_ERR(q);
 
 
 
        for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) {
                ret = mt76_init_tx_queue(&dev->mphy, i, wmm_queue_map[i],
-                                        MT7603_TX_RING_SIZE, MT_TX_RING_BASE);
+                                        MT7603_TX_RING_SIZE, MT_TX_RING_BASE, 0);
                if (ret)
                        return ret;
        }
 
        ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_PSD, MT_TX_HW_QUEUE_MGMT,
-                                MT7603_PSD_RING_SIZE, MT_TX_RING_BASE);
+                                MT7603_PSD_RING_SIZE, MT_TX_RING_BASE, 0);
        if (ret)
                return ret;
 
                return ret;
 
        ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_BEACON, MT_TX_HW_QUEUE_BCN,
-                                MT_MCU_RING_SIZE, MT_TX_RING_BASE);
+                                MT_MCU_RING_SIZE, MT_TX_RING_BASE, 0);
        if (ret)
                return ret;
 
        ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_CAB, MT_TX_HW_QUEUE_BMC,
-                                MT_MCU_RING_SIZE, MT_TX_RING_BASE);
+                                MT_MCU_RING_SIZE, MT_TX_RING_BASE, 0);
        if (ret)
                return ret;
 
 
        for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) {
                ret = mt76_init_tx_queue(&dev->mphy, i, wmm_queue_map[i],
                                         MT7615_TX_RING_SIZE / 2,
-                                        MT_TX_RING_BASE);
+                                        MT_TX_RING_BASE, 0);
                if (ret)
                        return ret;
        }
 
        ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_PSD, MT7622_TXQ_MGMT,
                                 MT7615_TX_MGMT_RING_SIZE,
-                                MT_TX_RING_BASE);
+                                MT_TX_RING_BASE, 0);
        if (ret)
                return ret;
 
                return mt7622_init_tx_queues_multi(dev);
 
        ret = mt76_init_tx_queue(&dev->mphy, 0, 0, MT7615_TX_RING_SIZE,
-                                MT_TX_RING_BASE);
+                                MT_TX_RING_BASE, 0);
        if (ret)
                return ret;
 
 
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                ret = mt76_init_tx_queue(&dev->mphy, i, mt76_ac_to_hwq(i),
                                         MT76x02_TX_RING_SIZE,
-                                        MT_TX_RING_BASE);
+                                        MT_TX_RING_BASE, 0);
                if (ret)
                        return ret;
        }
 
        ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_PSD, MT_TX_HW_QUEUE_MGMT,
-                                MT76x02_PSD_RING_SIZE, MT_TX_RING_BASE);
+                                MT76x02_PSD_RING_SIZE, MT_TX_RING_BASE, 0);
        if (ret)
                return ret;
 
 
 static int
 mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc, int ring_base)
 {
+       struct mt7915_dev *dev = phy->dev;
        int i, err;
 
-       err = mt76_init_tx_queue(phy->mt76, 0, idx, n_desc, ring_base);
+       if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
+               ring_base = MT_WED_TX_RING_BASE;
+               idx -= MT_TXQ_ID(0);
+       }
+
+       err = mt76_init_tx_queue(phy->mt76, 0, idx, n_desc, ring_base,
+                                MT_WED_Q_TX(idx));
        if (err < 0)
                return err;
 
        if (dev->dbdc_support || dev->phy.band_idx)
                irq_mask |= MT_INT_BAND1_RX_DONE;
 
+       if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+               u32 wed_irq_mask = irq_mask;
+
+               wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1;
+               mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask);
+               mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask);
+       }
+
        mt7915_irq_enable(dev, irq_mask);
 
        return 0;
 int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
 {
        struct mt76_dev *mdev = &dev->mt76;
+       u32 wa_rx_base, wa_rx_idx;
        u32 hif1_ofs = 0;
        int ret;
 
 
        mt7915_dma_disable(dev, true);
 
+       if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+               mt76_set(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED);
+
+               mt76_wr(dev, MT_WFDMA_WED_RING_CONTROL,
+                       FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX0, 18) |
+                       FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) |
+                       FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, 1));
+       } else {
+               mt76_clear(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED);
+       }
+
        /* init tx queue */
        ret = mt7915_init_tx_queues(&dev->phy,
                                    MT_TXQ_ID(dev->phy.band_idx),
                return ret;
 
        /* event from WA */
+       if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+               wa_rx_base = MT_WED_RX_RING_BASE;
+               wa_rx_idx = MT7915_RXQ_MCU_WA;
+               dev->mt76.q_rx[MT_RXQ_MCU_WA].flags = MT_WED_Q_TXFREE;
+       } else {
+               wa_rx_base = MT_RXQ_RING_BASE(MT_RXQ_MCU_WA);
+               wa_rx_idx = MT_RXQ_ID(MT_RXQ_MCU_WA);
+       }
        ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
-                              MT_RXQ_ID(MT_RXQ_MCU_WA),
-                              MT7915_RX_MCU_RING_SIZE,
-                              MT_RX_BUF_SIZE,
-                              MT_RXQ_RING_BASE(MT_RXQ_MCU_WA));
+                              wa_rx_idx, MT7915_RX_MCU_RING_SIZE,
+                              MT_RX_BUF_SIZE, wa_rx_base);
        if (ret)
                return ret;
 
 
        return 0;
 }
 
+u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
+{
+       struct mt7915_txp *txp = ptr + MT_TXD_SIZE;
+       __le32 *txwi = ptr;
+       u32 val;
+
+       memset(ptr, 0, MT_TXD_SIZE + sizeof(*txp));
+
+       val = FIELD_PREP(MT_TXD0_TX_BYTES, MT_TXD_SIZE) |
+             FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CT);
+       txwi[0] = cpu_to_le32(val);
+
+       val = MT_TXD1_LONG_FORMAT |
+             FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3);
+       txwi[1] = cpu_to_le32(val);
+
+       txp->token = cpu_to_le16(token_id);
+       txp->nbuf = 1;
+       txp->buf[0] = cpu_to_le32(phys + MT_TXD_SIZE + sizeof(*txp));
+
+       return MT_TXD_SIZE + sizeof(*txp);
+}
+
 static void
 mt7915_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
 {
 
        txp = mt7915_txwi_to_txp(dev, t);
        for (i = 0; i < txp->nbuf; i++)
-               dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]),
+               dma_unmap_single(dev->dma_dev, le32_to_cpu(txp->buf[i]),
                                 le16_to_cpu(txp->len[i]), DMA_TO_DEVICE);
 }
 
                 struct ieee80211_sta *sta, struct list_head *free_list)
 {
        struct mt76_dev *mdev = &dev->mt76;
+       struct mt7915_sta *msta;
        struct mt76_wcid *wcid;
        __le32 *txwi;
        u16 wcid_idx;
        if (sta) {
                wcid = (struct mt76_wcid *)sta->drv_priv;
                wcid_idx = wcid->idx;
-
-               if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
-                       mt7915_tx_check_aggr(sta, txwi);
        } else {
                wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
+               wcid = rcu_dereference(dev->mt76.wcid[wcid_idx]);
+
+               if (wcid && wcid->sta) {
+                       msta = container_of(wcid, struct mt7915_sta, wcid);
+                       sta = container_of((void *)msta, struct ieee80211_sta,
+                                         drv_priv);
+                       spin_lock_bh(&dev->sta_poll_lock);
+                       if (list_empty(&msta->poll_list))
+                               list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+                       spin_unlock_bh(&dev->sta_poll_lock);
+               }
        }
 
+       if (sta && likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
+               mt7915_tx_check_aggr(sta, txwi);
+
        __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list);
 
 out:
        mt76_put_txwi(mdev, t);
 }
 
+static void
+mt7915_mac_tx_free_prepare(struct mt7915_dev *dev)
+{
+       struct mt76_dev *mdev = &dev->mt76;
+       struct mt76_phy *mphy_ext = mdev->phy2;
+
+       /* 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 (mphy_ext) {
+               mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[MT_TXQ_PSD], false);
+               mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[MT_TXQ_BE], false);
+       }
+}
+
+static void
+mt7915_mac_tx_free_done(struct mt7915_dev *dev,
+                       struct list_head *free_list, bool wake)
+{
+       struct sk_buff *skb, *tmp;
+
+       mt7915_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);
+       }
+}
+
 static void
 mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
 {
        struct mt7915_tx_free *free = (struct mt7915_tx_free *)data;
        struct mt76_dev *mdev = &dev->mt76;
-       struct mt76_phy *mphy_ext = mdev->phy2;
        struct mt76_txwi_cache *txwi;
        struct ieee80211_sta *sta = NULL;
        LIST_HEAD(free_list);
-       struct sk_buff *skb, *tmp;
        void *end = data + len;
        bool v3, wake = false;
        u16 total, count = 0;
        u32 txd = le32_to_cpu(free->txd);
        __le32 *cur_info;
 
-       /* 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 (mphy_ext) {
-               mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[MT_TXQ_PSD], false);
-               mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[MT_TXQ_BE], false);
-       }
+       mt7915_mac_tx_free_prepare(dev);
 
        total = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT);
        v3 = (FIELD_GET(MT_TX_FREE_VER, txd) == 0x4);
                }
        }
 
-       mt7915_mac_sta_poll(dev);
+       mt7915_mac_tx_free_done(dev, &free_list, wake);
+}
 
-       if (wake)
-               mt76_set_tx_blocked(&dev->mt76, false);
+static void
+mt7915_mac_tx_free_v0(struct mt7915_dev *dev, void *data, int len)
+{
+       struct mt7915_tx_free *free = (struct mt7915_tx_free *)data;
+       struct mt76_dev *mdev = &dev->mt76;
+       __le16 *info = (__le16 *)free->info;
+       void *end = data + len;
+       LIST_HEAD(free_list);
+       bool wake = false;
+       u8 i, count;
 
-       mt76_worker_schedule(&dev->mt76.tx_worker);
+       mt7915_mac_tx_free_prepare(dev);
 
-       list_for_each_entry_safe(skb, tmp, &free_list, list) {
-               skb_list_del_init(skb);
-               napi_consume_skb(skb, 1);
+       count = FIELD_GET(MT_TX_FREE_MSDU_CNT_V0, le16_to_cpu(free->ctrl));
+       if (WARN_ON_ONCE((void *)&info[count] > end))
+               return;
+
+       for (i = 0; i < count; i++) {
+               struct mt76_txwi_cache *txwi;
+               u16 msdu = le16_to_cpu(info[i]);
+
+               txwi = mt76_token_release(mdev, msdu, &wake);
+               if (!txwi)
+                       continue;
+
+               mt7915_txwi_free(dev, txwi, NULL, &free_list);
        }
+
+       mt7915_mac_tx_free_done(dev, &free_list, wake);
 }
 
 static bool
        case PKT_TYPE_TXRX_NOTIFY:
                mt7915_mac_tx_free(dev, data, len);
                return false;
+       case PKT_TYPE_TXRX_NOTIFY_V0:
+               mt7915_mac_tx_free_v0(dev, data, len);
+               return false;
        case PKT_TYPE_TXS:
                for (rxd += 2; rxd + 8 <= end; rxd += 8)
                    mt7915_mac_add_txs(dev, rxd);
                mt7915_mac_tx_free(dev, skb->data, skb->len);
                napi_consume_skb(skb, 1);
                break;
+       case PKT_TYPE_TXRX_NOTIFY_V0:
+               mt7915_mac_tx_free_v0(dev, skb->data, skb->len);
+               napi_consume_skb(skb, 1);
+               break;
        case PKT_TYPE_RX_EVENT:
                mt7915_mcu_rx_event(dev, skb);
                break;
 
        PKT_TYPE_TXRX_NOTIFY,
        PKT_TYPE_RX_EVENT,
        PKT_TYPE_RX_FW_MONITOR = 0x0c,
+       PKT_TYPE_TXRX_NOTIFY_V0 = 0x18,
 };
 
 /* RXD DW1 */
 
 #define MT_TX_FREE_VER                 GENMASK(18, 16)
 #define MT_TX_FREE_MSDU_CNT            GENMASK(9, 0)
+#define MT_TX_FREE_MSDU_CNT_V0 GENMASK(6, 0)
 #define MT_TX_FREE_WLAN_ID             GENMASK(23, 14)
 #define MT_TX_FREE_LATENCY             GENMASK(12, 0)
 /* 0: success, others: dropped */
 
        return ret;
 }
 
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+static int
+mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_sta *sta,
+                            struct net_device_path_ctx *ctx,
+                            struct net_device_path *path)
+{
+       struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+       struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+       struct mt7915_dev *dev = mt7915_hw_dev(hw);
+       struct mt7915_phy *phy = mt7915_hw_phy(hw);
+       struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+
+       if (!mtk_wed_device_active(wed))
+               return -ENODEV;
+
+       if (msta->wcid.idx > 0xff)
+               return -EIO;
+
+       path->type = DEV_PATH_MTK_WDMA;
+       path->dev = ctx->dev;
+       path->mtk_wdma.wdma_idx = wed->wdma_idx;
+       path->mtk_wdma.bss = mvif->mt76.idx;
+       path->mtk_wdma.wcid = msta->wcid.idx;
+       path->mtk_wdma.queue = phy != &dev->phy;
+
+       ctx->dev = NULL;
+
+       return 0;
+}
+#endif
+
 const struct ieee80211_ops mt7915_ops = {
        .tx = mt7915_tx,
        .start = mt7915_start,
        .sta_add_debugfs = mt7915_sta_add_debugfs,
 #endif
        .set_radar_background = mt7915_set_radar_background,
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+       .net_fill_forward_path = mt7915_net_fill_forward_path,
+#endif
 };
 
        if (ret)
                return ret;
 
+       if (mtk_wed_device_active(&dev->mt76.mmio.wed))
+               mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(CAPABILITY), 0, 0, 0);
+
        ret = mt7915_mcu_set_mwds(dev, 1);
        if (ret)
                return ret;
 
 static void mt7915_irq_tasklet(struct tasklet_struct *t)
 {
        struct mt7915_dev *dev = from_tasklet(dev, t, irq_tasklet);
+       struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
        u32 intr, intr1, mask;
 
-       mt76_wr(dev, MT_INT_MASK_CSR, 0);
-       if (dev->hif2)
-               mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+       if (mtk_wed_device_active(wed)) {
+               mtk_wed_device_irq_set_mask(wed, 0);
+               intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask);
+       } else {
+               mt76_wr(dev, MT_INT_MASK_CSR, 0);
+               if (dev->hif2)
+                       mt76_wr(dev, MT_INT1_MASK_CSR, 0);
 
-       intr = mt76_rr(dev, MT_INT_SOURCE_CSR);
-       intr &= dev->mt76.mmio.irqmask;
-       mt76_wr(dev, MT_INT_SOURCE_CSR, intr);
+               intr = mt76_rr(dev, MT_INT_SOURCE_CSR);
+               intr &= dev->mt76.mmio.irqmask;
+               mt76_wr(dev, MT_INT_SOURCE_CSR, intr);
+       }
 
        if (dev->hif2) {
                intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR);
 irqreturn_t mt7915_irq_handler(int irq, void *dev_instance)
 {
        struct mt7915_dev *dev = dev_instance;
+       struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
 
-       mt76_wr(dev, MT_INT_MASK_CSR, 0);
-       if (dev->hif2)
-               mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+       if (mtk_wed_device_active(wed)) {
+               mtk_wed_device_irq_set_mask(wed, 0);
+       } else {
+               mt76_wr(dev, MT_INT_MASK_CSR, 0);
+               if (dev->hif2)
+                       mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+       }
 
        if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
                return IRQ_NONE;
 
 void mt7915_wfsys_reset(struct mt7915_dev *dev);
 irqreturn_t mt7915_irq_handler(int irq, void *dev_instance);
 u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif);
+u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
+
 int mt7915_register_device(struct mt7915_dev *dev);
 void mt7915_unregister_device(struct mt7915_dev *dev);
 int mt7915_eeprom_init(struct mt7915_dev *dev);
 
 #include "mac.h"
 #include "../trace.h"
 
+static bool wed_enable = false;
+module_param(wed_enable, bool, 0644);
+
 static LIST_HEAD(hif_list);
 static DEFINE_SPINLOCK(hif_lock);
 static u32 hif_idx;
        return 0;
 }
 
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+static int mt7915_wed_offload_enable(struct mtk_wed_device *wed)
+{
+       struct mt7915_dev *dev;
+       int ret;
+
+       dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+
+       spin_lock_bh(&dev->mt76.token_lock);
+       dev->mt76.token_size = wed->wlan.token_start;
+       spin_unlock_bh(&dev->mt76.token_lock);
+
+       ret = wait_event_timeout(dev->mt76.tx_wait,
+                                !dev->mt76.wed_token_count, HZ);
+       if (!ret)
+               return -EAGAIN;
+
+       return 0;
+}
+
+static void mt7915_wed_offload_disable(struct mtk_wed_device *wed)
+{
+       struct mt7915_dev *dev;
+
+       dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+
+       spin_lock_bh(&dev->mt76.token_lock);
+       dev->mt76.token_size = MT7915_TOKEN_SIZE;
+       spin_unlock_bh(&dev->mt76.token_lock);
+}
+#endif
+
+static int
+mt7915_pci_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, int *irq)
+{
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+       struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+       int ret;
+
+       if (!wed_enable)
+               return 0;
+
+       wed->wlan.pci_dev = pdev;
+       wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) +
+                              MT_WFDMA_EXT_CSR_BASE;
+       wed->wlan.nbuf = 4096;
+       wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf;
+       wed->wlan.init_buf = mt7915_wed_init_buf;
+       wed->wlan.offload_enable = mt7915_wed_offload_enable;
+       wed->wlan.offload_disable = mt7915_wed_offload_disable;
+
+       if (mtk_wed_device_attach(wed) != 0)
+               return 0;
+
+       *irq = wed->irq;
+       dev->mt76.dma_dev = wed->dev;
+
+       ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32));
+       if (ret)
+               return ret;
+
+       return 1;
+#else
+       return 0;
+#endif
+}
+
 static int mt7915_pci_probe(struct pci_dev *pdev,
                            const struct pci_device_id *id)
 {
+       struct mt7915_hif *hif2 = NULL;
        struct mt7915_dev *dev;
        struct mt76_dev *mdev;
-       struct mt7915_hif *hif2;
        int irq;
        int ret;
 
        mt7915_wfsys_reset(dev);
        hif2 = mt7915_pci_init_hif2(pdev);
 
-       ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+       ret = mt7915_pci_wed_init(dev, pdev, &irq);
        if (ret < 0)
-               goto free_device;
+               goto free_wed_or_irq_vector;
+
+       if (!ret) {
+               hif2 = mt7915_pci_init_hif2(pdev);
+
+               ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+               if (ret < 0)
+                       goto free_device;
+
+               irq = pdev->irq;
+       }
 
-       irq = pdev->irq;
        ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler,
                               IRQF_SHARED, KBUILD_MODNAME, dev);
        if (ret)
-               goto free_irq_vector;
+               goto free_wed_or_irq_vector;
 
        /* master switch of PCIe tnterrupt enable */
        mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
        if (dev->hif2)
                put_device(dev->hif2->dev);
        devm_free_irq(mdev->dev, irq, dev);
-free_irq_vector:
-       pci_free_irq_vectors(pdev);
+free_wed_or_irq_vector:
+       if (mtk_wed_device_active(&mdev->mmio.wed))
+               mtk_wed_device_detach(&mdev->mmio.wed);
+       else
+               pci_free_irq_vectors(pdev);
 free_device:
        mt76_free_device(&dev->mt76);
 
 
 
 /* WFDMA CSR */
 #define MT_WFDMA_EXT_CSR_BASE          __REG(WFDMA_EXT_CSR_ADDR)
+#define MT_WFDMA_EXT_CSR_PHYS_BASE     0x18027000
 #define MT_WFDMA_EXT_CSR(ofs)          (MT_WFDMA_EXT_CSR_BASE + (ofs))
+#define MT_WFDMA_EXT_CSR_PHYS(ofs)     (MT_WFDMA_EXT_CSR_PHYS_BASE + (ofs))
 
-#define MT_WFDMA_HOST_CONFIG           MT_WFDMA_EXT_CSR(0x30)
+#define MT_WFDMA_HOST_CONFIG           MT_WFDMA_EXT_CSR_PHYS(0x30)
 #define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0)
+#define MT_WFDMA_HOST_CONFIG_WED       BIT(1)
 
-#define MT_WFDMA_EXT_CSR_HIF_MISC      MT_WFDMA_EXT_CSR(0x44)
+#define MT_WFDMA_WED_RING_CONTROL      MT_WFDMA_EXT_CSR_PHYS(0x34)
+#define MT_WFDMA_WED_RING_CONTROL_TX0  GENMASK(4, 0)
+#define MT_WFDMA_WED_RING_CONTROL_TX1  GENMASK(12, 8)
+#define MT_WFDMA_WED_RING_CONTROL_RX1  GENMASK(20, 16)
+
+#define MT_WFDMA_EXT_CSR_HIF_MISC      MT_WFDMA_EXT_CSR_PHYS(0x44)
 #define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0)
 
 #define MT_PCIE_RECOG_ID               0xd7090
 #define MT_PCIE_RECOG_ID_MASK          GENMASK(30, 0)
 #define MT_PCIE_RECOG_ID_SEM           BIT(31)
 
+#define MT_INT_WED_MASK_CSR            MT_WFDMA_EXT_CSR(0x204)
+
+#define MT_WED_TX_RING_BASE            MT_WFDMA_EXT_CSR(0x300)
+#define MT_WED_RX_RING_BASE            MT_WFDMA_EXT_CSR(0x400)
+
 /* WFDMA0 PCIE1 */
 #define MT_WFDMA0_PCIE1_BASE           __REG(WFDMA0_PCIE1_ADDR)
 #define MT_WFDMA0_PCIE1(ofs)           (MT_WFDMA0_PCIE1_BASE + (ofs))
 
 {
        int i, err;
 
-       err = mt76_init_tx_queue(phy->mt76, 0, idx, n_desc, MT_TX_RING_BASE);
+       err = mt76_init_tx_queue(phy->mt76, 0, idx, n_desc, MT_TX_RING_BASE, 0);
        if (err < 0)
                return err;
 
 
        if (token >= 0)
                dev->token_count++;
 
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+       if (mtk_wed_device_active(&dev->mmio.wed) &&
+           token >= dev->mmio.wed.wlan.token_start)
+               dev->wed_token_count++;
+#endif
+
        if (dev->token_count >= dev->token_size - MT76_TOKEN_FREE_THR)
                __mt76_set_tx_blocked(dev, true);
 
        spin_lock_bh(&dev->token_lock);
 
        txwi = idr_remove(&dev->token, token);
-       if (txwi)
+       if (txwi) {
                dev->token_count--;
 
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+               if (mtk_wed_device_active(&dev->mmio.wed) &&
+                   token >= dev->mmio.wed.wlan.token_start &&
+                   --dev->wed_token_count == 0)
+                       wake_up(&dev->tx_wait);
+#endif
+       }
+
        if (dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR &&
            dev->phy.q_tx[0]->blocked)
                *wake = true;