[Add meta-filogic bsp for rdkb development]

[Description]
Add meta-filogic bsp for rdkb development
1. rdkb base on dunfell rdkb-next (> 2022q1)
2. arm64/arm 32bit bsp both can run on rdkb

[Release-log]
N/A

diff --git a/recipes-kernel/linux-mt76/files/patches/0001-mt76-mt7915-rework-testmode-init-registers.patch b/recipes-kernel/linux-mt76/files/patches/0001-mt76-mt7915-rework-testmode-init-registers.patch
new file mode 100644
index 0000000..4b45104
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0001-mt76-mt7915-rework-testmode-init-registers.patch
@@ -0,0 +1,179 @@
+From 9d16552ff5dc96dd576d15f263ac1ae180ac615e Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 19 Jan 2022 15:46:06 +0800
+Subject: [PATCH 1/6] mt76: mt7915: rework testmode init registers
+
+---
+ mt7915/mmio.c     |  2 ++
+ mt7915/regs.h     | 16 +++++++++++++--
+ mt7915/testmode.c | 52 ++++++++++++++++++++++++++++++++++-------------
+ 3 files changed, 54 insertions(+), 16 deletions(-)
+
+diff --git a/mt7915/mmio.c b/mt7915/mmio.c
+index 5062e0d8..2466907e 100644
+--- a/mt7915/mmio.c
++++ b/mt7915/mmio.c
+@@ -53,6 +53,7 @@ static const u32 mt7986_reg[] = {
+ };
+ 
+ static const u32 mt7915_offs[] = {
++	[TMAC_TCR2]		= 0x05c,
+ 	[TMAC_CDTR]		= 0x090,
+ 	[TMAC_ODTR]		= 0x094,
+ 	[TMAC_ATCR]		= 0x098,
+@@ -126,6 +127,7 @@ static const u32 mt7915_offs[] = {
+ };
+ 
+ static const u32 mt7916_offs[] = {
++	[TMAC_TCR2]		= 0x004,
+ 	[TMAC_CDTR]		= 0x0c8,
+ 	[TMAC_ODTR]		= 0x0cc,
+ 	[TMAC_ATCR]		= 0x00c,
+diff --git a/mt7915/regs.h b/mt7915/regs.h
+index e5f93c40..999dd7fc 100644
+--- a/mt7915/regs.h
++++ b/mt7915/regs.h
+@@ -34,6 +34,7 @@ enum reg_rev {
+ };
+ 
+ enum offs_rev {
++	TMAC_TCR2,
+ 	TMAC_CDTR,
+ 	TMAC_ODTR,
+ 	TMAC_ATCR,
+@@ -172,6 +173,12 @@ enum offs_rev {
+ #define MT_MDP_TO_HIF			0
+ #define MT_MDP_TO_WM			1
+ 
++#define MT_MDP_TOP_DBG_WDT_CTRL			MT_MDP(0x0d0)
++#define MT_MDP_TOP_DBG_WDT_CTRL_TDP_DIS_BLK	BIT(7)
++
++#define MT_MDP_TOP_DBG_CTRL			MT_MDP(0x0dc)
++#define MT_MDP_TOP_DBG_CTRL_ENQ_MODE		BIT(30)
++
+ /* TMAC: band 0(0x820e4000), band 1(0x820f4000) */
+ #define MT_WF_TMAC_BASE(_band)		((_band) ? 0x820f4000 : 0x820e4000)
+ #define MT_WF_TMAC(_band, ofs)		(MT_WF_TMAC_BASE(_band) + (ofs))
+@@ -180,6 +187,9 @@ enum offs_rev {
+ #define MT_TMAC_TCR0_TX_BLINK		GENMASK(7, 6)
+ #define MT_TMAC_TCR0_TBTT_STOP_CTRL	BIT(25)
+ 
++#define MT_TMAC_TCR2(_band)		MT_WF_TMAC(_band, __OFFS(TMAC_TCR2))
++#define MT_TMAC_TCR2_SCH_DET_DIS	BIT(19)
++
+ #define MT_TMAC_CDTR(_band)		MT_WF_TMAC(_band, __OFFS(TMAC_CDTR))
+  #define MT_TMAC_ODTR(_band)		MT_WF_TMAC(_band, __OFFS(TMAC_ODTR))
+ #define MT_TIMEOUT_VAL_PLCP		GENMASK(15, 0)
+@@ -451,8 +461,10 @@ enum offs_rev {
+ #define MT_AGG_PCR0_VHT_PROT		BIT(13)
+ #define MT_AGG_PCR0_PTA_WIN_DIS		BIT(15)
+ 
+-#define MT_AGG_PCR1_RTS0_NUM_THRES	GENMASK(31, 23)
+-#define MT_AGG_PCR1_RTS0_LEN_THRES	GENMASK(19, 0)
++#define MT_AGG_PCR1_RTS0_NUM_THRES		GENMASK(31, 23)
++#define MT_AGG_PCR1_RTS0_LEN_THRES		GENMASK(19, 0)
++#define MT_AGG_PCR1_RTS0_NUM_THRES_MT7916	GENMASK(29, 24)
++#define MT_AGG_PCR1_RTS0_LEN_THRES_MT7916	GENMASK(22, 0)
+ 
+ #define MT_AGG_ACR0(_band)		MT_WF_AGG(_band, __OFFS(AGG_ACR0))
+ #define MT_AGG_ACR_CFEND_RATE		GENMASK(13, 0)
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index 20f63644..8d7ec9e8 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -30,7 +30,7 @@ struct reg_band {
+ 		{ _list.band[0] = MT_##_reg(0, _idx);	\
+ 		  _list.band[1] = MT_##_reg(1, _idx); }
+ 
+-#define TM_REG_MAX_ID	17
++#define TM_REG_MAX_ID	20
+ static struct reg_band reg_backup_list[TM_REG_MAX_ID];
+ 
+ 
+@@ -334,7 +334,7 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
+ {
+ 	int n_regs = ARRAY_SIZE(reg_backup_list);
+ 	struct mt7915_dev *dev = phy->dev;
+-	u32 *b = phy->test.reg_backup;
++	u32 *b = phy->test.reg_backup, val;
+ 	int i;
+ 
+ 	REG_BAND_IDX(reg_backup_list[0], AGG_PCR0, 0);
+@@ -346,18 +346,28 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
+ 	REG_BAND(reg_backup_list[6], AGG_MRCR);
+ 	REG_BAND(reg_backup_list[7], TMAC_TFCR0);
+ 	REG_BAND(reg_backup_list[8], TMAC_TCR0);
+-	REG_BAND(reg_backup_list[9], AGG_ATCR1);
+-	REG_BAND(reg_backup_list[10], AGG_ATCR3);
+-	REG_BAND(reg_backup_list[11], TMAC_TRCR0);
+-	REG_BAND(reg_backup_list[12], TMAC_ICR0);
+-	REG_BAND_IDX(reg_backup_list[13], ARB_DRNGR0, 0);
+-	REG_BAND_IDX(reg_backup_list[14], ARB_DRNGR0, 1);
+-	REG_BAND(reg_backup_list[15], WF_RFCR);
+-	REG_BAND(reg_backup_list[16], WF_RFCR1);
++	REG_BAND(reg_backup_list[9], TMAC_TCR2);
++	REG_BAND(reg_backup_list[10], AGG_ATCR1);
++	REG_BAND(reg_backup_list[11], AGG_ATCR3);
++	REG_BAND(reg_backup_list[12], TMAC_TRCR0);
++	REG_BAND(reg_backup_list[13], TMAC_ICR0);
++	REG_BAND_IDX(reg_backup_list[14], ARB_DRNGR0, 0);
++	REG_BAND_IDX(reg_backup_list[15], ARB_DRNGR0, 1);
++	REG_BAND(reg_backup_list[16], WF_RFCR);
++	REG_BAND(reg_backup_list[17], WF_RFCR1);
++
++	if (is_mt7916(&dev->mt76)) {
++		reg_backup_list[18].band[phy->band_idx] = MT_MDP_TOP_DBG_WDT_CTRL;
++		reg_backup_list[19].band[phy->band_idx] = MT_MDP_TOP_DBG_CTRL;
++	}
+ 
+ 	if (phy->mt76->test.state == MT76_TM_STATE_OFF) {
+-		for (i = 0; i < n_regs; i++)
+-			mt76_wr(dev, reg_backup_list[i].band[phy->band_idx], b[i]);
++		for (i = 0; i < n_regs; i++) {
++			u8 reg = reg_backup_list[i].band[phy->band_idx];
++
++			if (reg)
++				mt76_wr(dev, reg, b[i]);
++		}
+ 		return;
+ 	}
+ 
+@@ -377,8 +387,13 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
+ 		   MT_AGG_PCR0_BW40_PROT | MT_AGG_PCR0_BW80_PROT);
+ 	mt76_set(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_PTA_WIN_DIS);
+ 
+-	mt76_wr(dev, MT_AGG_PCR0(phy->band_idx, 1), MT_AGG_PCR1_RTS0_NUM_THRES |
+-		MT_AGG_PCR1_RTS0_LEN_THRES);
++	if (is_mt7915(&dev->mt76))
++		val = MT_AGG_PCR1_RTS0_NUM_THRES | MT_AGG_PCR1_RTS0_LEN_THRES;
++	else
++		val = MT_AGG_PCR1_RTS0_NUM_THRES_MT7916 |
++		      MT_AGG_PCR1_RTS0_LEN_THRES_MT7916;
++
++	mt76_wr(dev, MT_AGG_PCR0(phy->band_idx, 1), val);
+ 
+ 	mt76_clear(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_BAR_CNT_LIMIT |
+ 		   MT_AGG_MRCR_LAST_RTS_CTS_RN | MT_AGG_MRCR_RTS_FAIL_LIMIT |
+@@ -391,10 +406,19 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
+ 
+ 	mt76_wr(dev, MT_TMAC_TFCR0(phy->band_idx), 0);
+ 	mt76_clear(dev, MT_TMAC_TCR0(phy->band_idx), MT_TMAC_TCR0_TBTT_STOP_CTRL);
++	mt76_set(dev, MT_TMAC_TCR2(phy->band_idx), MT_TMAC_TCR2_SCH_DET_DIS);
+ 
+ 	/* config rx filter for testmode rx */
+ 	mt76_wr(dev, MT_WF_RFCR(phy->band_idx), 0xcf70a);
+ 	mt76_wr(dev, MT_WF_RFCR1(phy->band_idx), 0);
++
++	if (is_mt7916(&dev->mt76)) {
++		/* enable MDP Tx block mode */
++		mt76_clear(dev, MT_MDP_TOP_DBG_WDT_CTRL,
++			   MT_MDP_TOP_DBG_WDT_CTRL_TDP_DIS_BLK);
++		mt76_clear(dev, MT_MDP_TOP_DBG_CTRL,
++			   MT_MDP_TOP_DBG_CTRL_ENQ_MODE);
++	}
+ }
+ 
+ static void
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/0002-mt76-testmode-rework-tx-antenna-setting.patch b/recipes-kernel/linux-mt76/files/patches/0002-mt76-testmode-rework-tx-antenna-setting.patch
new file mode 100644
index 0000000..2ece0e0
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0002-mt76-testmode-rework-tx-antenna-setting.patch
@@ -0,0 +1,76 @@
+From 2b65580db9081ac1ace74aed7b06cc855162d408 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 25 Feb 2022 09:36:01 +0800
+Subject: [PATCH 2/6] mt76: testmode: rework tx antenna setting
+
+---
+ mt7915/mcu.c      | 7 +------
+ mt7915/testmode.c | 9 +--------
+ testmode.c        | 4 ++--
+ 3 files changed, 4 insertions(+), 16 deletions(-)
+
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index 2aba342c..549281a4 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -2822,14 +2822,9 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
+ 
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	if (phy->mt76->test.tx_antenna_mask &&
+-	    (phy->mt76->test.state == MT76_TM_STATE_TX_FRAMES ||
+-	     phy->mt76->test.state == MT76_TM_STATE_RX_FRAMES ||
+-	     phy->mt76->test.state == MT76_TM_STATE_TX_CONT)) {
++	    mt76_testmode_enabled(phy->mt76)) {
+ 		req.tx_streams_num = fls(phy->mt76->test.tx_antenna_mask);
+ 		req.rx_streams = phy->mt76->test.tx_antenna_mask;
+-
+-		if (phy != &dev->phy)
+-			req.rx_streams >>= dev->chainshift;
+ 	}
+ #endif
+ 
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index 8d7ec9e8..d6f71436 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -473,11 +473,7 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
+ 		if (td->tx_spe_idx) {
+ 			phy->test.spe_idx = td->tx_spe_idx;
+ 		} else {
+-			u8 tx_ant = td->tx_antenna_mask;
+-
+-			if (phy != &dev->phy)
+-				tx_ant >>= dev->chainshift;
+-			phy->test.spe_idx = spe_idx_map[tx_ant];
++			phy->test.spe_idx = spe_idx_map[td->tx_antenna_mask];
+ 		}
+ 	}
+ 
+@@ -728,9 +724,6 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
+ 	    td->state == MT76_TM_STATE_OFF)
+ 		return 0;
+ 
+-	if (td->tx_antenna_mask & ~mphy->chainmask)
+-		return -EINVAL;
+-
+ 	for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
+ 		if (tb[tm_change_map[i]])
+ 			changed |= BIT(i);
+diff --git a/testmode.c b/testmode.c
+index 382b4563..7cd00794 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -446,8 +446,8 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_LDPC], &td->tx_rate_ldpc, 0, 1) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_STBC], &td->tx_rate_stbc, 0, 1) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_LTF], &td->tx_ltf, 0, 2) ||
+-	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_ANTENNA],
+-			   &td->tx_antenna_mask, 0, 0xff) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_ANTENNA], &td->tx_antenna_mask,
++			   1, phy->antenna_mask) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_SPE_IDX], &td->tx_spe_idx, 0, 27) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE],
+ 			   &td->tx_duty_cycle, 0, 99) ||
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/0003-mt76-mt7915-rework-rx-testmode-stats.patch b/recipes-kernel/linux-mt76/files/patches/0003-mt76-mt7915-rework-rx-testmode-stats.patch
new file mode 100644
index 0000000..822aaad
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0003-mt76-mt7915-rework-rx-testmode-stats.patch
@@ -0,0 +1,305 @@
+From 0b8c7d725830b5873c648777ab7813fff9d5951f Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 3 Jan 2022 17:09:53 +0800
+Subject: [PATCH 3/6] mt76: mt7915: rework rx testmode stats
+
+---
+ mac80211.c        |  3 +-
+ mt76.h            |  5 +++
+ mt76_connac_mcu.h |  1 +
+ mt7915/mcu.h      |  1 +
+ mt7915/testmode.c | 82 ++++++++++++++++++++++++++++++++++++++---------
+ mt7915/testmode.h | 28 ++++++++++++++++
+ testmode.c        |  3 ++
+ testmode.h        |  3 ++
+ 8 files changed, 109 insertions(+), 17 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 5b53d008..5a4ac5de 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -737,7 +737,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
+ 	}
+ 
+ #ifdef CONFIG_NL80211_TESTMODE
+-	if (phy->test.state == MT76_TM_STATE_RX_FRAMES) {
++	if (!(phy->test.flag & MT_TM_FW_RX_COUNT) &&
++	    phy->test.state == MT76_TM_STATE_RX_FRAMES) {
+ 		phy->test.rx_stats.packets[q]++;
+ 		if (status->flag & RX_FLAG_FAILED_FCS_CRC)
+ 			phy->test.rx_stats.fcs_error[q]++;
+diff --git a/mt76.h b/mt76.h
+index 81078be3..d5f8650f 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -583,6 +583,8 @@ struct mt76_testmode_ops {
+ 	int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
+ };
+ 
++#define MT_TM_FW_RX_COUNT	BIT(0)
++
+ struct mt76_testmode_data {
+ 	enum mt76_testmode_state state;
+ 
+@@ -614,6 +616,8 @@ struct mt76_testmode_data {
+ 
+ 	u8 addr[3][ETH_ALEN];
+ 
++	u8 flag;
++
+ 	u32 tx_pending;
+ 	u32 tx_queued;
+ 	u16 tx_queued_limit;
+@@ -621,6 +625,7 @@ struct mt76_testmode_data {
+ 	struct {
+ 		u64 packets[__MT_RXQ_MAX];
+ 		u64 fcs_error[__MT_RXQ_MAX];
++		u64 len_mismatch;
+ 	} rx_stats;
+ };
+ 
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index c3c93338..54419864 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -980,6 +980,7 @@ enum {
+ 	MCU_EXT_CMD_OFFCH_SCAN_CTRL = 0x9a,
+ 	MCU_EXT_CMD_SET_RDD_TH = 0x9d,
+ 	MCU_EXT_CMD_MURU_CTRL = 0x9f,
++	MCU_EXT_CMD_RX_STAT = 0xa4,
+ 	MCU_EXT_CMD_SET_SPR = 0xa8,
+ 	MCU_EXT_CMD_GROUP_PRE_CAL_INFO = 0xab,
+ 	MCU_EXT_CMD_DPD_PRE_CAL_INFO = 0xac,
+diff --git a/mt7915/mcu.h b/mt7915/mcu.h
+index 960072a4..52368dc3 100644
+--- a/mt7915/mcu.h
++++ b/mt7915/mcu.h
+@@ -28,6 +28,7 @@ struct mt7915_mcu_txd {
+ enum {
+ 	MCU_ATE_SET_TRX = 0x1,
+ 	MCU_ATE_SET_FREQ_OFFSET = 0xa,
++	MCU_ATE_SET_PHY_COUNT = 0x11,
+ 	MCU_ATE_SET_SLOT_TIME = 0x13,
+ 	MCU_ATE_CLEAN_TXQUEUE = 0x1c,
+ };
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index d6f71436..e8bf616c 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -133,6 +133,21 @@ mt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid)
+ 				 sizeof(req), false);
+ }
+ 
++static int
++mt7915_tm_set_phy_count(struct mt7915_phy *phy, u8 control)
++{
++	struct mt7915_dev *dev = phy->dev;
++	struct mt7915_tm_cmd req = {
++		.testmode_en = 1,
++		.param_idx = MCU_ATE_SET_PHY_COUNT,
++		.param.cfg.enable = control,
++		.param.cfg.band = phy != &dev->phy,
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
++				 sizeof(req), false);
++}
++
+ static int
+ mt7915_tm_set_slot_time(struct mt7915_phy *phy, u8 slot_time, u8 sifs)
+ {
+@@ -438,6 +453,8 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
+ 	mt7915_mcu_add_bss_info(phy, phy->monitor_vif, en);
+ 	mt7915_mcu_add_sta(dev, phy->monitor_vif, NULL, en);
+ 
++	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
++
+ 	if (!en)
+ 		mt7915_tm_set_tam_arb(phy, en, 0);
+ }
+@@ -503,18 +520,63 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
+ 	mt7915_tm_set_trx(phy, TM_MAC_TX, en);
+ }
+ 
++static int
++mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
++{
++#define CMD_RX_STAT_BAND	0x3
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7915_tm_rx_stat_band *rs_band;
++	struct mt7915_dev *dev = phy->dev;
++	struct sk_buff *skb;
++	struct {
++		u8 format_id;
++		u8 band;
++		u8 _rsv[2];
++	} __packed req = {
++		.format_id = CMD_RX_STAT_BAND,
++		.band = phy != &dev->phy,
++	};
++	int ret;
++
++	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(RX_STAT),
++					&req, sizeof(req), true, &skb);
++	if (ret)
++		return ret;
++
++	rs_band = (struct mt7915_tm_rx_stat_band *)skb->data;
++	/* pr_info("mdrdy_cnt = %d\n", le32_to_cpu(rs_band->mdrdy_cnt)); */
++	/* pr_info("fcs_err = %d\n", le16_to_cpu(rs_band->fcs_err)); */
++	/* pr_info("len_mismatch = %d\n", le16_to_cpu(rs_band->len_mismatch)); */
++	/* pr_info("fcs_ok = %d\n", le16_to_cpu(rs_band->fcs_succ)); */
++
++	if (!clear) {
++		enum mt76_rxq_id q = req.band ? MT_RXQ_EXT : MT_RXQ_MAIN;
++
++		td->rx_stats.packets[q] += le32_to_cpu(rs_band->mdrdy_cnt);
++		td->rx_stats.fcs_error[q] += le16_to_cpu(rs_band->fcs_err);
++		td->rx_stats.len_mismatch += le16_to_cpu(rs_band->len_mismatch);
++	}
++
++	dev_kfree_skb(skb);
++
++	return 0;
++}
++
+ static void
+ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
+ {
+ 	mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, false);
+ 
+ 	if (en) {
+-		struct mt7915_dev *dev = phy->dev;
+-
+ 		mt7915_tm_update_channel(phy);
+ 
+ 		/* read-clear */
+-		mt76_rr(dev, MT_MIB_SDR3(phy != &dev->phy));
++		mt7915_tm_get_rx_stats(phy, true);
++
++		/* clear fw count */
++		mt7915_tm_set_phy_count(phy, 0);
++		mt7915_tm_set_phy_count(phy, 1);
++
+ 		mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en);
+ 	}
+ }
+@@ -738,12 +800,8 @@ static int
+ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
+ {
+ 	struct mt7915_phy *phy = mphy->priv;
+-	struct mt7915_dev *dev = phy->dev;
+-	enum mt76_rxq_id q;
+ 	void *rx, *rssi;
+-	u16 fcs_err;
+ 	int i;
+-	u32 cnt;
+ 
+ 	rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX);
+ 	if (!rx)
+@@ -787,15 +845,7 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
+ 
+ 	nla_nest_end(msg, rx);
+ 
+-	cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx));
+-	fcs_err = is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) :
+-		FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt);
+-
+-	q = phy->band_idx ? MT_RXQ_EXT : MT_RXQ_MAIN;
+-	mphy->test.rx_stats.packets[q] += fcs_err;
+-	mphy->test.rx_stats.fcs_error[q] += fcs_err;
+-
+-	return 0;
++	return mt7915_tm_get_rx_stats(phy, false);
+ }
+ 
+ const struct mt76_testmode_ops mt7915_testmode_ops = {
+diff --git a/mt7915/testmode.h b/mt7915/testmode.h
+index 5573ac30..a1c54c89 100644
+--- a/mt7915/testmode.h
++++ b/mt7915/testmode.h
+@@ -33,6 +33,12 @@ struct mt7915_tm_clean_txq {
+ 	u8 rsv;
+ };
+ 
++struct mt7915_tm_cfg {
++	u8 enable;
++	u8 band;
++	u8 _rsv[2];
++};
++
+ struct mt7915_tm_cmd {
+ 	u8 testmode_en;
+ 	u8 param_idx;
+@@ -43,6 +49,7 @@ struct mt7915_tm_cmd {
+ 		struct mt7915_tm_freq_offset freq;
+ 		struct mt7915_tm_slot_time slot;
+ 		struct mt7915_tm_clean_txq clean;
++		struct mt7915_tm_cfg cfg;
+ 		u8 test[72];
+ 	} param;
+ } __packed;
+@@ -102,4 +109,25 @@ enum {
+ 	TAM_ARB_OP_MODE_FORCE_SU = 5,
+ };
+ 
++struct mt7915_tm_rx_stat_band {
++	u8 category;
++
++	/* mac */
++	__le16 fcs_err;
++	__le16 len_mismatch;
++	__le16 fcs_succ;
++	__le32 mdrdy_cnt;
++	/* phy */
++	__le16 fcs_err_cck;
++	__le16 fcs_err_ofdm;
++	__le16 pd_cck;
++	__le16 pd_ofdm;
++	__le16 sig_err_cck;
++	__le16 sfd_err_cck;
++	__le16 sig_err_ofdm;
++	__le16 tag_err_ofdm;
++	__le16 mdrdy_cnt_cck;
++	__le16 mdrdy_cnt_ofdm;
++};
++
+ #endif
+diff --git a/testmode.c b/testmode.c
+index 7cd00794..e6d1f702 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -559,6 +559,9 @@ mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg)
+ 	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets,
+ 			      MT76_TM_STATS_ATTR_PAD) ||
+ 	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error,
++			      MT76_TM_STATS_ATTR_PAD) ||
++	    nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
++			      td->rx_stats.len_mismatch,
+ 			      MT76_TM_STATS_ATTR_PAD))
+ 		return -EMSGSIZE;
+ 
+diff --git a/testmode.h b/testmode.h
+index 5e2792d8..89613266 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -101,6 +101,8 @@ enum mt76_testmode_attr {
+  * @MT76_TM_STATS_ATTR_RX_FCS_ERROR: number of rx packets with FCS error (u64)
+  * @MT76_TM_STATS_ATTR_LAST_RX: information about the last received packet
+  *	see &enum mt76_testmode_rx_attr
++ * @MT76_TM_STATS_ATTR_RX_LEN_MISMATCH: number of rx packets with length
++ *	mismatch error (u64)
+  */
+ enum mt76_testmode_stats_attr {
+ 	MT76_TM_STATS_ATTR_UNSPEC,
+@@ -113,6 +115,7 @@ enum mt76_testmode_stats_attr {
+ 	MT76_TM_STATS_ATTR_RX_PACKETS,
+ 	MT76_TM_STATS_ATTR_RX_FCS_ERROR,
+ 	MT76_TM_STATS_ATTR_LAST_RX,
++	MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
+ 
+ 	/* keep last */
+ 	NUM_MT76_TM_STATS_ATTRS,
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/0004-mt76-mt7915-fix-tx-descriptor.patch b/recipes-kernel/linux-mt76/files/patches/0004-mt76-mt7915-fix-tx-descriptor.patch
new file mode 100644
index 0000000..c84c60e
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0004-mt76-mt7915-fix-tx-descriptor.patch
@@ -0,0 +1,24 @@
+From d0a61bbe57616c1a87a3bb4676f141ed54110add Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 19 Jan 2022 15:51:01 +0800
+Subject: [PATCH 4/6] mt76: mt7915: fix tx descriptor
+
+---
+ mt7915/mac.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/mt7915/mac.c b/mt7915/mac.c
+index 47d5a993..887292da 100644
+--- a/mt7915/mac.c
++++ b/mt7915/mac.c
+@@ -1001,6 +1001,7 @@ mt7915_mac_write_txwi_tm(struct mt7915_phy *phy, __le32 *txwi,
+ 	if (td->tx_rate_ldpc || (bw > 0 && mode >= MT_PHY_TYPE_HE_SU))
+ 		val |= MT_TXD6_LDPC;
+ 
++	txwi[1] &= ~cpu_to_le32(MT_TXD1_VTA);
+ 	txwi[3] &= ~cpu_to_le32(MT_TXD3_SN_VALID);
+ 	txwi[6] |= cpu_to_le32(val);
+ 	txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX,
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/0005-mt76-mt7915-fix-MBSS-index-condition-in-DBDC-mode.patch b/recipes-kernel/linux-mt76/files/patches/0005-mt76-mt7915-fix-MBSS-index-condition-in-DBDC-mode.patch
new file mode 100644
index 0000000..5e0d81f
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0005-mt76-mt7915-fix-MBSS-index-condition-in-DBDC-mode.patch
@@ -0,0 +1,43 @@
+From c931d9454ecfce777ac68071d4687f1ebb302917 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Mon, 7 Mar 2022 19:32:29 +0800
+Subject: [PATCH 5/6] mt76: mt7915: fix MBSS index condition in DBDC mode
+
+MT7915_MAX_INTERFACES is per-band declartion.
+
+Signed-off-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Signed-off-by: Bo Jiao <bo.jiao@mediatek.com>
+---
+ mt76.h        | 2 +-
+ mt7915/main.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index d5f8650f..6e528e42 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -732,7 +732,7 @@ struct mt76_dev {
+ 	u32 wcid_mask[DIV_ROUND_UP(MT76_N_WCIDS, 32)];
+ 	u32 wcid_phy_mask[DIV_ROUND_UP(MT76_N_WCIDS, 32)];
+ 
+-	u32 vif_mask;
++	u64 vif_mask;
+ 
+ 	struct mt76_wcid global_wcid;
+ 	struct mt76_wcid __rcu *wcid[MT76_N_WCIDS];
+diff --git a/mt7915/main.c b/mt7915/main.c
+index c3f44d80..3111217b 100644
+--- a/mt7915/main.c
++++ b/mt7915/main.c
+@@ -205,7 +205,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
+ 		phy->monitor_vif = vif;
+ 
+ 	mvif->mt76.idx = ffs(~dev->mt76.vif_mask) - 1;
+-	if (mvif->mt76.idx >= MT7915_MAX_INTERFACES) {
++	if (mvif->mt76.idx >= MT7915_MAX_INTERFACES * (dev->dbdc_support + 1)) {
+ 		ret = -ENOSPC;
+ 		goto out;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/0006-mt76-mt7915-support-VHT-MCS10-11.patch b/recipes-kernel/linux-mt76/files/patches/0006-mt76-mt7915-support-VHT-MCS10-11.patch
new file mode 100644
index 0000000..81242fc
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0006-mt76-mt7915-support-VHT-MCS10-11.patch
@@ -0,0 +1,26 @@
+From 777b1a1dfe9d7f649c7d7bb3732ff1fba8437ae0 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 11 Mar 2022 12:15:35 +0800
+Subject: [PATCH 6/6] mt76: mt7915: support VHT MCS10/11
+
+Support receiving MCS10/11 in VHT mode.
+---
+ mt7915/mac.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7915/mac.c b/mt7915/mac.c
+index 887292da..fe718102 100644
+--- a/mt7915/mac.c
++++ b/mt7915/mac.c
+@@ -519,7 +519,7 @@ mt7915_mac_fill_rx_rate(struct mt7915_dev *dev,
+ 		status->encoding = RX_ENC_VHT;
+ 		if (gi)
+ 			status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+-		if (i > 9)
++		if (i > 11)
+ 			return -EINVAL;
+ 		break;
+ 	case MT_PHY_TYPE_HE_MU:
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/0007-mt76-mt7915-update-mt7986-CR-for-different-adie-vers.patch b/recipes-kernel/linux-mt76/files/patches/0007-mt76-mt7915-update-mt7986-CR-for-different-adie-vers.patch
new file mode 100644
index 0000000..d5c72aa
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0007-mt76-mt7915-update-mt7986-CR-for-different-adie-vers.patch
@@ -0,0 +1,68 @@
+From 87efddcc9bb605802fdabe8bf3408a106bf5b997 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 15 Mar 2022 14:21:13 +0800
+Subject: [PATCH] mt76: mt7915: update mt7986 CR for different adie version
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7915/regs.h |  1 +
+ mt7915/soc.c  | 24 +++++++++++++++++++++---
+ 2 files changed, 22 insertions(+), 3 deletions(-)
+
+diff --git a/mt7915/regs.h b/mt7915/regs.h
+index e5f93c40..a69ba562 100644
+--- a/mt7915/regs.h
++++ b/mt7915/regs.h
+@@ -794,6 +794,7 @@ enum offs_rev {
+ 
+ /* ADIE */
+ #define MT_ADIE_CHIP_ID			0x02c
++#define MT_ADIE_VERSION_MASK		GENMASK(15, 0)
+ #define MT_ADIE_CHIP_ID_MASK		GENMASK(31, 16)
+ #define MT_ADIE_IDX0			GENMASK(15, 0)
+ #define MT_ADIE_IDX1			GENMASK(31, 16)
+diff --git a/mt7915/soc.c b/mt7915/soc.c
+index 04df47fd..e1892368 100644
+--- a/mt7915/soc.c
++++ b/mt7915/soc.c
+@@ -468,16 +468,34 @@ static int mt7986_wmac_adie_xtal_trim_7976(struct mt7915_dev *dev, u8 adie)
+ static int mt7986_wmac_adie_patch_7976(struct mt7915_dev *dev, u8 adie)
+ {
+ 	int ret;
++	u32 id, version;
+ 
+-	ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_TOP_THADC, 0x4a563b00);
++
++	ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_CHIP_ID, &id);
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_01, 0x1d59080f);
++	version = FIELD_GET(MT_ADIE_VERSION_MASK, id);
++
++	ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_TOP_THADC, 0x4a563b00);
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_03, 0x34c00fe0);
++	if (version == 0x8a00 || version == 0x8a10 || version == 0x8b00) {
++		ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_01, 0x1d59080f);
++		if (ret)
++			return ret;
++
++		mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_03, 0x34c00fe0);
++	} else {
++		ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_01, 0x1959c80f);
++		if (ret)
++			return ret;
++
++		mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_03, 0x34d00fe0);
++	}
++
++	return ret;
+ }
+ 
+ static int
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/0008-mt76-mt7915-disable-mt7986-rx-hdr-trans-short.patch b/recipes-kernel/linux-mt76/files/patches/0008-mt76-mt7915-disable-mt7986-rx-hdr-trans-short.patch
new file mode 100644
index 0000000..ba8919b
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0008-mt76-mt7915-disable-mt7986-rx-hdr-trans-short.patch
@@ -0,0 +1,58 @@
+From 00e4fb5cf4d22681b379cdb92e0750cf74b367e7 Mon Sep 17 00:00:00 2001
+From: "lian.chen" <lian.chen@mediatek.com>
+Date: Wed, 16 Mar 2022 15:14:05 +0800
+Subject: [PATCH] mt76: mt7915: disable mt7986 RX_HDR_TRANS_SHORT
+
+Signed-off-by: lian.chen <lian.chen@mediatek.com>
+---
+ mt7915/init.c | 3 +++
+ mt7915/mac.c  | 4 ----
+ mt7915/regs.h | 3 +++
+ 3 files changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/mt7915/init.c b/mt7915/init.c
+index f57a3d18..223a4f77 100644
+--- a/mt7915/init.c
++++ b/mt7915/init.c
+@@ -451,6 +451,9 @@ static void mt7915_mac_init(struct mt7915_dev *dev)
+ 
+ 	mt76_rmw_field(dev, MT_MDP_DCR1, MT_MDP_DCR1_MAX_RX_LEN, rx_len);
+ 
++	/* disable RX_TRANS_SHORT */
++	mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT);
++
+ 	/* enable hardware de-agg */
+ 	mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN);
+ 
+diff --git a/mt7915/mac.c b/mt7915/mac.c
+index fe718102..eedd901f 100644
+--- a/mt7915/mac.c
++++ b/mt7915/mac.c
+@@ -835,10 +835,6 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
+ 	if (!status->wcid || !ieee80211_is_data_qos(fc))
+ 		return 0;
+ 
+-	/* drop no data frame */
+-	if (fc & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))
+-		return -EINVAL;
+-
+ 	status->aggr = unicast &&
+ 		       !ieee80211_is_qos_nullfunc(fc);
+ 	status->qos_ctl = qos_ctl;
+diff --git a/mt7915/regs.h b/mt7915/regs.h
+index e7d83458..6ddfa48f 100644
+--- a/mt7915/regs.h
++++ b/mt7915/regs.h
+@@ -159,6 +159,9 @@ enum offs_rev {
+ #define MT_MDP_DCR1			MT_MDP(0x004)
+ #define MT_MDP_DCR1_MAX_RX_LEN		GENMASK(15, 3)
+ 
++#define MT_MDP_DCR2			MT_MDP(0x0e8)
++#define MT_MDP_DCR2_RX_TRANS_SHORT		BIT(2)
++
+ #define MT_MDP_BNRCFR0(_band)		MT_MDP(__OFFS(MDP_BNRCFR0) + \
+ 					       ((_band) << 8))
+ #define MT_MDP_RCFR0_MCU_RX_MGMT	GENMASK(5, 4)
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1100-mt76-testmode-support-eeprom-handle.patch b/recipes-kernel/linux-mt76/files/patches/1100-mt76-testmode-support-eeprom-handle.patch
new file mode 100755
index 0000000..ee5b283
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1100-mt76-testmode-support-eeprom-handle.patch
@@ -0,0 +1,284 @@
+From b6c6afbe347c8cd3ab43d4cab07e840ecf5d3ad6 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 29 Jun 2021 14:30:44 +0800
+Subject: [PATCH 1100/1112] mt76: testmode: support eeprom handle
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/mt76.h     |  1 +
+ .../net/wireless/mediatek/mt76/mt7915/init.c  |  2 +-
+ .../net/wireless/mediatek/mt76/mt7915/mcu.c   |  5 +-
+ .../wireless/mediatek/mt76/mt7915/mt7915.h    |  2 +-
+ .../wireless/mediatek/mt76/mt7915/testmode.c  | 54 +++++++++++++++++++
+ drivers/net/wireless/mediatek/mt76/testmode.c | 46 +++++++++++++++-
+ drivers/net/wireless/mediatek/mt76/testmode.h | 30 +++++++++++
+ 7 files changed, 133 insertions(+), 7 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 58b324c..8ad7674 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -581,6 +581,7 @@ struct mt76_testmode_ops {
+ 	int (*set_params)(struct mt76_phy *phy, struct nlattr **tb,
+ 			  enum mt76_testmode_state new_state);
+ 	int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
++	int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
+ };
+ 
+ #define MT_TM_FW_RX_COUNT	BIT(0)
+diff --git a/mt7915/init.c b/mt7915/init.c
+index cd69174..92d57f9 100644
+--- a/mt7915/init.c
++++ b/mt7915/init.c
+@@ -569,7 +569,7 @@ static void mt7915_init_work(struct work_struct *work)
+ 	struct mt7915_dev *dev = container_of(work, struct mt7915_dev,
+ 				 init_work);
+ 
+-	mt7915_mcu_set_eeprom(dev);
++	mt7915_mcu_set_eeprom(dev, dev->flash_mode);
+ 	mt7915_mac_init(dev);
+ 	mt7915_init_txpower(dev, &dev->mphy.sband_2g.sband);
+ 	mt7915_init_txpower(dev, &dev->mphy.sband_5g.sband);
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index d55e9d0..2b57459 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -289,7 +289,6 @@ mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ 	if (mcu_txd->ext_cid) {
+ 		mcu_txd->ext_cid_ack = 1;
+ 
+-		/* do not use Q_SET for efuse */
+ 		if (cmd & __MCU_CMD_FIELD_QUERY)
+ 			mcu_txd->set_query = MCU_Q_QUERY;
+ 		else
+@@ -2913,14 +2912,14 @@ static int mt7915_mcu_set_eeprom_flash(struct mt7915_dev *dev)
+ 	return 0;
+ }
+ 
+-int mt7915_mcu_set_eeprom(struct mt7915_dev *dev)
++int mt7915_mcu_set_eeprom(struct mt7915_dev *dev, bool flash_mode)
+ {
+ 	struct mt7915_mcu_eeprom req = {
+ 		.buffer_mode = EE_MODE_EFUSE,
+ 		.format = EE_FORMAT_WHOLE,
+ 	};
+ 
+-	if (dev->flash_mode)
++	if (flash_mode)
+ 		return mt7915_mcu_set_eeprom_flash(dev);
+ 
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EFUSE_BUFFER_MODE),
+diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
+index a8726fe..274afff 100644
+--- a/mt7915/mt7915.h
++++ b/mt7915/mt7915.h
+@@ -539,7 +539,7 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev,
+ 				   struct ieee80211_vif *vif,
+ 				   struct ieee80211_sta *sta,
+ 				   void *data, u32 field);
+-int mt7915_mcu_set_eeprom(struct mt7915_dev *dev);
++int mt7915_mcu_set_eeprom(struct mt7915_dev *dev, bool flash_mode);
+ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset);
+ int mt7915_mcu_get_eeprom_free_block(struct mt7915_dev *dev, u8 *block_num);
+ int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index e8bf616..2c859f6 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -848,8 +848,62 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
+ 	return mt7915_tm_get_rx_stats(phy, false);
+ }
+ 
++static int
++mt7915_tm_write_back_to_efuse(struct mt7915_dev *dev)
++{
++	struct mt7915_mcu_eeprom_info req = {};
++	u8 *eeprom = dev->mt76.eeprom.data;
++	int i, ret = -EINVAL;
++
++	/* prevent from damaging chip id in efuse */
++	if (mt76_chip(&dev->mt76) != get_unaligned_le16(eeprom))
++		goto out;
++
++	for (i = 0; i < MT7915_EEPROM_SIZE; i += MT76_TM_EEPROM_BLOCK_SIZE) {
++		req.addr = cpu_to_le32(i);
++		memcpy(&req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
++
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EFUSE_ACCESS),
++					&req, sizeof(req), true);
++		if (ret)
++			return ret;
++	}
++
++out:
++	return ret;
++}
++
++static int
++mt7915_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
++{
++	struct mt7915_phy *phy = mphy->priv;
++	struct mt7915_dev *dev = phy->dev;
++	u8 *eeprom = dev->mt76.eeprom.data;
++	int ret = 0;
++
++	if (offset >= MT7915_EEPROM_SIZE)
++		return -EINVAL;
++
++	switch (action) {
++	case MT76_TM_EEPROM_ACTION_UPDATE_DATA:
++		memcpy(eeprom + offset, val, MT76_TM_EEPROM_BLOCK_SIZE);
++		break;
++	case MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE:
++		ret = mt7915_mcu_set_eeprom(dev, true);
++		break;
++	case MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE:
++		ret = mt7915_tm_write_back_to_efuse(dev);
++		break;
++	default:
++		break;
++	}
++
++	return ret;
++}
++
+ const struct mt76_testmode_ops mt7915_testmode_ops = {
+ 	.set_state = mt7915_tm_set_state,
+ 	.set_params = mt7915_tm_set_params,
+ 	.dump_stats = mt7915_tm_dump_stats,
++	.set_eeprom = mt7915_tm_set_eeprom,
+ };
+diff --git a/testmode.c b/testmode.c
+index e6d1f70..1fbca66 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -402,6 +402,44 @@ mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max)
+ 	return 0;
+ }
+ 
++static int
++mt76_testmode_set_eeprom(struct mt76_phy *phy, struct nlattr **tb)
++{
++	struct mt76_dev *dev = phy->dev;
++	u8 action, val[MT76_TM_EEPROM_BLOCK_SIZE];
++	u32 offset = 0;
++	int err = -EINVAL;
++
++	if (!dev->test_ops->set_eeprom)
++		return -EOPNOTSUPP;
++
++	if (mt76_tm_get_u8(tb[MT76_TM_ATTR_EEPROM_ACTION], &action,
++			   0, MT76_TM_EEPROM_ACTION_MAX))
++		goto out;
++
++	if (tb[MT76_TM_ATTR_EEPROM_OFFSET]) {
++		struct nlattr *cur;
++		int rem, idx = 0;
++
++		offset = nla_get_u32(tb[MT76_TM_ATTR_EEPROM_OFFSET]);
++		if (!!(offset % MT76_TM_EEPROM_BLOCK_SIZE) ||
++		    !tb[MT76_TM_ATTR_EEPROM_VAL])
++			goto out;
++
++		nla_for_each_nested(cur, tb[MT76_TM_ATTR_EEPROM_VAL], rem) {
++			if (nla_len(cur) != 1 || idx >= ARRAY_SIZE(val))
++				goto out;
++
++			val[idx++] = nla_get_u8(cur);
++		}
++	}
++
++	err = dev->test_ops->set_eeprom(phy, offset, val, action);
++
++out:
++	return err;
++}
++
+ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		      void *data, int len)
+ {
+@@ -425,6 +463,11 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	mutex_lock(&dev->mutex);
+ 
++	if (tb[MT76_TM_ATTR_EEPROM_ACTION]) {
++		err = mt76_testmode_set_eeprom(phy, tb);
++		goto out;
++	}
++
+ 	if (tb[MT76_TM_ATTR_RESET]) {
+ 		mt76_testmode_set_state(phy, MT76_TM_STATE_OFF);
+ 		memset(td, 0, sizeof(*td));
+@@ -484,8 +527,7 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 
+ 	if (tb[MT76_TM_ATTR_TX_POWER]) {
+ 		struct nlattr *cur;
+-		int idx = 0;
+-		int rem;
++		int rem, idx = 0;
+ 
+ 		nla_for_each_nested(cur, tb[MT76_TM_ATTR_TX_POWER], rem) {
+ 			if (nla_len(cur) != 1 ||
+diff --git a/testmode.h b/testmode.h
+index 8961326..5900c76 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -6,6 +6,7 @@
+ #define __MT76_TESTMODE_H
+ 
+ #define MT76_TM_TIMEOUT	10
++#define MT76_TM_EEPROM_BLOCK_SIZE	16
+ 
+ /**
+  * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA
+@@ -47,6 +48,13 @@
+  * @MT76_TM_ATTR_DRV_DATA: driver specific netlink attrs (nested)
+  *
+  * @MT76_TM_ATTR_MAC_ADDRS: array of nested MAC addresses (nested)
++ *
++ * @MT76_TM_ATTR_EEPROM_ACTION: eeprom setting actions
++ * 	(u8, see &enum mt76_testmode_eeprom_action)
++ * @MT76_TM_ATTR_EEPROM_OFFSET: offset of eeprom data block for writing (u32)
++ * @MT76_TM_ATTR_EEPROM_VAL: values for writing into a 16-byte data block
++ * 	(nested, u8 attrs)
++ *
+  */
+ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_UNSPEC,
+@@ -85,6 +93,10 @@ enum mt76_testmode_attr {
+ 
+ 	MT76_TM_ATTR_MAC_ADDRS,
+ 
++	MT76_TM_ATTR_EEPROM_ACTION,
++	MT76_TM_ATTR_EEPROM_OFFSET,
++	MT76_TM_ATTR_EEPROM_VAL,
++
+ 	/* keep last */
+ 	NUM_MT76_TM_ATTRS,
+ 	MT76_TM_ATTR_MAX = NUM_MT76_TM_ATTRS - 1,
+@@ -198,4 +210,22 @@ enum mt76_testmode_tx_mode {
+ 
+ extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
+ 
++/**
++ * enum mt76_testmode_eeprom_action - eeprom setting actions
++ *
++ * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
++ * 	eeprom data block
++ * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
++ * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
++ */
++enum mt76_testmode_eeprom_action {
++	MT76_TM_EEPROM_ACTION_UPDATE_DATA,
++	MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE,
++	MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE,
++
++	/* keep last */
++	NUM_MT76_TM_EEPROM_ACTION,
++	MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
++};
++
+ #endif
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1101-mt76-enable-more-5g-channels.patch b/recipes-kernel/linux-mt76/files/patches/1101-mt76-enable-more-5g-channels.patch
new file mode 100755
index 0000000..c8f5b55
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1101-mt76-enable-more-5g-channels.patch
@@ -0,0 +1,55 @@
+From a45a4416976cd7604bf90103e24fe78150b9de6f Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 29 Sep 2021 14:03:02 +0800
+Subject: [PATCH 1101/1112] mt76: enable more 5g channels
+
+This is necessary for testmode.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/mac80211.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/mac80211.c b/mac80211.c
+index 89ca644..e473227 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -45,6 +45,9 @@ static const struct ieee80211_channel mt76_channels_2ghz[] = {
+ };
+ 
+ static const struct ieee80211_channel mt76_channels_5ghz[] = {
++	CHAN5G(12, 5060),
++	CHAN5G(16, 5080),
++
+ 	CHAN5G(36, 5180),
+ 	CHAN5G(40, 5200),
+ 	CHAN5G(44, 5220),
+@@ -55,6 +58,13 @@ static const struct ieee80211_channel mt76_channels_5ghz[] = {
+ 	CHAN5G(60, 5300),
+ 	CHAN5G(64, 5320),
+ 
++	CHAN5G(68, 5340),
++	CHAN5G(80, 5400),
++	CHAN5G(84, 5420),
++	CHAN5G(88, 5440),
++	CHAN5G(92, 5460),
++	CHAN5G(96, 5480),
++
+ 	CHAN5G(100, 5500),
+ 	CHAN5G(104, 5520),
+ 	CHAN5G(108, 5540),
+@@ -75,6 +85,11 @@ static const struct ieee80211_channel mt76_channels_5ghz[] = {
+ 	CHAN5G(165, 5825),
+ 	CHAN5G(169, 5845),
+ 	CHAN5G(173, 5865),
++
++	CHAN5G(184, 4920),
++	CHAN5G(188, 4940),
++	CHAN5G(192, 4960),
++	CHAN5G(196, 4980),
+ };
+ 
+ static const struct ieee80211_channel mt76_channels_6ghz[] = {
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1102-mt76-testmode-add-attributes-for-setting-rf-config.patch b/recipes-kernel/linux-mt76/files/patches/1102-mt76-testmode-add-attributes-for-setting-rf-config.patch
new file mode 100755
index 0000000..6fedd3c
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1102-mt76-testmode-add-attributes-for-setting-rf-config.patch
@@ -0,0 +1,106 @@
+From fdf988d26cbea1d432e6cfb9a0ca82c160101771 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 4 Jun 2021 18:22:07 +0800
+Subject: [PATCH 1102/1112] mt76: testmode: add attributes for setting rf
+ config
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/mt76.h     |  5 ++++
+ drivers/net/wireless/mediatek/mt76/testmode.c | 17 +++++++++++++-
+ drivers/net/wireless/mediatek/mt76/testmode.h | 23 +++++++++++++++++++
+ 3 files changed, 44 insertions(+), 1 deletion(-)
+
+diff --git a/mt76.h b/mt76.h
+index 8ad7674..157fd6d 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -619,6 +619,11 @@ struct mt76_testmode_data {
+ 
+ 	u8 flag;
+ 
++	struct {
++		u8 type;
++		u8 enable;
++	} cfg;
++
+ 	u32 tx_pending;
+ 	u32 tx_queued;
+ 	u16 tx_queued_limit;
+diff --git a/testmode.c b/testmode.c
+index 1fbca66..f31e124 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -547,7 +547,22 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			if (nla_len(cur) != ETH_ALEN || idx >= 3)
+ 				goto out;
+ 
+-			memcpy(td->addr[idx], nla_data(cur), ETH_ALEN);
++			memcpy(td->addr[idx++], nla_data(cur), ETH_ALEN);
++		}
++	}
++
++	if (tb[MT76_TM_ATTR_CFG]) {
++		struct nlattr *cur;
++		int rem, idx = 0;
++
++		nla_for_each_nested(cur, tb[MT76_TM_ATTR_CFG], rem) {
++			if (nla_len(cur) != 1 || idx >= 2)
++				goto out;
++
++			if (idx == 0)
++				td->cfg.type = nla_get_u8(cur);
++			else
++				td->cfg.enable = nla_get_u8(cur);
+ 			idx++;
+ 		}
+ 	}
+diff --git a/testmode.h b/testmode.h
+index 5900c76..c469ce6 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -55,6 +55,8 @@
+  * @MT76_TM_ATTR_EEPROM_VAL: values for writing into a 16-byte data block
+  * 	(nested, u8 attrs)
+  *
++ * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
++ *
+  */
+ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_UNSPEC,
+@@ -97,6 +99,8 @@ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_EEPROM_OFFSET,
+ 	MT76_TM_ATTR_EEPROM_VAL,
+ 
++	MT76_TM_ATTR_CFG,
++
+ 	/* keep last */
+ 	NUM_MT76_TM_ATTRS,
+ 	MT76_TM_ATTR_MAX = NUM_MT76_TM_ATTRS - 1,
+@@ -228,4 +232,23 @@ enum mt76_testmode_eeprom_action {
+ 	MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
+ };
+ 
++/**
++ * enum mt76_testmode_cfg - packet tx phy mode
++ *
++ * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
++ * 	eeprom data block
++ * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
++ * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
++ */
++enum mt76_testmode_cfg {
++	MT76_TM_CFG_TSSI,
++	MT76_TM_CFG_DPD,
++	MT76_TM_CFG_RATE_POWER_OFFSET,
++	MT76_TM_CFG_THERMAL_COMP,
++
++	/* keep last */
++	NUM_MT76_TM_CFG,
++	MT76_TM_CFG_MAX = NUM_MT76_TM_CFG - 1,
++};
++
+ #endif
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1103-mt76-mt7915-implement-config-set-in-testmode.patch b/recipes-kernel/linux-mt76/files/patches/1103-mt76-mt7915-implement-config-set-in-testmode.patch
new file mode 100755
index 0000000..bdba37f
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1103-mt76-mt7915-implement-config-set-in-testmode.patch
@@ -0,0 +1,87 @@
+From 63de755813ec9d82c785b4d70c4f59d5fb00ca69 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 4 Jun 2021 18:25:21 +0800
+Subject: [PATCH 1103/1112] mt76: mt7915: implement config set in testmode
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ .../net/wireless/mediatek/mt76/mt7915/mcu.h   |  4 +++
+ .../wireless/mediatek/mt76/mt7915/testmode.c  | 26 +++++++++++++++++++
+ 2 files changed, 30 insertions(+)
+
+diff --git a/mt7915/mcu.h b/mt7915/mcu.h
+index c15f89b..4b78468 100644
+--- a/mt7915/mcu.h
++++ b/mt7915/mcu.h
+@@ -27,6 +27,10 @@ struct mt7915_mcu_txd {
+ 
+ enum {
+ 	MCU_ATE_SET_TRX = 0x1,
++	MCU_ATE_SET_TSSI = 0x5,
++	MCU_ATE_SET_DPD = 0x6,
++	MCU_ATE_SET_RATE_POWER_OFFSET = 0x7,
++	MCU_ATE_SET_THERMAL_COMP = 0x8,
+ 	MCU_ATE_SET_FREQ_OFFSET = 0xa,
+ 	MCU_ATE_SET_PHY_COUNT = 0x11,
+ 	MCU_ATE_SET_SLOT_TIME = 0x13,
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index 2c859f6..98431d6 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -9,6 +9,7 @@
+ enum {
+ 	TM_CHANGED_TXPOWER,
+ 	TM_CHANGED_FREQ_OFFSET,
++	TM_CHANGED_CFG,
+ 
+ 	/* must be last */
+ 	NUM_TM_CHANGED
+@@ -17,6 +18,7 @@ enum {
+ static const u8 tm_change_map[] = {
+ 	[TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER,
+ 	[TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
++	[TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
+ };
+ 
+ struct reg_band {
+@@ -182,6 +184,28 @@ mt7915_tm_set_tam_arb(struct mt7915_phy *phy, bool enable, bool mu)
+ 	return mt7915_mcu_set_muru_ctrl(dev, MURU_SET_ARB_OP_MODE, op_mode);
+ }
+ 
++static int
++mt7915_tm_set_cfg(struct mt7915_phy *phy)
++{
++	static const u8 cfg_cmd[] = {
++		[MT76_TM_CFG_TSSI] = MCU_ATE_SET_TSSI,
++		[MT76_TM_CFG_DPD] = MCU_ATE_SET_DPD,
++		[MT76_TM_CFG_RATE_POWER_OFFSET] = MCU_ATE_SET_RATE_POWER_OFFSET,
++		[MT76_TM_CFG_THERMAL_COMP] = MCU_ATE_SET_THERMAL_COMP,
++	};
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7915_dev *dev = phy->dev;
++	struct mt7915_tm_cmd req = {
++		.testmode_en = !(phy->mt76->test.state == MT76_TM_STATE_OFF),
++		.param_idx = cfg_cmd[td->cfg.type],
++		.param.cfg.enable = td->cfg.enable,
++		.param.cfg.band = phy != &dev->phy,
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
++				 sizeof(req), false);
++}
++
+ static int
+ mt7915_tm_set_wmm_qid(struct mt7915_dev *dev, u8 qid, u8 aifs, u8 cw_min,
+ 		      u16 cw_max, u16 txop)
+@@ -727,6 +751,8 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
+ 		mt7915_tm_set_freq_offset(phy, en, en ? td->freq_offset : 0);
+ 	if (changed & BIT(TM_CHANGED_TXPOWER))
+ 		mt7915_tm_set_tx_power(phy);
++	if (changed & BIT(TM_CHANGED_CFG))
++		mt7915_tm_set_cfg(phy);
+ }
+ 
+ static int
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1104-mt76-testmode-add-attributes-to-support-off-channel-.patch b/recipes-kernel/linux-mt76/files/patches/1104-mt76-testmode-add-attributes-to-support-off-channel-.patch
new file mode 100755
index 0000000..98b1d7b
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1104-mt76-testmode-add-attributes-to-support-off-channel-.patch
@@ -0,0 +1,92 @@
+From c11cb393f5d03ff73809510a1056f7aef1799de9 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 28 Jun 2021 10:46:14 +0800
+Subject: [PATCH 1104/1112] mt76: testmode: add attributes to support off
+ channel scan
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/mt76.h     |  5 +++++
+ drivers/net/wireless/mediatek/mt76/testmode.c | 21 +++++++++++++++++++
+ drivers/net/wireless/mediatek/mt76/testmode.h | 10 +++++++++
+ 3 files changed, 36 insertions(+)
+
+diff --git a/mt76.h b/mt76.h
+index 157fd6d..ab9482c 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -624,6 +624,11 @@ struct mt76_testmode_data {
+ 		u8 enable;
+ 	} cfg;
+ 
++	u8 off_ch_scan_ch;
++	u8 off_ch_scan_center_ch;
++	u8 off_ch_scan_bw;
++	u8 off_ch_scan_path;
++
+ 	u32 tx_pending;
+ 	u32 tx_queued;
+ 	u16 tx_queued_limit;
+diff --git a/testmode.c b/testmode.c
+index f31e124..2376e00 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -567,6 +567,27 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		}
+ 	}
+ 
++	if (tb[MT76_TM_ATTR_OFF_CH_SCAN_CH]) {
++		u8 ch = nla_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CH]);
++		struct ieee80211_supported_band *sband;
++
++		sband = ch > 14 ? &phy->sband_5g.sband :
++				  &phy->sband_2g.sband;
++		if (ch && (ch < sband->channels[0].hw_value ||
++			   ch > sband->channels[sband->n_channels - 1].hw_value))
++			goto out;
++
++		td->off_ch_scan_ch = ch;
++
++		if (mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH],
++				   &td->off_ch_scan_center_ch, ch - 6, ch + 6) ||
++		    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW],
++				   &td->off_ch_scan_bw, 0, 6) ||
++		    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_PATH],
++				   &td->off_ch_scan_path, 1, 0xff))
++			goto out;
++	}
++
+ 	if (dev->test_ops->set_params) {
+ 		err = dev->test_ops->set_params(phy, tb, state);
+ 		if (err)
+diff --git a/testmode.h b/testmode.h
+index c469ce6..0fc0ddd 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -57,6 +57,11 @@
+  *
+  * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
+  *
++ * @MT76_TM_ATTR_OFF_CH_SCAN_CH: monitored channel for off channel scan (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH: monitored channel for off channel scan (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_BW: monitored bw for off channel scan (u8)
++ * @MT76_TM_ATTR_OFF_CH_SCAN_PATH: monitored rx path for off channel scan (u8)
++ *
+  */
+ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_UNSPEC,
+@@ -101,6 +106,11 @@ enum mt76_testmode_attr {
+ 
+ 	MT76_TM_ATTR_CFG,
+ 
++	MT76_TM_ATTR_OFF_CH_SCAN_CH,
++	MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
++	MT76_TM_ATTR_OFF_CH_SCAN_BW,
++	MT76_TM_ATTR_OFF_CH_SCAN_PATH,
++
+ 	/* keep last */
+ 	NUM_MT76_TM_ATTRS,
+ 	MT76_TM_ATTR_MAX = NUM_MT76_TM_ATTRS - 1,
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1105-mt76-mt7915-add-off-channel-scan-support-in-testmode.patch b/recipes-kernel/linux-mt76/files/patches/1105-mt76-mt7915-add-off-channel-scan-support-in-testmode.patch
new file mode 100755
index 0000000..ecad61f
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1105-mt76-mt7915-add-off-channel-scan-support-in-testmode.patch
@@ -0,0 +1,145 @@
+From 623e57c672ee85f8a4a9455888237d09df405962 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 28 Jun 2021 10:46:39 +0800
+Subject: [PATCH 1105/1112] mt76: mt7915: add off channel scan support in
+ testmode
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ .../wireless/mediatek/mt76/mt7915/testmode.c  | 72 +++++++++++++++++++
+ .../wireless/mediatek/mt76/mt7915/testmode.h  | 10 +++
+ 2 files changed, 82 insertions(+)
+
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index 98431d6..08bb700 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -10,6 +10,7 @@ enum {
+ 	TM_CHANGED_TXPOWER,
+ 	TM_CHANGED_FREQ_OFFSET,
+ 	TM_CHANGED_CFG,
++	TM_CHANGED_OFF_CH_SCAN_CH,
+ 
+ 	/* must be last */
+ 	NUM_TM_CHANGED
+@@ -19,6 +20,7 @@ static const u8 tm_change_map[] = {
+ 	[TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER,
+ 	[TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
+ 	[TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
++	[TM_CHANGED_OFF_CH_SCAN_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CH,
+ };
+ 
+ struct reg_band {
+@@ -36,6 +38,25 @@ struct reg_band {
+ static struct reg_band reg_backup_list[TM_REG_MAX_ID];
+ 
+ 
++static u8 mt7915_tm_chan_bw(enum nl80211_chan_width width)
++{
++	static const u8 width_to_bw[] = {
++		[NL80211_CHAN_WIDTH_40] = TM_CBW_40MHZ,
++		[NL80211_CHAN_WIDTH_80] = TM_CBW_80MHZ,
++		[NL80211_CHAN_WIDTH_80P80] = TM_CBW_8080MHZ,
++		[NL80211_CHAN_WIDTH_160] = TM_CBW_160MHZ,
++		[NL80211_CHAN_WIDTH_5] = TM_CBW_5MHZ,
++		[NL80211_CHAN_WIDTH_10] = TM_CBW_10MHZ,
++		[NL80211_CHAN_WIDTH_20] = TM_CBW_20MHZ,
++		[NL80211_CHAN_WIDTH_20_NOHT] = TM_CBW_20MHZ,
++	};
++
++	if (width >= ARRAY_SIZE(width_to_bw))
++		return 0;
++
++	return width_to_bw[width];
++}
++
+ static int
+ mt7915_tm_set_tx_power(struct mt7915_phy *phy)
+ {
+@@ -206,6 +227,55 @@ mt7915_tm_set_cfg(struct mt7915_phy *phy)
+ 				 sizeof(req), false);
+ }
+ 
++static int
++mt7915_tm_set_off_channel_scan(struct mt7915_phy *phy)
++{
++#define OFF_CH_SCAN_SIMPLE_RX	2
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7915_dev *dev = phy->dev;
++	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++	int freq1 = chandef->center_freq1;
++	struct {
++		u8 cur_pri_ch;
++		u8 cur_center_ch;
++		u8 cur_bw;
++		u8 cur_tx_path;
++		u8 cur_rx_path;
++
++		u8 scan_pri_ch;
++		u8 scan_center_ch;
++		u8 scan_bw;
++		u8 scan_tx_path;
++		u8 scan_rx_path;
++
++		u8 enable;
++		u8 band_idx;
++		u8 type;
++		u8 is_5g;
++		u8 _rsv[2];
++	} __packed req = {
++		.cur_pri_ch = chandef->chan->hw_value,
++		.cur_center_ch = ieee80211_frequency_to_channel(freq1),
++		.cur_bw = mt7915_tm_chan_bw(chandef->width),
++		.cur_tx_path = td->tx_antenna_mask,
++		.cur_rx_path = td->tx_antenna_mask,
++
++		.scan_pri_ch = td->off_ch_scan_ch,
++		.scan_center_ch = td->off_ch_scan_center_ch,
++		.scan_bw = td->off_ch_scan_bw,
++		.scan_tx_path = td->off_ch_scan_path,
++		.scan_rx_path = td->off_ch_scan_path,
++
++		.enable = !!td->off_ch_scan_ch,
++		.band_idx = phy != &dev->phy,
++		.type = OFF_CH_SCAN_SIMPLE_RX,
++		.is_5g = td->off_ch_scan_ch > 14 ? 1 : 0,
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(OFFCH_SCAN_CTRL), &req,
++				 sizeof(req), false);
++}
++
+ static int
+ mt7915_tm_set_wmm_qid(struct mt7915_dev *dev, u8 qid, u8 aifs, u8 cw_min,
+ 		      u16 cw_max, u16 txop)
+@@ -753,6 +823,8 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
+ 		mt7915_tm_set_tx_power(phy);
+ 	if (changed & BIT(TM_CHANGED_CFG))
+ 		mt7915_tm_set_cfg(phy);
++	if (changed & BIT(TM_CHANGED_OFF_CH_SCAN_CH))
++		mt7915_tm_set_off_channel_scan(phy);
+ }
+ 
+ static int
+diff --git a/mt7915/testmode.h b/mt7915/testmode.h
+index a1c54c8..d22aabe 100644
+--- a/mt7915/testmode.h
++++ b/mt7915/testmode.h
+@@ -130,4 +130,14 @@ struct mt7915_tm_rx_stat_band {
+ 	__le16 mdrdy_cnt_ofdm;
+ };
+ 
++enum {
++	TM_CBW_20MHZ,
++	TM_CBW_40MHZ,
++	TM_CBW_80MHZ,
++	TM_CBW_10MHZ,
++	TM_CBW_5MHZ,
++	TM_CBW_160MHZ,
++	TM_CBW_8080MHZ,
++};
++
+ #endif
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1106-mt76-testmode-add-virtual-stations-support.patch b/recipes-kernel/linux-mt76/files/patches/1106-mt76-testmode-add-virtual-stations-support.patch
new file mode 100755
index 0000000..16b0858
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1106-mt76-testmode-add-virtual-stations-support.patch
@@ -0,0 +1,224 @@
+From ced1d19944f5da249dfacc0a4ef3d5616efc4f87 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 10 May 2021 20:50:43 +0800
+Subject: [PATCH 1106/1112] mt76: testmode: add virtual stations support
+
+Introduce a virtual station struct mt76_testmode_sta for the
+preparation of HE-MU and RU setting support in testmode.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/mt76.h     | 103 +++++++++++++++---
+ drivers/net/wireless/mediatek/mt76/testmode.c |   6 +-
+ drivers/net/wireless/mediatek/mt76/testmode.h |   5 +
+ drivers/net/wireless/mediatek/mt76/tx.c       |   3 +-
+ 4 files changed, 99 insertions(+), 18 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index ab9482c..ce4a098 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -586,6 +586,22 @@ struct mt76_testmode_ops {
+ 
+ #define MT_TM_FW_RX_COUNT	BIT(0)
+ 
++struct mt76_testmode_sta_data {
++	u16 tx_mpdu_len;
++	u8 tx_rate_idx;
++	u8 tx_rate_nss;
++	u8 tx_rate_ldpc;
++
++	u8 aid;
++	u8 ru_alloc;
++	u8 ru_idx;
++};
++
++struct mt76_testmode_sta {
++	struct sk_buff *tx_skb;
++	struct mt76_testmode_sta_data sd;
++};
++
+ struct mt76_testmode_data {
+ 	enum mt76_testmode_state state;
+ 
+@@ -593,13 +609,9 @@ struct mt76_testmode_data {
+ 	struct sk_buff *tx_skb;
+ 
+ 	u32 tx_count;
+-	u16 tx_mpdu_len;
+ 
+ 	u8 tx_rate_mode;
+-	u8 tx_rate_idx;
+-	u8 tx_rate_nss;
+ 	u8 tx_rate_sgi;
+-	u8 tx_rate_ldpc;
+ 	u8 tx_rate_stbc;
+ 	u8 tx_ltf;
+ 
+@@ -629,6 +641,22 @@ struct mt76_testmode_data {
+ 	u8 off_ch_scan_bw;
+ 	u8 off_ch_scan_path;
+ 
++	struct mt76_wcid *tm_wcid[MT76_TM_MAX_STA_NUM + 1];
++	u16 tm_sta_mask;
++	union {
++		struct mt76_testmode_sta_data sd;
++		struct {
++			u16 tx_mpdu_len;
++			u8 tx_rate_idx;
++			u8 tx_rate_nss;
++			u8 tx_rate_ldpc;
++
++			u8 aid;
++			u8 ru_alloc;
++			u8 ru_idx;
++		};
++	};
++
+ 	u32 tx_pending;
+ 	u32 tx_queued;
+ 	u16 tx_queued_limit;
+@@ -1107,22 +1135,69 @@ static inline bool mt76_testmode_enabled(struct mt76_phy *phy)
+ #endif
+ }
+ 
++#ifdef CONFIG_NL80211_TESTMODE
++static inline bool
++mt76_testmode_has_sta(struct mt76_phy *phy)
++{
++	return phy->test.tm_sta_mask != 0;
++}
++
++static inline struct mt76_testmode_sta *
++mt76_testmode_aid_get_sta(struct mt76_phy *phy, u8 aid)
++{
++	struct mt76_wcid *wcid = phy->test.tm_wcid[aid];
++
++	if (!wcid || !aid)
++		return NULL;
++
++	return (struct mt76_testmode_sta *)((u8 *)wcid + phy->hw->sta_data_size);
++}
++
++#define mt76_testmode_for_each_sta(phy, aid, tm_sta)	\
++	for (aid = 1, tm_sta = mt76_testmode_aid_get_sta(phy, 1);	\
++	     aid <= hweight16(phy->test.tm_sta_mask);	\
++	     aid = phy->test.tm_sta_mask >> aid ?	\
++		   ffs(phy->test.tm_sta_mask >> aid) + aid :	\
++		   aid + 1,	\
++	     tm_sta = mt76_testmode_aid_get_sta(phy, aid))
++
++static inline bool
++__mt76_testmode_check_skb(struct mt76_phy *phy, struct sk_buff *skb)
++{
++	struct mt76_testmode_sta *tm_sta;
++	int i;
++
++	if (!mt76_testmode_has_sta(phy))
++		return false;
++
++	mt76_testmode_for_each_sta(phy, i, tm_sta) {
++		if (tm_sta->tx_skb == skb)
++			return true;
++	}
++
++	return false;
++}
++
+ static inline bool mt76_is_testmode_skb(struct mt76_dev *dev,
+ 					struct sk_buff *skb,
+ 					struct ieee80211_hw **hw)
+ {
+-#ifdef CONFIG_NL80211_TESTMODE
+-	if (skb == dev->phy.test.tx_skb)
+-		*hw = dev->phy.hw;
+-	else if (dev->phy2 && skb == dev->phy2->test.tx_skb)
+-		*hw = dev->phy2->hw;
+-	else
+-		return false;
+-	return true;
+-#else
++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++	struct mt76_phy *phy = &dev->phy;
++
++	if ((info->hw_queue & MT_TX_HW_QUEUE_EXT_PHY) && dev->phy2)
++		phy = dev->phy2;
++
++	if (mt76_testmode_enabled(phy) &&
++	    (skb == phy->test.tx_skb ||
++	    __mt76_testmode_check_skb(phy, skb))) {
++		*hw = phy->hw;
++		return true;
++	}
++
+ 	return false;
+-#endif
+ }
++#endif
+ 
+ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb);
+ void mt76_tx(struct mt76_phy *dev, struct ieee80211_sta *sta,
+diff --git a/testmode.c b/testmode.c
+index 2376e00..682ca3d 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -382,7 +382,6 @@ int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state
+ 	}
+ 
+ 	return __mt76_testmode_set_state(phy, state);
+-
+ }
+ EXPORT_SYMBOL(mt76_testmode_set_state);
+ 
+@@ -495,7 +494,10 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE],
+ 			   &td->tx_duty_cycle, 0, 99) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
+-			   &td->tx_power_control, 0, 1))
++			   &td->tx_power_control, 0, 1) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_RU_ALLOC], &td->ru_alloc, 0, 0xff) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_RU_IDX], &td->ru_idx, 0, 68))
+ 		goto out;
+ 
+ 	if (tb[MT76_TM_ATTR_TX_LENGTH]) {
+diff --git a/testmode.h b/testmode.h
+index 0fc0ddd..b360d7a 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -7,6 +7,7 @@
+ 
+ #define MT76_TM_TIMEOUT	10
+ #define MT76_TM_EEPROM_BLOCK_SIZE	16
++#define MT76_TM_MAX_STA_NUM	16
+ 
+ /**
+  * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA
+@@ -111,6 +112,10 @@ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_OFF_CH_SCAN_BW,
+ 	MT76_TM_ATTR_OFF_CH_SCAN_PATH,
+ 
++	MT76_TM_ATTR_AID,
++	MT76_TM_ATTR_RU_ALLOC,
++	MT76_TM_ATTR_RU_IDX,
++
+ 	/* keep last */
+ 	NUM_MT76_TM_ATTRS,
+ 	MT76_TM_ATTR_MAX = NUM_MT76_TM_ATTRS - 1,
+diff --git a/tx.c b/tx.c
+index 6b8c9dc..ca5e6d9 100644
+--- a/tx.c
++++ b/tx.c
+@@ -245,8 +245,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
+ 	if (mt76_is_testmode_skb(dev, skb, &hw)) {
+ 		struct mt76_phy *phy = hw->priv;
+ 
+-		if (skb == phy->test.tx_skb)
+-			phy->test.tx_done++;
++		phy->test.tx_done++;
+ 		if (phy->test.tx_queued == phy->test.tx_done)
+ 			wake_up(&dev->tx_wait);
+ 
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1107-mt76-testmode-support-to-dump-stats-from-different-v.patch b/recipes-kernel/linux-mt76/files/patches/1107-mt76-testmode-support-to-dump-stats-from-different-v.patch
new file mode 100755
index 0000000..78ad215
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1107-mt76-testmode-support-to-dump-stats-from-different-v.patch
@@ -0,0 +1,94 @@
+From e5b15e6a5f8f8ee282e818172f9b1a9cb5a63942 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Mon, 17 May 2021 11:27:17 +0800
+Subject: [PATCH 1107/1112] mt76: testmode: support to dump stats from
+ different virtual stations
+
+Support to
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/testmode.c | 36 ++++++++++++++++---
+ 1 file changed, 31 insertions(+), 5 deletions(-)
+
+diff --git a/testmode.c b/testmode.c
+index 682ca3d..bb15388 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -331,8 +331,11 @@ __mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state)
+ 	struct mt76_dev *dev = phy->dev;
+ 	int err;
+ 
+-	if (prev_state == MT76_TM_STATE_TX_FRAMES)
++	if (prev_state == MT76_TM_STATE_TX_FRAMES) {
++		if (phy->test.tx_rate_mode == MT76_TM_TX_MODE_HE_MU)
++			dev->test_ops->set_state(phy, MT76_TM_STATE_IDLE);
+ 		mt76_testmode_tx_stop(phy);
++	}
+ 
+ 	if (state == MT76_TM_STATE_TX_FRAMES) {
+ 		err = mt76_testmode_tx_init(phy);
+@@ -654,6 +657,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	struct mt76_phy *phy = hw->priv;
+ 	struct mt76_dev *dev = phy->dev;
+ 	struct mt76_testmode_data *td = &phy->test;
++	struct mt76_testmode_sta_data *sd = &td->sd;
+ 	struct nlattr *tb[NUM_MT76_TM_ATTRS] = {};
+ 	int err = 0;
+ 	void *a;
+@@ -686,6 +690,23 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 		goto out;
+ 	}
+ 
++	if (tb[MT76_TM_ATTR_AID]) {
++		struct mt76_testmode_sta *tm_sta;
++		u8 aid;
++
++		err = mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &aid, 1, 16);
++		if (err)
++			goto out;
++
++		tm_sta = mt76_testmode_aid_get_sta(phy, aid);
++		if (!tm_sta) {
++			err = -EINVAL;
++			goto out;
++		}
++
++		sd = &tm_sta->sd;
++	}
++
+ 	mt76_testmode_init_defaults(phy);
+ 
+ 	err = -EMSGSIZE;
+@@ -698,12 +719,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 		goto out;
+ 
+ 	if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
+-	    nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, td->tx_mpdu_len) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, td->tx_rate_mode) ||
+-	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, td->tx_rate_nss) ||
+-	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, td->tx_rate_idx) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) ||
+-	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
+ 	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
+@@ -723,6 +740,15 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	     nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
+ 		goto out;
+ 
++	if (nla_put_u8(msg, MT76_TM_ATTR_AID, sd->aid) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, sd->tx_rate_nss) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, sd->tx_rate_idx) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, sd->tx_rate_ldpc) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_RU_ALLOC, sd->ru_alloc) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_RU_IDX, sd->ru_idx) ||
++	    nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, sd->tx_mpdu_len))
++		goto out;
++
+ 	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
+ 		a = nla_nest_start(msg, MT76_TM_ATTR_TX_POWER);
+ 		if (!a)
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1108-mt76-testmode-rework-the-flow-of-init-tx-skb.patch b/recipes-kernel/linux-mt76/files/patches/1108-mt76-testmode-rework-the-flow-of-init-tx-skb.patch
new file mode 100755
index 0000000..45e5af7
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1108-mt76-testmode-rework-the-flow-of-init-tx-skb.patch
@@ -0,0 +1,193 @@
+From 323105f9f7d5057ffb445948318525f81b76506c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 11 May 2021 10:24:46 +0800
+Subject: [PATCH 1108/1112] mt76: testmode: rework the flow of init tx skb
+
+This is the preparation for supporting virtual stations in testmode.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/mt76.h     |  3 +-
+ .../wireless/mediatek/mt76/mt7915/testmode.c  |  2 +-
+ drivers/net/wireless/mediatek/mt76/testmode.c | 73 +++++++++++++++----
+ 3 files changed, 61 insertions(+), 17 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index ce4a098..b5f1367 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -1289,7 +1289,7 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
+ 		       struct netlink_callback *cb, void *data, int len);
+ int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
+-int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
++int mt76_testmode_init_skb(struct mt76_phy *phy, u32 len, u8 aid, struct sk_buff **skb);
+ 
+ static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
+ {
+@@ -1303,7 +1303,6 @@ static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
+ #endif
+ }
+ 
+-
+ /* internal */
+ static inline struct ieee80211_hw *
+ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index 08bb700..054829e 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -431,7 +431,7 @@ mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time)
+ 	bitrate = cfg80211_calculate_bitrate(&rate);
+ 	tx_len = bitrate * tx_time / 10 / 8;
+ 
+-	ret = mt76_testmode_alloc_skb(phy->mt76, tx_len);
++	ret = mt76_testmode_init_skb(phy->mt76, tx_len, 0, &td->tx_skb);
+ 	if (ret)
+ 		return ret;
+ 
+diff --git a/testmode.c b/testmode.c
+index bb15388..0f93338 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -87,15 +87,34 @@ mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode)
+ }
+ 
+ static void
+-mt76_testmode_free_skb(struct mt76_phy *phy)
++mt76_testmode_free_skb(struct sk_buff **tx_skb)
++{
++	dev_kfree_skb(*tx_skb);
++	*tx_skb = NULL;
++}
++
++static void
++mt76_testmode_free_skb_all(struct mt76_phy *phy)
+ {
+ 	struct mt76_testmode_data *td = &phy->test;
+ 
+-	dev_kfree_skb(td->tx_skb);
+-	td->tx_skb = NULL;
++	if (mt76_testmode_has_sta(phy)) {
++		struct mt76_testmode_sta *tm_sta;
++		int i;
++
++		mt76_testmode_for_each_sta(phy, i, tm_sta) {
++			mt76_testmode_free_skb(&tm_sta->tx_skb);
++		}
++
++		return;
++	}
++
++	mt76_testmode_free_skb(&td->tx_skb);
+ }
+ 
+-int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len)
++static int
++mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len,
++			struct sk_buff **tx_skb, u8 *da)
+ {
+ #define MT_TXP_MAX_LEN	4095
+ 	u16 fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
+@@ -128,7 +147,9 @@ int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len)
+ 	hdr->frame_control = cpu_to_le16(fc);
+ 	memcpy(hdr->addr1, td->addr[0], ETH_ALEN);
+ 	memcpy(hdr->addr2, td->addr[1], ETH_ALEN);
+-	memcpy(hdr->addr3, td->addr[2], ETH_ALEN);
++	/* memcpy(hdr->addr3, td->addr[2], ETH_ALEN); */
++	memcpy(hdr->addr3, da, ETH_ALEN);
++
+ 	skb_set_queue_mapping(head, IEEE80211_AC_BE);
+ 
+ 	info = IEEE80211_SKB_CB(head);
+@@ -152,7 +173,7 @@ int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len)
+ 
+ 		frag = alloc_skb(frag_len, GFP_KERNEL);
+ 		if (!frag) {
+-			mt76_testmode_free_skb(phy);
++			mt76_testmode_free_skb(tx_skb);
+ 			dev_kfree_skb(head);
+ 			return -ENOMEM;
+ 		}
+@@ -165,23 +186,25 @@ int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len)
+ 		frag_tail = &(*frag_tail)->next;
+ 	}
+ 
+-	mt76_testmode_free_skb(phy);
+-	td->tx_skb = head;
++	mt76_testmode_free_skb(tx_skb);
++	*tx_skb = head;
+ 
+ 	return 0;
+ }
+-EXPORT_SYMBOL(mt76_testmode_alloc_skb);
+ 
+-static int
+-mt76_testmode_tx_init(struct mt76_phy *phy)
++int mt76_testmode_init_skb(struct mt76_phy *phy, u32 len, u8 aid,
++			   struct sk_buff **tx_skb)
+ {
+ 	struct mt76_testmode_data *td = &phy->test;
+ 	struct ieee80211_tx_info *info;
+ 	struct ieee80211_tx_rate *rate;
+ 	u8 max_nss = hweight8(phy->antenna_mask);
++	u8 da[ETH_ALEN];
+ 	int ret;
+ 
+-	ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
++	ether_addr_copy(da, phy->macaddr);
++	da[0] += aid * 4;
++	ret = mt76_testmode_alloc_skb(phy, len, tx_skb, da);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -191,7 +214,7 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
+ 	if (td->tx_antenna_mask)
+ 		max_nss = min_t(u8, max_nss, hweight8(td->tx_antenna_mask));
+ 
+-	info = IEEE80211_SKB_CB(td->tx_skb);
++	info = IEEE80211_SKB_CB(*tx_skb);
+ 	rate = &info->control.rates[0];
+ 	rate->count = 1;
+ 	rate->idx = td->tx_rate_idx;
+@@ -263,6 +286,28 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
+ out:
+ 	return 0;
+ }
++EXPORT_SYMBOL(mt76_testmode_init_skb);
++
++static int
++mt76_testmode_tx_init(struct mt76_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->test;
++	struct mt76_testmode_sta *tm_sta;
++	int ret, i;
++
++	if (!mt76_testmode_has_sta(phy))
++		return mt76_testmode_init_skb(phy, td->tx_mpdu_len,
++					      0, &td->tx_skb);
++
++	mt76_testmode_for_each_sta(phy, i, tm_sta) {
++		ret = mt76_testmode_init_skb(phy, tm_sta->sd.tx_mpdu_len,
++					     tm_sta->sd.aid, &tm_sta->tx_skb);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
++}
+ 
+ static void
+ mt76_testmode_tx_start(struct mt76_phy *phy)
+@@ -291,7 +336,7 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
+ 	wait_event_timeout(dev->tx_wait, td->tx_done == td->tx_queued,
+ 			   MT76_TM_TIMEOUT * HZ);
+ 
+-	mt76_testmode_free_skb(phy);
++	mt76_testmode_free_skb_all(phy);
+ }
+ 
+ static inline void
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1109-mt76-testmode-add-support-to-queue-skb-of-multiple-s.patch b/recipes-kernel/linux-mt76/files/patches/1109-mt76-testmode-add-support-to-queue-skb-of-multiple-s.patch
new file mode 100755
index 0000000..e92177a
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1109-mt76-testmode-add-support-to-queue-skb-of-multiple-s.patch
@@ -0,0 +1,144 @@
+From 19e0036562d574c6ffe6a47790dbfa953b35050c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 11 May 2021 15:17:31 +0800
+Subject: [PATCH 1109/1112] mt76: testmode: add support to queue skb of
+ multiple stations
+
+Rework queue skb flow to support sending packet for multiple virtual
+stations.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/mt76.h     |  1 +
+ drivers/net/wireless/mediatek/mt76/testmode.c | 70 ++++++++++++++++---
+ 2 files changed, 63 insertions(+), 8 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index b5f1367..4b502c6 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -642,6 +642,7 @@ struct mt76_testmode_data {
+ 	u8 off_ch_scan_path;
+ 
+ 	struct mt76_wcid *tm_wcid[MT76_TM_MAX_STA_NUM + 1];
++	u8 cur_aid;
+ 	u16 tm_sta_mask;
+ 	union {
+ 		struct mt76_testmode_sta_data sd;
+diff --git a/testmode.c b/testmode.c
+index 0f93338..9da490c 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -25,18 +25,18 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ };
+ EXPORT_SYMBOL_GPL(mt76_tm_policy);
+ 
+-void mt76_testmode_tx_pending(struct mt76_phy *phy)
++static u16
++mt76_testmode_queue_tx(struct mt76_phy *phy, struct mt76_wcid *wcid,
++		       struct sk_buff *skb, u32 limit)
+ {
+ 	struct mt76_testmode_data *td = &phy->test;
+ 	struct mt76_dev *dev = phy->dev;
+-	struct mt76_wcid *wcid = &dev->global_wcid;
+-	struct sk_buff *skb = td->tx_skb;
+ 	struct mt76_queue *q;
+-	u16 tx_queued_limit;
++	u16 tx_queued_limit, count = 0;
+ 	int qid;
+ 
+-	if (!skb || !td->tx_pending)
+-		return;
++	if (!skb)
++		return 0;
+ 
+ 	qid = skb_get_queue_mapping(skb);
+ 	q = phy->q_tx[qid];
+@@ -45,7 +45,7 @@ void mt76_testmode_tx_pending(struct mt76_phy *phy)
+ 
+ 	spin_lock_bh(&q->lock);
+ 
+-	while (td->tx_pending > 0 &&
++	while (count < limit &&
+ 	       td->tx_queued - td->tx_done < tx_queued_limit &&
+ 	       q->queued < q->ndesc / 2) {
+ 		int ret;
+@@ -55,13 +55,56 @@ void mt76_testmode_tx_pending(struct mt76_phy *phy)
+ 		if (ret < 0)
+ 			break;
+ 
+-		td->tx_pending--;
+ 		td->tx_queued++;
++		count++;
+ 	}
+ 
+ 	dev->queue_ops->kick(dev, q);
+ 
+ 	spin_unlock_bh(&q->lock);
++
++	return count;
++}
++
++void mt76_testmode_tx_pending(struct mt76_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->test;
++	u16 count;
++
++	if (!td->tx_pending)
++		return;
++
++	if (!mt76_testmode_has_sta(phy)) {
++		count = mt76_testmode_queue_tx(phy, &phy->dev->global_wcid,
++					       td->tx_skb, td->tx_pending);
++		td->tx_pending -= count;
++
++		return;
++	}
++
++	while (true) {
++		struct mt76_testmode_sta *tm_sta;
++		struct mt76_wcid *wcid;
++		u32 limit, per_sta_cnt = 1;
++
++		if (td->tx_rate_mode != MT76_TM_TX_MODE_HE_MU)
++			per_sta_cnt = td->tx_count / hweight16(phy->test.tm_sta_mask);
++
++		limit = td->tx_pending % per_sta_cnt;
++		if (limit == 0)
++			limit = per_sta_cnt;
++
++		tm_sta = mt76_testmode_aid_get_sta(phy, td->cur_aid);
++		wcid = td->tm_wcid[td->cur_aid];
++		count = mt76_testmode_queue_tx(phy, wcid, tm_sta->tx_skb, limit);
++
++		td->tx_pending -= count;
++
++		if (td->tx_pending && (td->tx_pending % per_sta_cnt == 0))
++			td->cur_aid = ffs(td->tm_sta_mask >> td->cur_aid) + td->cur_aid;
++		else
++			break;
++	}
+ }
+ 
+ static u32
+@@ -318,6 +361,17 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
+ 	td->tx_queued = 0;
+ 	td->tx_done = 0;
+ 	td->tx_pending = td->tx_count;
++
++	if (mt76_testmode_has_sta(phy)) {
++		td->cur_aid = ffs(td->tm_sta_mask);
++
++		/* The actual tx count of MU packets will be pass to FW
++		 * by a mcu command in testmode.
++		 */
++		if (td->tx_rate_mode == MT76_TM_TX_MODE_HE_MU)
++			td->tx_pending = hweight16(phy->test.tm_sta_mask);
++	}
++
+ 	mt76_worker_schedule(&dev->tx_worker);
+ }
+ 
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1110-mt76-mt7915-implement-aid-support-in-testmode.patch b/recipes-kernel/linux-mt76/files/patches/1110-mt76-mt7915-implement-aid-support-in-testmode.patch
new file mode 100755
index 0000000..90b53ea
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1110-mt76-mt7915-implement-aid-support-in-testmode.patch
@@ -0,0 +1,420 @@
+From 8027e94f1564089d719a6fb0eab7d29bb2981bf0 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 11 May 2021 16:24:09 +0800
+Subject: [PATCH 1110/1112] mt76: mt7915: implement aid support in testmode
+
+Add support for virtual stations in mt7915 testmode.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ .../wireless/mediatek/mt76/mt76_connac_mcu.c  |   5 +
+ .../net/wireless/mediatek/mt76/mt7915/mac.c   |  25 +-
+ .../wireless/mediatek/mt76/mt7915/testmode.c  | 231 +++++++++++++++---
+ 3 files changed, 216 insertions(+), 45 deletions(-)
+
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index eac096c..a361ab6 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -389,6 +389,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct sk_buff *skb,
+ 	switch (vif->type) {
+ 	case NL80211_IFTYPE_MESH_POINT:
+ 	case NL80211_IFTYPE_AP:
++	case NL80211_IFTYPE_MONITOR:
+ 		if (vif->p2p)
+ 			conn_type = CONNECTION_P2P_GC;
+ 		else
+@@ -577,6 +578,10 @@ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev,
+ 					     wtbl_tlv, sta_wtbl);
+ 	spe = (struct wtbl_spe *)tlv;
+ 	spe->spe_idx = 24;
++
++	/* check */
++	if (vif->type == NL80211_IFTYPE_MONITOR)
++		rx->rca1 = 0;
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_generic_tlv);
+ 
+diff --git a/mt7915/mac.c b/mt7915/mac.c
+index fb42446..2ad4cb1 100644
+--- a/mt7915/mac.c
++++ b/mt7915/mac.c
+@@ -906,16 +906,28 @@ mt7915_mac_write_txwi_tm(struct mt7915_phy *phy, __le32 *txwi,
+ {
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt76_testmode_sta_data *sd = &td->sd;
+ 	const struct ieee80211_rate *r;
+-	u8 bw, mode, nss = td->tx_rate_nss;
+-	u8 rate_idx = td->tx_rate_idx;
++	u8 bw, mode, nss, rate_idx;
+ 	u16 rateval = 0;
+ 	u32 val;
+ 	bool cck = false;
+ 	int band;
+ 
+-	if (skb != phy->mt76->test.tx_skb)
+-		return;
++	if (mt76_testmode_has_sta(phy->mt76)) {
++		struct mt76_testmode_sta *tm_sta;
++		int i;
++
++		mt76_testmode_for_each_sta(phy->mt76, i, tm_sta) {
++			if (tm_sta->tx_skb == skb) {
++				sd = &tm_sta->sd;
++				break;
++			}
++		}
++	}
++
++	nss = sd->tx_rate_nss;
++	rate_idx = sd->tx_rate_idx;
+ 
+ 	switch (td->tx_rate_mode) {
+ 	case MT76_TM_TX_MODE_HT:
+@@ -1005,7 +1017,7 @@ mt7915_mac_write_txwi_tm(struct mt7915_phy *phy, __le32 *txwi,
+ 	if (mode >= MT_PHY_TYPE_HE_SU)
+ 		val |= FIELD_PREP(MT_TXD6_HELTF, td->tx_ltf);
+ 
+-	if (td->tx_rate_ldpc || (bw > 0 && mode >= MT_PHY_TYPE_HE_SU))
++	if (sd->tx_rate_ldpc || (bw > 0 && mode >= MT_PHY_TYPE_HE_SU))
+ 		val |= MT_TXD6_LDPC;
+ 
+ 	txwi[1] &= ~cpu_to_le32(MT_TXD1_VTA);
+@@ -1474,6 +1486,9 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
+ 				continue;
+ 
+ 			msta = container_of(wcid, struct mt7915_sta, wcid);
++			if (mt76_testmode_enabled(msta->vif->phy->mt76))
++				continue;
++
+ 			spin_lock_bh(&dev->sta_poll_lock);
+ 			if (list_empty(&msta->poll_list))
+ 				list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index 054829e..29c173d 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -11,6 +11,7 @@ enum {
+ 	TM_CHANGED_FREQ_OFFSET,
+ 	TM_CHANGED_CFG,
+ 	TM_CHANGED_OFF_CH_SCAN_CH,
++	TM_CHANGED_AID,
+ 
+ 	/* must be last */
+ 	NUM_TM_CHANGED
+@@ -21,6 +22,7 @@ static const u8 tm_change_map[] = {
+ 	[TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
+ 	[TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
+ 	[TM_CHANGED_OFF_CH_SCAN_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CH,
++	[TM_CHANGED_AID] = MT76_TM_ATTR_AID,
+ };
+ 
+ struct reg_band {
+@@ -142,18 +144,33 @@ mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en)
+ }
+ 
+ static int
+-mt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid)
++mt7915_tm_clean_hwq(struct mt7915_phy *phy)
+ {
+ 	struct mt7915_dev *dev = phy->dev;
+ 	struct mt7915_tm_cmd req = {
+ 		.testmode_en = 1,
+ 		.param_idx = MCU_ATE_CLEAN_TXQUEUE,
+-		.param.clean.wcid = wcid,
+ 		.param.clean.band = phy != &dev->phy,
+ 	};
++	struct mt76_testmode_sta *tm_sta;
++	int ret, i;
+ 
+-	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
+-				 sizeof(req), false);
++	if (!mt76_testmode_has_sta(phy->mt76)) {
++		req.param.clean.wcid = dev->mt76.global_wcid.idx;
++
++		return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL),
++					 &req, sizeof(req), false);
++	}
++
++	mt76_testmode_for_each_sta(phy->mt76, i, tm_sta) {
++		req.param.clean.wcid = phy->mt76->test.tm_wcid[i]->idx;
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL),
++					&req, sizeof(req), false);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
+ }
+ 
+ static int
+@@ -530,27 +547,109 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
+ 	}
+ }
+ 
++static int
++mt7915_tm_sta_add(struct mt7915_phy *phy, u8 aid,
++		  struct mt76_testmode_sta_data *sd)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt76_testmode_sta *tm_sta;
++
++	if (!aid)
++		return 0;
++
++	if (!td->tm_wcid[aid]) {
++		struct ieee80211_vif *vif = phy->monitor_vif;
++		struct ieee80211_sband_iftype_data *data;
++		struct ieee80211_supported_band *sband;
++		struct ieee80211_sta *sta;
++		struct mt7915_sta *msta;
++		int ret;
++
++		sta = kzalloc(sizeof(*sta) + phy->mt76->hw->sta_data_size +
++			      sizeof(*tm_sta), GFP_KERNEL);
++		if (!sta)
++			return -ENOMEM;
++
++		if (phy->mt76->chandef.chan->band == NL80211_BAND_5GHZ) {
++			sband = &phy->mt76->sband_5g.sband;
++			data = phy->iftype[NL80211_BAND_5GHZ];
++		} else {
++			sband = &phy->mt76->sband_2g.sband;
++			data = phy->iftype[NL80211_BAND_2GHZ];
++		}
++
++		ether_addr_copy(sta->addr, phy->mt76->macaddr);
++		sta->addr[0] += aid * 4;
++		memcpy(&sta->ht_cap, &sband->ht_cap, sizeof(sta->ht_cap));
++		memcpy(&sta->vht_cap, &sband->vht_cap, sizeof(sta->vht_cap));
++		memcpy(&sta->he_cap, &data[NL80211_IFTYPE_STATION].he_cap,
++		       sizeof(sta->he_cap));
++		sta->aid = aid;
++		sta->wme = 1;
++
++		ret = mt7915_mac_sta_add(&phy->dev->mt76, vif, sta);
++		if (ret) {
++			kfree(sta);
++			return ret;
++		}
++
++		msta = (struct mt7915_sta *)sta->drv_priv;
++		td->tm_wcid[aid] = &msta->wcid;
++		td->tm_sta_mask |= BIT(aid - 1);
++	}
++
++	tm_sta = mt76_testmode_aid_get_sta(phy->mt76, aid);
++	memcpy(&tm_sta->sd, sd, sizeof(tm_sta->sd));
++
++	return 0;
++}
++
+ static void
+-mt7915_tm_init(struct mt7915_phy *phy, bool en)
++mt7915_tm_sta_remove(struct mt7915_phy *phy, u8 aid)
+ {
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt76_wcid *wcid = td->tm_wcid[aid];
+ 	struct mt7915_dev *dev = phy->dev;
++	struct ieee80211_sta *sta = wcid_to_sta(wcid);
+ 
+-	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
++	mt7915_mac_sta_remove(&dev->mt76, phy->monitor_vif, sta);
++	mt76_wcid_mask_clear(dev->mt76.wcid_mask, wcid->idx);
++
++	kfree(sta);
++	td->tm_wcid[aid] = NULL;
++	td->tm_sta_mask &= ~BIT(aid - 1);
++}
++
++static void
++mt7915_tm_sta_remove_all(struct mt7915_phy *phy)
++{
++	int i;
++
++	if (!mt76_testmode_has_sta(phy->mt76))
+ 		return;
+ 
+-	mt7915_mcu_set_sku_en(phy, !en);
++	for (i = 1; i < ARRAY_SIZE(phy->mt76->test.tm_wcid); i++) {
++		if (phy->mt76->test.tm_wcid[i])
++			mt7915_tm_sta_remove(phy, i);
++	}
++}
+ 
+-	mt7915_tm_mode_ctrl(dev, en);
+-	mt7915_tm_reg_backup_restore(phy);
+-	mt7915_tm_set_trx(phy, TM_MAC_TXRX, !en);
++static int
++mt7915_tm_set_sta(struct mt7915_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
+ 
+-	mt7915_mcu_add_bss_info(phy, phy->monitor_vif, en);
+-	mt7915_mcu_add_sta(dev, phy->monitor_vif, NULL, en);
++	if (!td->aid) {
++		mt7915_tm_sta_remove_all(phy);
++		return 0;
++	}
+ 
+-	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
++	if (td->tx_count == 0) {
++		mt7915_tm_sta_remove(phy, td->aid);
++		return 0;
++	}
+ 
+-	if (!en)
+-		mt7915_tm_set_tam_arb(phy, en, 0);
++	return mt7915_tm_sta_add(phy, td->aid, &td->sd);
+ }
+ 
+ static void
+@@ -563,22 +662,48 @@ mt7915_tm_update_channel(struct mt7915_phy *phy)
+ 	mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD(SET_RX_PATH));
+ }
+ 
++static bool
++mt7915_tm_check_skb(struct mt7915_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct ieee80211_tx_info *info;
++
++	if (!mt76_testmode_has_sta(phy->mt76)) {
++		if (!td->tx_skb)
++			return false;
++
++		info = IEEE80211_SKB_CB(td->tx_skb);
++		info->control.vif = phy->monitor_vif;
++	} else {
++		struct mt76_testmode_sta *tm_sta;
++		int i;
++
++		mt76_testmode_for_each_sta(phy->mt76, i, tm_sta) {
++			if (!tm_sta->tx_skb)
++				return false;
++
++			info = IEEE80211_SKB_CB(tm_sta->tx_skb);
++			info->control.vif = phy->monitor_vif;
++		}
++	}
++
++	return true;
++}
++
+ static void
+ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
+ {
+ 	static const u8 spe_idx_map[] = {0, 0, 1, 0, 3, 2, 4, 0,
+ 					 9, 8, 6, 10, 16, 12, 18, 0};
+ 	struct mt76_testmode_data *td = &phy->mt76->test;
+-	struct mt7915_dev *dev = phy->dev;
+-	struct ieee80211_tx_info *info;
+-	u8 duty_cycle = td->tx_duty_cycle;
+-	u32 tx_time = td->tx_time;
+-	u32 ipg = td->tx_ipg;
+ 
+ 	mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, false);
+-	mt7915_tm_clean_hwq(phy, dev->mt76.global_wcid.idx);
++	mt7915_tm_set_trx(phy, TM_MAC_TX, false);
+ 
+ 	if (en) {
++		u32 tx_time = td->tx_time, ipg = td->tx_ipg;
++		u8 duty_cycle = td->tx_duty_cycle;
++
+ 		mt7915_tm_update_channel(phy);
+ 
+ 		if (td->tx_spe_idx) {
+@@ -586,30 +711,29 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
+ 		} else {
+ 			phy->test.spe_idx = spe_idx_map[td->tx_antenna_mask];
+ 		}
+-	}
+ 
+-	mt7915_tm_set_tam_arb(phy, en,
+-			      td->tx_rate_mode == MT76_TM_TX_MODE_HE_MU);
+-
+-	/* if all three params are set, duty_cycle will be ignored */
+-	if (duty_cycle && tx_time && !ipg) {
+-		ipg = tx_time * 100 / duty_cycle - tx_time;
+-	} else if (duty_cycle && !tx_time && ipg) {
+-		if (duty_cycle < 100)
+-			tx_time = duty_cycle * ipg / (100 - duty_cycle);
+-	}
++		/* if all three params are set, duty_cycle will be ignored */
++		if (duty_cycle && tx_time && !ipg) {
++			ipg = tx_time * 100 / duty_cycle - tx_time;
++		} else if (duty_cycle && !tx_time && ipg) {
++			if (duty_cycle < 100)
++				tx_time = duty_cycle * ipg / (100 - duty_cycle);
++		}
+ 
+-	mt7915_tm_set_ipg_params(phy, ipg, td->tx_rate_mode);
+-	mt7915_tm_set_tx_len(phy, tx_time);
++		mt7915_tm_set_ipg_params(phy, ipg, td->tx_rate_mode);
++		mt7915_tm_set_tx_len(phy, tx_time);
+ 
+-	if (ipg)
+-		td->tx_queued_limit = MT76_TM_TIMEOUT * 1000000 / ipg / 2;
++		if (ipg)
++			td->tx_queued_limit = MT76_TM_TIMEOUT * 1000000 / ipg / 2;
+ 
+-	if (!en || !td->tx_skb)
+-		return;
++		if (!mt7915_tm_check_skb(phy))
++			return;
++	} else {
++		mt7915_tm_clean_hwq(phy);
++	}
+ 
+-	info = IEEE80211_SKB_CB(td->tx_skb);
+-	info->control.vif = phy->monitor_vif;
++	mt7915_tm_set_tam_arb(phy, en,
++			      td->tx_rate_mode == MT76_TM_TX_MODE_HE_MU);
+ 
+ 	mt7915_tm_set_trx(phy, TM_MAC_TX, en);
+ }
+@@ -811,6 +935,31 @@ out:
+ 				 sizeof(req), true);
+ }
+ 
++static void
++mt7915_tm_init(struct mt7915_phy *phy, bool en)
++{
++	struct mt7915_dev *dev = phy->dev;
++
++	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
++		return;
++
++	mt7915_mcu_set_sku_en(phy, !en);
++
++	mt7915_tm_mode_ctrl(dev, en);
++	mt7915_tm_reg_backup_restore(phy);
++	mt7915_tm_set_trx(phy, TM_MAC_TXRX, !en);
++
++	mt7915_mcu_add_bss_info(phy, phy->monitor_vif, en);
++	mt7915_mcu_add_sta(dev, phy->monitor_vif, NULL, en);
++
++	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
++
++	if (!en) {
++		mt7915_tm_set_tam_arb(phy, en, 0);
++		mt7915_tm_sta_remove_all(phy);
++	}
++}
++
+ static void
+ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
+ {
+@@ -825,6 +974,8 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
+ 		mt7915_tm_set_cfg(phy);
+ 	if (changed & BIT(TM_CHANGED_OFF_CH_SCAN_CH))
+ 		mt7915_tm_set_off_channel_scan(phy);
++	if (changed & BIT(TM_CHANGED_AID))
++		mt7915_tm_set_sta(phy);
+ }
+ 
+ static int
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1111-mt76-tool-add-more-commands.patch b/recipes-kernel/linux-mt76/files/patches/1111-mt76-tool-add-more-commands.patch
new file mode 100755
index 0000000..630a68c
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1111-mt76-tool-add-more-commands.patch
@@ -0,0 +1,134 @@
+From ce9be27dcc763b3a9b399dfe8f62ee1c0fed9734 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 4 Feb 2022 21:35:14 +0800
+Subject: [PATCH 1111/1112] mt76: tool: add more commands
+
+---
+ .../net/wireless/mediatek/mt76/tools/fields.c | 76 +++++++++++++++++++
+ 1 file changed, 76 insertions(+)
+
+diff --git a/tools/fields.c b/tools/fields.c
+index e3f6908..036406c 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
+ 	[MT76_TM_STATE_IDLE] = "idle",
+ 	[MT76_TM_STATE_TX_FRAMES] = "tx_frames",
+ 	[MT76_TM_STATE_RX_FRAMES] = "rx_frames",
++	[MT76_TM_STATE_TX_CONT] = "tx_cont",
+ };
+ 
+ static const char * const testmode_tx_mode[] = {
+@@ -201,6 +202,63 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
+ 	printf("%srx_per=%.02f%%\n", prefix, 100 * failed / total);
+ }
+ 
++static bool parse_mac(const struct tm_field *field, int idx,
++		      struct nl_msg *msg, const char *val)
++{
++#define ETH_ALEN	6
++	bool ret = true;
++	char *str, *cur, *ap;
++	void *a;
++
++	ap = str = strdup(val);
++
++	a = nla_nest_start(msg, idx);
++
++	idx = 0;
++	while ((cur = strsep(&ap, ",")) != NULL) {
++		unsigned char addr[ETH_ALEN];
++		char *val, *tmp = cur;
++		int i = 0;
++
++		while ((val = strsep(&tmp, ":")) != NULL) {
++			if (i >= ETH_ALEN)
++				break;
++
++			addr[i++] = strtoul(val, NULL, 16);
++		}
++
++		nla_put(msg, idx, ETH_ALEN, addr);
++
++		idx++;
++	}
++
++	nla_nest_end(msg, a);
++
++	free(str);
++
++	return ret;
++}
++
++static void print_mac(const struct tm_field *field, struct nlattr *attr)
++{
++#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
++#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
++	unsigned char addr[3][6];
++	struct nlattr *cur;
++	int idx = 0;
++	int rem;
++
++	nla_for_each_nested(cur, attr, rem) {
++		if (nla_len(cur) != 6)
++			continue;
++		memcpy(addr[idx++], nla_data(cur), 6);
++	}
++
++	printf("" MACSTR "," MACSTR "," MACSTR "",
++	       MAC2STR(addr[0]), MAC2STR(addr[1]), MAC2STR(addr[2]));
++
++	return;
++}
+ 
+ #define FIELD_GENERIC(_field, _name, ...)	\
+ 	[FIELD_NAME(_field)] = {			\
+@@ -250,6 +308,13 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
+ 		 ##__VA_ARGS__				\
+ 	)
+ 
++#define FIELD_MAC(_field, _name)			\
++	[FIELD_NAME(_field)] = {			\
++		.name = _name,				\
++		.parse = parse_mac,			\
++		.print = print_mac			\
++	}
++
+ #define FIELD_NAME(_field) MT76_TM_RX_ATTR_##_field
+ static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
+ 	FIELD_RO(s32, FREQ_OFFSET, "freq_offset"),
+@@ -300,10 +365,16 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ 	FIELD(u8, TX_RATE_LDPC, "tx_rate_ldpc"),
+ 	FIELD(u8, TX_RATE_STBC, "tx_rate_stbc"),
+ 	FIELD(u8, TX_LTF, "tx_ltf"),
++	FIELD(u8, TX_DUTY_CYCLE, "tx_duty_cycle"),
++	FIELD(u32, TX_IPG, "tx_ipg"),
++	FIELD(u32, TX_TIME, "tx_time"),
+ 	FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
+ 	FIELD_ARRAY(u8, TX_POWER, "tx_power"),
+ 	FIELD(u8, TX_ANTENNA, "tx_antenna"),
++	FIELD(u8, TX_SPE_IDX, "tx_spe_idx"),
+ 	FIELD(u32, FREQ_OFFSET, "freq_offset"),
++	FIELD(u8, AID, "aid"),
++	FIELD_MAC(MAC_ADDRS, "mac_addrs"),
+ 	FIELD_NESTED_RO(STATS, stats, "",
+ 			.print_extra = print_extra_stats),
+ };
+@@ -322,9 +393,14 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
+ 	[MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_AID] = { .type = NLA_U8 },
+ 	[MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
+ };
+ 
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/2001-mt76-mt7915-add-L0.5-SER-for-mt7986.patch b/recipes-kernel/linux-mt76/files/patches/2001-mt76-mt7915-add-L0.5-SER-for-mt7986.patch
new file mode 100755
index 0000000..bd6cc19
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/2001-mt76-mt7915-add-L0.5-SER-for-mt7986.patch
@@ -0,0 +1,945 @@
+From 60822f57c815c088dca838f12e8b6e909fbea497 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Thu, 17 Feb 2022 00:35:03 +0800
+Subject: [PATCH 2001/2001] mt76: mt7915: add L0.5 SER for mt7986
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ .../wireless/mediatek/mt76/mt7915/debugfs.c   | 168 ++++++++++++-
+ .../net/wireless/mediatek/mt76/mt7915/dma.c   |  49 ++++
+ .../net/wireless/mediatek/mt76/mt7915/init.c  |  16 +-
+ .../net/wireless/mediatek/mt76/mt7915/mac.c   | 231 +++++++++++++++++-
+ .../net/wireless/mediatek/mt76/mt7915/main.c  |  16 +-
+ .../net/wireless/mediatek/mt76/mt7915/mcu.c   |  52 +++-
+ .../net/wireless/mediatek/mt76/mt7915/mmio.c  |  12 +-
+ .../wireless/mediatek/mt76/mt7915/mt7915.h    |  27 ++
+ .../net/wireless/mediatek/mt76/mt7915/regs.h  |  37 ++-
+ 9 files changed, 578 insertions(+), 30 deletions(-)
+
+diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
+index 1857420..ea0b4a5 100644
+--- a/mt7915/debugfs.c
++++ b/mt7915/debugfs.c
+@@ -47,7 +47,8 @@ mt7915_implicit_txbf_get(void *data, u64 *val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7915_implicit_txbf_get,
+ 			 mt7915_implicit_txbf_set, "%lld\n");
+ 
+-/* test knob of system layer 1/2 error recovery */
++/* test knob of system layer 0.5/1/2 error recovery */
++/*
+ static int mt7915_ser_trigger_set(void *data, u64 val)
+ {
+ 	enum {
+@@ -74,9 +75,172 @@ static int mt7915_ser_trigger_set(void *data, u64 val)
+ 	return ret;
+ }
+ 
++*/
++static int mt7915_ser_trigger_set(void *data, u64 val)
++{
++#define SER_SET		GENMASK(3, 0)
++#define SER_BAND	GENMASK(7, 4)
++#define SER_ACTION	GENMASK(11, 8)
++	enum {
++		SER_ACTION_SET = 1,
++		SER_ACTION_SET_MASK = 2,
++		SER_ACTION_TRIGGER = 3,
++	};
++
++	struct mt7915_dev *dev = data;
++	u8 ser_action, ser_band, ser_set, set_val;
++
++	ser_action = FIELD_GET(SER_ACTION, val);
++	ser_set = set_val = FIELD_GET(SER_SET, val);
++	ser_band = (ser_action == SER_ACTION_TRIGGER) ?
++		   FIELD_GET(SER_BAND, val) : 0;
++
++	if (ser_band > 1)
++		return -1;
++
++	switch (ser_action) {
++	case SER_ACTION_SET:
++		/*
++		 * 0x100: disable system error recovery function.
++		 * 0x101: enable system error recovery function.
++		 */
++		ser_set = !!set_val;
++		break;
++	case SER_ACTION_SET_MASK:
++		/*
++		 * 0x200: enable system error tracking.
++		 * 0x201: enable system error L1 recover.
++		 * 0x202: enable system error L2 recover.
++		 * 0x203: enable system error L3 rx abort.
++		 * 0x204: enable system error L3 tx abort.
++		 * 0x205: enable system error L3 tx disable.
++		 * 0x206: enable system error L3 bf recover.
++		 * 0x207: enable system error all recover.
++		 */
++		ser_set = set_val > 7 ? 0x7f : BIT(set_val);
++		break;
++	case SER_ACTION_TRIGGER:
++		/*
++		 * 0x300: trigger L0 recover.
++		 * 0x301/0x311: trigger L1 recover for band0/band1.
++		 * 0x302/0x312: trigger L2 recover for band0/band1.
++		 * 0x303/0x313: trigger L3 rx abort for band0/band1.
++		 * 0x304/0x314: trigger L3 tx abort for band0/band1.
++		 * 0x305/0x315: trigger L3 tx disable for band0/band1.
++		 * 0x306/0x316: trigger L3 bf recover for band0/band1.
++		 */
++		if (0x300 == val || 0x310 == val) {
++			mt7915_reset(dev, SER_TYPE_FULL_RESET);
++			return 0;
++		}
++
++		if (ser_set > 6)
++			return -1;
++		break;
++	default:
++		return -1;
++	}
++
++	return mt7915_mcu_set_ser(dev, ser_action, ser_set, ser_band);
++}
++
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_ser_trigger, NULL,
+ 			 mt7915_ser_trigger_set, "%lld\n");
+ 
++static int
++mt7915_ser_stats_show(struct seq_file *s, void *data)
++{
++#define	SER_ACTION_QUERY	0
++	struct mt7915_dev *dev = dev_get_drvdata(s->private);
++	int ret = 0;
++
++	/* get more info from firmware */
++	ret = mt7915_mcu_set_ser(dev, SER_ACTION_QUERY, 0, 0);
++	msleep(100);
++
++	seq_printf(s, "::E  R , SER_STATUS        = 0x%08X\n",
++		   MT_SWDEF_SER_STATUS);
++	seq_printf(s, "::E  R , SER_PLE_ERR       = 0x%08X\n",
++		   MT_SWDEF_PLE_STATUS);
++	seq_printf(s, "::E  R , SER_PLE_ERR_1     = 0x%08X\n",
++		   MT_SWDEF_PLE1_STATUS);
++	seq_printf(s, "::E  R , SER_PLE_ERR_AMSDU = 0x%08X\n",
++		   MT_SWDEF_PLE_AMSDU_STATUS);
++	seq_printf(s, "::E  R , SER_PSE_ERR       = 0x%08X\n",
++		   MT_SWDEF_PSE_STATUS);
++	seq_printf(s, "::E  R , SER_PSE_ERR_1     = 0x%08X\n",
++		   MT_SWDEF_PSE1_STATUS);
++	seq_printf(s, "::E  R , SER_LMAC_WISR6_B0 = 0x%08X\n",
++		   MT_SWDEF_LAMC_WISR6_BN0_STATUS);
++	seq_printf(s, "::E  R , SER_LMAC_WISR6_B1 = 0x%08X\n",
++		   MT_SWDEF_LAMC_WISR6_BN1_STATUS);
++	seq_printf(s, "::E  R , SER_LMAC_WISR7_B0 = 0x%08X\n",
++		   MT_SWDEF_LAMC_WISR7_BN0_STATUS);
++	seq_printf(s, "::E  R , SER_LMAC_WISR7_B1 = 0x%08X\n",
++		   MT_SWDEF_LAMC_WISR7_BN1_STATUS);
++
++	seq_printf(s, "\nWF RESET STATUS: WM %d, WA %d, WO %d\n",
++		   dev->ser.wf_reset_wm_count,
++		   dev->ser.wf_reset_wa_count,
++		   dev->ser.wf_reset_wo_count);
++
++	return ret;
++}
++
++
++int mt7915_fw_exception_chk(struct mt7915_dev *dev)
++{
++	u32 reg_val;
++
++	reg_val = mt76_rr(dev, MT_EXCEPTION_ADDR);
++
++	if (is_mt7915(&dev->mt76))
++		reg_val >>= 8;
++
++	return !!(reg_val & 0xff);
++}
++
++void mt7915_fw_heart_beat_chk(struct mt7915_dev *dev)
++{
++#define WM_TIMEOUT_COUNT_CHECK 5
++#define WM_HANG_COUNT_CHECK 9
++	u32 cnt, cidx, didx, queue;
++	u32 idx, i;
++	static struct {
++		u32 cidx;
++		u32 didx;
++	} dma_rec[5];
++
++	if (dev->ser.hw_full_reset)
++		return;
++
++	if (dev->ser.cmd_fail_cnt >= WM_TIMEOUT_COUNT_CHECK) {
++
++		cnt = mt76_rr(dev, WF_WFDMA_MEM_DMA_RX_RING_CTL + 4);
++		cidx = mt76_rr(dev, WF_WFDMA_MEM_DMA_RX_RING_CTL + 8);
++		didx = mt76_rr(dev, WF_WFDMA_MEM_DMA_RX_RING_CTL + 12);
++		queue = (didx > cidx) ?
++			(didx - cidx - 1) : (didx - cidx + cnt - 1);
++
++		idx = (dev->ser.cmd_fail_cnt - WM_TIMEOUT_COUNT_CHECK) % 5;
++		dma_rec[idx].cidx = cidx;
++		dma_rec[idx].cidx = didx;
++
++		if (((cnt - 1) == queue) &&
++		    (dev->ser.cmd_fail_cnt >= WM_HANG_COUNT_CHECK)) {
++
++			for (i = 0; i < 5; i++) {
++				if ((dma_rec[i].cidx != cidx) ||
++				    (dma_rec[i].didx != didx))
++					return;
++			}
++
++			mt7915_reset(dev, SER_TYPE_FULL_RESET);
++			dev->ser.cmd_fail_cnt = 0;
++		}
++	}
++}
++
+ static int
+ mt7915_radar_trigger(void *data, u64 val)
+ {
+@@ -914,6 +1078,8 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
+ 				    mt7915_twt_stats);
+ 	debugfs_create_file("ser_trigger", 0200, dir, dev, &fops_ser_trigger);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "ser_show", dir,
++				    mt7915_ser_stats_show);
+ 	if (!dev->dbdc_support || phy->band_idx) {
+ 		debugfs_create_u32("dfs_hw_pattern", 0400, dir,
+ 				   &dev->hw_pattern);
+diff --git a/mt7915/dma.c b/mt7915/dma.c
+index 49b4d8a..b6144b6 100644
+--- a/mt7915/dma.c
++++ b/mt7915/dma.c
+@@ -443,6 +443,55 @@ int mt7915_dma_init(struct mt7915_dev *dev)
+ 	return 0;
+ }
+ 
++
++int mt7915_dma_reset(struct mt7915_dev *dev, bool force)
++{
++	struct mt76_phy *mphy_ext = dev->mt76.phy2;
++	int i;
++
++	/* clean up hw queues */
++	for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++) {
++		mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
++		if (mphy_ext)
++			mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true);
++	}
++
++	for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++)
++		mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
++
++	for (i = 0; i < __MT_RXQ_MAX; i++)
++		mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
++
++	/* reset wfsys */
++	if (force)
++		mt7915_wfsys_reset(dev);
++
++	/* disable wfdma */
++	mt7915_dma_disable(dev, force);
++
++	/* reset hw queues */
++	for (i = 0; i < __MT_TXQ_MAX; i++) {
++		mt76_queue_reset(dev, dev->mphy.q_tx[i]);
++		if (mphy_ext)
++			mt76_queue_reset(dev, mphy_ext->q_tx[i]);
++	}
++
++	for (i = 0; i < __MT_MCUQ_MAX; i++)
++		mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
++
++	for (i = 0; i < __MT_RXQ_MAX; i++)
++		mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
++
++	mt76_tx_status_check(&dev->mt76, true);
++
++	mt7915_dma_enable(dev);
++
++	for (i = 0; i < __MT_RXQ_MAX; i++)
++		mt76_queue_rx_reset(dev, i);
++
++	return 0;
++}
++
+ void mt7915_dma_cleanup(struct mt7915_dev *dev)
+ {
+ 	mt7915_dma_disable(dev, true);
+diff --git a/mt7915/init.c b/mt7915/init.c
+index 6323744..6606e19 100644
+--- a/mt7915/init.c
++++ b/mt7915/init.c
+@@ -262,7 +262,7 @@ static void mt7915_led_set_brightness(struct led_classdev *led_cdev,
+ 		mt7915_led_set_config(led_cdev, 0xff, 0);
+ }
+ 
+-static void
++void
+ mt7915_init_txpower(struct mt7915_dev *dev,
+ 		    struct ieee80211_supported_band *sband)
+ {
+@@ -447,7 +447,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band)
+ 	mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
+ }
+ 
+-static void mt7915_mac_init(struct mt7915_dev *dev)
++void mt7915_mac_init(struct mt7915_dev *dev)
+ {
+ 	int i;
+ 	u32 rx_len = is_mt7915(&dev->mt76) ? 0x400 : 0x680;
+@@ -474,7 +474,7 @@ static void mt7915_mac_init(struct mt7915_dev *dev)
+ 	}
+ }
+ 
+-static int mt7915_txbf_init(struct mt7915_dev *dev)
++int mt7915_txbf_init(struct mt7915_dev *dev)
+ {
+ 	int ret;
+ 
+@@ -581,7 +581,7 @@ static void mt7915_init_work(struct work_struct *work)
+ 	dev->dbg.muru_onoff = OFDMA_DL | MUMIMO_UL | MUMIMO_DL;
+ }
+ 
+-static void mt7915_wfsys_reset(struct mt7915_dev *dev)
++void mt7915_wfsys_reset(struct mt7915_dev *dev)
+ {
+ #define MT_MCU_DUMMY_RANDOM	GENMASK(15, 0)
+ #define MT_MCU_DUMMY_DEFAULT	GENMASK(31, 16)
+@@ -1144,7 +1144,13 @@ int mt7915_register_device(struct mt7915_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt7915_init_debugfs(&dev->phy);
++	ret = mt7915_init_debugfs(&dev->phy);
++	if (ret)
++		return ret;
++
++	dev->ser.hw_init_done = true;
++
++	return 0;
+ }
+ 
+ void mt7915_unregister_device(struct mt7915_dev *dev)
+diff --git a/mt7915/mac.c b/mt7915/mac.c
+index 94579e4..0f4084b 100644
+--- a/mt7915/mac.c
++++ b/mt7915/mac.c
+@@ -3,6 +3,7 @@
+ 
+ #include <linux/etherdevice.h>
+ #include <linux/timekeeping.h>
++#include <linux/pci.h>
+ #include "mt7915.h"
+ #include "../dma.h"
+ #include "mac.h"
+@@ -1968,9 +1969,9 @@ mt7915_update_beacons(struct mt7915_dev *dev)
+ 		IEEE80211_IFACE_ITER_RESUME_ALL,
+ 		mt7915_update_vif_beacon, dev->mt76.phy2->hw);
+ }
+-
++#if 0
+ static void
+-mt7915_dma_reset(struct mt7915_dev *dev)
++mt7915_dma_reset(struct mt7915_dev *dev, bool force)
+ {
+ 	struct mt76_phy *mphy_ext = dev->mt76.phy2;
+ 	u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+@@ -2035,6 +2036,7 @@ mt7915_dma_reset(struct mt7915_dev *dev)
+ 				 MT_WFDMA1_GLO_CFG_OMIT_RX_INFO);
+ 	}
+ }
++#endif
+ 
+ void mt7915_tx_token_put(struct mt7915_dev *dev)
+ {
+@@ -2050,6 +2052,172 @@ void mt7915_tx_token_put(struct mt7915_dev *dev)
+ 	idr_destroy(&dev->mt76.token);
+ }
+ 
++static int
++mt7915_mac_reset(struct mt7915_dev *dev)
++{
++	struct mt7915_phy *phy2;
++	struct mt76_phy *ext_phy;
++	struct mt76_dev *mdev = &dev->mt76;
++	int i, ret;
++	u32 irq_mask;
++
++	ext_phy = dev->mt76.phy2;
++	phy2 = ext_phy ? ext_phy->priv : NULL;
++
++	/* irq disable */
++	mt76_wr(dev, MT_INT_MASK_CSR, 0x0);
++	mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
++	if (dev->hif2) {
++		mt76_wr(dev, MT_INT1_MASK_CSR, 0x0);
++		mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
++	}
++	if (dev_is_pci(mdev->dev)) {
++		mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
++		if (dev->hif2)
++			mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0x0);
++	}
++
++	set_bit(MT76_RESET, &dev->mphy.state);
++	wake_up(&dev->mt76.mcu.wait);
++	if (ext_phy)
++		set_bit(MT76_RESET, &ext_phy->state);
++
++	/* lock/unlock all queues to ensure that no tx is pending */
++	mt76_txq_schedule_all(&dev->mphy);
++	if (ext_phy)
++		mt76_txq_schedule_all(ext_phy);
++
++	/* disable all tx/rx napi */
++	mt76_worker_disable(&dev->mt76.tx_worker);
++	mt76_for_each_q_rx(mdev, i) {
++		if (mdev->q_rx[i].ndesc)
++			napi_disable(&dev->mt76.napi[i]);
++	}
++	napi_disable(&dev->mt76.tx_napi);
++
++	/* token reinit */
++	mt7915_tx_token_put(dev);
++	idr_init(&dev->mt76.token);
++
++	mt7915_dma_reset(dev, true);
++	mt76_for_each_q_rx(mdev, i) {
++		if (mdev->q_rx[i].ndesc) {
++			napi_enable(&dev->mt76.napi[i]);
++			napi_schedule(&dev->mt76.napi[i]);
++		}
++	}
++	clear_bit(MT76_MCU_RESET, &dev->mphy.state);
++	clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
++
++	mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask);
++	mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
++	if (dev->hif2) {
++		mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask);
++		mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
++	}
++	if (dev_is_pci(mdev->dev)) {
++		mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
++		if (dev->hif2)
++			mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff);
++	}
++
++	/* load firmware */
++	ret = mt7915_run_firmware(dev);
++	if (ret)
++		goto out;
++
++	/* set the necessary init items */
++	ret = mt7915_mcu_set_eeprom(dev, dev->flash_mode);
++	if (ret)
++		goto out;
++
++	mt7915_mac_init(dev);
++	mt7915_init_txpower(dev, &dev->mphy.sband_2g.sband);
++	mt7915_init_txpower(dev, &dev->mphy.sband_5g.sband);
++	ret = mt7915_txbf_init(dev);
++
++out:
++	/* reset done */
++	clear_bit(MT76_RESET, &dev->mphy.state);
++	if (phy2)
++		clear_bit(MT76_RESET, &phy2->mt76->state);
++
++	napi_enable(&dev->mt76.tx_napi);
++	napi_schedule(&dev->mt76.tx_napi);
++	mt76_worker_enable(&dev->mt76.tx_worker);
++
++	return ret;
++}
++
++static void
++mt7915_mac_full_reset(struct mt7915_dev *dev)
++{
++	struct mt7915_phy *phy2;
++	struct mt76_phy *ext_phy;
++	int i;
++	struct cfg80211_scan_info info = {
++		.aborted = true,
++	};
++
++	ext_phy = dev->mt76.phy2;
++	phy2 = ext_phy ? ext_phy->priv : NULL;
++
++	dev->ser.hw_full_reset = true;
++	if (READ_ONCE(dev->reset_state) & MT_MCU_CMD_WA_WDT)
++		dev->ser.wf_reset_wa_count++;
++	else
++		dev->ser.wf_reset_wm_count++;
++
++	wake_up(&dev->mt76.mcu.wait);
++	ieee80211_stop_queues(mt76_hw(dev));
++	if (ext_phy)
++		ieee80211_stop_queues(ext_phy->hw);
++
++	cancel_delayed_work_sync(&dev->mphy.mac_work);
++	if (ext_phy)
++		cancel_delayed_work_sync(&ext_phy->mac_work);
++
++	mutex_lock(&dev->mt76.mutex);
++	for (i = 0; i < 10; i++) {
++		if (!mt7915_mac_reset(dev))
++			break;
++	}
++	mutex_unlock(&dev->mt76.mutex);
++
++	if (i == 10)
++		dev_err(dev->mt76.dev, "chip full reset failed\n");
++
++	if (test_and_clear_bit(MT76_HW_SCANNING, &dev->mphy.state))
++		ieee80211_scan_completed(dev->mphy.hw, &info);
++
++	if (test_and_clear_bit(MT76_HW_SCANNING, &ext_phy->state))
++		ieee80211_scan_completed(ext_phy->hw, &info);
++
++	ieee80211_wake_queues(mt76_hw(dev));
++	if (ext_phy)
++		ieee80211_wake_queues(ext_phy->hw);
++
++	if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state))
++		__mt7915_start(dev->mphy.hw);
++
++	if (ext_phy &&
++	    test_bit(MT76_STATE_RUNNING, &ext_phy->state))
++		__mt7915_start(ext_phy->hw);
++
++	dev->ser.hw_full_reset = false;
++
++	ieee80211_restart_hw(mt76_hw(dev));
++	if (ext_phy)
++		ieee80211_restart_hw(ext_phy->hw);
++
++	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
++				     MT7915_WATCHDOG_TIME);
++	if (ext_phy)
++		ieee80211_queue_delayed_work(ext_phy->hw,
++					     &ext_phy->mac_work,
++					     MT7915_WATCHDOG_TIME);
++}
++
+ /* system error recovery */
+ void mt7915_mac_reset_work(struct work_struct *work)
+ {
+@@ -2061,6 +2229,25 @@ void mt7915_mac_reset_work(struct work_struct *work)
+ 	ext_phy = dev->mt76.phy2;
+ 	phy2 = ext_phy ? ext_phy->priv : NULL;
+ 
++	/* chip full reset */
++	if (dev->ser.reset_type == SER_TYPE_FULL_RESET) {
++		u32 val;
++
++		/* disable wa/wm watchdog interuppt */
++		val = mt76_rr(dev, MT_WFDMA0_MCU_HOST_INT_ENA);
++		val &= ~(MT_MCU_CMD_WA_WDT | MT_MCU_CMD_WM_WDT);
++		mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, val);
++
++		mt7915_mac_full_reset(dev);
++
++		/* enable the mcu irq*/
++		mt7915_irq_enable(dev, MT_INT_MCU_CMD);
++		mt7915_irq_disable(dev, 0);
++		return;
++	}
++
++	/* chip partial reset */
++	dev->ser.reset_type = SER_TYPE_NONE;
+ 	if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA))
+ 		return;
+ 
+@@ -2087,7 +2274,7 @@ void mt7915_mac_reset_work(struct work_struct *work)
+ 	mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
+ 
+ 	if (mt7915_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
+-		mt7915_dma_reset(dev);
++		mt7915_dma_reset(dev, false);
+ 
+ 		mt7915_tx_token_put(dev);
+ 		idr_init(&dev->mt76.token);
+@@ -2138,6 +2325,44 @@ void mt7915_mac_reset_work(struct work_struct *work)
+ 					     MT7915_WATCHDOG_TIME);
+ }
+ 
++
++void mt7915_reset(struct mt7915_dev *dev, u8 type)
++{
++	struct mt76_phy *ext_phy = dev->mt76.phy2;
++
++	if (!dev->ser.hw_init_done)
++		return;
++
++	if (dev->ser.hw_full_reset)
++		return;
++
++	dev_err(dev->mt76.dev, "SER: reset_state(0x%08X) type(0x%08X)\n",
++		READ_ONCE(dev->reset_state), type);
++
++	/* WM/WA exception: do full recovery */
++	if ((READ_ONCE(dev->reset_state) & MT_MCU_CMD_WDT_MASK) ||
++	    type == SER_TYPE_FULL_RESET) {
++
++		if (!is_mt7986(&dev->mt76))
++			return;
++
++		dev->ser.reset_type = SER_TYPE_FULL_RESET;
++		set_bit(MT76_MCU_RESET, &dev->mphy.state);
++		if (ext_phy)
++			set_bit(MT76_MCU_RESET, &ext_phy->state);
++
++		ieee80211_queue_work(mt76_hw(dev), &dev->reset_work);
++		return;
++	}
++
++	/* do partial recovery */
++	mt7915_irq_enable(dev, MT_INT_MCU_CMD);
++	mt7915_irq_disable(dev, 0);
++	dev->ser.reset_type = SER_TYPE_PARTIAL_RESET;
++	ieee80211_queue_work(mt76_hw(dev), &dev->reset_work);
++	wake_up(&dev->reset_wait);
++}
++
+ void mt7915_mac_update_stats(struct mt7915_phy *phy)
+ {
+ 	struct mt7915_dev *dev = phy->dev;
+diff --git a/mt7915/main.c b/mt7915/main.c
+index 1a384f1..7f148a3 100644
+--- a/mt7915/main.c
++++ b/mt7915/main.c
+@@ -20,7 +20,7 @@ static bool mt7915_dev_running(struct mt7915_dev *dev)
+ 	return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ }
+ 
+-static int mt7915_start(struct ieee80211_hw *hw)
++int __mt7915_start(struct ieee80211_hw *hw)
+ {
+ 	struct mt7915_dev *dev = mt7915_hw_dev(hw);
+ 	struct mt7915_phy *phy = mt7915_hw_phy(hw);
+@@ -29,8 +29,6 @@ static int mt7915_start(struct ieee80211_hw *hw)
+ 
+ 	flush_work(&dev->init_work);
+ 
+-	mutex_lock(&dev->mt76.mutex);
+-
+ 	running = mt7915_dev_running(dev);
+ 
+ 	if (!running) {
+@@ -95,6 +93,18 @@ out:
+ 	return ret;
+ }
+ 
++static int mt7915_start(struct ieee80211_hw *hw)
++{
++	struct mt7915_dev *dev = mt7915_hw_dev(hw);
++	int ret;
++
++	mutex_lock(&dev->mt76.mutex);
++	ret = __mt7915_start(hw);
++	mutex_unlock(&dev->mt76.mutex);
++
++	return ret;
++}
++
+ static void mt7915_stop(struct ieee80211_hw *hw)
+ {
+ 	struct mt7915_dev *dev = mt7915_hw_dev(hw);
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index 9033125..e0fcc8f 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -212,14 +212,31 @@ mt7915_mcu_parse_response(struct mt76_dev *mdev, int cmd,
+ 			  struct sk_buff *skb, int seq)
+ {
+ 	struct mt7915_mcu_rxd *rxd;
++	struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
+ 	int ret = 0;
+ 
+ 	if (!skb) {
+ 		dev_err(mdev->dev, "Message %08x (seq %d) timeout\n",
+ 			cmd, seq);
++
++		dev->ser.cmd_fail_cnt++;
++
++		if (dev->ser.cmd_fail_cnt < 5) {
++			int exp_type = mt7915_fw_exception_chk(dev);
++
++			dev_err(mdev->dev, "Fw is status(%d)\n", exp_type);
++			if (exp_type) {
++				dev->ser.reset_type = SER_TYPE_FULL_RESET;
++				mt7915_reset(dev, SER_TYPE_FULL_RESET);
++			}
++		}
++		mt7915_fw_heart_beat_chk(dev);
++
+ 		return -ETIMEDOUT;
+ 	}
+ 
++	dev->ser.cmd_fail_cnt = 0;
++
+ 	rxd = (struct mt7915_mcu_rxd *)skb->data;
+ 	if (seq != rxd->seq)
+ 		return -EAGAIN;
+@@ -243,11 +260,17 @@ mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ {
+ 	struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
+ 	struct mt7915_mcu_txd *mcu_txd;
++	//struct mt76_phy *ext_phy = mdev->phy2;
+ 	enum mt76_mcuq_id qid;
+ 	__le32 *txd;
+ 	u32 val;
+ 	u8 seq;
+ 
++	if (test_bit(MT76_MCU_RESET, &mdev->phy.state)) {
++		dev_err(mdev->dev, "%s assert\n", __func__);
++		return -EBUSY;
++	}
++
+ 	/* TODO: make dynamic based on msg type */
+ 	mdev->mcu.timeout = 20 * HZ;
+ 
+@@ -2420,25 +2443,14 @@ mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev)
+ 				 sizeof(req), true);
+ }
+ 
+-int mt7915_mcu_init(struct mt7915_dev *dev)
++int mt7915_run_firmware(struct mt7915_dev *dev)
+ {
+-	static const struct mt76_mcu_ops mt7915_mcu_ops = {
+-		.headroom = sizeof(struct mt7915_mcu_txd),
+-		.mcu_skb_send_msg = mt7915_mcu_send_message,
+-		.mcu_parse_response = mt7915_mcu_parse_response,
+-		.mcu_restart = mt76_connac_mcu_restart,
+-	};
+ 	int ret;
+ 
+-	dev->mt76.mcu_ops = &mt7915_mcu_ops;
+-
+ 	/* force firmware operation mode into normal state,
+ 	 * which should be set before firmware download stage.
+ 	 */
+-	if (is_mt7915(&dev->mt76))
+-		mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
+-	else
+-		mt76_wr(dev, MT_SWDEF_MODE_MT7916, MT_SWDEF_NORMAL_MODE);
++	mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
+ 
+ 	ret = mt7915_driver_own(dev, 0);
+ 	if (ret)
+@@ -2480,6 +2492,20 @@ int mt7915_mcu_init(struct mt7915_dev *dev)
+ 				 MCU_WA_PARAM_RED, 0, 0);
+ }
+ 
++int mt7915_mcu_init(struct mt7915_dev *dev)
++{
++	static const struct mt76_mcu_ops mt7915_mcu_ops = {
++		.headroom = sizeof(struct mt7915_mcu_txd),
++		.mcu_skb_send_msg = mt7915_mcu_send_message,
++		.mcu_parse_response = mt7915_mcu_parse_response,
++		.mcu_restart = mt76_connac_mcu_restart,
++	};
++
++	dev->mt76.mcu_ops = &mt7915_mcu_ops;
++
++	return mt7915_run_firmware(dev);
++}
++
+ void mt7915_mcu_exit(struct mt7915_dev *dev)
+ {
+ 	__mt76_mcu_restart(&dev->mt76);
+diff --git a/mt7915/mmio.c b/mt7915/mmio.c
+index 2466907..561ac65 100644
+--- a/mt7915/mmio.c
++++ b/mt7915/mmio.c
+@@ -22,6 +22,8 @@ static const u32 mt7915_reg[] = {
+ 	[WFDMA_EXT_CSR_ADDR]	= 0xd7000,
+ 	[CBTOP1_PHY_END]	= 0x77ffffff,
+ 	[INFRA_MCU_ADDR_END]	= 0x7c3fffff,
++	[EXCEPTION_BASE_ADDR]	= 0x219848,
++	[SWDEF_BASE_ADDR]	= 0x41f200,
+ };
+ 
+ static const u32 mt7916_reg[] = {
+@@ -36,6 +38,8 @@ static const u32 mt7916_reg[] = {
+ 	[WFDMA_EXT_CSR_ADDR]	= 0xd7000,
+ 	[CBTOP1_PHY_END]	= 0x7fffffff,
+ 	[INFRA_MCU_ADDR_END]	= 0x7c085fff,
++	[EXCEPTION_BASE_ADDR]	= 0x022050BC,
++	[SWDEF_BASE_ADDR]	= 0x411400,
+ };
+ 
+ static const u32 mt7986_reg[] = {
+@@ -50,6 +54,8 @@ static const u32 mt7986_reg[] = {
+ 	[WFDMA_EXT_CSR_ADDR]	= 0x27000,
+ 	[CBTOP1_PHY_END]	= 0x7fffffff,
+ 	[INFRA_MCU_ADDR_END]	= 0x7c085fff,
++	[EXCEPTION_BASE_ADDR]	= 0x02204FFC,
++	[SWDEF_BASE_ADDR]	= 0x411400,
+ };
+ 
+ static const u32 mt7915_offs[] = {
+@@ -601,10 +607,10 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t)
+ 		u32 val = mt76_rr(dev, MT_MCU_CMD);
+ 
+ 		mt76_wr(dev, MT_MCU_CMD, val);
+-		if (val & MT_MCU_CMD_ERROR_MASK) {
++		mt7915_irq_disable(dev, MT_INT_MCU_CMD);
++		if (val & MT_MCU_CMD_ALL_ERROR_MASK) {
+ 			dev->reset_state = val;
+-			ieee80211_queue_work(mt76_hw(dev), &dev->reset_work);
+-			wake_up(&dev->reset_wait);
++			mt7915_reset(dev, SER_TYPE_UNKNOWN);
+ 		}
+ 	}
+ }
+diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
+index 436ae35..219cec8 100644
+--- a/mt7915/mt7915.h
++++ b/mt7915/mt7915.h
+@@ -342,6 +342,15 @@ struct mt7915_dev {
+ 	struct work_struct reset_work;
+ 	wait_queue_head_t reset_wait;
+ 	u32 reset_state;
++	struct {
++		bool hw_full_reset:1;
++		bool hw_init_done:1;
++		u32 reset_type;
++		u32 cmd_fail_cnt;
++		u32 wf_reset_wm_count;
++		u32 wf_reset_wa_count;
++		u32 wf_reset_wo_count;
++	}ser;
+ 
+ 	struct list_head sta_rc_list;
+ 	struct list_head sta_poll_list;
+@@ -433,6 +442,13 @@ enum mt7915_rdd_cmd {
+ 	RDD_IRQ_OFF,
+ };
+ 
++enum {
++	SER_TYPE_NONE,
++	SER_TYPE_PARTIAL_RESET,
++	SER_TYPE_FULL_RESET,
++	SER_TYPE_UNKNOWN,
++};
++
+ static inline struct mt7915_phy *
+ mt7915_hw_phy(struct ieee80211_hw *hw)
+ {
+@@ -650,6 +666,17 @@ int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms);
+ int mt7915_init_debugfs(struct mt7915_phy *phy);
+ void mt7915_debugfs_rx_fw_monitor(struct mt7915_dev *dev, const void *data, int len);
+ bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len);
++int mt7915_fw_exception_chk(struct mt7915_dev *dev);
++void mt7915_fw_heart_beat_chk(struct mt7915_dev *dev);
++void mt7915_wfsys_reset(struct mt7915_dev *dev);
++void mt7915_reset(struct mt7915_dev *dev, u8 type);
++int mt7915_dma_reset(struct mt7915_dev *dev, bool force);
++int __mt7915_start(struct ieee80211_hw *hw);
++void mt7915_init_txpower(struct mt7915_dev *dev,
++		    struct ieee80211_supported_band *sband);
++int mt7915_txbf_init(struct mt7915_dev *dev);
++void mt7915_mac_init(struct mt7915_dev *dev);
++int mt7915_run_firmware(struct mt7915_dev *dev);
+ #ifdef CONFIG_MAC80211_DEBUGFS
+ void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    struct ieee80211_sta *sta, struct dentry *dir);
+diff --git a/mt7915/regs.h b/mt7915/regs.h
+index 2f3d170..e283702 100644
+--- a/mt7915/regs.h
++++ b/mt7915/regs.h
+@@ -30,6 +30,8 @@ enum reg_rev {
+ 	WFDMA_EXT_CSR_ADDR,
+ 	CBTOP1_PHY_END,
+ 	INFRA_MCU_ADDR_END,
++	EXCEPTION_BASE_ADDR,
++	SWDEF_BASE_ADDR,
+ 	__MT_REG_MAX,
+ };
+ 
+@@ -111,6 +113,11 @@ enum offs_rev {
+ #define __REG(id)			(dev->reg.reg_rev[(id)])
+ #define __OFFS(id)			(dev->reg.offs_rev[(id)])
+ 
++/* MEM WFDMA */
++#define WF_WFDMA_MEM_DMA		0x58000000
++
++#define WF_WFDMA_MEM_DMA_RX_RING_CTL	(WF_WFDMA_MEM_DMA + (0x510))
++
+ /* MCU WFDMA0 */
+ #define MT_MCU_WFDMA0_BASE		0x2000
+ #define MT_MCU_WFDMA0(ofs)		(MT_MCU_WFDMA0_BASE + (ofs))
+@@ -552,6 +559,10 @@ enum offs_rev {
+ #define MT_WFDMA0_PRI_DLY_INT_CFG1	MT_WFDMA0(0x2f4)
+ #define MT_WFDMA0_PRI_DLY_INT_CFG2	MT_WFDMA0(0x2f8)
+ 
++#define MT_WFDMA0_MCU_HOST_INT_ENA	MT_WFDMA0(0x1f4)
++#define MT_WFDMA0_MT_WA_WDT_INT		BIT(31)
++#define MT_WFDMA0_MT_WM_WDT_INT		BIT(30)
++
+ /* WFDMA1 */
+ #define MT_WFDMA1_BASE			0xd5000
+ #define MT_WFDMA1(ofs)			(MT_WFDMA1_BASE + (ofs))
+@@ -684,6 +695,12 @@ enum offs_rev {
+ #define MT_MCU_CMD_NORMAL_STATE		BIT(5)
+ #define MT_MCU_CMD_ERROR_MASK		GENMASK(5, 1)
+ 
++#define MT_MCU_CMD_WA_WDT		BIT(31)
++#define MT_MCU_CMD_WM_WDT		BIT(30)
++#define MT_MCU_CMD_WDT_MASK		GENMASK(31, 30)
++#define MT_MCU_CMD_ALL_ERROR_MASK	(MT_MCU_CMD_ERROR_MASK |	\
++					 MT_MCU_CMD_WDT_MASK)
++
+ /* TOP RGU */
+ #define MT_TOP_RGU_BASE			0x18000000
+ #define MT_TOP_PWR_CTRL			(MT_TOP_RGU_BASE + (0x0))
+@@ -924,12 +941,25 @@ enum offs_rev {
+ #define MT_ADIE_TYPE_MASK		BIT(1)
+ 
+ /* FW MODE SYNC */
+-#define MT_SWDEF_MODE			0x41f23c
+-#define MT_SWDEF_MODE_MT7916		0x41143c
++#define MT_SWDEF_BASE			__REG(SWDEF_BASE_ADDR)
++
++#define MT_SWDEF(ofs)			(MT_SWDEF_BASE + (ofs))
++#define MT_SWDEF_MODE			MT_SWDEF(0x3c)
+ #define MT_SWDEF_NORMAL_MODE		0
+ #define MT_SWDEF_ICAP_MODE		1
+ #define MT_SWDEF_SPECTRUM_MODE		2
+ 
++#define MT_SWDEF_SER_STATUS		MT_SWDEF(0x040)
++#define MT_SWDEF_PLE_STATUS		MT_SWDEF(0x044)
++#define MT_SWDEF_PLE1_STATUS		MT_SWDEF(0x048)
++#define MT_SWDEF_PLE_AMSDU_STATUS	MT_SWDEF(0x04C)
++#define MT_SWDEF_PSE_STATUS		MT_SWDEF(0x050)
++#define MT_SWDEF_PSE1_STATUS		MT_SWDEF(0x054)
++#define MT_SWDEF_LAMC_WISR6_BN0_STATUS	MT_SWDEF(0x058)
++#define MT_SWDEF_LAMC_WISR6_BN1_STATUS	MT_SWDEF(0x05C)
++#define MT_SWDEF_LAMC_WISR7_BN0_STATUS	MT_SWDEF(0x060)
++#define MT_SWDEF_LAMC_WISR7_BN1_STATUS	MT_SWDEF(0x064)
++
+ #define MT_DIC_CMD_REG_BASE		0x41f000
+ #define MT_DIC_CMD_REG(ofs)		(MT_DIC_CMD_REG_BASE + (ofs))
+ #define MT_DIC_CMD_REG_CMD		MT_DIC_CMD_REG(0x10)
+@@ -1011,6 +1041,9 @@ enum offs_rev {
+ 
+ #define MT_MCU_BUS_REMAP		MT_MCU_BUS(0x120)
+ 
++/* MISC */
++#define MT_EXCEPTION_ADDR		__REG(EXCEPTION_BASE_ADDR)
++
+ /* TOP CFG */
+ #define MT_TOP_CFG_BASE			0x184b0000
+ #define MT_TOP_CFG(ofs)			(MT_TOP_CFG_BASE + (ofs))
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/patches.inc b/recipes-kernel/linux-mt76/files/patches/patches.inc
new file mode 100644
index 0000000..a0fc20b
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/patches.inc
@@ -0,0 +1,24 @@
+#patch patches (come from openwrt/lede/target/linux/mediatek)
+SRC_URI_append = " \
+    file://0001-mt76-mt7915-rework-testmode-init-registers.patch \
+    file://0002-mt76-testmode-rework-tx-antenna-setting.patch \
+    file://0003-mt76-mt7915-rework-rx-testmode-stats.patch \
+    file://0004-mt76-mt7915-fix-tx-descriptor.patch \
+    file://0005-mt76-mt7915-fix-MBSS-index-condition-in-DBDC-mode.patch \
+    file://0006-mt76-mt7915-support-VHT-MCS10-11.patch \
+    file://0007-mt76-mt7915-update-mt7986-CR-for-different-adie-vers.patch \
+    file://0008-mt76-mt7915-disable-mt7986-rx-hdr-trans-short.patch \
+    file://1100-mt76-testmode-support-eeprom-handle.patch \
+    file://1101-mt76-enable-more-5g-channels.patch \
+    file://1102-mt76-testmode-add-attributes-for-setting-rf-config.patch \
+    file://1103-mt76-mt7915-implement-config-set-in-testmode.patch \
+    file://1104-mt76-testmode-add-attributes-to-support-off-channel-.patch \
+    file://1105-mt76-mt7915-add-off-channel-scan-support-in-testmode.patch \
+    file://1106-mt76-testmode-add-virtual-stations-support.patch \
+    file://1107-mt76-testmode-support-to-dump-stats-from-different-v.patch \
+    file://1108-mt76-testmode-rework-the-flow-of-init-tx-skb.patch \
+    file://1109-mt76-testmode-add-support-to-queue-skb-of-multiple-s.patch \
+    file://1110-mt76-mt7915-implement-aid-support-in-testmode.patch \
+    file://1111-mt76-tool-add-more-commands.patch \
+    file://2001-mt76-mt7915-add-L0.5-SER-for-mt7986.patch \
+    "