From 5e99b61e2fd5cb15423025d7befeabccbf5f1b15 Mon Sep 17 00:00:00 2001
From: Bo Jiao <Bo.Jiao@mediatek.com>
Date: Mon, 6 Feb 2023 13:50:56 +0800
Subject: [PATCH 09/10] wifi: mt76: mt7996: add hardware rro support

add hardware rro support, This is the preliminary patch for WED3.0 support.

Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
Change-Id: I7e113b1392bcf085ec02c8a44ffbb7cf7c3fa027
---
 dma.c           | 162 +++++++++++++++++----
 dma.h           |  19 +++
 mac80211.c      |   5 +
 mt76.h          |  71 ++++++++-
 mt7996/dma.c    | 151 +++++++++++++++++++
 mt7996/init.c   | 119 ++++++++++++++-
 mt7996/mac.c    | 376 +++++++++++++++++++++++++++++++++++++++++++++++-
 mt7996/mcu.c    |   8 +-
 mt7996/mmio.c   |   3 +
 mt7996/mt7996.h | 103 +++++++++++++
 mt7996/pci.c    |   4 +
 mt7996/regs.h   |  62 +++++++-
 12 files changed, 1044 insertions(+), 39 deletions(-)

diff --git a/dma.c b/dma.c
index 3f7f6783..0b42afc5 100644
--- a/dma.c
+++ b/dma.c
@@ -183,8 +183,13 @@ EXPORT_SYMBOL_GPL(mt76_free_pending_rxwi);
 static void
 mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
 {
+	int ndesc = q->ndesc;
+
+	if (q->flags & MT_QFLAG_MAGIC)
+		ndesc |= MT_DMA_MAGIC_EN;
+
 	Q_WRITE(dev, q, desc_base, q->desc_dma);
-	Q_WRITE(dev, q, ring_size, q->ndesc);
+	Q_WRITE(dev, q, ring_size, ndesc);
 	q->head = Q_READ(dev, q, dma_idx);
 	q->tail = q->head;
 }
@@ -197,10 +202,14 @@ mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
 	if (!q || !q->ndesc)
 		return;
 
+	if (!q->desc)
+		goto done;
+
 	/* clear descriptors */
 	for (i = 0; i < q->ndesc; i++)
 		q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
 
+done:
 	Q_WRITE(dev, q, cpu_idx, 0);
 	Q_WRITE(dev, q, dma_idx, 0);
 	mt76_dma_sync_idx(dev, q);
@@ -210,16 +219,23 @@ 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_desc *desc;
 	struct mt76_queue_entry *entry = &q->entry[q->head];
 	struct mt76_rxwi_cache *rxwi = NULL;
-	u32 buf1 = 0, ctrl;
+	u32 buf1 = 0, ctrl, info = 0;
 	int idx = q->head;
 	int rx_token;
+	void *e_buf = data;
+
+	if (mt76_queue_is_rro_ind(q)) {
+		e_buf = &q->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)) {
+	if (mt76_queue_is_wed_rx(q) || mt76_queue_is_rro_data(q)) {
 		rxwi = mt76_get_rxwi(dev);
 		if (!rxwi)
 			return -ENOMEM;
@@ -232,17 +248,30 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
 
 		buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token);
 		ctrl |= MT_DMA_CTL_TO_HOST;
+		rxwi->qid = q - dev->q_rx;
+	}
+
+	if (mt76_queue_is_rro_msdu_pg(q)) {
+		if (dev->drv->rx_rro_fill_msdu_pg(dev, q, buf->addr, data))
+			return	-ENOMEM;
+	}
+
+	if (q->flags & MT_QFLAG_MAGIC) {
+		info |= FIELD_PREP(MT_DMA_MAGIC_MASK, q->magic_cnt);
+		if ((q->head + 1) == q->ndesc)
+			q->magic_cnt = (q->magic_cnt + 1) % MT_DMA_MAGIC_CNT;
 	}
 
 	WRITE_ONCE(desc->buf0, cpu_to_le32(buf->addr));
 	WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
 	WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
-	WRITE_ONCE(desc->info, 0);
+	WRITE_ONCE(desc->info, cpu_to_le32(info));
 
+done:
 	entry->dma_addr[0] = buf->addr;
 	entry->dma_len[0] = buf->len;
 	entry->rxwi = rxwi;
-	entry->buf = data;
+	entry->buf = e_buf;
 	entry->wcid = 0xffff;
 	entry->skip_buf1 = true;
 	q->head = (q->head + 1) % q->ndesc;
@@ -390,10 +419,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 (q->flags & MT_QFLAG_RRO)
+		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);
 	}
@@ -401,6 +434,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);
@@ -419,22 +458,16 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
 
 		mt76_put_rxwi(dev, r);
 
-		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;
 }
 
@@ -448,11 +481,29 @@ 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)))
-		return NULL;
+	if (mt76_queue_is_rro_data(q) ||
+	    mt76_queue_is_rro_msdu_pg(q)) {
+		goto done;
+	} else if (mt76_queue_is_rro_ind(q)) {
+		struct mt76_rro_ind *cmd;
+
+		if (flush)
+			goto done;
 
+		cmd = q->entry[idx].buf;
+		if (cmd->magic_cnt != q->magic_cnt)
+			return NULL;
+
+		if (q->tail == q->ndesc -1)
+			q->magic_cnt = (q->magic_cnt + 1) % MT_DMA_IND_CMD_MAGIC_CNT;
+	} else {
+		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;
+	}
+
+done:
 	q->tail = (q->tail + 1) % q->ndesc;
 	q->queued--;
 
@@ -602,7 +653,10 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
 		struct mt76_queue_buf qbuf;
 		dma_addr_t addr;
 		int offset;
-		void *buf;
+		void *buf = NULL;
+
+		if (mt76_queue_is_rro_ind(q))
+			goto done;
 
 		buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
 		if (!buf)
@@ -612,6 +666,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
 		dir = page_pool_get_dma_dir(q->page_pool);
 		dma_sync_single_for_device(dev->dma_dev, addr, len, dir);
 
+done:
 		qbuf.addr = addr + q->buf_offset;
 		qbuf.len = len - q->buf_offset;
 		qbuf.skip_unmap = false;
@@ -699,10 +754,25 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
 	q->hw_idx = idx;
 
 	size = q->ndesc * sizeof(struct mt76_desc);
+	if (mt76_queue_is_rro_ind(q))
+		size = q->ndesc * sizeof(struct mt76_rro_desc);
+
 	q->desc = dmam_alloc_coherent(dev->dma_dev, size, &q->desc_dma, GFP_KERNEL);
 	if (!q->desc)
 		return -ENOMEM;
 
+	if (mt76_queue_is_rro_ind(q)) {
+		struct mt76_rro_ind *cmd;
+		int i;
+
+		q->rro_desc = (struct mt76_rro_desc *)(q->desc);
+		q->desc = NULL;
+		for (i = 0; i < q->ndesc; i++) {
+			cmd = (struct mt76_rro_ind *) &q->rro_desc[i];
+			cmd->magic_cnt = MT_DMA_IND_CMD_MAGIC_CNT - 1;
+		}
+	}
+
 	size = q->ndesc * sizeof(*q->entry);
 	q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
 	if (!q->entry)
@@ -738,6 +808,9 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
 		if (!buf)
 			break;
 
+		if (q->flags & MT_QFLAG_RRO)
+			continue;
+
 		mt76_put_page_pool_buf(buf, false);
 	} while (1);
 
@@ -758,9 +831,13 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
 	if (!q->ndesc)
 		return;
 
+	if (!q->desc)
+		goto done;
+
 	for (i = 0; i < q->ndesc; i++)
 		q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
 
+done:
 	mt76_dma_rx_cleanup(dev, q);
 
 	/* reset WED rx queues */
@@ -771,8 +848,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
 	}
 }
 
-static void
-mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+void mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
 		  int len, bool more, u32 info)
 {
 	struct sk_buff *skb = q->rx_head;
@@ -797,6 +873,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
 	else
 		dev_kfree_skb(skb);
 }
+EXPORT_SYMBOL_GPL(mt76_add_fragment);
 
 static int
 mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
@@ -807,8 +884,9 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
 	bool check_ddone = false;
 	bool more;
 
-	if (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
-	    q->flags == MT_WED_Q_TXFREE) {
+	if ((q->flags & MT_QFLAG_MAGIC) ||
+	    (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
+	     q->flags == MT_WED_Q_TXFREE)) {
 		dma_idx = Q_READ(dev, q, dma_idx);
 		check_ddone = true;
 	}
@@ -830,6 +908,14 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
 		if (!data)
 			break;
 
+		if (mt76_queue_is_rro_ind(q) && dev->drv->rx_rro_ind_process)
+			dev->drv->rx_rro_ind_process(dev, data);
+
+		if (q->flags & MT_QFLAG_RRO) {
+			done++;
+			continue;
+		}
+
 		if (drop)
 			goto free_frag;
 
@@ -907,11 +993,18 @@ int mt76_dma_rx_poll(struct napi_struct *napi, int budget)
 EXPORT_SYMBOL_GPL(mt76_dma_rx_poll);
 
 static int
-mt76_dma_init(struct mt76_dev *dev,
+__mt76_dma_init(struct mt76_dev *dev, enum mt76_rxq_id qid,
 	      int (*poll)(struct napi_struct *napi, int budget))
 {
 	int i;
 
+	if (qid < __MT_RXQ_MAX && dev->q_rx[qid].ndesc) {
+		netif_napi_add(&dev->napi_dev, &dev->napi[qid], poll);
+		mt76_dma_rx_fill(dev, &dev->q_rx[qid], false);
+		napi_enable(&dev->napi[qid]);
+		return 0;
+	}
+
 	init_dummy_netdev(&dev->napi_dev);
 	init_dummy_netdev(&dev->tx_napi_dev);
 	snprintf(dev->napi_dev.name, sizeof(dev->napi_dev.name), "%s",
@@ -929,6 +1022,20 @@ mt76_dma_init(struct mt76_dev *dev,
 	return 0;
 }
 
+static int
+mt76_dma_rx_queue_init(struct mt76_dev *dev, enum mt76_rxq_id qid,
+	      int (*poll)(struct napi_struct *napi, int budget))
+{
+	return __mt76_dma_init(dev, qid, poll);
+}
+
+static int
+mt76_dma_init(struct mt76_dev *dev,
+	      int (*poll)(struct napi_struct *napi, int budget))
+{
+	return __mt76_dma_init(dev, __MT_RXQ_MAX, poll);
+}
+
 static const struct mt76_queue_ops mt76_dma_ops = {
 	.init = mt76_dma_init,
 	.alloc = mt76_dma_alloc_queue,
@@ -936,6 +1043,7 @@ static const struct mt76_queue_ops mt76_dma_ops = {
 	.tx_queue_skb_raw = mt76_dma_tx_queue_skb_raw,
 	.tx_queue_skb = mt76_dma_tx_queue_skb,
 	.tx_cleanup = mt76_dma_tx_cleanup,
+	.rx_init = mt76_dma_rx_queue_init,
 	.rx_cleanup = mt76_dma_rx_cleanup,
 	.rx_reset = mt76_dma_rx_reset,
 	.kick = mt76_dma_kick_queue,
diff --git a/dma.h b/dma.h
index 1b090d78..d6f4e0cd 100644
--- a/dma.h
+++ b/dma.h
@@ -25,6 +25,18 @@
 #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_SDP0			GENMASK(15, 0)
+#define MT_DMA_TOKEN_ID		GENMASK(31, 16)
+#define MT_DMA_MAGIC_MASK		GENMASK(31, 28)
+#define MT_DMA_PPE_VLD_MASK		BIT(31)
+#define MT_DMA_MAGIC_EN		BIT(13)
+
+#define MT_DMA_MAGIC_CNT		16
+#define MT_DMA_IND_CMD_MAGIC_CNT	8
+
 #define MT_DMA_HDR_LEN			4
 #define MT_RX_INFO_LEN			4
 #define MT_FCE_INFO_LEN			4
@@ -37,6 +49,11 @@ struct mt76_desc {
 	__le32 info;
 } __packed __aligned(4);
 
+struct mt76_rro_desc {
+	__le32 buf0;
+	__le32 buf1;
+} __packed __aligned(4);
+
 enum mt76_qsel {
 	MT_QSEL_MGMT,
 	MT_QSEL_HCCA,
@@ -54,6 +71,8 @@ enum mt76_mcu_evt_type {
 	EVT_EVENT_DFS_DETECT_RSP,
 };
 
+void mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+		  int len, bool more, u32 info);
 int mt76_dma_rx_poll(struct napi_struct *napi, int budget);
 void mt76_dma_attach(struct mt76_dev *dev);
 void mt76_dma_cleanup(struct mt76_dev *dev);
diff --git a/mac80211.c b/mac80211.c
index d3a171e1..25ae08a7 100644
--- a/mac80211.c
+++ b/mac80211.c
@@ -761,6 +761,7 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
 	struct sk_buff *skb = phy->rx_amsdu[q].head;
 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
 	struct mt76_dev *dev = phy->dev;
+	struct mt76_queue *rxq = &dev->q_rx[q];
 
 	phy->rx_amsdu[q].head = NULL;
 	phy->rx_amsdu[q].tail = NULL;
@@ -789,6 +790,10 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
 			return;
 		}
 	}
+
+	if (mt76_queue_is_rro_data(rxq))
+		q = MT_RXQ_RRO_IND;
+
 	__skb_queue_tail(&dev->rx_skb[q], skb);
 }
 
diff --git a/mt76.h b/mt76.h
index 90345f47..252bb137 100644
--- a/mt76.h
+++ b/mt76.h
@@ -27,6 +27,18 @@
 
 #define MT76_TOKEN_FREE_THR	64
 
+#define MT_QFLAG_RRO_RING	GENMASK(6, 5)
+#define MT_QFLAG_RRO_TYPE	GENMASK(8, 7)
+#define MT_QFLAG_RRO		BIT(9)
+#define MT_QFLAG_MAGIC		BIT(10)
+
+#define __MT_RRO_Q(_type, _n)	(MT_QFLAG_RRO | \
+				 FIELD_PREP(MT_QFLAG_RRO_TYPE, _type) | \
+				 FIELD_PREP(MT_QFLAG_RRO_RING, _n))
+#define MT_RRO_Q_DATA(_n)	__MT_RRO_Q(MT76_RRO_Q_DATA, _n)
+#define MT_RRO_Q_MSDU_PG(_n)	__MT_RRO_Q(MT76_RRO_Q_MSDU_PG, _n)
+#define MT_RRO_Q_IND		__MT_RRO_Q(MT76_RRO_Q_IND, 0)
+
 #define MT_QFLAG_WED_RING	GENMASK(1, 0)
 #define MT_QFLAG_WED_TYPE	GENMASK(3, 2)
 #define MT_QFLAG_WED		BIT(4)
@@ -60,6 +72,12 @@ enum mt76_wed_type {
 	MT76_WED_Q_RX,
 };
 
+enum mt76_RRO_type {
+	MT76_RRO_Q_DATA,
+	MT76_RRO_Q_MSDU_PG,
+	MT76_RRO_Q_IND,
+};
+
 struct mt76_bus_ops {
 	u32 (*rr)(struct mt76_dev *dev, u32 offset);
 	void (*wr)(struct mt76_dev *dev, u32 offset, u32 val);
@@ -106,6 +124,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
 };
 
@@ -184,6 +212,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;
@@ -197,8 +226,8 @@ struct mt76_queue {
 
 	u8 buf_offset;
 	u8 hw_idx;
-	u8 flags;
-
+	u8 magic_cnt;
+	u32 flags;
 	u32 wed_regs;
 
 	dma_addr_t desc_dma;
@@ -248,6 +277,9 @@ struct mt76_queue_ops {
 	void (*tx_cleanup)(struct mt76_dev *dev, struct mt76_queue *q,
 			   bool flush);
 
+	int (*rx_init)(struct mt76_dev *dev, enum mt76_rxq_id qid,
+		      int (*poll)(struct napi_struct *napi, int budget));
+
 	void (*rx_cleanup)(struct mt76_dev *dev, struct mt76_queue *q);
 
 	void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
@@ -350,6 +382,17 @@ struct mt76_txq {
 	bool aggr;
 };
 
+struct mt76_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;
@@ -362,6 +405,7 @@ struct mt76_rxwi_cache {
 	dma_addr_t dma_addr;
 
 	void *ptr;
+	u8 qid;
 };
 
 struct mt76_rx_tid {
@@ -466,6 +510,10 @@ struct mt76_driver_ops {
 	void (*rx_skb)(struct mt76_dev *dev, enum mt76_rxq_id q,
 		       struct sk_buff *skb, u32 *info);
 
+	void (*rx_rro_ind_process)(struct mt76_dev *dev, void *data);
+	int (*rx_rro_fill_msdu_pg)(struct mt76_dev *dev, struct mt76_queue *q,
+				   dma_addr_t p, void *data);
+
 	void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q);
 
 	void (*sta_ps)(struct mt76_dev *dev, struct ieee80211_sta *sta,
@@ -963,6 +1011,7 @@ static inline u16 mt76_rev(struct mt76_dev *dev)
 #define mt76_tx_queue_skb(dev, ...)	(dev)->mt76.queue_ops->tx_queue_skb(&((dev)->mt76), __VA_ARGS__)
 #define mt76_queue_rx_reset(dev, ...)	(dev)->mt76.queue_ops->rx_reset(&((dev)->mt76), __VA_ARGS__)
 #define mt76_queue_tx_cleanup(dev, ...)	(dev)->mt76.queue_ops->tx_cleanup(&((dev)->mt76), __VA_ARGS__)
+#define mt76_queue_rx_init(dev, ...)	(dev)->mt76.queue_ops->rx_init(&((dev)->mt76), __VA_ARGS__)
 #define mt76_queue_rx_cleanup(dev, ...)	(dev)->mt76.queue_ops->rx_cleanup(&((dev)->mt76), __VA_ARGS__)
 #define mt76_queue_kick(dev, ...)	(dev)->mt76.queue_ops->kick(&((dev)->mt76), __VA_ARGS__)
 #define mt76_queue_reset(dev, ...)	(dev)->mt76.queue_ops->reset_q(&((dev)->mt76), __VA_ARGS__)
@@ -1456,6 +1505,24 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
 	       FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX;
 }
 
+static inline bool mt76_queue_is_rro_msdu_pg(struct mt76_queue *q)
+{
+	return (q->flags & MT_QFLAG_RRO) &&
+	       FIELD_GET(MT_QFLAG_RRO_TYPE, q->flags) == MT76_RRO_Q_MSDU_PG;
+}
+
+static inline bool mt76_queue_is_rro_data(struct mt76_queue *q)
+{
+	return (q->flags & MT_QFLAG_RRO) &&
+	       FIELD_GET(MT_QFLAG_RRO_TYPE, q->flags) == MT76_RRO_Q_DATA;
+}
+
+static inline bool mt76_queue_is_rro_ind(struct mt76_queue *q)
+{
+	return (q->flags & MT_QFLAG_RRO) &&
+	       FIELD_GET(MT_QFLAG_RRO_TYPE, q->flags) == MT76_RRO_Q_IND;
+}
+
 struct mt76_txwi_cache *
 mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
 int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
diff --git a/mt7996/dma.c b/mt7996/dma.c
index ffba81b0..27213bff 100644
--- a/mt7996/dma.c
+++ b/mt7996/dma.c
@@ -45,6 +45,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->rro_support) {
+		/* 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);
@@ -72,6 +95,22 @@ static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
 	mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2_WA) + ofs, PREFETCH(0x180, 0x2));
 	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->rro_support) {
+		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, 0x2));
+		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND2) + ofs,
+			PREFETCH(0x680, 0x2));
+	}
 
 	mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + ofs, WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE);
 }
@@ -235,6 +274,113 @@ static int mt7996_dma_enable(struct mt7996_dev *dev)
 	return 0;
 }
 
+int mt7996_dma_rro_init(struct mt7996_dev *dev)
+{
+	int ret;
+	u32 hif1_ofs = 0;
+	if (dev->hif2)
+		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+
+	/* ind cmd */
+	dev->mt76.q_rx[MT_RXQ_RRO_IND].flags = MT_RRO_Q_IND;
+	ret = mt76_queue_alloc(dev, &dev->mt76.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;
+
+	/* rx rro data queue for band0 */
+	dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags = MT_RRO_Q_DATA(0);
+	dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags |= MT_QFLAG_MAGIC;
+	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;
+
+	/* rx msdu page queue for band0 */
+	dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].flags = MT_RRO_Q_MSDU_PG(0);
+	dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].flags |= MT_QFLAG_MAGIC;
+	ret = mt76_queue_alloc(dev, &dev->mt76.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;
+
+	/* tx free notify event from MAC for band0 */
+	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 (mt7996_band_valid(dev, MT_BAND1)) {
+		/* rx msdu page queue for band1 */
+		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags = MT_RRO_Q_MSDU_PG(1);
+		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags |= MT_QFLAG_MAGIC;
+		ret = mt76_queue_alloc(dev, &dev->mt76.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 (mt7996_band_valid(dev, MT_BAND2)) {
+		/* rx rro data queue for band2 */
+		dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags = MT_RRO_Q_DATA(2);
+		dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags |= MT_QFLAG_MAGIC;
+		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;
+
+		/* rx msdu page queue for band2 */
+		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].flags = MT_RRO_Q_MSDU_PG(2);
+		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].flags |= MT_QFLAG_MAGIC;
+		ret = mt76_queue_alloc(dev, &dev->mt76.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;
+
+		/* tx free notify event from MAC for band2 */
+		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;
+	}
+	mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND0, mt76_dma_rx_poll);
+	mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND1, mt76_dma_rx_poll);
+	mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND2, mt76_dma_rx_poll);
+	mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND0, mt76_dma_rx_poll);
+	mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND1, mt76_dma_rx_poll);
+	mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND2, mt76_dma_rx_poll);
+	mt76_queue_rx_init(dev, MT_RXQ_TXFREE_BAND0, mt76_dma_rx_poll);
+	mt76_queue_rx_init(dev, MT_RXQ_TXFREE_BAND2, mt76_dma_rx_poll);
+	mt76_queue_rx_init(dev, MT_RXQ_RRO_IND, mt76_dma_rx_poll);
+
+	mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE);
+
+	return 0;
+}
+
 int mt7996_dma_init(struct mt7996_dev *dev)
 {
 	u32 hif1_ofs = 0;
@@ -384,6 +530,11 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
 	mt76_for_each_q_rx(&dev->mt76, i)
 		mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
 
+	if (dev->rro_support) {
+		mt7996_rro_msdu_pg_free(dev);
+		mt7996_rx_token_put(dev);
+	}
+
 	mt76_tx_status_check(&dev->mt76, true);
 
 	/* reset wfsys */
diff --git a/mt7996/init.c b/mt7996/init.c
index 92697e93..d7d06cff 100644
--- a/mt7996/init.c
+++ b/mt7996/init.c
@@ -326,8 +326,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->rro_support) {
+		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,
@@ -468,6 +473,104 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
 	msleep(20);
 }
 
+static int mt7996_rro_init(struct mt7996_dev *dev)
+{
+	struct mt7996_rro_addr *ptr;
+	struct mt7996_rro_cfg *rro = &dev->rro;
+	u32 size, val = 0, reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
+	int i, j;
+	void *buf;
+
+	for (i = 0; i < MT7996_RRO_BA_BITMAP_CR_CNT; i++) {
+		buf = dmam_alloc_coherent(dev->mt76.dma_dev,
+					  MT7996_BA_BITMAP_SZ_PER_CR,
+					  &rro->ba_bitmap_cache_pa[i],
+					  GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+
+		rro->ba_bitmap_cache_va[i] = buf;
+	}
+
+	rro->win_sz = MT7996_RRO_WIN_SIZE_MAX;
+	for (i = 0; i < MT7996_RRO_ADDR_ELEM_CR_CNT; i++) {
+		size = MT7996_RRO_SESSION_PER_CR *
+		       rro->win_sz * sizeof(struct mt7996_rro_addr);
+
+		buf = dmam_alloc_coherent(dev->mt76.dma_dev, size,
+					  &rro->addr_elem_alloc_pa[i],
+					  GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+		rro->addr_elem_alloc_va[i] = buf;
+
+		memset(rro->addr_elem_alloc_va[i], 0, size);
+
+		ptr = rro->addr_elem_alloc_va[i];
+		for (j = 0; j < MT7996_RRO_SESSION_PER_CR * rro->win_sz; j++, ptr++)
+			ptr->signature = 0xff;
+	}
+
+	rro->particular_se_id = MT7996_RRO_SESSION_MAX;
+	size = rro->win_sz * sizeof(struct mt7996_rro_addr);
+	buf = dmam_alloc_coherent(dev->mt76.dma_dev, size,
+				  &rro->particular_session_pa,
+				  GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	rro->particular_session_va = buf;
+	ptr = rro->particular_session_va;
+	for (j = 0; j < rro->win_sz; j++, ptr++)
+		ptr->signature = 0xff;
+
+	INIT_LIST_HEAD(&rro->pg_addr_cache);
+	for (i = 0; i < MT7996_RRO_MSDU_PG_HASH_SIZE; i++)
+		INIT_LIST_HEAD(&rro->pg_hash_head[i]);
+
+	/* 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,
+		rro->ba_bitmap_cache_pa[0]);
+	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
+	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
+		rro->ba_bitmap_cache_pa[1]);
+	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
+
+	/* setup Address element address */
+	for (i = 0; i < MT7996_RRO_ADDR_ELEM_CR_CNT; i++) {
+		mt76_wr(dev, reg, rro->addr_elem_alloc_pa[i] >> 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);
+
+	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0);
+	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1, 0);
+
+	/* particular session configure */
+	/* use max session idx + 1 as particular session id */
+	mt76_wr(dev, MT_RRO_PARTICULAR_CFG0,
+		rro->particular_session_pa);
+
+	val = FIELD_PREP(MT_RRO_PARTICULAR_SID,
+			 MT7996_RRO_SESSION_MAX);
+	val |= MT_RRO_PARTICULAR_CONFG_EN;
+	mt76_wr(dev, MT_RRO_PARTICULAR_CFG1, val);
+
+	/* 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);
+}
+
 static int mt7996_init_hardware(struct mt7996_dev *dev)
 {
 	int ret, idx;
@@ -481,6 +584,9 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
 	dev->tbtc_support = mt7996_band_valid(dev, MT_BAND1) &&
 			    mt7996_band_valid(dev, MT_BAND2);
 
+	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
+		dev->rro_support = true;
+
 	ret = mt7996_dma_init(dev);
 	if (ret)
 		return ret;
@@ -491,6 +597,12 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
 	if (ret)
 		return ret;
 
+	if (dev->rro_support) {
+		ret = mt7996_rro_init(dev);
+		if (ret)
+			return ret;
+	}
+
 	ret = mt7996_eeprom_init(dev);
 	if (ret < 0)
 		return ret;
@@ -951,6 +1063,9 @@ void mt7996_unregister_device(struct mt7996_dev *dev)
 	mt7996_mcu_exit(dev);
 	mt7996_tx_token_put(dev);
 	mt7996_dma_cleanup(dev);
+	if (dev->rro_support)
+		mt7996_rro_msdu_pg_free(dev);
+	mt7996_rx_token_put(dev);
 	tasklet_disable(&dev->mt76.irq_tasklet);
 
 	mt76_free_device(&dev->mt76);
diff --git a/mt7996/mac.c b/mt7996/mac.c
index e55889fe..0c492182 100644
--- a/mt7996/mac.c
+++ b/mt7996/mac.c
@@ -655,7 +655,9 @@ 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;
 
+	hw_aggr = status->aggr;
 	memset(status, 0, sizeof(*status));
 
 	band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1);
@@ -886,7 +888,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
 	if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
 		mt7996_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 &&
@@ -1634,6 +1636,378 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
 	}
 }
 
+static struct mt7996_msdu_pg_addr *
+mt7996_alloc_pg_addr(struct mt7996_dev *dev)
+{
+	struct mt7996_msdu_pg_addr *p;
+	int size;
+
+	size = L1_CACHE_ALIGN(sizeof(*p));
+	p = kzalloc(size, GFP_ATOMIC);
+	if (!p)
+		return NULL;
+
+	INIT_LIST_HEAD(&p->list);
+
+	return p;
+}
+
+static struct mt7996_msdu_pg_addr *
+__mt7996_get_pg_addr(struct mt7996_dev *dev)
+{
+	struct mt7996_msdu_pg_addr *p = NULL;
+
+	spin_lock(&dev->rro.lock);
+	if (!list_empty(&dev->rro.pg_addr_cache)) {
+		p = list_first_entry(&dev->rro.pg_addr_cache,
+				     struct mt7996_msdu_pg_addr,
+				     list);
+		if(p)
+			list_del(&p->list);
+	}
+	spin_unlock(&dev->rro.lock);
+
+	return p;
+}
+
+struct mt7996_msdu_pg_addr *
+mt7996_get_pg_addr(struct mt7996_dev *dev)
+{
+	struct mt7996_msdu_pg_addr *p = __mt7996_get_pg_addr(dev);
+
+	if (p)
+		return p;
+
+	return mt7996_alloc_pg_addr(dev);
+}
+
+static void
+mt7996_put_pg_addr(struct mt7996_dev *dev,
+		struct mt7996_msdu_pg_addr *p)
+{
+	if (!p)
+		return;
+
+	if (p->buf) {
+		mt76_put_page_pool_buf(p->buf, false);
+		p->buf = NULL;
+	}
+
+	spin_lock(&dev->rro.lock);
+	list_add(&p->list, &dev->rro.pg_addr_cache);
+	spin_unlock(&dev->rro.lock);
+}
+
+static void
+mt7996_free_pg_addr(struct mt7996_dev *dev)
+{
+	struct mt7996_msdu_pg_addr *pg_addr;
+
+	local_bh_disable();
+	while ((pg_addr = __mt7996_get_pg_addr(dev)) != NULL) {
+		if (pg_addr->buf) {
+			mt76_put_page_pool_buf(pg_addr->buf, false);
+			pg_addr->buf = NULL;
+		}
+		kfree(pg_addr);
+	}
+	local_bh_enable();
+}
+
+static u32
+mt7996_rro_msdu_pg_hash(dma_addr_t pa)
+{
+	u32 sum = 0;
+	u16 i = 0;
+
+	while (pa != 0) {
+		sum += (u32) ((pa & 0xff) + i) % MT7996_RRO_MSDU_PG_HASH_SIZE;
+		pa >>= 8;
+		i += 13;
+	}
+
+	return sum % MT7996_RRO_MSDU_PG_HASH_SIZE;
+}
+
+static struct mt7996_msdu_pg_addr *
+mt7996_rro_msdu_pg_search(struct mt7996_dev *dev, dma_addr_t pa)
+{
+	struct mt7996_rro_cfg *rro = &dev->rro;
+	struct mt7996_msdu_pg_addr *pg_addr, *tmp;
+	u32 hash_idx =  mt7996_rro_msdu_pg_hash(pa);
+	struct list_head *head;
+	u8 found = 0;
+
+	spin_lock(&rro->lock);
+	head = &rro->pg_hash_head[hash_idx];
+	list_for_each_entry_safe(pg_addr, tmp, head, list) {
+		if (pg_addr->dma_addr == pa) {
+			list_del(&pg_addr->list);
+			found = 1;
+			break;
+		}
+	}
+	spin_unlock(&rro->lock);
+
+	return (found == 1) ? pg_addr : NULL;
+}
+
+void mt7996_rro_msdu_pg_free(struct mt7996_dev *dev)
+{
+	struct mt7996_msdu_pg_addr *pg_addr, *tmp;
+	struct list_head *head;
+	u32 i;
+
+	local_bh_disable();
+	for (i = 0; i < MT7996_RRO_MSDU_PG_HASH_SIZE; i++) {
+		head = &dev->rro.pg_hash_head[i];
+		list_for_each_entry_safe(pg_addr, tmp, head, list) {
+			list_del_init(&pg_addr->list);
+			dma_sync_single_for_cpu(dev->mt76.dma_dev, pg_addr->dma_addr,
+						SKB_WITH_OVERHEAD(pg_addr->q->buf_size),
+						page_pool_get_dma_dir(pg_addr->q->page_pool));
+			if (pg_addr->buf) {
+				mt76_put_page_pool_buf(pg_addr->buf, false);
+				pg_addr->buf = NULL;
+			}
+			kfree(pg_addr);
+		}
+	}
+	local_bh_enable();
+
+	mt7996_free_pg_addr(dev);
+
+	mt76_for_each_q_rx(&dev->mt76, i) {
+		struct mt76_queue *q = &dev->mt76.q_rx[i];
+
+		if (mt76_queue_is_rro_msdu_pg(q))
+			page_pool_destroy(q->page_pool);
+	}
+}
+
+static struct mt7996_rro_addr *
+mt7996_rro_get_addr_elem(struct mt7996_dev *dev, u16 seid, u16 sn)
+{
+	struct mt7996_rro_cfg *rro = &dev->rro;
+	u32 idx;
+	void *addr;
+
+	if (seid == rro->particular_se_id) {
+		addr = rro->particular_session_va;
+		idx = sn % rro->win_sz;
+	} else {
+		addr = rro->addr_elem_alloc_va[seid / MT7996_RRO_SESSION_PER_CR];
+		idx = (seid % MT7996_RRO_SESSION_PER_CR) * rro->win_sz
+			+ (sn % rro->win_sz);
+	}
+	return addr + idx * sizeof(struct mt7996_rro_addr);
+}
+
+void mt7996_rx_token_put(struct mt7996_dev *dev)
+{
+	struct mt76_queue *q;
+	struct page *page;
+	int i;
+
+	for (i = 0; i < dev->mt76.rx_token_size; i++) {
+		struct mt76_rxwi_cache *r;
+
+		r = mt76_rx_token_release(&dev->mt76, i);
+		if (!r || !r->ptr)
+			continue;
+
+		q = &dev->mt76.q_rx[r->qid];
+		dma_sync_single_for_cpu(mdev->dma_dev, r->dma_addr,
+					SKB_WITH_OVERHEAD(q->buf_size),
+					page_pool_get_dma_dir(q->page_pool));
+		mt76_put_page_pool_buf(r->ptr, false);
+		r->dma_addr = 0;
+		r->ptr = NULL;
+
+		mt76_put_rxwi(&dev->mt76, r);
+	}
+
+	mt76_for_each_q_rx(&dev->mt76, i) {
+		struct mt76_queue *q = &dev->mt76.q_rx[i];
+
+		if (mt76_queue_is_rro_data(q))
+			page_pool_destroy(q->page_pool);
+	}
+
+	mt76_free_pending_rxwi(&dev->mt76);
+}
+
+int mt7996_rro_fill_msdu_page(struct mt76_dev *mdev, struct mt76_queue *q,
+			 dma_addr_t p, void *data)
+{
+	struct mt7996_msdu_pg_addr *pg_addr;
+	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+	struct mt7996_msdu_pg *pg = data;
+	u32 hash_idx;
+
+	pg->owner = 1;
+	pg_addr = mt7996_get_pg_addr(dev);
+	if (!pg_addr)
+		return -ENOMEM;
+
+	pg_addr->buf = data;
+	pg_addr->dma_addr = p;
+	pg_addr->q = q;
+	hash_idx = mt7996_rro_msdu_pg_hash(pg_addr->dma_addr);
+
+	spin_lock(&dev->rro.lock);
+	list_add_tail(&pg_addr->list,
+		      &dev->rro.pg_hash_head[hash_idx]);
+	spin_unlock(&dev->rro.lock);
+
+	return 0;
+}
+
+void mt7996_rro_rx_process(struct mt76_dev *mdev, void *data)
+{
+	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+	struct mt76_rro_ind *cmd = (struct mt76_rro_ind *)data;
+	struct mt76_rxwi_cache *r;
+	struct mt76_rx_status *status;
+	struct mt76_queue *q;
+	struct mt7996_rro_cfg *rro = &dev->rro;
+	struct mt7996_rro_addr *elem;
+	struct mt7996_msdu_pg_addr *pg_addr = NULL;
+	struct mt7996_msdu_pg *pg = NULL;
+	struct mt7996_rro_hif *rxd;
+	struct sk_buff *skb;
+	dma_addr_t msdu_pg_pa;
+	int len, data_len, i, j, sn;
+	void *buf;
+	u8 more, qid;
+	u32 info = 0;
+
+	for (i = 0; i < cmd->ind_cnt; i++) {
+		sn = (cmd->start_sn + i) & GENMASK(11, 0);
+		elem = mt7996_rro_get_addr_elem(dev, cmd->se_id, sn);
+		if (elem->signature != (sn / rro->win_sz)) {
+			elem->signature = 0xff;
+			goto update_ack_sn;
+		}
+
+		msdu_pg_pa = elem->head_pkt_h;
+		msdu_pg_pa <<= 32;
+		msdu_pg_pa |= elem->head_pkt_l;
+
+		for (j = 0; j < elem->seg_cnt; j++) {
+			if (pg_addr == NULL) {
+				pg_addr = mt7996_rro_msdu_pg_search(dev, msdu_pg_pa);
+
+				if (pg_addr == NULL) {
+					dev_info(mdev->dev, "pg_addr(%llx) search fail\n",
+						 msdu_pg_pa);
+					continue;
+				}
+
+				dma_sync_single_for_cpu(dev->dma_dev, pg_addr->dma_addr,
+							SKB_WITH_OVERHEAD(pg_addr->q->buf_size),
+							page_pool_get_dma_dir(pg_addr->q->page_pool));
+
+				pg = (struct mt7996_msdu_pg *) pg_addr->buf;
+			}
+
+			rxd = &pg->rxd[j % MT7996_MAX_HIF_RXD_IN_PG];
+			more = !rxd->ls;
+			len = rxd->sdl;
+
+			r = mt76_rx_token_release(mdev, rxd->rx_token_id);
+			if (!r)
+				goto next_page_chk;
+
+			qid = r->qid;
+			buf = r->ptr;
+			q = &mdev->q_rx[qid];
+			dma_sync_single_for_cpu(mdev->dma_dev, r->dma_addr,
+						SKB_WITH_OVERHEAD(q->buf_size),
+						page_pool_get_dma_dir(q->page_pool));
+			r->dma_addr = 0;
+			r->ptr = NULL;
+			mt76_put_rxwi(mdev, r);
+			if (!buf)
+				goto next_page_chk;
+
+			if (q->rx_head)
+				data_len = q->buf_size;
+			else
+				data_len = SKB_WITH_OVERHEAD(q->buf_size);
+
+			if (data_len < len + q->buf_offset) {
+				dev_kfree_skb(q->rx_head);
+				mt76_put_page_pool_buf(buf, true);
+				q->rx_head = NULL;
+				goto next_page_chk;
+			}
+
+			if (q->rx_head) {
+				/* TDO: fragment error, skip handle */
+				//mt76_add_fragment(mdev, q, buf, len, more, info);
+				mt76_put_page_pool_buf(buf, true);
+				if (!more) {
+					dev_kfree_skb(q->rx_head);
+					q->rx_head = NULL;
+				}
+				goto next_page_chk;
+			}
+
+			if (!more && !mt7996_rx_check(mdev, buf, len))
+				goto next_page_chk;
+
+			skb = build_skb(buf, q->buf_size);
+			if (!skb)
+				goto next_page_chk;
+
+			skb_reserve(skb, q->buf_offset);
+			__skb_put(skb, len);
+
+			if (cmd->ind_reason == 1 || cmd->ind_reason == 2) {
+				dev_kfree_skb(skb);
+				goto next_page_chk;
+			}
+
+			if (more) {
+				q->rx_head = skb;
+				goto next_page_chk;
+			}
+
+			status = (struct mt76_rx_status *)skb->cb;
+			if (cmd->se_id != rro->particular_se_id)
+				status->aggr = true;
+
+			mt7996_queue_rx_skb(mdev, qid, skb, &info);
+
+next_page_chk:
+			if ((j + 1) % MT7996_MAX_HIF_RXD_IN_PG == 0) {
+				msdu_pg_pa = pg->next_pg_h;
+				msdu_pg_pa <<= 32;
+				msdu_pg_pa |= pg->next_pg_l;
+				mt7996_put_pg_addr(dev, pg_addr);
+				pg_addr = NULL;
+			}
+		}
+update_ack_sn:
+		if ((i + 1) % 4 == 0)
+			mt76_wr(dev, MT_RRO_ACK_SN_CTRL,
+				FIELD_PREP(MT_RRO_ACK_SN_CTRL_SESSION_MASK, cmd->se_id) |
+				FIELD_PREP(MT_RRO_ACK_SN_CTRL_SN_MASK, sn));
+		if (pg_addr) {
+			mt7996_put_pg_addr(dev, pg_addr);
+			pg_addr = NULL;
+		}
+	}
+
+	/* update ack_sn for remaining addr_elem */
+	if (i % 4 != 0)
+		mt76_wr(dev, MT_RRO_ACK_SN_CTRL,
+			FIELD_PREP(MT_RRO_ACK_SN_CTRL_SESSION_MASK, cmd->se_id) |
+			FIELD_PREP(MT_RRO_ACK_SN_CTRL_SN_MASK, sn));
+}
+
+
 void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy)
 {
 	struct mt7996_dev *dev = phy->dev;
diff --git a/mt7996/mcu.c b/mt7996/mcu.c
index 2ff0f63b..3fedf248 100644
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -883,7 +883,7 @@ static int
 mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
 		  struct ieee80211_ampdu_params *params,
 		  struct mt76_wcid *wcid,
-		  bool enable, bool tx)
+		  bool enable, bool tx, bool rro_enable)
 {
 	struct sta_rec_ba_uni *ba;
 	struct sk_buff *skb;
@@ -903,6 +903,8 @@ 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;
+	if (rro_enable && !tx && enable)
+		ba->ba_rdd_rro = true;
 
 	return mt76_mcu_skb_send_msg(dev, skb,
 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
@@ -924,7 +926,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
 		mlink->wcid.amsdu = false;
 
 	return mt7996_mcu_sta_ba(&dev->mt76, &mconf->mt76, params, &mlink->wcid,
-				 enable, true);
+				 enable, true, dev->rro_support);
 }
 
 int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
@@ -939,7 +941,7 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
 	mconf = mconf_dereference_protected(msta->vif, msta->pri_link);
 
 	return mt7996_mcu_sta_ba(&dev->mt76, &mconf->mt76, params, &mlink->wcid,
-				 enable, false);
+				 enable, false, dev->rro_support);
 }
 
 static void
diff --git a/mt7996/mmio.c b/mt7996/mmio.c
index 3a591a7b..54055216 100644
--- a/mt7996/mmio.c
+++ b/mt7996/mmio.c
@@ -333,11 +333,14 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
 				SURVEY_INFO_TIME_RX |
 				SURVEY_INFO_TIME_BSS_RX,
 		.token_size = MT7996_TOKEN_SIZE,
+		.rx_token_size = MT7996_RX_TOKEN_SIZE,
 		.tx_prepare_skb = mt7996_tx_prepare_skb,
 		.tx_complete_skb = mt76_connac_tx_complete_skb,
 		.rx_skb = mt7996_queue_rx_skb,
 		.rx_check = mt7996_rx_check,
 		.rx_poll_complete = mt7996_rx_poll_complete,
+		.rx_rro_ind_process = mt7996_rro_rx_process,
+		.rx_rro_fill_msdu_pg = mt7996_rro_fill_msdu_page,
 		.sta_add = mt7996_mac_sta_add,
 		.sta_remove = mt7996_mac_sta_remove,
 		.update_survey = mt7996_update_channel,
diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
index 2fac334a..60af6084 100644
--- a/mt7996/mt7996.h
+++ b/mt7996/mt7996.h
@@ -38,6 +38,7 @@
 #define MT7996_EEPROM_SIZE		7680
 #define MT7996_EEPROM_BLOCK_SIZE	16
 #define MT7996_TOKEN_SIZE		16384
+#define MT7996_RX_TOKEN_SIZE		16384
 
 #define MT7996_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
 #define MT7996_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
@@ -52,6 +53,23 @@
 
 #define MT7996_BUILD_TIME_LEN		24
 
+#define MT7996_MAX_HIF_RXD_IN_PG	5
+#define MT7996_RRO_MSDU_PG_HASH_SIZE	127
+#define MT7996_RRO_SESSION_MAX		1024
+#define MT7996_RRO_WIN_SIZE_MAX		1024
+#define MT7996_RRO_ADDR_ELEM_CR_CNT	128
+#define MT7996_RRO_BA_BITMAP_CR_CNT	2
+#define MT7996_RRO_SESSION_PER_CR	(MT7996_RRO_SESSION_MAX /	\
+					 MT7996_RRO_ADDR_ELEM_CR_CNT)
+#define MT7996_BA_BITMAP_SZ_PER_SESSION	128
+#define MT7996_BA_BITMAP_SZ_PER_CR	((MT7996_RRO_SESSION_MAX *		\
+					 MT7996_BA_BITMAP_SZ_PER_SESSION) /	\
+					 MT7996_RRO_BA_BITMAP_CR_CNT)
+#define MT7996_SKB_TRUESIZE(x)		((x) +	\
+					 SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+#define MT7996_RX_BUF_SIZE		MT7996_SKB_TRUESIZE(1800)
+#define MT7996_RX_MSDU_PAGE_SIZE	MT7996_SKB_TRUESIZE(128)
+
 struct mt7996_vif;
 struct mt7996_sta;
 struct mt7996_dfs_pulse;
@@ -82,6 +100,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 {
@@ -282,6 +310,73 @@ struct mt7996_hif {
 	int irq;
 };
 
+struct mt7996_rro_addr {
+	u32 head_pkt_l;
+	u32 head_pkt_h	: 4;
+	u32 seg_cnt	: 11;
+	u32 out_of_range: 1;
+	u32 rsv		: 8;
+	u32 signature	: 8;
+};
+
+struct mt7996_rro_hif {
+       u32 rx_blk_base_l;
+       u32 rx_blk_base_h: 4;
+       u32 eth_hdr_ofst : 7;
+       u32 rsv          : 1;
+       u32 ring_no      : 2;
+       u32 dst_sel      : 2;
+       u32 sdl          :14;
+       u32 ls           : 1;
+       u32 rsv2         : 1;
+       u32 pn_31_0;
+       u32 pn_47_32     :16;
+       u32 cs_status    : 4;
+       u32 cs_type      : 4;
+       u32 c            : 1;
+       u32 f            : 1;
+       u32 un           : 1;
+       u32 rsv3         : 1;
+       u32 is_fc_data   : 1;
+       u32 uc           : 1;
+       u32 mc           : 1;
+       u32 bc           : 1;
+       u16 rx_token_id;
+       u16 rsv4;
+       u32 rsv5;
+};
+
+struct mt7996_msdu_pg {
+	struct mt7996_rro_hif rxd[MT7996_MAX_HIF_RXD_IN_PG];
+	u32 next_pg_l;
+	u32 next_pg_h	: 4;
+	u32 rsv		:27;
+	u32 owner	: 1;
+};
+
+struct mt7996_msdu_pg_addr {
+	struct list_head list;
+	dma_addr_t dma_addr;
+	struct mt76_queue *q;
+	void *buf;
+};
+
+struct mt7996_rro_cfg {
+	u32 ind_signature;
+	void *ba_bitmap_cache_va[MT7996_RRO_BA_BITMAP_CR_CNT];
+	void *addr_elem_alloc_va[MT7996_RRO_ADDR_ELEM_CR_CNT];
+	void *particular_session_va;
+	u32 particular_se_id;
+	dma_addr_t ba_bitmap_cache_pa[MT7996_RRO_BA_BITMAP_CR_CNT];
+	dma_addr_t addr_elem_alloc_pa[MT7996_RRO_ADDR_ELEM_CR_CNT];
+	dma_addr_t particular_session_pa;
+	u16 win_sz;
+
+	spinlock_t lock;
+	struct list_head pg_addr_cache;
+	struct list_head pg_hash_head[MT7996_RRO_MSDU_PG_HASH_SIZE];
+};
+
 struct mt7996_phy {
 	struct mt76_phy *mt76;
 	struct mt7996_dev *dev;
@@ -373,6 +468,8 @@ struct mt7996_dev {
 	bool tbtc_support:1;
 	bool flash_mode:1;
 	bool has_eht:1;
+	bool rro_support:1;
+	struct mt7996_rro_cfg rro;
 
 	bool ibf;
 	u8 fw_debug_wm;
@@ -668,8 +765,14 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
 			  struct ieee80211_sta *sta,
 			  struct mt76_tx_info *tx_info);
 void mt7996_tx_token_put(struct mt7996_dev *dev);
+int mt7996_dma_rro_init(struct mt7996_dev *dev);
 void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
 			 struct sk_buff *skb, u32 *info);
+void mt7996_rx_token_put(struct mt7996_dev *dev);
+void mt7996_rro_msdu_pg_free(struct mt7996_dev *dev);
+void mt7996_rro_rx_process(struct mt76_dev *mdev, void *data);
+int mt7996_rro_fill_msdu_page(struct mt76_dev *mdev, struct mt76_queue *q,
+		       dma_addr_t p, void *data);
 bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
 void mt7996_stats_work(struct work_struct *work);
 int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
diff --git a/mt7996/pci.c b/mt7996/pci.c
index c5301050..d95aa10c 100644
--- a/mt7996/pci.c
+++ b/mt7996/pci.c
@@ -11,6 +11,9 @@
 #include "mac.h"
 #include "../trace.h"
 
+static bool rro_enable = false;
+module_param(rro_enable, bool, 0644);
+
 static LIST_HEAD(hif_list);
 static DEFINE_SPINLOCK(hif_lock);
 static u32 hif_idx;
@@ -121,6 +124,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
 	if (IS_ERR(dev))
 		return PTR_ERR(dev);
 
+	dev->rro_support = rro_enable;
 	mdev = &dev->mt76;
 	mt7996_wfsys_reset(dev);
 	hif2 = mt7996_pci_init_hif2(pdev);
diff --git a/mt7996/regs.h b/mt7996/regs.h
index 6ee27208..0653a67b 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_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)
@@ -387,6 +419,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)
@@ -413,6 +446,15 @@ enum base_rev {
 #define MT_INT_RX_TXFREE_TRI			BIT(15)
 #define MT_INT_MCU_CMD				BIT(29)
 
+#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 +462,32 @@ 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_RRO_IND) |		\
+						 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.39.2

