[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/100-Revert-of-net-pass-the-dst-buffer-to-of_get_mac_addr.patch b/recipes-kernel/linux-mt76/files/100-Revert-of-net-pass-the-dst-buffer-to-of_get_mac_addr.patch
new file mode 100644
index 0000000..24b1240
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/100-Revert-of-net-pass-the-dst-buffer-to-of_get_mac_addr.patch
@@ -0,0 +1,26 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 23 Nov 2021 17:01:45 +0100
+Subject: [PATCH] Revert "of: net: pass the dst buffer to of_get_mac_address()"
+
+This reverts commit 4932c5d80153c336c77dbe8d7af9f8fdd879d01f.
+---
+
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -105,9 +105,15 @@ mt76_eeprom_override(struct mt76_phy *ph
+ {
+ struct mt76_dev *dev = phy->dev;
+
++#ifdef CONFIG_OF
+ struct device_node *np = dev->dev->of_node;
++ const u8 *mac = NULL;
+
+- of_get_mac_address(np, phy->macaddr);
++ if (np)
++ mac = of_get_mac_address(np);
++ if (!IS_ERR_OR_NULL(mac))
++ ether_addr_copy(phy->macaddr, mac);
++#endif
+
+ if (!is_valid_ether_addr(phy->macaddr)) {
+ eth_random_addr(phy->macaddr);
diff --git a/recipes-kernel/linux-mt76/files/COPYING b/recipes-kernel/linux-mt76/files/COPYING
new file mode 100644
index 0000000..7975bdb
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/COPYING
@@ -0,0 +1 @@
+Copyright (c) Mediatek 2020
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 \
+ "