[rdkb][common][bsp][Refactor and sync wifi from openwrt]
[Description]
cc3c51c7 [MAC80211][wifi6][Release][Update MT7986 Firmware]
ea393925 [MAC80211[WiFi6/7]][hostapd][Avoid unnecessary beacon update]
6763be22 [MAC80211][wifi7][Misc][Sync Internal patches to External Release Folder]
d3ed9bc8 [mac80211][wifi7][mt76][Rebase WED patch due to Cheetah MT76 modifications]
2f4b03c3 [mac80211][wifi6][mt76][Bring-up hardware path for Cheetah MT76]
cb573d78 [MAC80211][WiFi7][app][Add ibf atenl support for eagle]
04fe6893 [mac80211][wifi6][mt76][Fix rebase errors]
c301e69a [MAC80211][WiFi7][mt76][Update Kite EEPROM bin files]
463b00fb [MAC80211][WiFi7][infra][Add slot1 for kite]
8774388d [MAC80211][WiFi7][misc][Prevent deadlock in a multiple AP+STA mode]
68d43fea [MAC80211][hostapd][show acs channels config]
5aca83c6 [MAC80211][hostapd][Prevent hostapd from setting beacon too frequently]
636da369 [MAC80211][wifi6/7][misc][use current epoch date]
9eb3456f [MAC80211][hostapd][refactor AP/STA CSA handling flow]
5543f3d8 [MAC80211][WiFi6][mt76][Refactor assignment of STA BSS group]
c5631fc2 [mac80211][mt76][wifi6][Add debugfs knob for RTS threshold]
6a92a2d4 [mac80211][wifi6][mt76][Bring-up software path for Cheetah MT76]
bf66c519 [MAC80211][netifd][Remove unnecessary netifd patch]
2f141c75 [mac80211][mt76][wifi6/7][Fix build failed]
cf30db1e [mac80211[hostapd][wifi7][Fix build fial]
dc391fc0 [MAC80211][wifi6][Release][Update MT7986 Firmware]
71a7b95a [MAC80211][WiFi7][mt76][Add kite fw & eeprom to eagle codebase]
5a7bd025 [MAC80211][WiFi6][misc][Add mt7981 default eeprom bin]
e40da697 [MAC80211][WED][Fix reinsert wifi module cause memory leak issue]
0a22f8f4 [MAC80211][WiFi7][misc][fix mt7988-mt7996-mac980211 release build fail]
25f3fe1c [[Eagle][SQC3.0][BE19000][MT7996][E2][MT7976_MT7977][256][ePA][MT7988A][WiFi] [MAP][SnS][Muti-client][UES]Traffic stuck in Agent2 with invalid tid 8 found]
f59b5dad [MAC80211][WiFi6][mt76][Add additional chain signal info in station dump for merlin]
fada400d [MAC80211][WiFi6/7][mt76][Add HT40- capab when enable ACS]
98e273af [MAC80211][WiFi6/7][app][Add SKU_EN & RSSI ATTR in atenl]
aaa8eb14 [MAC80211][WiFi6][mt76][Add rssi & sku_en in testmode]
eda14aac [MAC80211][core][Remove wrong assignment on the variable "changed" when changing beacon]
000329aa [MAC80211][netifd][Move netifd patch for wifi7 used only]
57bfe0c7 [MAC80211][WiFi6][misc][fix build fail due to mt76 upgration]
fa29bb39 [MAC80211][wifi6/7][mt76][update mt76 Makefile]
56f497ec [MAC80211][netifd][not to cache previous config to avoid wifi down failure]
be9abd4d [MAC80211][WiFi6][misc][fix build fail due to netifd]
af71e303 [MAC80211][WiFi6][mt76][Fix inconsistent BSS group setting of STA for VoW]
3e42972a [MAC80211][WiFi6][mt76][Fix txpower bbp CR]
81a68c03 [MAC80211][wifi7][hostapd][rebase internal hostapd patches]
336300b7 [MAC80211][WiFi6/7][hostapd][MAX 48 mbss 6G 连线概率连不上 ]
6bebc554 [MAC80211][wifi7][core][update for backports v6.5]
19daecfd [MAC80211][wifi7][ucode][Bandwidth Synchronization in AP/STA Mode]
ddf64afb [MAC80211][mt76][add debug log in SER flow]
44611a77 [mac80211][wifi6][mt76][Enhance debug log]
[Release-log]
Change-Id: Ibf5e835de5563fff4101a77e81056f696286670b
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-wed-sync-to-wed-upstream.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-wed-sync-to-wed-upstream.patch
new file mode 100644
index 0000000..25dfc5d
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-wed-sync-to-wed-upstream.patch
@@ -0,0 +1,2740 @@
+From 4709ca02ba0332508ac6885acbc779bdfac3f0be Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Fri, 6 Oct 2023 21:20:25 +0800
+Subject: [PATCH] wifi: mt76: wed: sync to wed upstream
+
+---
+ dma.c | 219 +++++++++++++++++++++++++++------------
+ dma.h | 12 +++
+ mac80211.c | 19 +++-
+ mmio.c | 97 +++++++++++++++++
+ mt76.h | 102 ++++++++++++++++--
+ mt7603/dma.c | 9 +-
+ mt7615/dma.c | 6 +-
+ mt76_connac.h | 3 +-
+ mt76_connac_mac.c | 5 +-
+ mt76x02_mmio.c | 5 +-
+ mt7915/dma.c | 18 ++--
+ mt7915/main.c | 16 +--
+ mt7915/mmio.c | 107 +------------------
+ mt7921/pci.c | 2 +-
+ mt7925/pci.c | 2 +-
+ mt7996/dma.c | 258 ++++++++++++++++++++++++++++++++++++++++++----
+ mt7996/init.c | 156 ++++++++++++++++++++++++++--
+ mt7996/mac.c | 72 +++++++++++--
+ mt7996/main.c | 42 ++++++++
+ mt7996/mcu.c | 13 ++-
+ mt7996/mmio.c | 208 +++++++++++++++++++++++++++++++++----
+ mt7996/mt7996.h | 67 +++++++++++-
+ mt7996/pci.c | 72 ++++++++++---
+ mt7996/regs.h | 65 +++++++++++-
+ 24 files changed, 1276 insertions(+), 299 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 643e18e..dd20271 100644
+--- a/dma.c
++++ b/dma.c
+@@ -9,11 +9,11 @@
+
+ #if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)
+
+-#define Q_READ(_dev, _q, _field) ({ \
++#define Q_READ(_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, \
++ _val = mtk_wed_device_reg_read((_q)->wed, \
+ ((_q)->wed_regs + \
+ _offset)); \
+ else \
+@@ -21,10 +21,10 @@
+ _val; \
+ })
+
+-#define Q_WRITE(_dev, _q, _field, _val) do { \
++#define Q_WRITE(_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, \
++ mtk_wed_device_reg_write((_q)->wed, \
+ ((_q)->wed_regs + _offset), \
+ _val); \
+ else \
+@@ -33,8 +33,8 @@
+
+ #else
+
+-#define Q_READ(_dev, _q, _field) readl(&(_q)->regs->_field)
+-#define Q_WRITE(_dev, _q, _field, _val) writel(_val, &(_q)->regs->_field)
++#define Q_READ(_q, _field) readl(&(_q)->regs->_field)
++#define Q_WRITE(_q, _field, _val) writel(_val, &(_q)->regs->_field)
+
+ #endif
+
+@@ -188,40 +188,63 @@ EXPORT_SYMBOL_GPL(mt76_free_pending_rxwi);
+ static void
+ mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
+ {
+- Q_WRITE(dev, q, desc_base, q->desc_dma);
+- Q_WRITE(dev, q, ring_size, q->ndesc);
+- q->head = Q_READ(dev, q, dma_idx);
++ Q_WRITE(q, desc_base, q->desc_dma);
++ if (q->flags & MT_QFLAG_WED_RRO_EN)
++ Q_WRITE(q, ring_size, MT_DMA_RRO_EN | q->ndesc);
++ else
++ Q_WRITE(q, ring_size, q->ndesc);
++ q->head = Q_READ(q, dma_idx);
+ q->tail = q->head;
+ }
+
+ static void
+-mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
++__mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
++ bool reset_idx)
+ {
+- int i;
+-
+ if (!q || !q->ndesc)
+ return;
+
+- /* clear descriptors */
+- for (i = 0; i < q->ndesc; i++)
+- q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
++ if (!mt76_queue_is_wed_rro_ind(q)) {
++ int i;
+
+- Q_WRITE(dev, q, cpu_idx, 0);
+- Q_WRITE(dev, q, dma_idx, 0);
++ /* clear descriptors */
++ for (i = 0; i < q->ndesc; i++)
++ q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
++ }
++
++ if (reset_idx) {
++ Q_WRITE(q, cpu_idx, 0);
++ Q_WRITE(q, dma_idx, 0);
++ }
+ mt76_dma_sync_idx(dev, q);
+ }
+
++static void
++mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
++{
++ __mt76_dma_queue_reset(dev, q, true);
++}
++
+ static int
+ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ struct mt76_queue_buf *buf, void *data)
+ {
+- struct mt76_desc *desc = &q->desc[q->head];
+ struct mt76_queue_entry *entry = &q->entry[q->head];
+ struct mt76_txwi_cache *txwi = NULL;
++ struct mt76_desc *desc;
+ u32 buf1 = 0, ctrl;
+ int idx = q->head;
+ int rx_token;
+
++ if (mt76_queue_is_wed_rro_ind(q)) {
++ struct mt76_wed_rro_desc *rro_desc;
++
++ rro_desc = (struct mt76_wed_rro_desc *)q->desc;
++ data = &rro_desc[q->head];
++ goto done;
++ }
++
++ desc = &q->desc[q->head];
+ ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
+
+ if (mt76_queue_is_wed_rx(q)) {
+@@ -244,6 +267,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
+ WRITE_ONCE(desc->info, 0);
+
++done:
+ entry->dma_addr[0] = buf->addr;
+ entry->dma_len[0] = buf->len;
+ entry->txwi = txwi;
+@@ -343,7 +367,7 @@ static void
+ mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ {
+ wmb();
+- Q_WRITE(dev, q, cpu_idx, q->head);
++ Q_WRITE(q, cpu_idx, q->head);
+ }
+
+ static void
+@@ -359,7 +383,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
+ if (flush)
+ last = -1;
+ else
+- last = Q_READ(dev, q, dma_idx);
++ last = Q_READ(q, dma_idx);
+
+ while (q->queued > 0 && q->tail != last) {
+ mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry);
+@@ -371,7 +395,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
+ }
+
+ if (!flush && q->tail == last)
+- last = Q_READ(dev, q, dma_idx);
++ last = Q_READ(q, dma_idx);
+ }
+ spin_unlock_bh(&q->cleanup_lock);
+
+@@ -392,10 +416,14 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ {
+ struct mt76_queue_entry *e = &q->entry[idx];
+ struct mt76_desc *desc = &q->desc[idx];
+- void *buf;
++ void *buf = e->buf;
++ u32 ctrl;
+
++ if (mt76_queue_is_wed_rro_ind(q))
++ goto done;
++
++ ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+ if (len) {
+- u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+ *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl);
+ *more = !(ctrl & MT_DMA_CTL_LAST_SEC0);
+ }
+@@ -403,6 +431,12 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ if (info)
+ *info = le32_to_cpu(desc->info);
+
++ if (drop) {
++ *drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A | MT_DMA_CTL_DROP));
++ if (ctrl & MT_DMA_CTL_VER_MASK)
++ *drop = !!(ctrl & MT_DMA_CTL_PN_CHK_FAIL);
++ }
++
+ if (mt76_queue_is_wed_rx(q)) {
+ u32 buf1 = le32_to_cpu(desc->buf1);
+ u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
+@@ -420,23 +454,16 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ t->ptr = NULL;
+
+ mt76_put_rxwi(dev, t);
+-
+- if (drop) {
+- u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+-
+- *drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A |
+- MT_DMA_CTL_DROP));
+-
++ if (drop)
+ *drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
+- }
+ } else {
+- buf = e->buf;
+- e->buf = NULL;
+ dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
+ SKB_WITH_OVERHEAD(q->buf_size),
+ page_pool_get_dma_dir(q->page_pool));
+ }
+
++done:
++ e->buf = NULL;
+ return buf;
+ }
+
+@@ -450,11 +477,16 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ if (!q->queued)
+ return NULL;
+
+- if (flush)
+- q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+- else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
++ if (mt76_queue_is_wed_rro_data(q))
+ return NULL;
+
++ if (!mt76_queue_is_wed_rro_ind(q)) {
++ if (flush)
++ q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE);
++ else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
++ return NULL;
++ }
++
+ q->tail = (q->tail + 1) % q->ndesc;
+ q->queued--;
+
+@@ -606,11 +638,14 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ spin_lock_bh(&q->lock);
+
+ while (q->queued < q->ndesc - 1) {
++ struct mt76_queue_buf qbuf = {};
+ enum dma_data_direction dir;
+- struct mt76_queue_buf qbuf;
+ dma_addr_t addr;
+ int offset;
+- void *buf;
++ void *buf = NULL;
++
++ if (mt76_queue_is_wed_rro_ind(q))
++ goto done;
+
+ buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
+ if (!buf)
+@@ -621,6 +656,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
+
+ qbuf.addr = addr + q->buf_offset;
++done:
+ qbuf.len = len - q->buf_offset;
+ qbuf.skip_unmap = false;
+ if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
+@@ -630,7 +666,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ frames++;
+ }
+
+- if (frames)
++ if (frames || mt76_queue_is_wed_rx(q))
+ mt76_dma_kick_queue(dev, q);
+
+ spin_unlock_bh(&q->lock);
+@@ -641,15 +677,14 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ {
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+- struct mtk_wed_device *wed = &dev->mmio.wed;
+- int ret, type, ring;
+- u8 flags;
++ int ret = 0, type, ring;
++ u16 flags;
+
+ if (!q || !q->ndesc)
+ return -EINVAL;
+
+ flags = q->flags;
+- if (!mtk_wed_device_active(wed))
++ if (!q->wed || !mtk_wed_device_active(q->wed))
+ q->flags &= ~MT_QFLAG_WED;
+
+ if (!(q->flags & MT_QFLAG_WED))
+@@ -660,29 +695,52 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+
+ switch (type) {
+ case MT76_WED_Q_TX:
+- ret = mtk_wed_device_tx_ring_setup(wed, ring, q->regs, reset);
++ ret = mtk_wed_device_tx_ring_setup(q->wed, ring, q->regs,
++ reset);
+ if (!ret)
+- q->wed_regs = wed->tx_ring[ring].reg_base;
++ q->wed_regs = q->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, false);
+- q->flags = flags;
+
+- ret = mtk_wed_device_txfree_ring_setup(wed, q->regs);
++ ret = mtk_wed_device_txfree_ring_setup(q->wed, q->regs);
+ if (!ret)
+- q->wed_regs = wed->txfree_ring.reg_base;
++ q->wed_regs = q->wed->txfree_ring.reg_base;
+ break;
+ case MT76_WED_Q_RX:
+- ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs, reset);
++ ret = mtk_wed_device_rx_ring_setup(q->wed, ring, q->regs,
++ reset);
+ if (!ret)
+- q->wed_regs = wed->rx_ring[ring].reg_base;
++ q->wed_regs = q->wed->rx_ring[ring].reg_base;
++ break;
++ case MT76_WED_RRO_Q_DATA:
++ q->flags &= ~MT_QFLAG_WED;
++ __mt76_dma_queue_reset(dev, q, false);
++ mtk_wed_device_rro_rx_ring_setup(q->wed, ring, q->regs);
++ q->head = q->ndesc - 1;
++ q->queued = q->head;
++ break;
++ case MT76_WED_RRO_Q_MSDU_PG:
++ q->flags &= ~MT_QFLAG_WED;
++ __mt76_dma_queue_reset(dev, q, false);
++ mtk_wed_device_msdu_pg_rx_ring_setup(q->wed, ring, q->regs);
++ q->head = q->ndesc - 1;
++ q->queued = q->head;
++ break;
++ case MT76_WED_RRO_Q_IND:
++ q->flags &= ~MT_QFLAG_WED;
++ mt76_dma_queue_reset(dev, q);
++ mt76_dma_rx_fill(dev, q, false);
++ mtk_wed_device_ind_rx_ring_setup(q->wed, q->regs);
+ break;
+ default:
+ ret = -EINVAL;
++ break;
+ }
++ q->flags = flags;
+
+ return ret;
+ #else
+@@ -706,11 +764,26 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ 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);
++ size = mt76_queue_is_wed_rro_ind(q) ? sizeof(struct mt76_wed_rro_desc)
++ : sizeof(struct mt76_desc);
++ q->desc = dmam_alloc_coherent(dev->dma_dev, q->ndesc * size,
++ &q->desc_dma, GFP_KERNEL);
+ if (!q->desc)
+ return -ENOMEM;
+
++ if (mt76_queue_is_wed_rro_ind(q)) {
++ struct mt76_wed_rro_desc *rro_desc;
++ int i;
++
++ rro_desc = (struct mt76_wed_rro_desc *)q->desc;
++ for (i = 0; i < q->ndesc; i++) {
++ struct mt76_wed_rro_ind *cmd;
++
++ cmd = (struct mt76_wed_rro_ind *)&rro_desc[i];
++ cmd->magic_cnt = MT_DMA_WED_IND_CMD_CNT - 1;
++ }
++ }
++
+ size = q->ndesc * sizeof(*q->entry);
+ q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
+ if (!q->entry)
+@@ -724,8 +797,13 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ if (ret)
+ return ret;
+
+- if (q->flags != MT_WED_Q_TXFREE)
+- mt76_dma_queue_reset(dev, q);
++ if (mtk_wed_device_active(&dev->mmio.wed)) {
++ if ((mtk_wed_get_rx_capa(&dev->mmio.wed) && mt76_queue_is_wed_rro(q)) ||
++ mt76_queue_is_wed_tx_free(q))
++ return 0;
++ }
++
++ mt76_dma_queue_reset(dev, q);
+
+ return 0;
+ }
+@@ -746,7 +824,8 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ if (!buf)
+ break;
+
+- mt76_put_page_pool_buf(buf, false);
++ if (!mt76_queue_is_wed_rro(q))
++ mt76_put_page_pool_buf(buf, false);
+ } while (1);
+
+ if (q->rx_head) {
+@@ -761,19 +840,22 @@ static void
+ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+ {
+ struct mt76_queue *q = &dev->q_rx[qid];
+- int i;
+
+ if (!q->ndesc)
+ return;
+
+- for (i = 0; i < q->ndesc; i++)
+- q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
++ if (!mt76_queue_is_wed_rro_ind(q)) {
++ int i;
++
++ for (i = 0; i < q->ndesc; i++)
++ q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
++ }
+
+ mt76_dma_rx_cleanup(dev, q);
+
+ /* reset WED rx queues */
+ mt76_dma_wed_setup(dev, q, true);
+- if (q->flags != MT_WED_Q_TXFREE) {
++ if (!mt76_queue_is_wed_tx_free(q)) {
+ mt76_dma_sync_idx(dev, q);
+ mt76_dma_rx_fill(dev, q, false);
+ }
+@@ -816,8 +898,8 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ bool more;
+
+ if (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
+- q->flags == MT_WED_Q_TXFREE) {
+- dma_idx = Q_READ(dev, q, dma_idx);
++ mt76_queue_is_wed_tx_free(q)) {
++ dma_idx = Q_READ(q, dma_idx);
+ check_ddone = true;
+ }
+
+@@ -827,7 +909,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+
+ if (check_ddone) {
+ if (q->tail == dma_idx)
+- dma_idx = Q_READ(dev, q, dma_idx);
++ dma_idx = Q_READ(q, dma_idx);
+
+ if (q->tail == dma_idx)
+ break;
+@@ -979,16 +1061,23 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+ mt76_for_each_q_rx(dev, i) {
+ struct mt76_queue *q = &dev->q_rx[i];
+
++ if (mtk_wed_device_active(&dev->mmio.wed) &&
++ mt76_queue_is_wed_rro(q))
++ continue;
++
+ netif_napi_del(&dev->napi[i]);
+ mt76_dma_rx_cleanup(dev, q);
+
+ page_pool_destroy(q->page_pool);
+ }
+
+- mt76_free_pending_txwi(dev);
+- mt76_free_pending_rxwi(dev);
+-
+ if (mtk_wed_device_active(&dev->mmio.wed))
+ mtk_wed_device_detach(&dev->mmio.wed);
++
++ if (mtk_wed_device_active(&dev->mmio.wed_hif2))
++ mtk_wed_device_detach(&dev->mmio.wed_hif2);
++
++ mt76_free_pending_txwi(dev);
++ mt76_free_pending_rxwi(dev);
+ }
+ EXPORT_SYMBOL_GPL(mt76_dma_cleanup);
+diff --git a/dma.h b/dma.h
+index 1b090d7..22b79d5 100644
+--- a/dma.h
++++ b/dma.h
+@@ -25,6 +25,13 @@
+ #define MT_DMA_PPE_ENTRY GENMASK(30, 16)
+ #define MT_DMA_INFO_PPE_VLD BIT(31)
+
++#define MT_DMA_CTL_PN_CHK_FAIL BIT(13)
++#define MT_DMA_CTL_VER_MASK BIT(7)
++
++#define MT_DMA_RRO_EN BIT(13)
++
++#define MT_DMA_WED_IND_CMD_CNT 8
++
+ #define MT_DMA_HDR_LEN 4
+ #define MT_RX_INFO_LEN 4
+ #define MT_FCE_INFO_LEN 4
+@@ -37,6 +44,11 @@ struct mt76_desc {
+ __le32 info;
+ } __packed __aligned(4);
+
++struct mt76_wed_rro_desc {
++ __le32 buf0;
++ __le32 buf1;
++} __packed __aligned(4);
++
+ enum mt76_qsel {
+ MT_QSEL_MGMT,
+ MT_QSEL_HCCA,
+diff --git a/mac80211.c b/mac80211.c
+index 12fcb2b..cd102dd 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1726,7 +1726,7 @@ EXPORT_SYMBOL_GPL(mt76_get_antenna);
+
+ struct mt76_queue *
+ mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
+- int ring_base, u32 flags)
++ int ring_base, void *wed, u32 flags)
+ {
+ struct mt76_queue *hwq;
+ int err;
+@@ -1736,6 +1736,7 @@ mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
+ return ERR_PTR(-ENOMEM);
+
+ hwq->flags = flags;
++ hwq->wed = wed;
+
+ err = dev->queue_ops->alloc(dev, hwq, idx, n_desc, 0, ring_base);
+ if (err < 0)
+@@ -1843,3 +1844,19 @@ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
+ return MT_DFS_STATE_ACTIVE;
+ }
+ EXPORT_SYMBOL_GPL(mt76_phy_dfs_state);
++
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++int mt76_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct net_device *netdev, enum tc_setup_type type,
++ void *type_data)
++{
++ struct mt76_phy *phy = hw->priv;
++ struct mtk_wed_device *wed = &phy->dev->mmio.wed;
++
++ if (!mtk_wed_device_active(wed))
++ return -EOPNOTSUPP;
++
++ return mtk_wed_device_setup_tc(wed, netdev, type, type_data);
++}
++EXPORT_SYMBOL_GPL(mt76_net_setup_tc);
++#endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+diff --git a/mmio.c b/mmio.c
+index 86e3d2a..c346249 100644
+--- a/mmio.c
++++ b/mmio.c
+@@ -4,6 +4,7 @@
+ */
+
+ #include "mt76.h"
++#include "dma.h"
+ #include "trace.h"
+
+ static u32 mt76_mmio_rr(struct mt76_dev *dev, u32 offset)
+@@ -84,6 +85,102 @@ void mt76_set_irq_mask(struct mt76_dev *dev, u32 addr,
+ }
+ EXPORT_SYMBOL_GPL(mt76_set_irq_mask);
+
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++void mt76_mmio_wed_release_rx_buf(struct mtk_wed_device *wed)
++{
++ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
++ int i;
++
++ for (i = 0; i < dev->rx_token_size; i++) {
++ struct mt76_txwi_cache *t;
++
++ t = mt76_rx_token_release(dev, i);
++ if (!t || !t->ptr)
++ continue;
++
++ mt76_put_page_pool_buf(t->ptr, false);
++ t->ptr = NULL;
++
++ mt76_put_rxwi(dev, t);
++ }
++
++ mt76_free_pending_rxwi(dev);
++}
++EXPORT_SYMBOL_GPL(mt76_mmio_wed_release_rx_buf);
++
++u32 mt76_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
++{
++ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
++ struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc;
++ struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
++ int i, len = SKB_WITH_OVERHEAD(q->buf_size);
++ struct mt76_txwi_cache *t = NULL;
++
++ for (i = 0; i < size; i++) {
++ enum dma_data_direction dir;
++ dma_addr_t addr;
++ u32 offset;
++ int token;
++ void *buf;
++
++ t = mt76_get_rxwi(dev);
++ if (!t)
++ goto unmap;
++
++ buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
++ if (!buf)
++ goto unmap;
++
++ addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
++ dir = page_pool_get_dma_dir(q->page_pool);
++ dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
++
++ desc->buf0 = cpu_to_le32(addr);
++ token = mt76_rx_token_consume(dev, buf, t, addr);
++ if (token < 0) {
++ mt76_put_page_pool_buf(buf, false);
++ goto unmap;
++ }
++
++ desc->token |= cpu_to_le32(FIELD_PREP(MT_DMA_CTL_TOKEN,
++ token));
++ desc++;
++ }
++
++ return 0;
++
++unmap:
++ if (t)
++ mt76_put_rxwi(dev, t);
++ mt76_mmio_wed_release_rx_buf(wed);
++
++ return -ENOMEM;
++}
++EXPORT_SYMBOL_GPL(mt76_mmio_wed_init_rx_buf);
++
++int mt76_mmio_wed_offload_enable(struct mtk_wed_device *wed)
++{
++ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
++
++ spin_lock_bh(&dev->token_lock);
++ dev->token_size = wed->wlan.token_start;
++ spin_unlock_bh(&dev->token_lock);
++
++ return !wait_event_timeout(dev->tx_wait, !dev->wed_token_count, HZ);
++}
++EXPORT_SYMBOL_GPL(mt76_mmio_wed_offload_enable);
++
++void mt76_mmio_wed_offload_disable(struct mtk_wed_device *wed)
++{
++ struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
++
++ spin_lock_bh(&dev->token_lock);
++ dev->token_size = dev->drv->token_size;
++ spin_unlock_bh(&dev->token_lock);
++}
++EXPORT_SYMBOL_GPL(mt76_mmio_wed_offload_disable);
++#endif /*CONFIG_NET_MEDIATEK_SOC_WED */
++
+ void mt76_mmio_init(struct mt76_dev *dev, void __iomem *regs)
+ {
+ static const struct mt76_bus_ops mt76_mmio_ops = {
+diff --git a/mt76.h b/mt76.h
+index a238216..7f93210 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -28,15 +28,22 @@
+ #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_QFLAG_WED_TYPE GENMASK(4, 2)
++#define MT_QFLAG_WED BIT(5)
++#define MT_QFLAG_WED_RRO BIT(6)
++#define MT_QFLAG_WED_RRO_EN BIT(7)
+
+ #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_RRO_Q(_type, _n) (MT_QFLAG_WED_RRO | __MT_WED_Q(_type, _n))
++
+ #define MT_WED_Q_TX(_n) __MT_WED_Q(MT76_WED_Q_TX, _n)
+ #define MT_WED_Q_RX(_n) __MT_WED_Q(MT76_WED_Q_RX, _n)
+ #define MT_WED_Q_TXFREE __MT_WED_Q(MT76_WED_Q_TXFREE, 0)
++#define MT_WED_RRO_Q_DATA(_n) __MT_WED_RRO_Q(MT76_WED_RRO_Q_DATA, _n)
++#define MT_WED_RRO_Q_MSDU_PG(_n) __MT_WED_RRO_Q(MT76_WED_RRO_Q_MSDU_PG, _n)
++#define MT_WED_RRO_Q_IND __MT_WED_RRO_Q(MT76_WED_RRO_Q_IND, 0)
+
+ struct mt76_dev;
+ struct mt76_phy;
+@@ -58,6 +65,9 @@ enum mt76_wed_type {
+ MT76_WED_Q_TX,
+ MT76_WED_Q_TXFREE,
+ MT76_WED_Q_RX,
++ MT76_WED_RRO_Q_DATA,
++ MT76_WED_RRO_Q_MSDU_PG,
++ MT76_WED_RRO_Q_IND,
+ };
+
+ struct mt76_bus_ops {
+@@ -106,6 +116,16 @@ enum mt76_rxq_id {
+ MT_RXQ_MAIN_WA,
+ MT_RXQ_BAND2,
+ MT_RXQ_BAND2_WA,
++ MT_RXQ_RRO_BAND0,
++ MT_RXQ_RRO_BAND1,
++ MT_RXQ_RRO_BAND2,
++ MT_RXQ_MSDU_PAGE_BAND0,
++ MT_RXQ_MSDU_PAGE_BAND1,
++ MT_RXQ_MSDU_PAGE_BAND2,
++ MT_RXQ_TXFREE_BAND0,
++ MT_RXQ_TXFREE_BAND1,
++ MT_RXQ_TXFREE_BAND2,
++ MT_RXQ_RRO_IND,
+ __MT_RXQ_MAX
+ };
+
+@@ -183,6 +203,7 @@ struct mt76_queue {
+ spinlock_t lock;
+ spinlock_t cleanup_lock;
+ struct mt76_queue_entry *entry;
++ struct mt76_rro_desc *rro_desc;
+ struct mt76_desc *desc;
+
+ u16 first;
+@@ -196,8 +217,9 @@ struct mt76_queue {
+
+ u8 buf_offset;
+ u8 hw_idx;
+- u8 flags;
++ u16 flags;
+
++ struct mtk_wed_device *wed;
+ u32 wed_regs;
+
+ dma_addr_t desc_dma;
+@@ -352,6 +374,17 @@ struct mt76_txq {
+ bool aggr;
+ };
+
++struct mt76_wed_rro_ind {
++ u32 se_id : 12;
++ u32 rsv : 4;
++ u32 start_sn : 12;
++ u32 ind_reason : 4;
++ u32 ind_cnt : 13;
++ u32 win_sz : 3;
++ u32 rsv2 : 13;
++ u32 magic_cnt : 3;
++};
++
+ struct mt76_txwi_cache {
+ struct list_head list;
+ dma_addr_t dma_addr;
+@@ -602,6 +635,7 @@ struct mt76_mmio {
+ u32 irqmask;
+
+ struct mtk_wed_device wed;
++ struct mtk_wed_device wed_hif2;
+ struct completion wed_reset;
+ struct completion wed_reset_complete;
+ };
+@@ -1046,6 +1080,12 @@ bool ____mt76_poll_msec(struct mt76_dev *dev, u32 offset, u32 mask, u32 val,
+ void mt76_mmio_init(struct mt76_dev *dev, void __iomem *regs);
+ void mt76_pci_disable_aspm(struct pci_dev *pdev);
+
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++int mt76_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct net_device *netdev, enum tc_setup_type type,
++ void *type_data);
++#endif /*CONFIG_NET_MEDIATEK_SOC_WED */
++
+ static inline u16 mt76_chip(struct mt76_dev *dev)
+ {
+ return dev->rev >> 16;
+@@ -1056,6 +1096,13 @@ static inline u16 mt76_rev(struct mt76_dev *dev)
+ return dev->rev & 0xffff;
+ }
+
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++u32 mt76_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size);
++void mt76_mmio_wed_release_rx_buf(struct mtk_wed_device *wed);
++int mt76_mmio_wed_offload_enable(struct mtk_wed_device *wed);
++void mt76_mmio_wed_offload_disable(struct mtk_wed_device *wed);
++#endif /*CONFIG_NET_MEDIATEK_SOC_WED */
++
+ #define mt76xx_chip(dev) mt76_chip(&((dev)->mt76))
+ #define mt76xx_rev(dev) mt76_rev(&((dev)->mt76))
+
+@@ -1105,15 +1152,16 @@ 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);
++ int ring_base, void *wed, u32 flags);
+ 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)
++ int n_desc, int ring_base, void *wed,
++ u32 flags)
+ {
+ struct mt76_queue *q;
+
+- q = mt76_init_queue(phy->dev, qid, idx, n_desc, ring_base, flags);
++ q = mt76_init_queue(phy->dev, qid, idx, n_desc, ring_base, wed, flags);
+ if (IS_ERR(q))
+ return PTR_ERR(q);
+
+@@ -1127,7 +1175,7 @@ static inline int mt76_init_mcu_queue(struct mt76_dev *dev, int qid, int idx,
+ {
+ struct mt76_queue *q;
+
+- q = mt76_init_queue(dev, qid, idx, n_desc, ring_base, 0);
++ q = mt76_init_queue(dev, qid, idx, n_desc, ring_base, NULL, 0);
+ if (IS_ERR(q))
+ return PTR_ERR(q);
+
+@@ -1541,10 +1589,38 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ struct mt76_power_limits *dest,
+ s8 target_power);
+
+-static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
++static inline bool mt76_queue_is_wed_tx_free(struct mt76_queue *q)
+ {
+ return (q->flags & MT_QFLAG_WED) &&
+- FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX;
++ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TXFREE;
++}
++
++static inline bool mt76_queue_is_wed_rro(struct mt76_queue *q)
++{
++ return q->flags & MT_QFLAG_WED_RRO;
++}
++
++static inline bool mt76_queue_is_wed_rro_ind(struct mt76_queue *q)
++{
++ return mt76_queue_is_wed_rro(q) &&
++ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_IND;
++}
++
++static inline bool mt76_queue_is_wed_rro_data(struct mt76_queue *q)
++{
++ return mt76_queue_is_wed_rro(q) &&
++ (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA ||
++ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_MSDU_PG);
++}
++
++static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
++{
++ if (!(q->flags & MT_QFLAG_WED))
++ return false;
++
++ return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX ||
++ mt76_queue_is_wed_rro_ind(q) || mt76_queue_is_wed_rro_data(q);
++
+ }
+
+ struct mt76_txwi_cache *
+@@ -1584,10 +1660,14 @@ static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
+ static inline int
+ mt76_token_get(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ {
+- int token;
++ int token, start = 0;
++
++ if (mtk_wed_device_active(&dev->mmio.wed))
++ start = dev->mmio.wed.wlan.nbuf;
+
+ spin_lock_bh(&dev->token_lock);
+- token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC);
++ token = idr_alloc(&dev->token, *ptxwi, start, start + dev->token_size,
++ GFP_ATOMIC);
+ spin_unlock_bh(&dev->token_lock);
+
+ return token;
+diff --git a/mt7603/dma.c b/mt7603/dma.c
+index 03ba11a..7a2f5d3 100644
+--- a/mt7603/dma.c
++++ b/mt7603/dma.c
+@@ -173,13 +173,14 @@ int mt7603_dma_init(struct mt7603_dev *dev)
+
+ 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, 0);
++ MT7603_TX_RING_SIZE, MT_TX_RING_BASE,
++ NULL, 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, 0);
++ MT7603_PSD_RING_SIZE, MT_TX_RING_BASE, NULL, 0);
+ if (ret)
+ return ret;
+
+@@ -189,12 +190,12 @@ int mt7603_dma_init(struct mt7603_dev *dev)
+ 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, 0);
++ MT_MCU_RING_SIZE, MT_TX_RING_BASE, NULL, 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, 0);
++ MT_MCU_RING_SIZE, MT_TX_RING_BASE, NULL, 0);
+ if (ret)
+ return ret;
+
+diff --git a/mt7615/dma.c b/mt7615/dma.c
+index 0ce01cc..e7135b2 100644
+--- a/mt7615/dma.c
++++ b/mt7615/dma.c
+@@ -26,14 +26,14 @@ mt7622_init_tx_queues_multi(struct mt7615_dev *dev)
+ 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, 0);
++ MT_TX_RING_BASE, NULL, 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, 0);
++ MT_TX_RING_BASE, NULL, 0);
+ if (ret)
+ return ret;
+
+@@ -55,7 +55,7 @@ mt7615_init_tx_queues(struct mt7615_dev *dev)
+ return mt7622_init_tx_queues_multi(dev);
+
+ ret = mt76_connac_init_tx_queues(&dev->mphy, 0, MT7615_TX_RING_SIZE,
+- MT_TX_RING_BASE, 0);
++ MT_TX_RING_BASE, NULL, 0);
+ if (ret)
+ return ret;
+
+diff --git a/mt76_connac.h b/mt76_connac.h
+index 1f29d8c..e5ebde1 100644
+--- a/mt76_connac.h
++++ b/mt76_connac.h
+@@ -391,7 +391,8 @@ mt76_connac_mutex_release(struct mt76_dev *dev, struct mt76_connac_pm *pm)
+
+ void mt76_connac_gen_ppe_thresh(u8 *he_ppet, int nss);
+ int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc,
+- int ring_base, u32 flags);
++ int ring_base, void *wed, u32 flags);
++
+ void mt76_connac_write_hw_txp(struct mt76_dev *dev,
+ struct mt76_tx_info *tx_info,
+ void *txp_ptr, u32 id);
+diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
+index 93402d2..c791464 100644
+--- a/mt76_connac_mac.c
++++ b/mt76_connac_mac.c
+@@ -256,11 +256,12 @@ void mt76_connac_txp_skb_unmap(struct mt76_dev *dev,
+ EXPORT_SYMBOL_GPL(mt76_connac_txp_skb_unmap);
+
+ int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc,
+- int ring_base, u32 flags)
++ int ring_base, void *wed, u32 flags)
+ {
+ int i, err;
+
+- err = mt76_init_tx_queue(phy, 0, idx, n_desc, ring_base, flags);
++ err = mt76_init_tx_queue(phy, 0, idx, n_desc, ring_base,
++ wed, flags);
+ if (err < 0)
+ return err;
+
+diff --git a/mt76x02_mmio.c b/mt76x02_mmio.c
+index 9b5e3fb..e5ad635 100644
+--- a/mt76x02_mmio.c
++++ b/mt76x02_mmio.c
+@@ -199,13 +199,14 @@ int mt76x02_dma_init(struct mt76x02_dev *dev)
+ 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, 0);
++ MT_TX_RING_BASE, NULL, 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, 0);
++ MT76x02_PSD_RING_SIZE, MT_TX_RING_BASE,
++ NULL, 0);
+ if (ret)
+ return ret;
+
+diff --git a/mt7915/dma.c b/mt7915/dma.c
+index 59a44d7..1bceeb5 100644
+--- a/mt7915/dma.c
++++ b/mt7915/dma.c
+@@ -9,18 +9,20 @@ static int
+ mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc, int ring_base)
+ {
+ struct mt7915_dev *dev = phy->dev;
++ struct mtk_wed_device *wed = NULL;
+
+- if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ if (is_mt798x(&dev->mt76))
+ ring_base += MT_TXQ_ID(0) * MT_RING_SIZE;
+ else
+ ring_base = MT_WED_TX_RING_BASE;
+
+ idx -= MT_TXQ_ID(0);
++ wed = &dev->mt76.mmio.wed;
+ }
+
+ return mt76_connac_init_tx_queues(phy->mt76, idx, n_desc, ring_base,
+- MT_WED_Q_TX(idx));
++ wed, MT_WED_Q_TX(idx));
+ }
+
+ static int mt7915_poll_tx(struct napi_struct *napi, int budget)
+@@ -492,7 +494,8 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
+ if (mtk_wed_device_active(&mdev->mmio.wed) && is_mt7915(mdev)) {
+ 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;
++ mdev->q_rx[MT_RXQ_MCU_WA].flags = MT_WED_Q_TXFREE;
++ mdev->q_rx[MT_RXQ_MCU_WA].wed = &mdev->mmio.wed;
+ } else {
+ wa_rx_base = MT_RXQ_RING_BASE(MT_RXQ_MCU_WA);
+ wa_rx_idx = MT_RXQ_ID(MT_RXQ_MCU_WA);
+@@ -507,9 +510,10 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
+ if (!dev->phy.mt76->band_idx) {
+ if (mtk_wed_device_active(&mdev->mmio.wed) &&
+ mtk_wed_get_rx_capa(&mdev->mmio.wed)) {
+- dev->mt76.q_rx[MT_RXQ_MAIN].flags =
++ mdev->q_rx[MT_RXQ_MAIN].flags =
+ MT_WED_Q_RX(MT7915_RXQ_BAND0);
+ dev->mt76.rx_token_size += MT7915_RX_RING_SIZE;
++ mdev->q_rx[MT_RXQ_MAIN].wed = &mdev->mmio.wed;
+ }
+
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+@@ -528,6 +532,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
+
+ if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ mdev->q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
++ mdev->q_rx[MT_RXQ_MAIN_WA].wed = &mdev->mmio.wed;
+ if (is_mt7916(mdev)) {
+ wa_rx_base = MT_WED_RX_RING_BASE;
+ wa_rx_idx = MT7915_RXQ_MCU_WA;
+@@ -544,9 +549,10 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
+ if (dev->dbdc_support || dev->phy.mt76->band_idx) {
+ if (mtk_wed_device_active(&mdev->mmio.wed) &&
+ mtk_wed_get_rx_capa(&mdev->mmio.wed)) {
+- dev->mt76.q_rx[MT_RXQ_BAND1].flags =
++ mdev->q_rx[MT_RXQ_BAND1].flags =
+ MT_WED_Q_RX(MT7915_RXQ_BAND1);
+ dev->mt76.rx_token_size += MT7915_RX_RING_SIZE;
++ mdev->q_rx[MT_RXQ_BAND1].wed = &mdev->mmio.wed;
+ }
+
+ /* rx data queue for band1 */
+@@ -643,7 +649,7 @@ int mt7915_dma_reset(struct mt7915_dev *dev, bool force)
+ mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
+
+ mt76_for_each_q_rx(&dev->mt76, i) {
+- if (dev->mt76.q_rx[i].flags == MT_WED_Q_TXFREE)
++ if (mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i]))
+ continue;
+
+ mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
+diff --git a/mt7915/main.c b/mt7915/main.c
+index a3fd54c..ba34c8e 100644
+--- a/mt7915/main.c
++++ b/mt7915/main.c
+@@ -1653,20 +1653,6 @@ 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 = {
+@@ -1721,6 +1707,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,
++ .net_setup_tc = mt76_net_setup_tc,
+ #endif
+ };
+diff --git a/mt7915/mmio.c b/mt7915/mmio.c
+index fc7ace6..85cb3fe 100644
+--- a/mt7915/mmio.c
++++ b/mt7915/mmio.c
+@@ -542,105 +542,6 @@ static u32 mt7915_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
+ }
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+-static int mt7915_mmio_wed_offload_enable(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 = wed->wlan.token_start;
+- spin_unlock_bh(&dev->mt76.token_lock);
+-
+- return !wait_event_timeout(dev->mt76.tx_wait,
+- !dev->mt76.wed_token_count, HZ);
+-}
+-
+-static void mt7915_mmio_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);
+-}
+-
+-static void mt7915_mmio_wed_release_rx_buf(struct mtk_wed_device *wed)
+-{
+- struct mt7915_dev *dev;
+- int i;
+-
+- dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+- for (i = 0; i < dev->mt76.rx_token_size; i++) {
+- struct mt76_txwi_cache *t;
+-
+- t = mt76_rx_token_release(&dev->mt76, i);
+- if (!t || !t->ptr)
+- continue;
+-
+- mt76_put_page_pool_buf(t->ptr, false);
+- t->ptr = NULL;
+-
+- mt76_put_rxwi(&dev->mt76, t);
+- }
+-
+- mt76_free_pending_rxwi(&dev->mt76);
+-}
+-
+-static u32 mt7915_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+-{
+- struct mtk_rxbm_desc *desc = wed->rx_buf_ring.desc;
+- struct mt76_txwi_cache *t = NULL;
+- struct mt7915_dev *dev;
+- struct mt76_queue *q;
+- int i, len;
+-
+- dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+- q = &dev->mt76.q_rx[MT_RXQ_MAIN];
+- len = SKB_WITH_OVERHEAD(q->buf_size);
+-
+- for (i = 0; i < size; i++) {
+- enum dma_data_direction dir;
+- dma_addr_t addr;
+- u32 offset;
+- int token;
+- void *buf;
+-
+- t = mt76_get_rxwi(&dev->mt76);
+- if (!t)
+- goto unmap;
+-
+- buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
+- if (!buf)
+- goto unmap;
+-
+- addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
+- dir = page_pool_get_dma_dir(q->page_pool);
+- dma_sync_single_for_device(dev->mt76.dma_dev, addr, len, dir);
+-
+- desc->buf0 = cpu_to_le32(addr);
+- token = mt76_rx_token_consume(&dev->mt76, buf, t, addr);
+- if (token < 0) {
+- mt76_put_page_pool_buf(buf, false);
+- goto unmap;
+- }
+-
+- desc->token |= cpu_to_le32(FIELD_PREP(MT_DMA_CTL_TOKEN,
+- token));
+- desc++;
+- }
+-
+- return 0;
+-
+-unmap:
+- if (t)
+- mt76_put_rxwi(&dev->mt76, t);
+- mt7915_mmio_wed_release_rx_buf(wed);
+- return -ENOMEM;
+-}
+-
+ static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed,
+ struct mtk_wed_wo_rx_stats *stats)
+ {
+@@ -778,10 +679,10 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
+ }
+
+ wed->wlan.init_buf = mt7915_wed_init_buf;
+- wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable;
+- wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable;
+- wed->wlan.init_rx_buf = mt7915_mmio_wed_init_rx_buf;
+- wed->wlan.release_rx_buf = mt7915_mmio_wed_release_rx_buf;
++ wed->wlan.offload_enable = mt76_mmio_wed_offload_enable;
++ wed->wlan.offload_disable = mt76_mmio_wed_offload_disable;
++ wed->wlan.init_rx_buf = mt76_mmio_wed_init_rx_buf;
++ wed->wlan.release_rx_buf = mt76_mmio_wed_release_rx_buf;
+ wed->wlan.update_wo_rx_stats = mt7915_mmio_wed_update_rx_stats;
+ wed->wlan.reset = mt7915_mmio_wed_reset;
+ wed->wlan.reset_complete = mt7915_mmio_wed_reset_complete;
+diff --git a/mt7921/pci.c b/mt7921/pci.c
+index 9647e4b..9ea7e0c 100644
+--- a/mt7921/pci.c
++++ b/mt7921/pci.c
+@@ -171,7 +171,7 @@ static int mt7921_dma_init(struct mt792x_dev *dev)
+ /* init tx queue */
+ ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7921_TXQ_BAND0,
+ MT7921_TX_RING_SIZE,
+- MT_TX_RING_BASE, 0);
++ MT_TX_RING_BASE, NULL, 0);
+ if (ret)
+ return ret;
+
+diff --git a/mt7925/pci.c b/mt7925/pci.c
+index 08ef75e..734f31e 100644
+--- a/mt7925/pci.c
++++ b/mt7925/pci.c
+@@ -218,7 +218,7 @@ static int mt7925_dma_init(struct mt792x_dev *dev)
+ /* init tx queue */
+ ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7925_TXQ_BAND0,
+ MT7925_TX_RING_SIZE,
+- MT_TX_RING_BASE, 0);
++ MT_TX_RING_BASE, NULL, 0);
+ if (ret)
+ return ret;
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 586e247..2221d22 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -7,6 +7,26 @@
+ #include "../dma.h"
+ #include "mac.h"
+
++int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, int n_desc,
++ int ring_base, struct mtk_wed_device *wed)
++{
++ struct mt7996_dev *dev = phy->dev;
++ u32 flags = 0;
++
++ if (mtk_wed_device_active(wed)) {
++ ring_base += MT_TXQ_ID(0) * MT_RING_SIZE;
++ idx -= MT_TXQ_ID(0);
++
++ if (phy->mt76->band_idx == MT_BAND2)
++ flags = MT_WED_Q_TX(0);
++ else
++ flags = MT_WED_Q_TX(idx);
++ }
++
++ return mt76_connac_init_tx_queues(phy->mt76, idx, n_desc,
++ ring_base, wed, flags);
++}
++
+ static int mt7996_poll_tx(struct napi_struct *napi, int budget)
+ {
+ struct mt7996_dev *dev;
+@@ -45,6 +65,29 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2);
+ RXQ_CONFIG(MT_RXQ_BAND2_WA, WFDMA0, MT_INT_RX_DONE_WA_TRI, MT7996_RXQ_MCU_WA_TRI);
+
++ if (dev->has_rro) {
++ /* band0 */
++ RXQ_CONFIG(MT_RXQ_RRO_BAND0, WFDMA0, MT_INT_RX_DONE_RRO_BAND0,
++ MT7996_RXQ_RRO_BAND0);
++ RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND0, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND0,
++ MT7996_RXQ_MSDU_PG_BAND0);
++ RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
++ MT7996_RXQ_TXFREE0);
++ /* band1 */
++ RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
++ MT7996_RXQ_MSDU_PG_BAND1);
++ /* band2 */
++ RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
++ MT7996_RXQ_RRO_BAND2);
++ RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
++ MT7996_RXQ_MSDU_PG_BAND2);
++ RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
++ MT7996_RXQ_TXFREE2);
++
++ RXQ_CONFIG(MT_RXQ_RRO_IND, WFDMA0, MT_INT_RX_DONE_RRO_IND,
++ MT7996_RXQ_RRO_IND);
++ }
++
+ /* data tx queue */
+ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+@@ -73,6 +116,24 @@ static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x1a0, 0x10));
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2) + ofs, PREFETCH(0x2a0, 0x10));
+
++ if (dev->has_rro) {
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND0) + ofs,
++ PREFETCH(0x3a0, 0x10));
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
++ PREFETCH(0x4a0, 0x10));
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs,
++ PREFETCH(0x5a0, 0x4));
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs,
++ PREFETCH(0x5e0, 0x4));
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND2) + ofs,
++ PREFETCH(0x620, 0x4));
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND0) + ofs,
++ PREFETCH(0x660, 0x4));
++ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND2) + ofs,
++ PREFETCH(0x6a0, 0x4));
++ }
++#undef PREFETCH
++
+ mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + ofs, WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE);
+ }
+
+@@ -128,8 +189,9 @@ static void mt7996_dma_disable(struct mt7996_dev *dev, bool reset)
+ }
+ }
+
+-void mt7996_dma_start(struct mt7996_dev *dev, bool reset)
++void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ {
++ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ u32 hif1_ofs = 0;
+ u32 irq_mask;
+
+@@ -138,11 +200,16 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset)
+
+ /* enable WFDMA Tx/Rx */
+ if (!reset) {
+- mt76_set(dev, MT_WFDMA0_GLO_CFG,
+- MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+- MT_WFDMA0_GLO_CFG_RX_DMA_EN |
+- MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+- MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
++ if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed))
++ mt76_set(dev, MT_WFDMA0_GLO_CFG,
++ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
++ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO);
++ else
++ mt76_set(dev, MT_WFDMA0_GLO_CFG,
++ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
++ MT_WFDMA0_GLO_CFG_RX_DMA_EN |
++ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
++ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+
+ if (dev->hif2)
+ mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+@@ -153,11 +220,7 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset)
+ }
+
+ /* enable interrupts for TX/RX rings */
+- irq_mask = MT_INT_MCU_CMD;
+- if (reset)
+- goto done;
+-
+- irq_mask = MT_INT_RX_DONE_MCU | MT_INT_TX_DONE_MCU;
++ irq_mask = MT_INT_MCU_CMD | MT_INT_RX_DONE_MCU | MT_INT_TX_DONE_MCU;
+
+ if (!dev->mphy.band_idx)
+ irq_mask |= MT_INT_BAND0_RX_DONE;
+@@ -168,7 +231,16 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset)
+ if (dev->tbtc_support)
+ irq_mask |= MT_INT_BAND2_RX_DONE;
+
+-done:
++ if (mtk_wed_device_active(wed) && wed_reset) {
++ u32 wed_irq_mask = irq_mask;
++
++ wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1;
++ mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
++ mtk_wed_device_start(wed, wed_irq_mask);
++ }
++
++ irq_mask = reset ? MT_INT_MCU_CMD : irq_mask;
++
+ mt7996_irq_enable(dev, irq_mask);
+ mt7996_irq_disable(dev, 0);
+ }
+@@ -241,17 +313,90 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ /* fix hardware limitation, pcie1's rx ring3 is not available
+ * so, redirect pcie0 rx ring3 interrupt to pcie1
+ */
+- mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
+- MT_WFDMA0_RX_INT_SEL_RING3);
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++ dev->has_rro)
++ mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
++ MT_WFDMA0_RX_INT_SEL_RING6);
++ else
++ mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
++ MT_WFDMA0_RX_INT_SEL_RING3);
++ }
++
++ mt7996_dma_start(dev, reset, true);
++}
++
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++int mt7996_dma_rro_init(struct mt7996_dev *dev)
++{
++ struct mt76_dev *mdev = &dev->mt76;
++ u32 irq_mask;
++ int ret;
++
++ /* ind cmd */
++ mdev->q_rx[MT_RXQ_RRO_IND].flags = MT_WED_RRO_Q_IND;
++ mdev->q_rx[MT_RXQ_RRO_IND].wed = &mdev->mmio.wed;
++ ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_RRO_IND],
++ MT_RXQ_ID(MT_RXQ_RRO_IND),
++ MT7996_RX_RING_SIZE,
++ 0, MT_RXQ_RRO_IND_RING_BASE);
++ if (ret)
++ return ret;
+
+- /* TODO: redirect rx ring6 interrupt to pcie0 for wed function */
++ /* rx msdu page queue for band0 */
++ mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].flags =
++ MT_WED_RRO_Q_MSDU_PG(0) | MT_QFLAG_WED_RRO_EN;
++ mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].wed = &mdev->mmio.wed;
++ ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0],
++ MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND0),
++ MT7996_RX_RING_SIZE,
++ MT7996_RX_MSDU_PAGE_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND0));
++ if (ret)
++ return ret;
++
++ if (dev->dbdc_support) {
++ /* rx msdu page queue for band1 */
++ mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags =
++ MT_WED_RRO_Q_MSDU_PG(1) | MT_QFLAG_WED_RRO_EN;
++ mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].wed = &mdev->mmio.wed;
++ ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1],
++ MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND1),
++ MT7996_RX_RING_SIZE,
++ MT7996_RX_MSDU_PAGE_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND1));
++ if (ret)
++ return ret;
++ }
++
++ if (dev->tbtc_support) {
++ /* rx msdu page queue for band2 */
++ mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].flags =
++ MT_WED_RRO_Q_MSDU_PG(2) | MT_QFLAG_WED_RRO_EN;
++ mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].wed = &mdev->mmio.wed;
++ ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2],
++ MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND2),
++ MT7996_RX_RING_SIZE,
++ MT7996_RX_MSDU_PAGE_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND2));
++ if (ret)
++ return ret;
+ }
+
+- mt7996_dma_start(dev, reset);
++ irq_mask = mdev->mmio.irqmask | MT_INT_RRO_RX_DONE |
++ MT_INT_TX_DONE_BAND2;
++ mt76_wr(dev, MT_INT_MASK_CSR, irq_mask);
++ mtk_wed_device_start_hw_rro(&mdev->mmio.wed, irq_mask, false);
++ mt7996_irq_enable(dev, irq_mask);
++
++ return 0;
+ }
++#endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+
+ int mt7996_dma_init(struct mt7996_dev *dev)
+ {
++ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++ struct mtk_wed_device *wed_hif2 = &dev->mt76.mmio.wed_hif2;
++ u32 rx_base;
+ u32 hif1_ofs = 0;
+ int ret;
+
+@@ -265,10 +410,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ mt7996_dma_disable(dev, true);
+
+ /* init tx queue */
+- ret = mt76_connac_init_tx_queues(dev->phy.mt76,
+- MT_TXQ_ID(dev->mphy.band_idx),
+- MT7996_TX_RING_SIZE,
+- MT_TXQ_RING_BASE(0), 0);
++ ret = mt7996_init_tx_queues(&dev->phy,
++ MT_TXQ_ID(dev->mphy.band_idx),
++ MT7996_TX_RING_SIZE,
++ MT_TXQ_RING_BASE(0),
++ wed);
+ if (ret)
+ return ret;
+
+@@ -315,6 +461,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ return ret;
+
+ /* rx data queue for band0 and band1 */
++ if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) {
++ dev->mt76.q_rx[MT_RXQ_MAIN].flags = MT_WED_Q_RX(0);
++ dev->mt76.q_rx[MT_RXQ_MAIN].wed = wed;
++ }
++
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+ MT_RXQ_ID(MT_RXQ_MAIN),
+ MT7996_RX_RING_SIZE,
+@@ -324,6 +475,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ return ret;
+
+ /* tx free notify event from WA for band0 */
++ if (mtk_wed_device_active(wed) && !dev->has_rro) {
++ dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
++ dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed;
++ }
++
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA],
+ MT_RXQ_ID(MT_RXQ_MAIN_WA),
+ MT7996_RX_MCU_RING_SIZE,
+@@ -334,17 +490,23 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+
+ if (dev->tbtc_support || dev->mphy.band_idx == MT_BAND2) {
+ /* rx data queue for band2 */
++ rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs;
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
+ MT_RXQ_ID(MT_RXQ_BAND2),
+ MT7996_RX_RING_SIZE,
+ MT_RX_BUF_SIZE,
+- MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs);
++ rx_base);
+ if (ret)
+ return ret;
+
+ /* tx free notify event from WA for band2
+ * use pcie0's rx ring3, but, redirect pcie0 rx ring3 interrupt to pcie1
+ */
++ if (mtk_wed_device_active(wed_hif2) && !dev->has_rro) {
++ dev->mt76.q_rx[MT_RXQ_BAND2_WA].flags = MT_WED_Q_TXFREE;
++ dev->mt76.q_rx[MT_RXQ_BAND2_WA].wed = wed_hif2;
++ }
++
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2_WA],
+ MT_RXQ_ID(MT_RXQ_BAND2_WA),
+ MT7996_RX_MCU_RING_SIZE,
+@@ -354,6 +516,60 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ return ret;
+ }
+
++ if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed) &&
++ dev->has_rro) {
++ /* rx rro data queue for band0 */
++ dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags =
++ MT_WED_RRO_Q_DATA(0) | MT_QFLAG_WED_RRO_EN;
++ dev->mt76.q_rx[MT_RXQ_RRO_BAND0].wed = wed;
++ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0],
++ MT_RXQ_ID(MT_RXQ_RRO_BAND0),
++ MT7996_RX_RING_SIZE,
++ MT7996_RX_BUF_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0));
++ if (ret)
++ return ret;
++
++ /* tx free notify event from WA for band0 */
++ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
++ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
++
++ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
++ MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
++ MT7996_RX_MCU_RING_SIZE,
++ MT7996_RX_BUF_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
++ if (ret)
++ return ret;
++
++ if (dev->tbtc_support || dev->mphy.band_idx == MT_BAND2) {
++ /* rx rro data queue for band2 */
++ dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags =
++ MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN;
++ dev->mt76.q_rx[MT_RXQ_RRO_BAND2].wed = wed;
++ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2],
++ MT_RXQ_ID(MT_RXQ_RRO_BAND2),
++ MT7996_RX_RING_SIZE,
++ MT7996_RX_BUF_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND2) + hif1_ofs);
++ if (ret)
++ return ret;
++
++ /* tx free notify event from MAC for band2 */
++ if (mtk_wed_device_active(wed_hif2)) {
++ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND2].flags = MT_WED_Q_TXFREE;
++ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND2].wed = wed_hif2;
++ }
++ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND2],
++ MT_RXQ_ID(MT_RXQ_TXFREE_BAND2),
++ MT7996_RX_MCU_RING_SIZE,
++ MT7996_RX_BUF_SIZE,
++ MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND2) + hif1_ofs);
++ if (ret)
++ return ret;
++ }
++ }
++
+ ret = mt76_init_queues(dev, mt76_dma_rx_poll);
+ if (ret < 0)
+ return ret;
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 12c2513..d335b58 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -155,7 +155,7 @@ mt7996_regd_notifier(struct wiphy *wiphy,
+ }
+
+ static void
+-mt7996_init_wiphy(struct ieee80211_hw *hw)
++mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
+ {
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt76_dev *mdev = &phy->dev->mt76;
+@@ -167,6 +167,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ hw->max_rx_aggregation_subframes = max_subframes;
+ hw->max_tx_aggregation_subframes = max_subframes;
+ hw->netdev_features = NETIF_F_RXCSUM;
++ if (mtk_wed_device_active(wed))
++ hw->netdev_features |= NETIF_F_HW_TC;
+
+ hw->radiotap_timestamp.units_pos =
+ IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US;
+@@ -312,8 +314,13 @@ void mt7996_mac_init(struct mt7996_dev *dev)
+
+ /* rro module init */
+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2);
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
+- mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
++ if (dev->has_rro) {
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 1);
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 0);
++ } else {
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
++ mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
++ }
+
+ mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+ MCU_WA_PARAM_HW_PATH_HIF_VER,
+@@ -350,6 +357,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ struct mt76_phy *mphy;
+ u32 mac_ofs, hif1_ofs = 0;
+ int ret;
++ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+
+ if (band != MT_BAND1 && band != MT_BAND2)
+ return 0;
+@@ -361,8 +369,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ if (phy)
+ return 0;
+
+- if (band == MT_BAND2 && dev->hif2)
++ if (band == MT_BAND2 && dev->hif2) {
+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++ wed = &dev->mt76.mmio.wed_hif2;
++ }
+
+ mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band);
+ if (!mphy)
+@@ -395,11 +405,12 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ mt76_eeprom_override(mphy);
+
+ /* init wiphy according to mphy and phy */
+- mt7996_init_wiphy(mphy->hw);
+- ret = mt76_connac_init_tx_queues(phy->mt76,
+- MT_TXQ_ID(band),
+- MT7996_TX_RING_SIZE,
+- MT_TXQ_RING_BASE(band) + hif1_ofs, 0);
++ mt7996_init_wiphy(mphy->hw, wed);
++ ret = mt7996_init_tx_queues(mphy->priv,
++ MT_TXQ_ID(band),
++ MT7996_TX_RING_SIZE,
++ MT_TXQ_RING_BASE(band) + hif1_ofs,
++ wed);
+ if (ret)
+ goto error;
+
+@@ -412,6 +423,13 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ if (ret)
+ goto error;
+
++ if (wed == &dev->mt76.mmio.wed_hif2 && mtk_wed_device_active(wed)) {
++ u32 irq_mask = dev->mt76.mmio.irqmask | MT_INT_TX_DONE_BAND2;
++
++ mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask);
++ mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, irq_mask);
++ }
++
+ return 0;
+
+ error:
+@@ -456,6 +474,120 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
+ msleep(20);
+ }
+
++static int mt7996_wed_rro_init(struct mt7996_dev *dev)
++{
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++ u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
++ struct mt7996_wed_rro_addr *addr;
++ void *ptr;
++ int i;
++
++ if (!dev->has_rro)
++ return 0;
++
++ if (!mtk_wed_device_active(wed))
++ return 0;
++
++ for (i = 0; i < ARRAY_SIZE(dev->wed_rro.ba_bitmap); i++) {
++ ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
++ MT7996_RRO_BA_BITMAP_CR_SIZE,
++ &dev->wed_rro.ba_bitmap[i].phy_addr,
++ GFP_KERNEL);
++ if (!ptr)
++ return -ENOMEM;
++
++ dev->wed_rro.ba_bitmap[i].ptr = ptr;
++ }
++
++ for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
++ int j;
++
++ ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
++ MT7996_RRO_WINDOW_MAX_SIZE * sizeof(*addr),
++ &dev->wed_rro.addr_elem[i].phy_addr,
++ GFP_KERNEL);
++ if (!ptr)
++ return -ENOMEM;
++
++ dev->wed_rro.addr_elem[i].ptr = ptr;
++ memset(dev->wed_rro.addr_elem[i].ptr, 0,
++ MT7996_RRO_WINDOW_MAX_SIZE * sizeof(*addr));
++
++ addr = dev->wed_rro.addr_elem[i].ptr;
++ for (j = 0; j < MT7996_RRO_WINDOW_MAX_SIZE; j++) {
++ addr->signature = 0xff;
++ addr++;
++ }
++
++ wed->wlan.ind_cmd.addr_elem_phys[i] =
++ dev->wed_rro.addr_elem[i].phy_addr;
++ }
++
++ ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
++ MT7996_RRO_WINDOW_MAX_LEN * sizeof(*addr),
++ &dev->wed_rro.session.phy_addr,
++ GFP_KERNEL);
++ if (!ptr)
++ return -ENOMEM;
++
++ dev->wed_rro.session.ptr = ptr;
++ addr = dev->wed_rro.session.ptr;
++ for (i = 0; i < MT7996_RRO_WINDOW_MAX_LEN; i++) {
++ addr->signature = 0xff;
++ addr++;
++ }
++
++ /* rro hw init */
++ /* TODO: remove line after WM has set */
++ mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
++
++ /* setup BA bitmap cache address */
++ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
++ dev->wed_rro.ba_bitmap[0].phy_addr);
++ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
++ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
++ dev->wed_rro.ba_bitmap[1].phy_addr);
++ mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
++
++ /* setup Address element address */
++ for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) {
++ mt76_wr(dev, reg, dev->wed_rro.addr_elem[i].phy_addr >> 4);
++ reg += 4;
++ }
++
++ /* setup Address element address - separate address segment mode */
++ mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
++ MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
++
++ wed->wlan.ind_cmd.win_size = ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6;
++ wed->wlan.ind_cmd.particular_sid = MT7996_RRO_MAX_SESSION;
++ wed->wlan.ind_cmd.particular_se_phys = dev->wed_rro.session.phy_addr;
++ wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN;
++ wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
++
++ mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
++ mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
++ MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
++
++ /* particular session configure */
++ /* use max session idx + 1 as particular session id */
++ mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr);
++ mt76_wr(dev, MT_RRO_PARTICULAR_CFG1,
++ MT_RRO_PARTICULAR_CONFG_EN |
++ FIELD_PREP(MT_RRO_PARTICULAR_SID, MT7996_RRO_MAX_SESSION));
++
++ /* interrupt enable */
++ mt76_wr(dev, MT_RRO_HOST_INT_ENA,
++ MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
++
++ /* rro ind cmd queue init */
++ return mt7996_dma_rro_init(dev);
++#else
++ return 0;
++#endif
++}
++
+ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ {
+ int ret, idx;
+@@ -477,6 +609,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
++ ret = mt7996_wed_rro_init(dev);
++ if (ret)
++ return ret;
++
+ ret = mt7996_eeprom_init(dev);
+ if (ret < 0)
+ return ret;
+@@ -884,7 +1020,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ if (ret)
+ return ret;
+
+- mt7996_init_wiphy(hw);
++ mt7996_init_wiphy(hw, &dev->mt76.mmio.wed);
+
+ ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+ ARRAY_SIZE(mt76_rates));
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 1a1e218..4be5410 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -449,8 +449,36 @@ mt7996_mac_fill_rx_rate(struct mt7996_dev *dev,
+ return 0;
+ }
+
++static void
++mt7996_wed_check_ppe(struct mt7996_dev *dev, struct mt76_queue *q,
++ struct mt7996_sta *msta, struct sk_buff *skb,
++ u32 info)
++{
++ struct ieee80211_vif *vif;
++ struct wireless_dev *wdev;
++
++ if (!msta || !msta->vif)
++ return;
++
++ if (!mt76_queue_is_wed_rx(q))
++ return;
++
++ if (!(info & MT_DMA_INFO_PPE_VLD))
++ return;
++
++ vif = container_of((void *)msta->vif, struct ieee80211_vif,
++ drv_priv);
++ wdev = ieee80211_vif_to_wdev(vif);
++ skb->dev = wdev->netdev;
++
++ mtk_wed_device_ppe_check(&dev->mt76.mmio.wed, skb,
++ FIELD_GET(MT_DMA_PPE_CPU_REASON, info),
++ FIELD_GET(MT_DMA_PPE_ENTRY, info));
++}
++
+ static int
+-mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
++mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
++ struct sk_buff *skb, u32 *info)
+ {
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct mt76_phy *mphy = &dev->mt76.phy;
+@@ -475,7 +503,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+ u16 seq_ctrl = 0;
+ __le16 fc = 0;
+ int idx;
++ u8 hw_aggr = false;
++ struct mt7996_sta *msta = NULL;
+
++ hw_aggr = status->aggr;
+ memset(status, 0, sizeof(*status));
+
+ band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1);
+@@ -502,8 +533,6 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+ status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
+
+ if (status->wcid) {
+- struct mt7996_sta *msta;
+-
+ msta = container_of(status->wcid, struct mt7996_sta, wcid);
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+ if (list_empty(&msta->wcid.poll_list))
+@@ -708,12 +737,14 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+ }
+ } else {
+ status->flag |= RX_FLAG_8023;
++ mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
++ *info);
+ }
+
+ 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))
++ if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr)
+ return 0;
+
+ status->aggr = unicast &&
+@@ -1010,6 +1041,29 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ return 0;
+ }
+
++u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
++{
++ struct mt76_connac_fw_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 = BIT(31) |
++ 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
+ mt7996_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+ {
+@@ -1388,6 +1442,12 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+
+ switch (type) {
+ case PKT_TYPE_TXRX_NOTIFY:
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2) &&
++ q == MT_RXQ_TXFREE_BAND2) {
++ dev_kfree_skb(skb);
++ break;
++ }
++
+ mt7996_mac_tx_free(dev, skb->data, skb->len);
+ napi_consume_skb(skb, 1);
+ break;
+@@ -1404,7 +1464,7 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ dev_kfree_skb(skb);
+ break;
+ case PKT_TYPE_NORMAL:
+- if (!mt7996_mac_fill_rx(dev, skb)) {
++ if (!mt7996_mac_fill_rx(dev, q, skb, info)) {
+ mt76_rx(&dev->mt76, q, skb);
+ return;
+ }
+@@ -1862,7 +1922,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
+
+ /* enable DMA Tx/Tx and interrupt */
+- mt7996_dma_start(dev, false);
++ mt7996_dma_start(dev, false, false);
+
+ clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+ clear_bit(MT76_RESET, &dev->mphy.state);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index a2ab668..ae4f0ce 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1368,6 +1368,44 @@ out:
+ return ret;
+ }
+
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++static int
++mt7996_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 mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ struct mt7996_dev *dev = mt7996_hw_dev(hw);
++ struct mt7996_phy *phy = mt7996_hw_phy(hw);
++ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++
++ if (phy != &dev->phy && phy->mt76->band_idx == MT_BAND2)
++ wed = &dev->mt76.mmio.wed_hif2;
++
++ if (!mtk_wed_device_active(wed))
++ return -ENODEV;
++
++ if (msta->wcid.idx > MT7996_WTBL_STA)
++ 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.queue = 0;
++ path->mtk_wdma.wcid = msta->wcid.idx;
++
++ path->mtk_wdma.amsdu = mtk_wed_is_amsdu_supported(wed);
++ ctx->dev = NULL;
++
++ return 0;
++}
++
++#endif
++
+ const struct ieee80211_ops mt7996_ops = {
+ .tx = mt7996_tx,
+ .start = mt7996_start,
+@@ -1412,4 +1450,8 @@ const struct ieee80211_ops mt7996_ops = {
+ .sta_add_debugfs = mt7996_sta_add_debugfs,
+ #endif
+ .set_radar_background = mt7996_set_radar_background,
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++ .net_fill_forward_path = mt7996_net_fill_forward_path,
++ .net_setup_tc = mt76_net_setup_tc,
++#endif
+ };
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 12bf4e5..3ff70c6 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -912,7 +912,7 @@ int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif)
+ }
+
+ static int
+-mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
++mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif *mvif,
+ struct ieee80211_ampdu_params *params,
+ bool enable, bool tx)
+ {
+@@ -921,7 +921,7 @@ mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
+ struct sk_buff *skb;
+ struct tlv *tlv;
+
+- skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mvif, wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+@@ -935,8 +935,9 @@ mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
+ ba->ba_en = enable << params->tid;
+ ba->amsdu = params->amsdu;
+ ba->tid = params->tid;
++ ba->ba_rdd_rro = !tx && enable && dev->has_rro;
+
+- return mt76_mcu_skb_send_msg(dev, skb,
++ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+
+@@ -951,8 +952,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ if (enable && !params->amsdu)
+ msta->wcid.amsdu = false;
+
+- return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
+- enable, true);
++ return mt7996_mcu_sta_ba(dev, &mvif->mt76, params, enable, true);
+ }
+
+ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+@@ -962,8 +962,7 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv;
+ struct mt7996_vif *mvif = msta->vif;
+
+- return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
+- enable, false);
++ return mt7996_mcu_sta_ba(dev, &mvif->mt76, params, enable, false);
+ }
+
+ static void
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 3a591a7..c7b6d4b 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -10,6 +10,10 @@
+ #include "mt7996.h"
+ #include "mac.h"
+ #include "../trace.h"
++#include "../dma.h"
++
++static bool wed_enable;
++module_param(wed_enable, bool, 0644);
+
+ static const struct __base mt7996_reg_base[] = {
+ [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } },
+@@ -191,6 +195,143 @@ static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
+ return dev->bus_ops->rmw(mdev, __mt7996_reg_addr(dev, offset), mask, val);
+ }
+
++int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
++ bool hif2, int *irq)
++{
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++ struct pci_dev *pci_dev = pdev_ptr;
++ u32 hif1_ofs = 0;
++ int ret;
++
++ if (!wed_enable)
++ return 0;
++
++ dev->has_rro = true;
++
++ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++
++ if (hif2)
++ wed = &dev->mt76.mmio.wed_hif2;
++
++ wed->wlan.pci_dev = pci_dev;
++ wed->wlan.bus_type = MTK_WED_BUS_PCIE;
++
++ wed->wlan.base = devm_ioremap(dev->mt76.dev,
++ pci_resource_start(pci_dev, 0),
++ pci_resource_len(pci_dev, 0));
++ wed->wlan.phy_base = pci_resource_start(pci_dev, 0);
++
++ if (hif2) {
++ wed->wlan.wpdma_int = wed->wlan.phy_base +
++ MT_INT_PCIE1_SOURCE_CSR_EXT;
++ wed->wlan.wpdma_mask = wed->wlan.phy_base +
++ MT_INT_PCIE1_MASK_CSR;
++ wed->wlan.wpdma_tx = wed->wlan.phy_base + hif1_ofs +
++ MT_TXQ_RING_BASE(0) +
++ MT7996_TXQ_BAND2 * MT_RING_SIZE;
++ if (dev->has_rro) {
++ wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
++ MT_RXQ_RING_BASE(0) +
++ MT7996_RXQ_TXFREE2 * MT_RING_SIZE;
++ wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1;
++ } else {
++ wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
++ MT_RXQ_RING_BASE(0) +
++ MT7996_RXQ_MCU_WA_TRI * MT_RING_SIZE;
++ wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_TRI) - 1;
++ }
++
++ wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
++ wed->wlan.wpdma_rx = wed->wlan.phy_base + hif1_ofs +
++ MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
++ MT7996_RXQ_BAND0 * MT_RING_SIZE;
++
++ wed->wlan.id = 0x7991;
++ wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1;
++ } else {
++ wed->wlan.hw_rro = dev->has_rro; /* default on */
++ wed->wlan.wpdma_int = wed->wlan.phy_base + MT_INT_SOURCE_CSR;
++ wed->wlan.wpdma_mask = wed->wlan.phy_base + MT_INT_MASK_CSR;
++ wed->wlan.wpdma_tx = wed->wlan.phy_base + MT_TXQ_RING_BASE(0) +
++ MT7996_TXQ_BAND0 * MT_RING_SIZE;
++
++ wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + MT_WFDMA0_GLO_CFG;
++
++ wed->wlan.wpdma_rx = wed->wlan.phy_base +
++ MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
++ MT7996_RXQ_BAND0 * MT_RING_SIZE;
++
++ wed->wlan.wpdma_rx_rro[0] = wed->wlan.phy_base +
++ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND0) +
++ MT7996_RXQ_RRO_BAND0 * MT_RING_SIZE;
++ wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
++ MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
++ MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
++ wed->wlan.wpdma_rx_pg = wed->wlan.phy_base +
++ MT_RXQ_RING_BASE(MT7996_RXQ_MSDU_PG_BAND0) +
++ MT7996_RXQ_MSDU_PG_BAND0 * MT_RING_SIZE;
++
++ wed->wlan.rx_nbuf = 65536;
++ wed->wlan.rx_npkt = dev->hif2 ? 32768 : 24576;
++ wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE);
++
++ wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1;
++ wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
++
++ wed->wlan.rro_rx_tbit[0] = ffs(MT_INT_RX_DONE_RRO_BAND0) - 1;
++ wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
++
++ wed->wlan.rx_pg_tbit[0] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND0) - 1;
++ wed->wlan.rx_pg_tbit[1] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND1) - 1;
++ wed->wlan.rx_pg_tbit[2] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND2) - 1;
++
++ wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1;
++ wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1;
++ if (dev->has_rro) {
++ wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
++ MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
++ wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
++ } else {
++ wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
++ wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
++ MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
++ }
++ dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt;
++ }
++
++ wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE;
++ wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf;
++
++ wed->wlan.amsdu_max_subframes = 8;
++ wed->wlan.amsdu_max_len = 1536;
++
++ wed->wlan.init_buf = mt7996_wed_init_buf;
++ wed->wlan.init_rx_buf = mt76_mmio_wed_init_rx_buf;
++ wed->wlan.release_rx_buf = mt76_mmio_wed_release_rx_buf;
++ wed->wlan.offload_enable = mt76_mmio_wed_offload_enable;
++ wed->wlan.offload_disable = mt76_mmio_wed_offload_disable;
++
++ if (mtk_wed_device_attach(wed))
++ 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;
++
++ ret = dma_set_coherent_mask(wed->dev, DMA_BIT_MASK(32));
++ if (ret)
++ return ret;
++
++ return 1;
++#else
++ return 0;
++#endif
++}
++
+ static int mt7996_mmio_init(struct mt76_dev *mdev,
+ void __iomem *mem_base,
+ u32 device_id)
+@@ -241,8 +382,17 @@ void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
+ mdev->mmio.irqmask |= set;
+
+ if (write_reg) {
+- mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
+- mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
++ if (mtk_wed_device_active(&mdev->mmio.wed)) {
++ mtk_wed_device_irq_set_mask(&mdev->mmio.wed,
++ mdev->mmio.irqmask);
++ if (mtk_wed_device_active(&mdev->mmio.wed_hif2)) {
++ mtk_wed_device_irq_set_mask(&mdev->mmio.wed_hif2,
++ mdev->mmio.irqmask);
++ }
++ } else {
++ mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
++ mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
++ }
+ }
+
+ spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags);
+@@ -260,22 +410,36 @@ static void mt7996_rx_poll_complete(struct mt76_dev *mdev,
+ static void mt7996_irq_tasklet(struct tasklet_struct *t)
+ {
+ struct mt7996_dev *dev = from_tasklet(dev, t, mt76.irq_tasklet);
++ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++ struct mtk_wed_device *wed_hif2 = &dev->mt76.mmio.wed_hif2;
+ u32 i, intr, mask, intr1;
+
+- 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);
+-
+- if (dev->hif2) {
+- intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR);
+- intr1 &= dev->mt76.mmio.irqmask;
+- mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1);
++ if (dev->hif2 && mtk_wed_device_active(wed_hif2)) {
++ mtk_wed_device_irq_set_mask(wed_hif2, 0);
++ intr1 = mtk_wed_device_irq_get(wed_hif2,
++ dev->mt76.mmio.irqmask);
++ if (intr1 & MT_INT_RX_TXFREE_EXT)
++ napi_schedule(&dev->mt76.napi[MT_RXQ_TXFREE_BAND2]);
++ }
+
+- intr |= intr1;
++ 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);
++ intr |= (intr1 & ~MT_INT_RX_TXFREE_EXT);
++ } 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);
++ if (dev->hif2) {
++ intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR);
++ intr1 &= dev->mt76.mmio.irqmask;
++ mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1);
++ intr |= intr1;
++ }
+ }
+
+ trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask);
+@@ -308,9 +472,17 @@ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance)
+ {
+ struct mt7996_dev *dev = dev_instance;
+
+- mt76_wr(dev, MT_INT_MASK_CSR, 0);
+- if (dev->hif2)
+- mt76_wr(dev, MT_INT1_MASK_CSR, 0);
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++ mtk_wed_device_irq_set_mask(&dev->mt76.mmio.wed, 0);
++ else
++ mt76_wr(dev, MT_INT_MASK_CSR, 0);
++
++ if (dev->hif2) {
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
++ mtk_wed_device_irq_set_mask(&dev->mt76.mmio.wed_hif2, 0);
++ else
++ mt76_wr(dev, MT_INT1_MASK_CSR, 0);
++ }
+
+ if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
+ return IRQ_NONE;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 7354e5c..c541eaa 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -37,6 +37,7 @@
+ #define MT7996_EEPROM_SIZE 7680
+ #define MT7996_EEPROM_BLOCK_SIZE 16
+ #define MT7996_TOKEN_SIZE 16384
++#define MT7996_HW_TOKEN_SIZE 8192
+
+ #define MT7996_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */
+ #define MT7996_CFEND_RATE_11B 0x03 /* 11B LP, 11M */
+@@ -49,6 +50,22 @@
+ #define MT7996_BASIC_RATES_TBL 11
+ #define MT7996_BEACON_RATES_TBL 25
+
++#define MT7996_RRO_MAX_SESSION 1024
++#define MT7996_RRO_WINDOW_MAX_LEN 1024
++#define MT7996_RRO_ADDR_ELEM_LEN 128
++#define MT7996_RRO_BA_BITMAP_LEN 2
++#define MT7996_RRO_BA_BITMAP_CR_SIZE ((MT7996_RRO_MAX_SESSION * 128) / \
++ MT7996_RRO_BA_BITMAP_LEN)
++#define MT7996_RRO_BA_BITMAP_SESSION_SIZE (MT7996_RRO_MAX_SESSION / \
++ MT7996_RRO_ADDR_ELEM_LEN)
++#define MT7996_RRO_WINDOW_MAX_SIZE (MT7996_RRO_WINDOW_MAX_LEN * \
++ MT7996_RRO_BA_BITMAP_SESSION_SIZE)
++
++#define MT7996_RX_BUF_SIZE (1800 + \
++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
++#define MT7996_RX_MSDU_PAGE_SIZE (128 + \
++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -78,6 +95,16 @@ enum mt7996_rxq_id {
+ MT7996_RXQ_BAND0 = 4,
+ MT7996_RXQ_BAND1 = 4,/* unused */
+ MT7996_RXQ_BAND2 = 5,
++ MT7996_RXQ_RRO_BAND0 = 8,
++ MT7996_RXQ_RRO_BAND1 = 8,/* unused */
++ MT7996_RXQ_RRO_BAND2 = 6,
++ MT7996_RXQ_MSDU_PG_BAND0 = 10,
++ MT7996_RXQ_MSDU_PG_BAND1 = 11,
++ MT7996_RXQ_MSDU_PG_BAND2 = 12,
++ MT7996_RXQ_TXFREE0 = 9,
++ MT7996_RXQ_TXFREE1 = 9,
++ MT7996_RXQ_TXFREE2 = 7,
++ MT7996_RXQ_RRO_IND = 0,
+ };
+
+ struct mt7996_twt_flow {
+@@ -147,6 +174,15 @@ struct mt7996_hif {
+ int irq;
+ };
+
++struct mt7996_wed_rro_addr {
++ u32 head_low;
++ u32 head_high : 4;
++ u32 count: 11;
++ u32 oor: 1;
++ u32 rsv : 8;
++ u32 signature : 8;
++};
++
+ struct mt7996_phy {
+ struct mt76_phy *mt76;
+ struct mt7996_dev *dev;
+@@ -226,6 +262,22 @@ struct mt7996_dev {
+ bool tbtc_support:1;
+ bool flash_mode:1;
+ bool has_eht:1;
++ bool has_rro:1;
++
++ struct {
++ struct {
++ void *ptr;
++ dma_addr_t phy_addr;
++ } ba_bitmap[MT7996_RRO_BA_BITMAP_LEN];
++ struct {
++ void *ptr;
++ dma_addr_t phy_addr;
++ } addr_elem[MT7996_RRO_ADDR_ELEM_LEN];
++ struct {
++ void *ptr;
++ dma_addr_t phy_addr;
++ } session;
++ } wed_rro;
+
+ bool ibf;
+ u8 fw_debug_wm;
+@@ -335,7 +387,9 @@ int mt7996_dma_init(struct mt7996_dev *dev);
+ void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
+ void mt7996_dma_prefetch(struct mt7996_dev *dev);
+ void mt7996_dma_cleanup(struct mt7996_dev *dev);
+-void mt7996_dma_start(struct mt7996_dev *dev, bool reset);
++void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset);
++int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
++ int n_desc, int ring_base, struct mtk_wed_device *wed);
+ void mt7996_init_txpower(struct mt7996_dev *dev,
+ struct ieee80211_supported_band *sband);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
+@@ -495,5 +549,16 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct dentry *dir);
+ #endif
++int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
++ bool hif2, int *irq);
++u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
++
++#ifdef CONFIG_MTK_DEBUG
++int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
++#endif
++
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++int mt7996_dma_rro_init(struct mt7996_dev *dev);
++#endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+
+ #endif
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index c530105..92869ca 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -125,15 +125,26 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ mt7996_wfsys_reset(dev);
+ hif2 = mt7996_pci_init_hif2(pdev);
+
+- ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
++ ret = mt7996_mmio_wed_init(dev, pdev, false, &irq);
+ if (ret < 0)
+- goto free_device;
++ goto free_wed_or_irq_vector;
+
+- irq = pdev->irq;
+- ret = devm_request_irq(mdev->dev, irq, mt7996_irq_handler,
++ if (!ret) {
++ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
++ if (ret < 0)
++ goto free_device;
++ }
++ ret = devm_request_irq(mdev->dev, pdev->irq, mt7996_irq_handler,
+ IRQF_SHARED, KBUILD_MODNAME, dev);
+ if (ret)
+- goto free_irq_vector;
++ goto free_wed_or_irq_vector;
++
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
++ ret = devm_request_irq(mdev->dev, irq, mt7996_irq_handler,
++ IRQF_SHARED, KBUILD_MODNAME "-wed", dev);
++ if (ret)
++ goto free_irq;
++ }
+
+ mt76_wr(dev, MT_INT_MASK_CSR, 0);
+ /* master switch of PCIe tnterrupt enable */
+@@ -143,16 +154,30 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
+ dev->hif2 = hif2;
+
+- ret = pci_alloc_irq_vectors(hif2_dev, 1, 1, PCI_IRQ_ALL_TYPES);
++ ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
+ if (ret < 0)
+- goto free_hif2;
++ goto free_irq;
++
++ if (!ret) {
++ ret = pci_alloc_irq_vectors(hif2_dev, 1, 1, PCI_IRQ_ALL_TYPES);
++ if (ret < 0)
++ goto free_hif2;
+
+- dev->hif2->irq = hif2_dev->irq;
+- ret = devm_request_irq(mdev->dev, dev->hif2->irq,
+- mt7996_irq_handler, IRQF_SHARED,
+- KBUILD_MODNAME "-hif", dev);
++ dev->hif2->irq = hif2_dev->irq;
++ }
++
++ ret = devm_request_irq(mdev->dev, hif2_dev->irq, mt7996_irq_handler,
++ IRQF_SHARED, KBUILD_MODNAME "-hif", dev);
+ if (ret)
+- goto free_hif2_irq_vector;
++ goto free_hif2;
++
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) {
++ ret = devm_request_irq(mdev->dev, irq,
++ mt7996_irq_handler, IRQF_SHARED,
++ KBUILD_MODNAME "-wed-hif", dev);
++ if (ret)
++ goto free_hif2_irq_vector;
++ }
+
+ mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+ /* master switch of PCIe tnterrupt enable */
+@@ -168,15 +193,28 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ free_hif2_irq:
+ if (dev->hif2)
+ devm_free_irq(mdev->dev, dev->hif2->irq, dev);
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
++ devm_free_irq(mdev->dev, dev->mt76.mmio.wed_hif2.irq, dev);
+ free_hif2_irq_vector:
+- if (dev->hif2)
+- pci_free_irq_vectors(hif2_dev);
++ if (dev->hif2) {
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2))
++ mtk_wed_device_detach(&dev->mt76.mmio.wed_hif2);
++ else
++ pci_free_irq_vectors(hif2_dev);
++ }
+ free_hif2:
+ if (dev->hif2)
+ put_device(dev->hif2->dev);
+- devm_free_irq(mdev->dev, irq, dev);
+-free_irq_vector:
+- pci_free_irq_vectors(pdev);
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++ devm_free_irq(mdev->dev, dev->mt76.mmio.wed.irq, dev);
++free_irq:
++ devm_free_irq(mdev->dev, pdev->irq, dev);
++free_wed_or_irq_vector:
++ if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++ mtk_wed_device_detach(&dev->mt76.mmio.wed);
++ else
++ pci_free_irq_vectors(pdev);
++
+ free_device:
+ mt76_free_device(&dev->mt76);
+
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 5702290..854390d 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -39,6 +39,38 @@ enum base_rev {
+
+ #define __BASE(_id, _band) (dev->reg.base[(_id)].band_base[(_band)])
+
++/* RRO TOP */
++#define MT_RRO_TOP_BASE 0xA000
++#define MT_RRO_TOP(ofs) (MT_RRO_TOP_BASE + (ofs))
++
++#define MT_RRO_BA_BITMAP_BASE0 MT_RRO_TOP(0x8)
++#define MT_RRO_BA_BITMAP_BASE1 MT_RRO_TOP(0xC)
++#define WF_RRO_AXI_MST_CFG MT_RRO_TOP(0xB8)
++#define WF_RRO_AXI_MST_CFG_DIDX_OK BIT(12)
++#define MT_RRO_ADDR_ARRAY_BASE1 MT_RRO_TOP(0x34)
++#define MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE BIT(31)
++
++#define MT_RRO_IND_CMD_SIGNATURE_BASE0 MT_RRO_TOP(0x38)
++#define MT_RRO_IND_CMD_SIGNATURE_BASE1 MT_RRO_TOP(0x3C)
++#define MT_RRO_IND_CMD_0_CTRL0 MT_RRO_TOP(0x40)
++#define MT_RRO_IND_CMD_SIGNATURE_BASE1_EN BIT(31)
++
++#define MT_RRO_PARTICULAR_CFG0 MT_RRO_TOP(0x5C)
++#define MT_RRO_PARTICULAR_CFG1 MT_RRO_TOP(0x60)
++#define MT_RRO_PARTICULAR_CONFG_EN BIT(31)
++#define MT_RRO_PARTICULAR_SID GENMASK(30, 16)
++
++#define MT_RRO_BA_BITMAP_BASE_EXT0 MT_RRO_TOP(0x70)
++#define MT_RRO_BA_BITMAP_BASE_EXT1 MT_RRO_TOP(0x74)
++#define MT_RRO_HOST_INT_ENA MT_RRO_TOP(0x204)
++#define MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA BIT(0)
++
++#define MT_RRO_ADDR_ELEM_SEG_ADDR0 MT_RRO_TOP(0x400)
++
++#define MT_RRO_ACK_SN_CTRL MT_RRO_TOP(0x50)
++#define MT_RRO_ACK_SN_CTRL_SN_MASK GENMASK(27, 16)
++#define MT_RRO_ACK_SN_CTRL_SESSION_MASK GENMASK(11, 0)
++
+ #define MT_MCU_INT_EVENT 0x2108
+ #define MT_MCU_INT_EVENT_DMA_STOPPED BIT(0)
+ #define MT_MCU_INT_EVENT_DMA_INIT BIT(1)
+@@ -323,6 +355,7 @@ enum base_rev {
+
+ #define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154)
+ #define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3)
++#define MT_WFDMA0_RX_INT_SEL_RING6 BIT(6)
+
+ #define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4)
+
+@@ -367,6 +400,9 @@ enum base_rev {
+ #define MT_WFDMA0_PCIE1_BASE 0xd8000
+ #define MT_WFDMA0_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs))
+
++#define MT_INT_PCIE1_SOURCE_CSR_EXT MT_WFDMA0_PCIE1(0x118)
++#define MT_INT_PCIE1_MASK_CSR MT_WFDMA0_PCIE1(0x11c)
++
+ #define MT_WFDMA0_PCIE1_BUSY_ENA MT_WFDMA0_PCIE1(0x13c)
+ #define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0)
+ #define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1)
+@@ -387,6 +423,7 @@ enum base_rev {
+ #define MT_MCUQ_RING_BASE(q) (MT_Q_BASE(q) + 0x300)
+ #define MT_TXQ_RING_BASE(q) (MT_Q_BASE(__TXQ(q)) + 0x300)
+ #define MT_RXQ_RING_BASE(q) (MT_Q_BASE(__RXQ(q)) + 0x500)
++#define MT_RXQ_RRO_IND_RING_BASE MT_RRO_TOP(0x40)
+
+ #define MT_MCUQ_EXT_CTRL(q) (MT_Q_BASE(q) + 0x600 + \
+ MT_MCUQ_ID(q) * 0x4)
+@@ -412,6 +449,15 @@ enum base_rev {
+ #define MT_INT_RX_TXFREE_MAIN BIT(17)
+ #define MT_INT_RX_TXFREE_TRI BIT(15)
+ #define MT_INT_MCU_CMD BIT(29)
++#define MT_INT_RX_TXFREE_EXT BIT(26)
++
++#define MT_INT_RX_DONE_RRO_BAND0 BIT(16)
++#define MT_INT_RX_DONE_RRO_BAND1 BIT(16)
++#define MT_INT_RX_DONE_RRO_BAND2 BIT(14)
++#define MT_INT_RX_DONE_RRO_IND BIT(11)
++#define MT_INT_RX_DONE_MSDU_PG_BAND0 BIT(18)
++#define MT_INT_RX_DONE_MSDU_PG_BAND1 BIT(19)
++#define MT_INT_RX_DONE_MSDU_PG_BAND2 BIT(23)
+
+ #define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)])
+ #define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)])
+@@ -420,20 +466,31 @@ enum base_rev {
+ MT_INT_RX(MT_RXQ_MCU_WA))
+
+ #define MT_INT_BAND0_RX_DONE (MT_INT_RX(MT_RXQ_MAIN) | \
+- MT_INT_RX(MT_RXQ_MAIN_WA))
++ MT_INT_RX(MT_RXQ_MAIN_WA) | \
++ MT_INT_RX(MT_RXQ_TXFREE_BAND0))
+
+ #define MT_INT_BAND1_RX_DONE (MT_INT_RX(MT_RXQ_BAND1) | \
+ MT_INT_RX(MT_RXQ_BAND1_WA) | \
+- MT_INT_RX(MT_RXQ_MAIN_WA))
++ MT_INT_RX(MT_RXQ_MAIN_WA) | \
++ MT_INT_RX(MT_RXQ_TXFREE_BAND0))
+
+ #define MT_INT_BAND2_RX_DONE (MT_INT_RX(MT_RXQ_BAND2) | \
+ MT_INT_RX(MT_RXQ_BAND2_WA) | \
+- MT_INT_RX(MT_RXQ_MAIN_WA))
++ MT_INT_RX(MT_RXQ_MAIN_WA) | \
++ MT_INT_RX(MT_RXQ_TXFREE_BAND0))
++
++#define MT_INT_RRO_RX_DONE (MT_INT_RX(MT_RXQ_RRO_BAND0) | \
++ MT_INT_RX(MT_RXQ_RRO_BAND1) | \
++ MT_INT_RX(MT_RXQ_RRO_BAND2) | \
++ MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND0) | \
++ MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND1) | \
++ MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND2))
+
+ #define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_MCU | \
+ MT_INT_BAND0_RX_DONE | \
+ MT_INT_BAND1_RX_DONE | \
+- MT_INT_BAND2_RX_DONE)
++ MT_INT_BAND2_RX_DONE | \
++ MT_INT_RRO_RX_DONE)
+
+ #define MT_INT_TX_DONE_FWDL BIT(26)
+ #define MT_INT_TX_DONE_MCU_WM BIT(27)
+--
+2.18.0
+