[rdkb][common][bsp][Refactor and sync wifi from openwrt]

[Description]
a0c7684 [MAC80211][mt76][update mt76 patches]
a2e0f7c [MAC80211][core][Remove start disabled patch in eagle]
4562ef0 [MAC80211][hostapd][Refactor hostapd patch for git am]
22614f8 [mt76][Add vendor cmd to get available color bitmap]
b7b4fef [mac80211][Track obss color bitmap]
3f4ab41 [hostapd][Add hostapd_cli cmd to get available color bitmap]
cb120e7 [MAC80211][core][remove ba timer disabled patches]
24f983f [MAC80211][misc][sync iproute2 package]
3e866ee [MAC80211][core][Mark DFS channel available for CSA]
bdc6428 [MAC80211][hostapd][Mark DFS channel available for CSA]
9113e92 [MAC80211][app][Add eagle testmode iwpriv wrapper support]
d6bd8f4 [mac80211][mt76][Refactor mt76 patches]
7829a83 [MAC80211][mt76][Add monitor vif check in testmode]
19ead62 [mt76][eagle][hostapd mbssid and ema support]

[Release-log]

Change-Id: I75bf6ff01bc50054404bca23fd31cff9d1bc8d86
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0000-sync-to-master.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0000-sync-to-master.patch
deleted file mode 100644
index 44a01ec..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0000-sync-to-master.patch
+++ /dev/null
@@ -1,249 +0,0 @@
-diff --git a/eeprom.c b/eeprom.c
-index 0a88048b..ea54b7af 100644
---- a/eeprom.c
-+++ b/eeprom.c
-@@ -138,6 +138,7 @@ mt76_find_power_limits_node(struct mt76_dev *dev)
- {
- 	struct device_node *np = dev->dev->of_node;
- 	const char *const region_names[] = {
-+		[NL80211_DFS_UNSET] = "ww",
- 		[NL80211_DFS_ETSI] = "etsi",
- 		[NL80211_DFS_FCC] = "fcc",
- 		[NL80211_DFS_JP] = "jp",
-diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
-index 614df85e..aed4ee95 100644
---- a/mt76_connac_mac.c
-+++ b/mt76_connac_mac.c
-@@ -823,7 +823,6 @@ void mt76_connac2_mac_decode_he_radiotap(struct mt76_dev *dev,
- 			 HE_BITS(DATA2_TXOP_KNOWN),
- 	};
- 	u32 ltf_size = le32_get_bits(rxv[2], MT_CRXV_HE_LTF_SIZE) + 1;
--	u32 txbf_mask = is_mt7996(dev) ? BIT(11) : BIT(10);
- 	struct ieee80211_radiotap_he *he;
- 
- 	status->flag |= RX_FLAG_RADIOTAP_HE;
-@@ -837,7 +836,7 @@ void mt76_connac2_mac_decode_he_radiotap(struct mt76_dev *dev,
- 	he->data5 = HE_PREP(DATA5_PE_DISAMBIG, PE_DISAMBIG, rxv[2]) |
- 		    le16_encode_bits(ltf_size,
- 				     IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE);
--	if (le32_to_cpu(rxv[0]) & txbf_mask)
-+	if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF)
- 		he->data5 |= HE_BITS(DATA5_TXBF);
- 	he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[14]) |
- 		    HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[14]);
-diff --git a/mt76x0/usb_mcu.c b/mt76x0/usb_mcu.c
-index 45502fd4..6dc1f51f 100644
---- a/mt76x0/usb_mcu.c
-+++ b/mt76x0/usb_mcu.c
-@@ -148,6 +148,7 @@ static int mt76x0u_load_firmware(struct mt76x02_dev *dev)
- 	mt76_wr(dev, MT_USB_DMA_CFG, val);
- 
- 	ret = mt76x0u_upload_firmware(dev, hdr);
-+	mt76x02_set_ethtool_fwver(dev, hdr);
- 	release_firmware(fw);
- 
- 	mt76_wr(dev, MT_FCE_PSE_CTRL, 1);
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 674cea1a..c9a9f0e3 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -12,6 +12,10 @@
- 
- #define to_rssi(field, rcpi)	((FIELD_GET(field, rcpi) - 220) / 2)
- 
-+#define HE_BITS(f)		cpu_to_le16(IEEE80211_RADIOTAP_HE_##f)
-+#define HE_PREP(f, m, v)	le16_encode_bits(le32_get_bits(v, MT_CRXV_HE_##m),\
-+						 IEEE80211_RADIOTAP_HE_##f)
-+
- static const struct mt7996_dfs_radar_spec etsi_radar_specs = {
- 	.pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
- 	.radar_pattern = {
-@@ -251,6 +255,178 @@ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
- 		mt76_clear(dev, addr, BIT(5));
- }
- 
-+static void
-+mt7996_mac_decode_he_radiotap_ru(struct mt76_rx_status *status,
-+				 struct ieee80211_radiotap_he *he,
-+				 __le32 *rxv)
-+{
-+	u32 ru_h, ru_l;
-+	u8 ru, offs = 0;
-+
-+	ru_l = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC_L);
-+	ru_h = le32_get_bits(rxv[1], MT_PRXV_HE_RU_ALLOC_H);
-+	ru = (u8)(ru_l | ru_h << 4);
-+
-+	status->bw = RATE_INFO_BW_HE_RU;
-+
-+	switch (ru) {
-+	case 0 ... 36:
-+		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26;
-+		offs = ru;
-+		break;
-+	case 37 ... 52:
-+		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52;
-+		offs = ru - 37;
-+		break;
-+	case 53 ... 60:
-+		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106;
-+		offs = ru - 53;
-+		break;
-+	case 61 ... 64:
-+		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242;
-+		offs = ru - 61;
-+		break;
-+	case 65 ... 66:
-+		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484;
-+		offs = ru - 65;
-+		break;
-+	case 67:
-+		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996;
-+		break;
-+	case 68:
-+		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996;
-+		break;
-+	}
-+
-+	he->data1 |= HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
-+	he->data2 |= HE_BITS(DATA2_RU_OFFSET_KNOWN) |
-+		     le16_encode_bits(offs,
-+				      IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET);
-+}
-+
-+static void
-+mt7996_mac_decode_he_mu_radiotap(struct sk_buff *skb, __le32 *rxv)
-+{
-+	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
-+	static const struct ieee80211_radiotap_he_mu mu_known = {
-+		.flags1 = HE_BITS(MU_FLAGS1_SIG_B_MCS_KNOWN) |
-+			  HE_BITS(MU_FLAGS1_SIG_B_DCM_KNOWN) |
-+			  HE_BITS(MU_FLAGS1_CH1_RU_KNOWN) |
-+			  HE_BITS(MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN),
-+		.flags2 = HE_BITS(MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN),
-+	};
-+	struct ieee80211_radiotap_he_mu *he_mu = NULL;
-+
-+	status->flag |= RX_FLAG_RADIOTAP_HE_MU;
-+
-+	he_mu = skb_push(skb, sizeof(mu_known));
-+	memcpy(he_mu, &mu_known, sizeof(mu_known));
-+
-+#define MU_PREP(f, v)	le16_encode_bits(v, IEEE80211_RADIOTAP_HE_MU_##f)
-+
-+	he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_MCS, status->rate_idx);
-+	if (status->he_dcm)
-+		he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_DCM, status->he_dcm);
-+
-+	he_mu->flags2 |= MU_PREP(FLAGS2_BW_FROM_SIG_A_BW, status->bw) |
-+			 MU_PREP(FLAGS2_SIG_B_SYMS_USERS,
-+				 le32_get_bits(rxv[2], MT_CRXV_HE_NUM_USER));
-+
-+	he_mu->ru_ch1[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU0);
-+
-+	if (status->bw >= RATE_INFO_BW_40) {
-+		he_mu->flags1 |= HE_BITS(MU_FLAGS1_CH2_RU_KNOWN);
-+		he_mu->ru_ch2[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU1);
-+	}
-+
-+	if (status->bw >= RATE_INFO_BW_80) {
-+		he_mu->ru_ch1[1] = le32_get_bits(rxv[3], MT_CRXV_HE_RU2);
-+		he_mu->ru_ch2[1] = le32_get_bits(rxv[3], MT_CRXV_HE_RU3);
-+	}
-+}
-+
-+static void
-+mt7996_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u8 mode)
-+{
-+	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
-+	static const struct ieee80211_radiotap_he known = {
-+		.data1 = HE_BITS(DATA1_DATA_MCS_KNOWN) |
-+			 HE_BITS(DATA1_DATA_DCM_KNOWN) |
-+			 HE_BITS(DATA1_STBC_KNOWN) |
-+			 HE_BITS(DATA1_CODING_KNOWN) |
-+			 HE_BITS(DATA1_LDPC_XSYMSEG_KNOWN) |
-+			 HE_BITS(DATA1_DOPPLER_KNOWN) |
-+			 HE_BITS(DATA1_SPTL_REUSE_KNOWN) |
-+			 HE_BITS(DATA1_BSS_COLOR_KNOWN),
-+		.data2 = HE_BITS(DATA2_GI_KNOWN) |
-+			 HE_BITS(DATA2_TXBF_KNOWN) |
-+			 HE_BITS(DATA2_PE_DISAMBIG_KNOWN) |
-+			 HE_BITS(DATA2_TXOP_KNOWN),
-+	};
-+	struct ieee80211_radiotap_he *he = NULL;
-+	u32 ltf_size = le32_get_bits(rxv[2], MT_CRXV_HE_LTF_SIZE) + 1;
-+
-+	status->flag |= RX_FLAG_RADIOTAP_HE;
-+
-+	he = skb_push(skb, sizeof(known));
-+	memcpy(he, &known, sizeof(known));
-+
-+	he->data3 = HE_PREP(DATA3_BSS_COLOR, BSS_COLOR, rxv[14]) |
-+		    HE_PREP(DATA3_LDPC_XSYMSEG, LDPC_EXT_SYM, rxv[2]);
-+	he->data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, SR_MASK, rxv[11]);
-+	he->data5 = HE_PREP(DATA5_PE_DISAMBIG, PE_DISAMBIG, rxv[2]) |
-+		    le16_encode_bits(ltf_size,
-+				     IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE);
-+	if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF)
-+		he->data5 |= HE_BITS(DATA5_TXBF);
-+	he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[14]) |
-+		    HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[14]);
-+
-+	switch (mode) {
-+	case MT_PHY_TYPE_HE_SU:
-+		he->data1 |= HE_BITS(DATA1_FORMAT_SU) |
-+			     HE_BITS(DATA1_UL_DL_KNOWN) |
-+			     HE_BITS(DATA1_BEAM_CHANGE_KNOWN) |
-+			     HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
-+
-+		he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG, rxv[14]) |
-+			     HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]);
-+		break;
-+	case MT_PHY_TYPE_HE_EXT_SU:
-+		he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) |
-+			     HE_BITS(DATA1_UL_DL_KNOWN) |
-+			     HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
-+
-+		he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]);
-+		break;
-+	case MT_PHY_TYPE_HE_MU:
-+		he->data1 |= HE_BITS(DATA1_FORMAT_MU) |
-+			     HE_BITS(DATA1_UL_DL_KNOWN);
-+
-+		he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]);
-+		he->data4 |= HE_PREP(DATA4_MU_STA_ID, MU_AID, rxv[7]);
-+
-+		mt7996_mac_decode_he_radiotap_ru(status, he, rxv);
-+		mt7996_mac_decode_he_mu_radiotap(skb, rxv);
-+		break;
-+	case MT_PHY_TYPE_HE_TB:
-+		he->data1 |= HE_BITS(DATA1_FORMAT_TRIG) |
-+			     HE_BITS(DATA1_SPTL_REUSE2_KNOWN) |
-+			     HE_BITS(DATA1_SPTL_REUSE3_KNOWN) |
-+			     HE_BITS(DATA1_SPTL_REUSE4_KNOWN);
-+
-+		he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE1, SR_MASK, rxv[11]) |
-+			     HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK, rxv[11]) |
-+			     HE_PREP(DATA4_TB_SPTL_REUSE3, SR2_MASK, rxv[11]) |
-+			     HE_PREP(DATA4_TB_SPTL_REUSE4, SR3_MASK, rxv[11]);
-+
-+		mt7996_mac_decode_he_radiotap_ru(status, he, rxv);
-+		break;
-+	default:
-+		break;
-+	}
-+}
-+
- /* The HW does not translate the mac header to 802.3 for mesh point */
- static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
- {
-@@ -686,8 +862,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
- 	}
- 
- 	if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
--		mt76_connac2_mac_decode_he_radiotap(&dev->mt76, skb, rxv,
--						    mode);
-+		mt7996_mac_decode_he_radiotap(skb, rxv, mode);
- 
- 	if (!status->wcid || !ieee80211_is_data_qos(fc))
- 		return 0;
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0001-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_tx_compl.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0001-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_tx_compl.patch
deleted file mode 100644
index 1022535..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0001-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_tx_compl.patch
+++ /dev/null
@@ -1,96 +0,0 @@
-From 6767bcfec5f48f14fdf63c9699c63e6bc741ad74 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 3 Feb 2023 11:00:59 +0800
-Subject: [PATCH 1/7] Revert "wifi: mt76: mt7996: rely on
- mt76_connac_tx_complete_skb"
-
-This reverts commit 8688756305c643b8a296c16d0626732aaae1d02a.
----
- mt7996/mac.c    | 21 +++++++++++++++++++++
- mt7996/mac.h    | 13 +++++++++++++
- mt7996/mmio.c   |  2 +-
- mt7996/mt7996.h |  1 +
- 4 files changed, 36 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index c9a9f0e3..0d718598 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1565,6 +1565,27 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- 	}
- }
- 
-+void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e)
-+{
-+	if (!e->txwi) {
-+		dev_kfree_skb_any(e->skb);
-+		return;
-+	}
-+
-+	/* error path */
-+	if (e->skb == DMA_DUMMY_DATA) {
-+		struct mt76_connac_txp_common *txp;
-+		struct mt76_txwi_cache *t;
-+
-+		txp = mt7996_txwi_to_txp(mdev, e->txwi);
-+		t = mt76_token_put(mdev, le16_to_cpu(txp->fw.token));
-+		e->skb = t ? t->skb : NULL;
-+	}
-+
-+	if (e->skb)
-+		mt76_tx_complete_skb(mdev, e->wcid, e->skb);
-+}
-+
- void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy)
- {
- 	struct mt7996_dev *dev = phy->dev;
-diff --git a/mt7996/mac.h b/mt7996/mac.h
-index 27184cba..10e08d66 100644
---- a/mt7996/mac.h
-+++ b/mt7996/mac.h
-@@ -371,4 +371,17 @@ struct mt7996_dfs_radar_spec {
- 	struct mt7996_dfs_pattern radar_pattern[16];
- };
- 
-+static inline struct mt76_connac_txp_common *
-+mt7996_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t)
-+{
-+	u8 *txwi;
-+
-+	if (!t)
-+		return NULL;
-+
-+	txwi = mt76_get_txwi_ptr(dev, t);
-+
-+	return (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
-+}
-+
- #endif
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 902370a2..2237f50a 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -326,7 +326,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- 				SURVEY_INFO_TIME_BSS_RX,
- 		.token_size = MT7996_TOKEN_SIZE,
- 		.tx_prepare_skb = mt7996_tx_prepare_skb,
--		.tx_complete_skb = mt76_connac_tx_complete_skb,
-+		.tx_complete_skb = mt7996_tx_complete_skb,
- 		.rx_skb = mt7996_queue_rx_skb,
- 		.rx_check = mt7996_rx_check,
- 		.rx_poll_complete = mt7996_rx_poll_complete,
-diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 018dfd2b..13f22432 100644
---- a/mt7996/mt7996.h
-+++ b/mt7996/mt7996.h
-@@ -508,6 +508,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 			  enum mt76_txq_id qid, struct mt76_wcid *wcid,
- 			  struct ieee80211_sta *sta,
- 			  struct mt76_tx_info *tx_info);
-+void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e);
- void mt7996_tx_token_put(struct mt7996_dev *dev);
- void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- 			 struct sk_buff *skb, u32 *info);
--- 
-2.25.1
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0001-wifi-mt76-mt7996-add-eht-rx-rate-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0001-wifi-mt76-mt7996-add-eht-rx-rate-support.patch
new file mode 100644
index 0000000..66ab9ce
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0001-wifi-mt76-mt7996-add-eht-rx-rate-support.patch
@@ -0,0 +1,92 @@
+From 995b09b6a62700568b36cc2e4ae6b6063456942b Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 10 Feb 2023 17:39:23 +0800
+Subject: [PATCH 01/19] wifi: mt76: mt7996: add eht rx rate support
+
+Add support to report eht rx rate.
+
+Change-Id: Iee067d891bd634a918c942c2ba90ae72cd40c538
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mac80211.c   | 11 ++++++++---
+ mt76.h       | 18 ++++++++++++++----
+ mt7996/mac.c |  9 +++++----
+ 3 files changed, 27 insertions(+), 11 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 87902f4b..e53166fc 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1067,9 +1067,14 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
+ 	status->enc_flags = mstat.enc_flags;
+ 	status->encoding = mstat.encoding;
+ 	status->bw = mstat.bw;
+-	status->he_ru = mstat.he_ru;
+-	status->he_gi = mstat.he_gi;
+-	status->he_dcm = mstat.he_dcm;
++	if (status->encoding == RX_ENC_EHT) {
++		status->eht.ru = mstat.eht.ru;
++		status->eht.gi = mstat.eht.gi;
++	} else {
++		status->he_ru = mstat.he_ru;
++		status->he_gi = mstat.he_gi;
++		status->he_dcm = mstat.he_dcm;
++	}
+ 	status->rate_idx = mstat.rate_idx;
+ 	status->nss = mstat.nss;
+ 	status->band = mstat.band;
+diff --git a/mt76.h b/mt76.h
+index 183b0fc5..c3d1313e 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -621,12 +621,22 @@ struct mt76_rx_status {
+ 	u16 freq;
+ 	u32 flag;
+ 	u8 enc_flags;
+-	u8 encoding:2, bw:3, he_ru:3;
+-	u8 he_gi:2, he_dcm:1;
++	u8 encoding:3, bw:4;
++	union {
++		struct {
++			u8 he_ru:3;
++			u8 he_gi:2;
++			u8 he_dcm:1;
++		};
++		struct {
++			u8 ru:4;
++			u8 gi:2;
++		} eht;
++	};
++
+ 	u8 amsdu:1, first_amsdu:1, last_amsdu:1;
+ 	u8 rate_idx;
+-	u8 nss;
+-	u8 band;
++	u8 nss:5, band:3;
+ 	s8 signal;
+ 	u8 chains;
+ 	s8 chain_signal[IEEE80211_MAX_CHAINS];
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 3c3506c7..d811b4e0 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -572,11 +572,12 @@ mt7996_mac_fill_rx_rate(struct mt7996_dev *dev,
+ 	case MT_PHY_TYPE_EHT_SU:
+ 	case MT_PHY_TYPE_EHT_TRIG:
+ 	case MT_PHY_TYPE_EHT_MU:
+-		/* TODO: currently report rx rate with HE rate */
+ 		status->nss = nss;
+-		status->encoding = RX_ENC_HE;
+-		bw = min_t(int, bw, IEEE80211_STA_RX_BW_160);
+-		i = min_t(int, i & 0xf, 11);
++		status->encoding = RX_ENC_EHT;
++		i &= GENMASK(3, 0);
++
++		if (gi <= NL80211_RATE_INFO_EHT_GI_3_2)
++			status->eht.gi = gi;
+ 		break;
+ 	default:
+ 		return -EINVAL;
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0002-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_txp_skb_.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0002-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_txp_skb_.patch
deleted file mode 100644
index fb6581a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0002-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_txp_skb_.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 9c2313ddded0179ae1a9e6112e2762a1a3e958d7 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 3 Feb 2023 11:01:11 +0800
-Subject: [PATCH 2/7] Revert "wifi: mt76: mt7996: rely on
- mt76_connac_txp_skb_unmap"
-
-This reverts commit bdb7dc38a6d150b5aa4b09b82a8954c6bb49ba4a.
----
- mt7996/mac.c | 14 +++++++++++++-
- 1 file changed, 13 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 0d718598..df97d7cf 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1187,6 +1187,18 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
- 		ieee80211_start_tx_ba_session(sta, tid, 0);
- }
- 
-+static void
-+mt7996_txp_skb_unmap(struct mt76_dev *dev, struct mt76_txwi_cache *t)
-+{
-+	struct mt76_connac_txp_common *txp;
-+	int i;
-+
-+	txp = mt7996_txwi_to_txp(dev, t);
-+	for (i = 0; i < txp->fw.nbuf; i++)
-+		dma_unmap_single(dev->dev, le32_to_cpu(txp->fw.buf[i]),
-+				 le16_to_cpu(txp->fw.len[i]), DMA_TO_DEVICE);
-+}
-+
- static void
- mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
- 		 struct ieee80211_sta *sta, struct list_head *free_list)
-@@ -1196,7 +1208,7 @@ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
- 	__le32 *txwi;
- 	u16 wcid_idx;
- 
--	mt76_connac_txp_skb_unmap(mdev, t);
-+	mt7996_txp_skb_unmap(mdev, t);
- 	if (!t->skb)
- 		goto out;
- 
--- 
-2.25.1
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-let-non-bufferable-MMPDUs-use-corre.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-let-non-bufferable-MMPDUs-use-corre.patch
new file mode 100644
index 0000000..872ef03
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-let-non-bufferable-MMPDUs-use-corre.patch
@@ -0,0 +1,92 @@
+From 5b858f7f944144d99fb0ca1ef26d6b8c6719890c Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 16 Feb 2023 10:52:22 +0800
+Subject: [PATCH 02/19] wifi: mt76: mt7996: let non-bufferable MMPDUs use
+ correct hw queue
+
+non-bufferable MMPDUs are expected to use ALTX hw queue, but current
+condition in mt7996_mac_write_txwi() won't let their tx descriptor
+be filled with correct q_idx. Fix this by passing qid parameter into
+the funciton.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I18bd1a5ea698912c7aa8f4d4317d4a428d4ac440
+---
+ mt7996/mac.c    | 9 +++++----
+ mt7996/mcu.c    | 4 ++--
+ mt7996/mt7996.h | 3 ++-
+ 3 files changed, 9 insertions(+), 7 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index d811b4e0..198eb711 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -982,7 +982,8 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ }
+ 
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+-			   struct sk_buff *skb, struct mt76_wcid *wcid, int pid,
++			   struct sk_buff *skb, struct mt76_wcid *wcid,
++			   enum mt76_txq_id qid, int pid,
+ 			   struct ieee80211_key_conf *key, u32 changed)
+ {
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+@@ -1014,7 +1015,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 	} else if (beacon) {
+ 		p_fmt = MT_TX_TYPE_FW;
+ 		q_idx = MT_LMAC_BCN0;
+-	} else if (skb_get_queue_mapping(skb) >= MT_TXQ_PSD) {
++	} else if (qid >= MT_TXQ_PSD) {
+ 		p_fmt = MT_TX_TYPE_CT;
+ 		q_idx = MT_LMAC_ALTX0;
+ 	} else {
+@@ -1123,8 +1124,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	memset(txwi_ptr, 0, MT_TXD_SIZE);
+ 	/* Transmit non qos data by 802.11 header and need to fill txd by host*/
+ 	if (!is_8023 || pid >= MT_PACKET_ID_FIRST)
+-		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, pid,
+-				      key, 0);
++		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, qid,
++				      pid, key, 0);
+ 
+ 	txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
+ 	for (i = 0; i < nbuf; i++) {
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8ad51cbf..dbe10576 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1906,7 +1906,7 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 	}
+ 
+ 	buf = (u8 *)bcn + sizeof(*bcn) - MAX_BEACON_SIZE;
+-	mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL,
++	mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, 0, NULL,
+ 			      BSS_CHANGED_BEACON);
+ 	memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
+ }
+@@ -2115,7 +2115,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 
+ 	buf = (u8 *)tlv + sizeof(*discov) - MAX_INBAND_FRAME_SIZE;
+ 
+-	mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL,
++	mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, 0, NULL,
+ 			      changed);
+ 
+ 	memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 018dfd2b..d20aa5f2 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -487,7 +487,8 @@ void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band);
+ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
+ 			      struct ieee80211_vif *vif, bool enable);
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+-			   struct sk_buff *skb, struct mt76_wcid *wcid, int pid,
++			   struct sk_buff *skb, struct mt76_wcid *wcid,
++			   enum mt76_txq_id qid, int pid,
+ 			   struct ieee80211_key_conf *key, u32 changed);
+ void mt7996_mac_set_timing(struct mt7996_phy *phy);
+ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0003-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_txp_comm.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0003-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_txp_comm.patch
deleted file mode 100644
index 43ea6c9..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0003-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_txp_comm.patch
+++ /dev/null
@@ -1,163 +0,0 @@
-From 8ba17e7cdbb5e921edd03102476cd8fb4f4e1b31 Mon Sep 17 00:00:00 2001
-From: Shayne Chen <shayne.chen@mediatek.com>
-Date: Fri, 3 Feb 2023 11:12:46 +0800
-Subject: [PATCH 3/7] Revert "wifi: mt76: mt7996: rely on
- mt76_connac_txp_common structure"
-
-This reverts commit 321edbb414dcc03d349bddd08916b7cc531802e8.
----
- mt7996/mac.c  | 38 +++++++++++++++++++-------------------
- mt7996/mac.h  | 15 +++++++++++++--
- mt7996/mmio.c |  2 +-
- 3 files changed, 33 insertions(+), 22 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index df97d7cf..e5b5fed6 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1088,8 +1088,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
- 	struct ieee80211_key_conf *key = info->control.hw_key;
- 	struct ieee80211_vif *vif = info->control.vif;
--	struct mt76_connac_txp_common *txp;
- 	struct mt76_txwi_cache *t;
-+	struct mt7996_txp *txp;
- 	int id, i, pid, nbuf = tx_info->nbuf - 1;
- 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
- 	u8 *txwi = (u8 *)txwi_ptr;
-@@ -1123,35 +1123,35 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, pid,
- 				      key, 0);
- 
--	txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
-+	txp = (struct mt7996_txp *)(txwi + MT_TXD_SIZE);
- 	for (i = 0; i < nbuf; i++) {
--		txp->fw.buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr);
--		txp->fw.len[i] = cpu_to_le16(tx_info->buf[i + 1].len);
-+		txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr);
-+		txp->len[i] = cpu_to_le16(tx_info->buf[i + 1].len);
- 	}
--	txp->fw.nbuf = nbuf;
-+	txp->nbuf = nbuf;
- 
--	txp->fw.flags = cpu_to_le16(MT_CT_INFO_FROM_HOST);
-+	txp->flags = cpu_to_le16(MT_CT_INFO_FROM_HOST);
- 
- 	if (!is_8023 || pid >= MT_PACKET_ID_FIRST)
--		txp->fw.flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD);
-+		txp->flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD);
- 
- 	if (!key)
--		txp->fw.flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME);
-+		txp->flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME);
- 
- 	if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control))
--		txp->fw.flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME);
-+		txp->flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME);
- 
- 	if (vif) {
- 		struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- 
--		txp->fw.bss_idx = mvif->mt76.idx;
-+		txp->bss_idx = mvif->mt76.idx;
- 	}
- 
--	txp->fw.token = cpu_to_le16(id);
-+	txp->token = cpu_to_le16(id);
- 	if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags))
--		txp->fw.rept_wds_wcid = cpu_to_le16(wcid->idx);
-+		txp->rept_wds_wcid = cpu_to_le16(wcid->idx);
- 	else
--		txp->fw.rept_wds_wcid = cpu_to_le16(0xfff);
-+		txp->rept_wds_wcid = cpu_to_le16(0xfff);
- 	tx_info->skb = DMA_DUMMY_DATA;
- 
- 	/* pass partial skb header to fw */
-@@ -1190,13 +1190,13 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
- static void
- mt7996_txp_skb_unmap(struct mt76_dev *dev, struct mt76_txwi_cache *t)
- {
--	struct mt76_connac_txp_common *txp;
-+	struct mt7996_txp *txp;
- 	int i;
- 
- 	txp = mt7996_txwi_to_txp(dev, t);
--	for (i = 0; i < txp->fw.nbuf; i++)
--		dma_unmap_single(dev->dev, le32_to_cpu(txp->fw.buf[i]),
--				 le16_to_cpu(txp->fw.len[i]), DMA_TO_DEVICE);
-+	for (i = 0; i < txp->nbuf; i++)
-+		dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]),
-+				 le16_to_cpu(txp->len[i]), DMA_TO_DEVICE);
- }
- 
- static void
-@@ -1586,11 +1586,11 @@ void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e)
- 
- 	/* error path */
- 	if (e->skb == DMA_DUMMY_DATA) {
--		struct mt76_connac_txp_common *txp;
- 		struct mt76_txwi_cache *t;
-+		struct mt7996_txp *txp;
- 
- 		txp = mt7996_txwi_to_txp(mdev, e->txwi);
--		t = mt76_token_put(mdev, le16_to_cpu(txp->fw.token));
-+		t = mt76_token_put(mdev, le16_to_cpu(txp->token));
- 		e->skb = t ? t->skb : NULL;
- 	}
- 
-diff --git a/mt7996/mac.h b/mt7996/mac.h
-index 10e08d66..9f688520 100644
---- a/mt7996/mac.h
-+++ b/mt7996/mac.h
-@@ -268,6 +268,17 @@ enum tx_mgnt_type {
- /* VHT/HE only use bits 0-3 */
- #define MT_TX_RATE_IDX			GENMASK(5, 0)
- 
-+struct mt7996_txp {
-+	__le16 flags;
-+	__le16 token;
-+	u8 bss_idx;
-+	__le16 rept_wds_wcid;
-+	u8 nbuf;
-+#define MT_TXP_MAX_BUF_NUM	6
-+	__le32 buf[MT_TXP_MAX_BUF_NUM];
-+	__le16 len[MT_TXP_MAX_BUF_NUM];
-+} __packed __aligned(4);
-+
- #define MT_TXFREE0_PKT_TYPE		GENMASK(31, 27)
- #define MT_TXFREE0_MSDU_CNT		GENMASK(25, 16)
- #define MT_TXFREE0_RX_BYTE		GENMASK(15, 0)
-@@ -371,7 +382,7 @@ struct mt7996_dfs_radar_spec {
- 	struct mt7996_dfs_pattern radar_pattern[16];
- };
- 
--static inline struct mt76_connac_txp_common *
-+static inline struct mt7996_txp *
- mt7996_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t)
- {
- 	u8 *txwi;
-@@ -381,7 +392,7 @@ mt7996_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t)
- 
- 	txwi = mt76_get_txwi_ptr(dev, t);
- 
--	return (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
-+	return (struct mt7996_txp *)(txwi + MT_TXD_SIZE);
- }
- 
- #endif
-diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-index 2237f50a..d8a2c1a7 100644
---- a/mt7996/mmio.c
-+++ b/mt7996/mmio.c
-@@ -318,7 +318,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
- {
- 	static const struct mt76_driver_ops drv_ops = {
- 		/* txwi_size = txd size + txp size */
--		.txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp),
-+		.txwi_size = MT_TXD_SIZE + sizeof(struct mt7996_txp),
- 		.drv_flags = MT_DRV_TXWI_NO_FREE |
- 			     MT_DRV_HW_MGMT_TXQ,
- 		.survey_flags = SURVEY_INFO_TIME_TX |
--- 
-2.25.1
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch
new file mode 100644
index 0000000..c91eb62
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch
@@ -0,0 +1,67 @@
+From 09ef1c1248c8b38c5f2c6e624397e64e8938349f Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 16 Feb 2023 00:39:01 +0800
+Subject: [PATCH 03/19] wifi: mt76: mt7996: reduce repeated bss_info and
+ sta_rec commands
+
+Refine the flow of setting bss_info and sta_rec commands to prevent from
+sending duplicated commands, especially for station mode.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: Iffd2c81f9ec98284793e75f4b7d39e9618977024
+---
+ mt7996/main.c | 21 ++++++---------------
+ 1 file changed, 6 insertions(+), 15 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 3e4da035..e80ee19e 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -241,8 +241,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	int idx = msta->wcid.idx;
+ 
+-	mt7996_mcu_add_bss_info(phy, vif, false);
+ 	mt7996_mcu_add_sta(dev, vif, NULL, false);
++	mt7996_mcu_add_bss_info(phy, vif, false);
+ 
+ 	if (vif == phy->monitor_vif)
+ 		phy->monitor_vif = NULL;
+@@ -510,17 +510,13 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ 	/* station mode uses BSSID to map the wlan entry to a peer,
+ 	 * and then peer references bss_info_rfch to set bandwidth cap.
+ 	 */
+-	if (changed & BSS_CHANGED_BSSID &&
+-	    vif->type == NL80211_IFTYPE_STATION) {
+-		bool join = !is_zero_ether_addr(info->bssid);
+-
+-		mt7996_mcu_add_bss_info(phy, vif, join);
+-		mt7996_mcu_add_sta(dev, vif, NULL, join);
++	if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) ||
++	    (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) ||
++	    (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) {
++		mt7996_mcu_add_bss_info(phy, vif, true);
++		mt7996_mcu_add_sta(dev, vif, NULL, true);
+ 	}
+ 
+-	if (changed & BSS_CHANGED_ASSOC)
+-		mt7996_mcu_add_bss_info(phy, vif, vif->cfg.assoc);
+-
+ 	if (changed & BSS_CHANGED_ERP_CTS_PROT)
+ 		mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot);
+ 
+@@ -533,11 +529,6 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ 		}
+ 	}
+ 
+-	if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) {
+-		mt7996_mcu_add_bss_info(phy, vif, true);
+-		mt7996_mcu_add_sta(dev, vif, NULL, true);
+-	}
+-
+ 	/* ensure that enable txcmd_mode after bss_info */
+ 	if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
+ 		mt7996_mcu_set_tx(dev, vif);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-move-radio-enable-command-to-mt7996.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-move-radio-enable-command-to-mt7996.patch
new file mode 100644
index 0000000..cc366e3
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-move-radio-enable-command-to-mt7996.patch
@@ -0,0 +1,61 @@
+From c86f2243472f00fd2011cc44b89a87f7f6e2066a Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 15 Feb 2023 18:38:04 +0800
+Subject: [PATCH 04/19] wifi: mt76: mt7996: move radio enable command to
+ mt7996_start()
+
+The radio enable and disable commands are used for per-phy radio, so
+move them into mt7996_start() and mt7996_stop(), respectively.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I610b170f5198e085eb86dbd371ee0745ac6ff50f
+---
+ mt7996/main.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index e80ee19e..a4fd9e24 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -46,6 +46,10 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ 	if (ret)
+ 		goto out;
+ 
++	ret = mt7996_mcu_set_radio_en(phy, true);
++	if (ret)
++		goto out;
++
+ 	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
+ 	if (ret)
+ 		goto out;
+@@ -77,6 +81,8 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
++	mt7996_mcu_set_radio_en(phy, false);
++
+ 	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+ 	ieee80211_iterate_interfaces(dev->mt76.hw,
+@@ -189,10 +195,6 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
+ 	if (ret)
+ 		goto out;
+ 
+-	ret = mt7996_mcu_set_radio_en(phy, true);
+-	if (ret)
+-		goto out;
+-
+ 	dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
+ 	phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
+ 
+@@ -248,7 +250,6 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ 		phy->monitor_vif = NULL;
+ 
+ 	mt7996_mcu_add_dev_info(phy, vif, false);
+-	mt7996_mcu_set_radio_en(phy, false);
+ 
+ 	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ 
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch
new file mode 100644
index 0000000..07dffec
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch
@@ -0,0 +1,49 @@
+From 5d4c7f88b2066f4a8ec1769cf6c067d0e88abfa3 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Thu, 16 Feb 2023 13:53:14 +0800
+Subject: [PATCH 05/19] wifi: mt76: connac: set correct muar_idx for connac3
+ chipset
+
+Set the muar_idx to 0xe for the hw bcast/mcast station entry of connac3
+chipset.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I7054c3b3c64ec447cc280ea810f4958afdfa9e02
+---
+ mt76_connac.h     | 5 +++++
+ mt76_connac_mcu.c | 3 +++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/mt76_connac.h b/mt76_connac.h
+index b339c50b..c8b91e86 100644
+--- a/mt76_connac.h
++++ b/mt76_connac.h
+@@ -216,6 +216,11 @@ static inline bool is_connac_v1(struct mt76_dev *dev)
+ 	return is_mt7615(dev) || is_mt7663(dev) || is_mt7622(dev);
+ }
+ 
++static inline bool is_connac_v3(struct mt76_dev *dev)
++{
++	return is_mt7996(dev);
++}
++
+ static inline bool is_mt76_fw_txp(struct mt76_dev *dev)
+ {
+ 	switch (mt76_chip(dev)) {
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index efb9bfaa..4e4f6b35 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -281,6 +281,9 @@ __mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif *mvif,
+ 	};
+ 	struct sk_buff *skb;
+ 
++	if (is_connac_v3(dev) && !wcid->sta)
++		hdr.muar_idx = 0xe;
++
+ 	mt76_connac_mcu_get_wlan_idx(dev, wcid, &hdr.wlan_idx_lo,
+ 				     &hdr.wlan_idx_hi);
+ 	skb = mt76_mcu_msg_alloc(dev, NULL, len);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-set-txd-v1.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-set-txd-v1.patch
deleted file mode 100644
index 8fddb93..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-set-txd-v1.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From d8bef73f22f88bf857698f4018db3aed6a2fca2d Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Tue, 29 Nov 2022 09:13:32 +0800
-Subject: [PATCH 5/7] wifi: mt76: mt7996: set txd v1
-
----
- mt7996/mac.c | 3 +++
- mt7996/mac.h | 3 ++-
- 2 files changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index e5b5fed6..51337dde 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1092,6 +1092,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 	struct mt7996_txp *txp;
- 	int id, i, pid, nbuf = tx_info->nbuf - 1;
- 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
-+	__le32 *txd = (__le32 *)txwi_ptr;
- 	u8 *txwi = (u8 *)txwi_ptr;
- 
- 	if (unlikely(tx_info->skb->len <= ETH_HLEN))
-@@ -1123,6 +1124,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, pid,
- 				      key, 0);
- 
-+	txd[0] |= le32_encode_bits(1, MT_TXD0_VER);
-+
- 	txp = (struct mt7996_txp *)(txwi + MT_TXD_SIZE);
- 	for (i = 0; i < nbuf; i++) {
- 		txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr);
-diff --git a/mt7996/mac.h b/mt7996/mac.h
-index 9f688520..470b701a 100644
---- a/mt7996/mac.h
-+++ b/mt7996/mac.h
-@@ -186,7 +186,8 @@ enum tx_mgnt_type {
- 
- #define MT_TXD0_Q_IDX			GENMASK(31, 25)
- #define MT_TXD0_PKT_FMT			GENMASK(24, 23)
--#define MT_TXD0_ETH_TYPE_OFFSET		GENMASK(22, 16)
-+#define MT_TXD0_VER			GENMASK(22, 19)
-+#define MT_TXD0_ETH_TYPE_OFFSET		GENMASK(18, 16)
- #define MT_TXD0_TX_BYTES		GENMASK(15, 0)
- 
- #define MT_TXD1_FIXED_RATE		BIT(31)
--- 
-2.25.1
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-fix-pointer-calculation-in-ie-count.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-fix-pointer-calculation-in-ie-count.patch
new file mode 100644
index 0000000..65596f4
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-fix-pointer-calculation-in-ie-count.patch
@@ -0,0 +1,34 @@
+From 00d76d4dc3cdb719b5fc234d9cf3bbef39f448ce Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 13 Feb 2023 14:48:10 +0800
+Subject: [PATCH 06/19] wifi: mt76: mt7996: fix pointer calculation in ie
+ countdown event
+
+Fix the tail and data pointers. The rxd->len in mt7996_mcu_rxd does not
+include the length of general rxd. It only includes the length of
+firmware event rxd. Use the skb->length to get the correct length.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I832194559e63e6b49b7ee00dc9c606b6106d8669
+---
+ mt7996/mcu.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index dbe10576..2e25572a 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -422,7 +422,8 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	if (hdr->band && dev->mt76.phys[hdr->band])
+ 		mphy = dev->mt76.phys[hdr->band];
+ 
+-	tail = skb->data + le16_to_cpu(rxd->len);
++	tail = skb->data + skb->len;
++	data += sizeof(struct header);
+ 	while (data + sizeof(struct tlv) < tail && le16_to_cpu(tlv->len)) {
+ 		switch (le16_to_cpu(tlv->tag)) {
+ 		case UNI_EVENT_IE_COUNTDOWN_CSA:
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7996-remove-mt7996_mcu_set_pm.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7996-remove-mt7996_mcu_set_pm.patch
new file mode 100644
index 0000000..7988196
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7996-remove-mt7996_mcu_set_pm.patch
@@ -0,0 +1,96 @@
+From c2dafe57df1726acb80e30987bc8d678b86af14a Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 14 Feb 2023 18:35:43 +0800
+Subject: [PATCH 07/19] wifi: mt76: mt7996: remove mt7996_mcu_set_pm()
+
+Currently using BSS_INFO_PS command will sometimes cause packet drop in
+hw rx queue.
+Temporarily remove this function until finding the cause.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I863fc1edb18e4d7b5dac20140dd0904875e1323c
+---
+ mt7996/main.c   |  8 --------
+ mt7996/mcu.c    | 26 --------------------------
+ mt7996/mt7996.h |  1 -
+ 3 files changed, 35 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index a4fd9e24..44d23e1d 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -56,10 +56,6 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ 
+ 	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+-	ieee80211_iterate_interfaces(dev->mt76.hw,
+-				     IEEE80211_IFACE_ITER_RESUME_ALL,
+-				     mt7996_mcu_set_pm, dev->mt76.hw);
+-
+ 	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+ 				     MT7996_WATCHDOG_TIME);
+ 
+@@ -85,10 +81,6 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 
+ 	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+-	ieee80211_iterate_interfaces(dev->mt76.hw,
+-				     IEEE80211_IFACE_ITER_RESUME_ALL,
+-				     mt7996_mcu_set_pm, dev->mt76.hw);
+-
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 2e25572a..f6947438 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3577,32 +3577,6 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ 				 &req, sizeof(req), true);
+ }
+ 
+-void mt7996_mcu_set_pm(void *priv, u8 *mac, struct ieee80211_vif *vif)
+-{
+-#define EXIT_PM_STATE	0
+-#define ENTER_PM_STATE	1
+-	struct ieee80211_hw *hw = priv;
+-	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+-	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+-	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+-	struct bss_power_save *ps;
+-	struct sk_buff *skb;
+-	struct tlv *tlv;
+-	bool running = test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+-
+-	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+-					 MT7996_BSS_UPDATE_MAX_SIZE);
+-	if (IS_ERR(skb))
+-		return;
+-
+-	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_PS, sizeof(*ps));
+-	ps = (struct bss_power_save *)tlv;
+-	ps->profile = running ? EXIT_PM_STATE : ENTER_PM_STATE;
+-
+-	mt76_mcu_skb_send_msg(&dev->mt76, skb,
+-			      MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+-}
+-
+ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val)
+ {
+ 	struct {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d20aa5f2..f9d8fbfd 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -432,7 +432,6 @@ int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev,
+ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
+ 			    const struct mt7996_dfs_pattern *pattern);
+ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
+-void mt7996_mcu_set_pm(void *priv, u8 *mac, struct ieee80211_vif *vif);
+ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
+ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-add-muru-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-add-muru-support.patch
similarity index 91%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-add-muru-support.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-add-muru-support.patch
index dab5182..20d7f48 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-add-muru-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-add-muru-support.patch
@@ -1,7 +1,7 @@
-From 7c8b8c77489ef907f2122c5d0edb7ff5e0fed177 Mon Sep 17 00:00:00 2001
+From b587a9f1615585f8db0fa98cc46791a3253ddac7 Mon Sep 17 00:00:00 2001
 From: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
 Date: Mon, 28 Nov 2022 14:36:09 +0800
-Subject: [PATCH 4/7] wifi: mt76: mt7996: add muru support
+Subject: [PATCH 08/19] wifi: mt76: mt7996: add muru support
 
 Add sta_rec_muru() and related phy cap for MU and RU support.
 
@@ -15,7 +15,7 @@
  3 files changed, 73 insertions(+), 2 deletions(-)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index a5e6ee4d..39667840 100644
+index 40a99e0c..6f30a0fb 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
 @@ -518,7 +518,8 @@ struct sta_rec_muru {
@@ -29,10 +29,10 @@
  
  	struct {
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index dbe30832..394dd15c 100644
+index f6947438..b6bd36c2 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
-@@ -1049,6 +1049,63 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+@@ -1050,6 +1050,63 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
  	}
  }
  
@@ -96,7 +96,7 @@
  static inline bool
  mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
  			struct ieee80211_sta *sta, bool bfee)
-@@ -1721,7 +1778,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+@@ -1722,7 +1779,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
  		mt7996_mcu_sta_he_6g_tlv(skb, sta);
  		/* starec eht */
  		mt7996_mcu_sta_eht_tlv(skb, sta);
@@ -106,7 +106,7 @@
  		/* starec bfee */
  		mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
  		/* starec hdr trans */
-@@ -2004,6 +2062,15 @@ mt7996_mcu_beacon_check_caps(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+@@ -2005,6 +2063,15 @@ mt7996_mcu_beacon_check_caps(struct mt7996_phy *phy, struct ieee80211_vif *vif,
  		vc->eht_su_ebfee =
  			EHT_PHY(CAP0_SU_BEAMFORMEE, eht->phy_cap_info[0]) &&
  			EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]);
@@ -123,7 +123,7 @@
  }
  
 diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 13f22432..64a804a4 100644
+index f9d8fbfd..997a0bf0 100644
 --- a/mt7996/mt7996.h
 +++ b/mt7996/mt7996.h
 @@ -125,6 +125,9 @@ struct mt7996_vif_cap {
@@ -137,5 +137,5 @@
  
  struct mt7996_vif {
 -- 
-2.25.1
+2.39.2
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0009-wifi-mt76-mt7996-set-txd-v1.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0009-wifi-mt76-mt7996-set-txd-v1.patch
new file mode 100644
index 0000000..cc7548a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0009-wifi-mt76-mt7996-set-txd-v1.patch
@@ -0,0 +1,48 @@
+From 9a1a54c7df9babadd8f6ae066deb2b5e49715cb0 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Feb 2023 10:40:33 +0800
+Subject: [PATCH 09/19] wifi: mt76: mt7996: set txd v1
+
+---
+ mt7996/mac.c | 3 +++
+ mt7996/mac.h | 3 ++-
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 198eb711..40ef5e4b 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1096,6 +1096,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	struct mt76_txwi_cache *t;
+ 	int id, i, pid, nbuf = tx_info->nbuf - 1;
+ 	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
++	__le32 *txd = (__le32 *)txwi_ptr;
+ 	u8 *txwi = (u8 *)txwi_ptr;
+ 
+ 	if (unlikely(tx_info->skb->len <= ETH_HLEN))
+@@ -1127,6 +1128,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, qid,
+ 				      pid, key, 0);
+ 
++	txd[0] |= le32_encode_bits(1, MT_TXD0_VER);
++
+ 	txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE);
+ 	for (i = 0; i < nbuf; i++) {
+ 		txp->fw.buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr);
+diff --git a/mt7996/mac.h b/mt7996/mac.h
+index 2cc218f7..4914d3e5 100644
+--- a/mt7996/mac.h
++++ b/mt7996/mac.h
+@@ -183,7 +183,8 @@ enum tx_mgnt_type {
+ 
+ #define MT_TXD0_Q_IDX			GENMASK(31, 25)
+ #define MT_TXD0_PKT_FMT			GENMASK(24, 23)
+-#define MT_TXD0_ETH_TYPE_OFFSET		GENMASK(22, 16)
++#define MT_TXD0_VER			GENMASK(22, 19)
++#define MT_TXD0_ETH_TYPE_OFFSET		GENMASK(18, 16)
+ #define MT_TXD0_TX_BYTES		GENMASK(15, 0)
+ 
+ #define MT_TXD1_FIXED_RATE		BIT(31)
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-add-thermal-protection-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-add-thermal-protection-support.patch
new file mode 100644
index 0000000..319eb1f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-add-thermal-protection-support.patch
@@ -0,0 +1,437 @@
+From 1744b5397ad4240ce41bf88bfff2eef34d76d587 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 2 Feb 2023 21:20:31 +0800
+Subject: [PATCH 10/19] wifi: mt76: mt7996: add thermal protection support
+
+This commit includes the following changes:
+1. implement MTK thermal protection driver API
+2. support Linux cooling device control
+
+Change-Id: I8fecc28f5b17ee50ae4644d1dd17d188dd694731
+---
+ mt76_connac_mcu.h |   1 +
+ mt7996/init.c     | 105 +++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/main.c     |   8 ++++
+ mt7996/mcu.c      | 106 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      |  44 +++++++++++++++++++
+ mt7996/mt7996.h   |  15 +++++++
+ 6 files changed, 279 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 6f30a0fb..fa10d823 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1009,6 +1009,7 @@ enum {
+ 	MCU_UNI_EVENT_FW_LOG_2_HOST = 0x04,
+ 	MCU_UNI_EVENT_IE_COUNTDOWN = 0x09,
+ 	MCU_UNI_EVENT_RDD_REPORT = 0x11,
++	MCU_UNI_EVENT_THERMAL = 0x35,
+ };
+ 
+ #define MCU_UNI_CMD_EVENT			BIT(1)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 946da93e..5a22cd81 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -41,6 +41,98 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ 	}
+ };
+ 
++static int
++mt7996_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
++				      unsigned long *state)
++{
++	*state = MT7996_CDEV_THROTTLE_MAX;
++
++	return 0;
++}
++
++static int
++mt7996_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
++				      unsigned long *state)
++{
++	struct mt7996_phy *phy = cdev->devdata;
++
++	*state = phy->cdev_state;
++
++	return 0;
++}
++
++static int
++mt7996_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
++				      unsigned long state)
++{
++	struct mt7996_phy *phy = cdev->devdata;
++	u8 throttling = MT7996_THERMAL_THROTTLE_MAX - state;
++	int ret;
++
++	if (state > MT7996_CDEV_THROTTLE_MAX) {
++		dev_err(phy->dev->mt76.dev,
++			"please specify a valid throttling state\n");
++		return -EINVAL;
++	}
++
++	if (state == phy->cdev_state)
++		return 0;
++
++	/*
++	 * cooling_device convention: 0 = no cooling, more = more cooling
++	 * mcu convention: 1 = max cooling, more = less cooling
++	 */
++	ret = mt7996_mcu_set_thermal_throttling(phy, throttling);
++	if (ret)
++		return ret;
++
++	phy->cdev_state = state;
++
++	return 0;
++}
++
++static const struct thermal_cooling_device_ops mt7996_thermal_ops = {
++	.get_max_state = mt7996_thermal_get_max_throttle_state,
++	.get_cur_state = mt7996_thermal_get_cur_throttle_state,
++	.set_cur_state = mt7996_thermal_set_cur_throttle_state,
++};
++
++static void mt7996_unregister_thermal(struct mt7996_phy *phy)
++{
++	struct wiphy *wiphy = phy->mt76->hw->wiphy;
++
++	if (!phy->cdev)
++	    return;
++
++	sysfs_remove_link(&wiphy->dev.kobj, "cooling_device");
++	thermal_cooling_device_unregister(phy->cdev);
++}
++
++static int mt7996_thermal_init(struct mt7996_phy *phy)
++{
++	struct wiphy *wiphy = phy->mt76->hw->wiphy;
++	struct thermal_cooling_device *cdev;
++	const char *name;
++
++	name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7996_%s",
++			      wiphy_name(wiphy));
++
++	cdev = thermal_cooling_device_register(name, phy, &mt7996_thermal_ops);
++	if (!IS_ERR(cdev)) {
++		if (sysfs_create_link(&wiphy->dev.kobj, &cdev->device.kobj,
++				      "cooling_device") < 0)
++			thermal_cooling_device_unregister(cdev);
++		else
++			phy->cdev = cdev;
++	}
++
++	/* initialize critical/maximum high temperature */
++	phy->throttle_temp[MT7996_CRIT_TEMP_IDX] = MT7996_CRIT_TEMP;
++	phy->throttle_temp[MT7996_MAX_TEMP_IDX] = MT7996_MAX_TEMP;
++
++	return 0;
++}
++
+ static void mt7996_led_set_config(struct led_classdev *led_cdev,
+ 				  u8 delay_on, u8 delay_off)
+ {
+@@ -367,6 +459,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	if (ret)
+ 		goto error;
+ 
++	ret = mt7996_thermal_init(phy);
++	if (ret)
++		goto error;
++
+ 	ret = mt7996_init_debugfs(phy);
+ 	if (ret)
+ 		goto error;
+@@ -387,6 +483,8 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ 	if (!phy)
+ 		return;
+ 
++	mt7996_unregister_thermal(phy);
++
+ 	mphy = phy->dev->mt76.phys[band];
+ 	mt76_unregister_phy(mphy);
+ 	ieee80211_free_hw(mphy->hw);
+@@ -876,6 +974,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	ret = mt7996_thermal_init(&dev->phy);
++	if (ret)
++		return ret;
++
+ 	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
+ 
+ 	ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1);
+@@ -893,6 +995,9 @@ void mt7996_unregister_device(struct mt7996_dev *dev)
+ {
+ 	mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
+ 	mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
++
++	mt7996_unregister_thermal(&dev->phy);
++
+ 	mt76_unregister_device(&dev->mt76);
+ 	mt7996_mcu_exit(dev);
+ 	mt7996_tx_token_put(dev);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 44d23e1d..d8d578c0 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -54,6 +54,14 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ 	if (ret)
+ 		goto out;
+ 
++	ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
++	if (ret)
++		goto out;
++
++	ret = mt7996_mcu_set_thermal_protect(phy);
++	if (ret)
++		goto out;
++
+ 	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+ 	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index b6bd36c2..3820a63e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -443,6 +443,34 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	}
+ }
+ 
++static void
++mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++#define THERMAL_NOTIFY_TAG 0x4
++#define THERMAL_NOTIFY 0x2
++	struct mt76_phy *mphy = &dev->mt76.phy;
++	struct mt7996_mcu_thermal_notify *n;
++	struct mt7996_phy *phy;
++
++	n = (struct mt7996_mcu_thermal_notify *)skb->data;
++
++	if (n->tag != THERMAL_NOTIFY_TAG)
++		return;
++
++	if (n->event_id != THERMAL_NOTIFY)
++		return;
++
++	if (n->band_idx > MT_BAND2)
++		return;
++
++	mphy = dev->mt76.phys[n->band_idx];
++	if (!mphy)
++		return;
++
++	phy = (struct mt7996_phy *)mphy->priv;
++	phy->throttle_state = n->duty_percent;
++}
++
+ static void
+ mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -487,6 +515,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_RDD_REPORT:
+ 		mt7996_mcu_rx_radar_detected(dev, skb);
+ 		break;
++	case MCU_UNI_EVENT_THERMAL:
++		mt7996_mcu_rx_thermal_notify(dev, skb);
++		break;
+ 	default:
+ 		break;
+ 	}
+@@ -3277,6 +3308,81 @@ out:
+ 	return 0;
+ }
+ 
++
++int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state)
++{
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		struct mt7996_mcu_thermal_ctrl ctrl;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.ctrl = {
++			.band_idx = phy->mt76->band_idx,
++		},
++	};
++	int level, ret;
++
++	/* set duty cycle and level */
++	for (level = 0; level < 4; level++) {
++		req.ctrl.duty.duty_level = level;
++		req.ctrl.duty.duty_cycle = state;
++		state /= 2;
++
++		ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
++					&req, sizeof(req), false);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
++}
++
++int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy)
++{
++#define SUSTAIN_PERIOD		10
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		struct mt7996_mcu_thermal_ctrl ctrl;
++		struct mt7996_mcu_thermal_enable enable;
++	} __packed req = {
++		.len = cpu_to_le16(sizeof(req) - 4 - sizeof(req.enable)),
++		.ctrl = {
++			.band_idx = phy->mt76->band_idx,
++			.type.protect_type = 1,
++			.type.trigger_type = 1,
++		},
++	};
++	int ret;
++
++	req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DISABLE);
++
++	ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
++				&req, sizeof(req) - sizeof(req.enable), false);
++	if (ret)
++		return ret;
++
++	/* set high-temperature trigger threshold */
++	req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_ENABLE);
++	/* add a safety margin ~10 */
++	req.enable.restore_temp = cpu_to_le32(phy->throttle_temp[0] - 10);
++	req.enable.trigger_temp = cpu_to_le32(phy->throttle_temp[1]);
++	req.enable.sustain_time = cpu_to_le16(SUSTAIN_PERIOD);
++
++	req.len = cpu_to_le16(sizeof(req) - 4);
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
++				 &req, sizeof(req), false);
++}
++
+ int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
+ {
+ 	struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index dd0c5ac5..7fefc28f 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -30,6 +30,28 @@ struct mt7996_mcu_uni_event {
+ 	__le32 status; /* 0: success, others: fail */
+ } __packed;
+ 
++struct mt7996_mcu_thermal_ctrl {
++	u8 ctrl_id;
++	u8 band_idx;
++	union {
++		struct {
++			u8 protect_type; /* 1: duty admit, 2: radio off */
++			u8 trigger_type; /* 0: low, 1: high */
++		} __packed type;
++		struct {
++			u8 duty_level;	/* level 0~3 */
++			u8 duty_cycle;
++		} __packed duty;
++	};
++} __packed;
++
++struct mt7996_mcu_thermal_enable {
++	__le32 trigger_temp;
++	__le32 restore_temp;
++	__le16 sustain_time;
++	u8 rsv[2];
++} __packed;
++
+ struct mt7996_mcu_csa_notify {
+ 	struct mt7996_mcu_rxd rxd;
+ 
+@@ -153,6 +175,22 @@ struct mt7996_mcu_mib {
+ 	__le64 data;
+ } __packed;
+ 
++struct mt7996_mcu_thermal_notify {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 __rsv1[4];
++
++	__le16 tag;
++	__le16 len;
++
++	u8 event_id;
++	u8 band_idx;
++	u8 level_idx;
++	u8 duty_percent;
++	__le32 restore_temp;
++	u8 __rsv2[4];
++} __packed;
++
+ enum mt7996_chan_mib_offs {
+ 	UNI_MIB_OBSS_AIRTIME = 26,
+ 	UNI_MIB_NON_WIFI_TIME = 27,
+@@ -642,6 +680,12 @@ enum{
+ 	UNI_CMD_SR_SET_SIGA = 0xd0,
+ };
+ 
++enum {
++	UNI_CMD_THERMAL_PROTECT_ENABLE = 0x6,
++	UNI_CMD_THERMAL_PROTECT_DISABLE,
++	UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
++};
++
+ enum {
+ 	UNI_CMD_ACCESS_REG_BASIC = 0x0,
+ 	UNI_CMD_ACCESS_RF_REG_BASIC,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 997a0bf0..25b20fa6 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -43,6 +43,13 @@
+ #define MT7996_MAX_STA_TWT_AGRT		8
+ #define MT7996_MAX_QUEUE		(__MT_RXQ_MAX +	__MT_MCUQ_MAX + 3)
+ 
++#define MT7996_THERMAL_THROTTLE_MAX	100
++#define MT7996_CDEV_THROTTLE_MAX	99
++#define MT7996_CRIT_TEMP_IDX		0
++#define MT7996_MAX_TEMP_IDX		1
++#define MT7996_CRIT_TEMP		110
++#define MT7996_MAX_TEMP			120
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -211,6 +218,11 @@ struct mt7996_phy {
+ 
+ 	struct ieee80211_vif *monitor_vif;
+ 
++	struct thermal_cooling_device *cdev;
++	u8 cdev_state;
++	u8 throttle_state;
++	u32 throttle_temp[2]; /* 0: critical high, 1: maximum */
++
+ 	u32 rxfilter;
+ 	u64 omac_mask;
+ 
+@@ -437,6 +449,9 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
+ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
+ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
+ int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
++int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
++int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
++int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy);
+ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ 		       u8 rx_sel, u8 val);
+ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0011-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0011-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch
new file mode 100644
index 0000000..3de5b90
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0011-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch
@@ -0,0 +1,184 @@
+From afdb511f0febad0e0cc5572461ab04794cf86852 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 2 Feb 2023 20:53:42 +0800
+Subject: [PATCH 11/19] wifi: mt76: mt7996: add thermal sensor device support
+
+---
+ mt7996/init.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.c  | 41 ++++++++++++++++++++++++
+ 2 files changed, 128 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 5a22cd81..631ada15 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -4,6 +4,8 @@
+  */
+ 
+ #include <linux/etherdevice.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
+ #include <linux/thermal.h>
+ #include "mt7996.h"
+ #include "mac.h"
+@@ -41,6 +43,81 @@ static const struct ieee80211_iface_combination if_comb[] = {
+ 	}
+ };
+ 
++static ssize_t mt7996_thermal_temp_show(struct device *dev,
++					struct device_attribute *attr,
++					char *buf)
++{
++	struct mt7996_phy *phy = dev_get_drvdata(dev);
++	int i = to_sensor_dev_attr(attr)->index;
++	int temperature;
++
++	switch (i) {
++	case 0:
++		temperature = mt7996_mcu_get_temperature(phy);
++		if (temperature < 0)
++			return temperature;
++		/* display in millidegree celcius */
++		return sprintf(buf, "%u\n", temperature * 1000);
++	case 1:
++	case 2:
++		return sprintf(buf, "%u\n",
++			       phy->throttle_temp[i - 1] * 1000);
++	case 3:
++		return sprintf(buf, "%hhu\n", phy->throttle_state);
++	default:
++		return -EINVAL;
++	}
++}
++
++static ssize_t mt7996_thermal_temp_store(struct device *dev,
++					 struct device_attribute *attr,
++					 const char *buf, size_t count)
++{
++	struct mt7996_phy *phy = dev_get_drvdata(dev);
++	int ret, i = to_sensor_dev_attr(attr)->index;
++	long val;
++
++	ret = kstrtol(buf, 10, &val);
++	if (ret < 0)
++		return ret;
++
++	mutex_lock(&phy->dev->mt76.mutex);
++	val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 40, 130);
++
++	if ((i - 1 == MT7996_CRIT_TEMP_IDX &&
++	     val > phy->throttle_temp[MT7996_MAX_TEMP_IDX]) ||
++	    (i - 1 == MT7996_MAX_TEMP_IDX &&
++	     val < phy->throttle_temp[MT7996_CRIT_TEMP_IDX])) {
++		dev_err(phy->dev->mt76.dev,
++			"temp1_max shall be greater than temp1_crit.");
++		mutex_unlock(&phy->dev->mt76.mutex);
++		return -EINVAL;
++	}
++
++	phy->throttle_temp[i - 1] = val;
++	mutex_unlock(&phy->dev->mt76.mutex);
++
++	ret = mt7996_mcu_set_thermal_protect(phy);
++	if (ret)
++		return ret;
++
++	return count;
++}
++
++static SENSOR_DEVICE_ATTR_RO(temp1_input, mt7996_thermal_temp, 0);
++static SENSOR_DEVICE_ATTR_RW(temp1_crit, mt7996_thermal_temp, 1);
++static SENSOR_DEVICE_ATTR_RW(temp1_max, mt7996_thermal_temp, 2);
++static SENSOR_DEVICE_ATTR_RO(throttle1, mt7996_thermal_temp, 3);
++
++static struct attribute *mt7996_hwmon_attrs[] = {
++	&sensor_dev_attr_temp1_input.dev_attr.attr,
++	&sensor_dev_attr_temp1_crit.dev_attr.attr,
++	&sensor_dev_attr_temp1_max.dev_attr.attr,
++	&sensor_dev_attr_throttle1.dev_attr.attr,
++	NULL,
++};
++ATTRIBUTE_GROUPS(mt7996_hwmon);
++
+ static int
+ mt7996_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
+ 				      unsigned long *state)
+@@ -112,6 +189,7 @@ static int mt7996_thermal_init(struct mt7996_phy *phy)
+ {
+ 	struct wiphy *wiphy = phy->mt76->hw->wiphy;
+ 	struct thermal_cooling_device *cdev;
++	struct device *hwmon;
+ 	const char *name;
+ 
+ 	name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7996_%s",
+@@ -130,6 +208,15 @@ static int mt7996_thermal_init(struct mt7996_phy *phy)
+ 	phy->throttle_temp[MT7996_CRIT_TEMP_IDX] = MT7996_CRIT_TEMP;
+ 	phy->throttle_temp[MT7996_MAX_TEMP_IDX] = MT7996_MAX_TEMP;
+ 
++	if (!IS_REACHABLE(CONFIG_HWMON))
++		return 0;
++
++	hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy,
++						       mt7996_hwmon_groups);
++
++	if (IS_ERR(hwmon))
++		return PTR_ERR(hwmon);
++
+ 	return 0;
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 3820a63e..b3326586 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3308,6 +3308,47 @@ out:
+ 	return 0;
+ }
+ 
++int mt7996_mcu_get_temperature(struct mt7996_phy *phy)
++{
++#define TEMPERATURE_QUERY 0
++#define GET_TEMPERATURE 0
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 rsv1;
++		u8 action;
++		u8 band_idx;
++		u8 rsv2;
++	} req = {
++		.tag = cpu_to_le16(TEMPERATURE_QUERY),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.action = GET_TEMPERATURE,
++		.band_idx = phy->mt76->band_idx,
++	};
++	struct mt7996_mcu_thermal {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++
++		__le32 rsv;
++		__le32 temperature;
++	} __packed *res;
++	struct sk_buff *skb;
++	int ret;
++
++	ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
++					&req, sizeof(req), true, &skb);
++	if (ret)
++		return ret;
++
++	res = (void *)skb->data;
++
++	return le32_to_cpu(res->temperature);
++}
+ 
+ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state)
+ {
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-add-802.11s-mesh-amsdu-de-amsdu-sup.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0012-wifi-mt76-mt7996-add-802.11s-mesh-amsdu-de-amsdu-sup.patch
similarity index 78%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-add-802.11s-mesh-amsdu-de-amsdu-sup.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0012-wifi-mt76-mt7996-add-802.11s-mesh-amsdu-de-amsdu-sup.patch
index 70867df..728b4e0 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-add-802.11s-mesh-amsdu-de-amsdu-sup.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0012-wifi-mt76-mt7996-add-802.11s-mesh-amsdu-de-amsdu-sup.patch
@@ -1,7 +1,7 @@
-From 6298046de15b190139552d74aeb668e8cb873b1d Mon Sep 17 00:00:00 2001
+From f78111386b1ac57a459687b9d58e1804a80d4970 Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Thu, 15 Dec 2022 18:22:37 +0800
-Subject: [PATCH 6/7] wifi: mt76: mt7996: add 802.11s mesh amsdu/de-amsdu
+Date: Mon, 6 Feb 2023 11:34:51 +0800
+Subject: [PATCH 12/19] wifi: mt76: mt7996: add 802.11s mesh amsdu/de-amsdu
  support
 
 Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
@@ -12,16 +12,12 @@
  mt7996/mcu.h  |  2 +-
  mt7996/mmio.c |  3 ++-
  5 files changed, 27 insertions(+), 4 deletions(-)
- mode change 100644 => 100755 mt7996/mac.c
- mode change 100644 => 100755 mt7996/mmio.c
 
 diff --git a/mt7996/mac.c b/mt7996/mac.c
-old mode 100644
-new mode 100755
-index 51337dde..d17b5478
+index 40ef5e4b..8dc3a621 100644
 --- a/mt7996/mac.c
 +++ b/mt7996/mac.c
-@@ -630,6 +630,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+@@ -633,6 +633,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
  	u32 rxd4 = le32_to_cpu(rxd[4]);
  	u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM;
  	u32 csum_status = *(u32 *)skb->cb;
@@ -29,7 +25,7 @@
  	bool unicast, insert_ccmp_hdr = false;
  	u8 remove_pad, amsdu_info, band_idx;
  	u8 mode = 0, qos_ctl = 0;
-@@ -822,6 +823,9 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+@@ -825,6 +826,9 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
  
  		skb_pull(skb, hdr_gap);
  		if (!hdr_trans && status->amsdu) {
@@ -39,7 +35,7 @@
  			pad_start = ieee80211_get_hdrlen_from_skb(skb);
  		} else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) {
  			/* When header translation failure is indicated,
-@@ -854,8 +858,17 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+@@ -857,8 +861,17 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
  		hdr = mt76_skb_get_hdr(skb);
  		fc = hdr->frame_control;
  		if (ieee80211_is_data_qos(fc)) {
@@ -59,7 +55,7 @@
  	} else {
  		status->flag |= RX_FLAG_8023;
 diff --git a/mt7996/mac.h b/mt7996/mac.h
-index 470b701a..fd0e5d1c 100644
+index 4914d3e5..e48cc68b 100644
 --- a/mt7996/mac.h
 +++ b/mt7996/mac.h
 @@ -12,6 +12,8 @@
@@ -72,10 +68,10 @@
  #define MT_RXD0_NORMAL_IP_SUM		BIT(23)
  #define MT_RXD0_NORMAL_UDP_TCP_SUM	BIT(24)
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 394dd15c..1e47b0ae 100644
+index b3326586..0dbe2e01 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
-@@ -1022,7 +1022,8 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+@@ -1054,7 +1054,8 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
  	struct tlv *tlv;
  
  	if (vif->type != NL80211_IFTYPE_STATION &&
@@ -85,7 +81,7 @@
  		return;
  
  	if (!sta->deflink.agg.max_amsdu_len)
-@@ -1528,6 +1529,12 @@ mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+@@ -1560,6 +1561,12 @@ mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
  		hdr_trans->to_ds = true;
  		hdr_trans->from_ds = true;
  	}
@@ -99,10 +95,10 @@
  
  static enum mcu_mmps_mode
 diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index dd0c5ac5..009f5f06 100644
+index 7fefc28f..ad66a1f8 100644
 --- a/mt7996/mcu.h
 +++ b/mt7996/mcu.h
-@@ -396,7 +396,7 @@ struct sta_rec_hdr_trans {
+@@ -434,7 +434,7 @@ struct sta_rec_hdr_trans {
  	u8 from_ds;
  	u8 to_ds;
  	u8 dis_rx_hdr_tran;
@@ -112,14 +108,12 @@
  
  struct hdr_trans_en {
 diff --git a/mt7996/mmio.c b/mt7996/mmio.c
-old mode 100644
-new mode 100755
-index d8a2c1a7..08164b1a
+index 902370a2..6610cc45 100644
 --- a/mt7996/mmio.c
 +++ b/mt7996/mmio.c
 @@ -320,7 +320,8 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
  		/* txwi_size = txd size + txp size */
- 		.txwi_size = MT_TXD_SIZE + sizeof(struct mt7996_txp),
+ 		.txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp),
  		.drv_flags = MT_DRV_TXWI_NO_FREE |
 -			     MT_DRV_HW_MGMT_TXQ,
 +			     MT_DRV_HW_MGMT_TXQ |
@@ -128,5 +122,5 @@
  				SURVEY_INFO_TIME_RX |
  				SURVEY_INFO_TIME_BSS_RX,
 -- 
-2.25.1
+2.39.2
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0013-wifi-mt76-mt7996-add-L0.5-system-error-recovery-supp.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0013-wifi-mt76-mt7996-add-L0.5-system-error-recovery-supp.patch
new file mode 100644
index 0000000..93ec12d
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0013-wifi-mt76-mt7996-add-L0.5-system-error-recovery-supp.patch
@@ -0,0 +1,948 @@
+From 6b8dfe2580a782be754be7bcd44da6ad79dc4231 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 13 Feb 2023 18:00:25 +0800
+Subject: [PATCH 13/19] wifi: mt76: mt7996: add L0.5 system error recovery
+ support
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mt7996/debugfs.c | 155 +++++++++++++++++++++++---
+ mt7996/dma.c     |  65 +++++++++++
+ mt7996/init.c    |   9 +-
+ mt7996/mac.c     | 285 +++++++++++++++++++++++++++++++++++++++--------
+ mt7996/main.c    |  18 ++-
+ mt7996/mcu.c     |  22 ++--
+ mt7996/mcu.h     |  28 +++--
+ mt7996/mmio.c    |   7 +-
+ mt7996/mt7996.h  |  17 ++-
+ mt7996/regs.h    |  36 +++++-
+ 10 files changed, 542 insertions(+), 100 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 9c5e9ac1..f2c46a50 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -48,12 +48,12 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7996_implicit_txbf_get,
+ 
+ /* test knob of system error recovery */
+ static ssize_t
+-mt7996_fw_ser_set(struct file *file, const char __user *user_buf,
+-		  size_t count, loff_t *ppos)
++mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
++			size_t count, loff_t *ppos)
+ {
+ 	struct mt7996_phy *phy = file->private_data;
+ 	struct mt7996_dev *dev = phy->dev;
+-	u8 band_idx = phy->mt76->band_idx;
++	bool band = phy->mt76->band_idx;
+ 	char buf[16];
+ 	int ret = 0;
+ 	u16 val;
+@@ -73,17 +73,49 @@ mt7996_fw_ser_set(struct file *file, const char __user *user_buf,
+ 		return -EINVAL;
+ 
+ 	switch (val) {
+-	case SER_SET_RECOVER_L1:
+-	case SER_SET_RECOVER_L2:
+-	case SER_SET_RECOVER_L3_RX_ABORT:
+-	case SER_SET_RECOVER_L3_TX_ABORT:
+-	case SER_SET_RECOVER_L3_TX_DISABLE:
+-	case SER_SET_RECOVER_L3_BF:
+-		ret = mt7996_mcu_set_ser(dev, SER_ENABLE, BIT(val), band_idx);
++	/*
++	 * 0: grab firmware current SER state.
++	 * 1: trigger & enable system error L1 recovery.
++	 * 2: trigger & enable system error L2 recovery.
++	 * 3: trigger & enable system error L3 rx abort.
++	 * 4: trigger & enable system error L3 tx abort.
++	 * 5: trigger & enable system error L3 tx disable.
++	 * 6: trigger & enable system error L3 bf recovery.
++	 * 7: trigger & enable system error L4 MDP recovery.
++	 * 8: trigger & enable system error full recovery.
++	 * 9: trigger firmware crash.
++	 */
++	case UNI_CMD_SER_QUERY:
++		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band);
++		break;
++	case UNI_CMD_SER_SET_RECOVER_L1:
++	case UNI_CMD_SER_SET_RECOVER_L2:
++	case UNI_CMD_SER_SET_RECOVER_L3_RX_ABORT:
++	case UNI_CMD_SER_SET_RECOVER_L3_TX_ABORT:
++	case UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE:
++	case UNI_CMD_SER_SET_RECOVER_L3_BF:
++	case UNI_CMD_SER_SET_RECOVER_L4_MDP:
++		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_SET, BIT(val), band);
++		if (ret)
++			return ret;
++
++		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, val, band);
++		break;
++
++	/* enable full chip reset */
++	case UNI_CMD_SER_SET_RECOVER_FULL:
++		mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
+ 		if (ret)
+ 			return ret;
+ 
+-		ret = mt7996_mcu_set_ser(dev, SER_RECOVER, val, band_idx);
++		dev->recovery.state |= MT_MCU_CMD_WDT_MASK;
++		mt7996_reset(dev);
++		break;
++
++	/* WARNING: trigger firmware crash */
++	case UNI_CMD_SER_SET_SYSTEM_ASSERT:
++		mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR, BIT(18));
++		mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_SOFT_ADDR, BIT(18));
+ 		break;
+ 	default:
+ 		break;
+@@ -92,9 +124,101 @@ mt7996_fw_ser_set(struct file *file, const char __user *user_buf,
+ 	return ret ? ret : count;
+ }
+ 
+-static const struct file_operations mt7996_fw_ser_ops = {
+-	.write = mt7996_fw_ser_set,
+-	/* TODO: ser read */
++static ssize_t
++mt7996_sys_recovery_get(struct file *file, char __user *user_buf,
++			size_t count, loff_t *ppos)
++{
++	struct mt7996_phy *phy = file->private_data;
++	struct mt7996_dev *dev = phy->dev;
++	char *buff;
++	int desc = 0;
++	ssize_t ret;
++	static const size_t bufsz = 1024;
++
++	buff = kmalloc(bufsz, GFP_KERNEL);
++	if (!buff)
++		return -ENOMEM;
++
++	/* HELP */
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "Please echo the correct value ...\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "0: grab firmware transient SER state\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "1: trigger system error L1 recovery\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "2: trigger system error L2 recovery\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "3: trigger system error L3 rx abort\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "4: trigger system error L3 tx abort\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "5: trigger system error L3 tx disable\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "6: trigger system error L3 bf recovery\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "7: trigger system error L4 MDP recovery\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "8: trigger system error full recovery\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "9: trigger firmware crash\n");
++
++	/* SER statistics */
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "\nlet's dump firmware SER statistics...\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_STATUS        = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_SER_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_PLE_ERR       = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_PLE_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_PLE_ERR_1     = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_PLE1_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_PLE_ERR_AMSDU = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_PLE_AMSDU_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_PSE_ERR       = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_PSE_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_PSE_ERR_1     = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_PSE1_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_LMAC_WISR6_B0 = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN0_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_LMAC_WISR6_B1 = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN1_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_LMAC_WISR6_B2 = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN2_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_LMAC_WISR7_B0 = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN0_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_LMAC_WISR7_B1 = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_LMAC_WISR7_B2 = 0x%08x\n",
++			  mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN2_STATS));
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "::E  R , SER_WFDMA_ERR = 0x%08x\n",
++			  mt76_rr(dev, WF_SWDEF_WFDMA_STATUS_ADDR));
++
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "\nSYS_RESET_COUNT: WM %d, WA %d\n",
++			  dev->recovery.wm_reset_count,
++			  dev->recovery.wa_reset_count);
++
++	ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
++	kfree(buff);
++	return ret;
++}
++
++static const struct file_operations mt7996_sys_recovery_ops = {
++	.write = mt7996_sys_recovery_set,
++	.read = mt7996_sys_recovery_get,
+ 	.open = simple_open,
+ 	.llseek = default_llseek,
+ };
+@@ -674,6 +798,8 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 	debugfs_create_file("xmit-queues", 0400, dir, phy,
+ 			    &mt7996_xmit_queues_fops);
+ 	debugfs_create_file("tx_stats", 0400, dir, phy, &mt7996_tx_stats_fops);
++	debugfs_create_file("sys_recovery", 0600, dir, phy,
++			    &mt7996_sys_recovery_ops);
+ 	debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
+ 	debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
+ 	debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
+@@ -684,7 +810,6 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 			    &fops_implicit_txbf);
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
+ 				    mt7996_twt_stats);
+-	debugfs_create_file("fw_ser", 0600, dir, phy, &mt7996_fw_ser_ops);
+ 	debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
+ 
+ 	if (phy->mt76->cap.has_5ghz) {
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index c09fe427..18ea758c 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -352,6 +352,71 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	return 0;
+ }
+ 
++void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
++{
++	struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1];
++	struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2];
++	u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++	int i;
++
++	mt76_clear(dev, MT_WFDMA0_GLO_CFG,
++		   MT_WFDMA0_GLO_CFG_TX_DMA_EN |
++		   MT_WFDMA0_GLO_CFG_RX_DMA_EN);
++
++	if (dev->hif2)
++		mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
++			   MT_WFDMA0_GLO_CFG_TX_DMA_EN |
++			   MT_WFDMA0_GLO_CFG_RX_DMA_EN);
++
++	usleep_range(1000, 2000);
++
++	for (i = 0; i < __MT_TXQ_MAX; i++) {
++		mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
++		if (phy2)
++			mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true);
++		if (phy3)
++			mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true);
++	}
++
++	for (i = 0; i < __MT_MCUQ_MAX; i++)
++		mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
++
++	mt76_for_each_q_rx(&dev->mt76, i)
++		//mt76_queue_rx_reset(dev, i);
++		mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
++
++	mt76_tx_status_check(&dev->mt76, true);
++
++	/* reset wfsys */
++	if (force)
++		mt7996_wfsys_reset(dev);
++
++	mt7996_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 (phy2)
++			mt76_queue_reset(dev, phy2->q_tx[i]);
++		if (phy3)
++			mt76_queue_reset(dev, phy3->q_tx[i]);
++	}
++
++	for (i = 0; i < __MT_MCUQ_MAX; i++)
++		mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
++
++	mt76_for_each_q_rx(&dev->mt76, i) {
++		mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
++	}
++
++	mt76_tx_status_check(&dev->mt76, true);
++
++	mt76_for_each_q_rx(&dev->mt76, i)
++		mt76_queue_rx_reset(dev, i);
++
++	mt7996_dma_enable(dev);
++}
++
+ void mt7996_dma_cleanup(struct mt7996_dev *dev)
+ {
+ 	mt7996_dma_disable(dev, true);
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 631ada15..ced38ac8 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -278,8 +278,7 @@ static void mt7996_led_set_brightness(struct led_classdev *led_cdev,
+ 		mt7996_led_set_config(led_cdev, 0xff, 0);
+ }
+ 
+-static void
+-mt7996_init_txpower(struct mt7996_dev *dev,
++void mt7996_init_txpower(struct mt7996_dev *dev,
+ 		    struct ieee80211_supported_band *sband)
+ {
+ 	int i, nss = hweight8(dev->mphy.antenna_mask);
+@@ -429,7 +428,7 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band)
+ 	mt76_rmw(dev, MT_WTBLOFF_RSCR(band), mask, set);
+ }
+ 
+-static void mt7996_mac_init(struct mt7996_dev *dev)
++void mt7996_mac_init(struct mt7996_dev *dev)
+ {
+ #define HIF_TXD_V2_1	4
+ 	int i;
+@@ -463,7 +462,7 @@ static void mt7996_mac_init(struct mt7996_dev *dev)
+ 		mt7996_mac_init_band(dev, i);
+ }
+ 
+-static int mt7996_txbf_init(struct mt7996_dev *dev)
++int mt7996_txbf_init(struct mt7996_dev *dev)
+ {
+ 	int ret;
+ 
+@@ -1075,6 +1074,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	dev->recovery.hw_init_done = true;
++
+ 	return mt7996_init_debugfs(&dev->phy);
+ }
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 8dc3a621..4c0c8f1e 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1724,7 +1724,7 @@ mt7996_wait_reset_state(struct mt7996_dev *dev, u32 state)
+ 	bool ret;
+ 
+ 	ret = wait_event_timeout(dev->reset_wait,
+-				 (READ_ONCE(dev->reset_state) & state),
++				 (READ_ONCE(dev->recovery.state) & state),
+ 				 MT7996_RESET_TIMEOUT);
+ 
+ 	WARN(!ret, "Timeout waiting for MCU reset state %x\n", state);
+@@ -1773,68 +1773,208 @@ mt7996_update_beacons(struct mt7996_dev *dev)
+ 					    mt7996_update_vif_beacon, phy3->hw);
+ }
+ 
+-static void
+-mt7996_dma_reset(struct mt7996_dev *dev)
++void mt7996_tx_token_put(struct mt7996_dev *dev)
+ {
+-	struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1];
+-	struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2];
+-	u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+-	int i;
++	struct mt76_txwi_cache *txwi;
++	int id;
+ 
+-	mt76_clear(dev, MT_WFDMA0_GLO_CFG,
+-		   MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+-		   MT_WFDMA0_GLO_CFG_RX_DMA_EN);
++	spin_lock_bh(&dev->mt76.token_lock);
++	idr_for_each_entry(&dev->mt76.token, txwi, id) {
++		mt7996_txwi_free(dev, txwi, NULL, NULL);
++		dev->mt76.token_count--;
++	}
++	spin_unlock_bh(&dev->mt76.token_lock);
++	idr_destroy(&dev->mt76.token);
++}
+ 
+-	if (dev->hif2)
+-		mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+-			   MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+-			   MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+ 
+-	usleep_range(1000, 2000);
++static int
++mt7996_mac_restart(struct mt7996_dev *dev)
++{
++	struct mt7996_phy *phy2, *phy3;
++	struct mt76_dev *mdev = &dev->mt76;
++	int i, ret;
+ 
+-	for (i = 0; i < __MT_TXQ_MAX; i++) {
+-		mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
+-		if (phy2)
+-			mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true);
+-		if (phy3)
+-			mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true);
++	phy2 = mt7996_phy2(dev);
++	phy3 = mt7996_phy3(dev);
++
++	if (dev->hif2) {
++		mt76_wr(dev, MT_INT1_MASK_CSR, 0x0);
++		mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
+ 	}
+ 
+-	for (i = 0; i < __MT_MCUQ_MAX; i++)
+-		mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
++	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);
++	}
+ 
+-	mt76_for_each_q_rx(&dev->mt76, i)
+-		mt76_queue_rx_reset(dev, i);
++	set_bit(MT76_RESET, &dev->mphy.state);
++	set_bit(MT76_MCU_RESET, &dev->mphy.state);
++	wake_up(&dev->mt76.mcu.wait);
++	if (phy2) {
++		set_bit(MT76_RESET, &phy2->mt76->state);
++		set_bit(MT76_MCU_RESET, &phy2->mt76->state);
++	}
++	if (phy3) {
++		set_bit(MT76_RESET, &phy3->mt76->state);
++		set_bit(MT76_MCU_RESET, &phy3->mt76->state);
++	}
+ 
+-	mt76_tx_status_check(&dev->mt76, true);
++	/* lock/unlock all queues to ensure that no tx is pending */
++	mt76_txq_schedule_all(&dev->mphy);
++	if (phy2)
++		mt76_txq_schedule_all(phy2->mt76);
++	if (phy3)
++		mt76_txq_schedule_all(phy3->mt76);
++
++	/* 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);
+ 
+-	/* re-init prefetch settings after reset */
+-	mt7996_dma_prefetch(dev);
++	/* token reinit */
++	mt7996_tx_token_put(dev);
++	idr_init(&dev->mt76.token);
+ 
+-	mt76_set(dev, MT_WFDMA0_GLO_CFG,
+-		 MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
++	mt7996_dma_reset(dev, true);
+ 
+-	if (dev->hif2)
+-		mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+-			 MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+-			 MT_WFDMA0_GLO_CFG_RX_DMA_EN);
++	local_bh_disable();
++	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]);
++		}
++	}
++	local_bh_enable();
++	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, dev->mt76.mmio.irqmask);
++		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 = mt7996_mcu_init_firmware(dev);
++	if (ret)
++		goto out;
++
++	/* set the necessary init items */
++	ret = mt7996_mcu_set_eeprom(dev);
++	if (ret)
++		goto out;
++
++	mt7996_mac_init(dev);
++	mt7996_init_txpower(dev, &dev->mphy.sband_2g.sband);
++	mt7996_init_txpower(dev, &dev->mphy.sband_5g.sband);
++	mt7996_init_txpower(dev, &dev->mphy.sband_6g.sband);
++	ret = mt7996_txbf_init(dev);
++
++	if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) {
++		ret = mt7996_run(dev->mphy.hw);
++		if (ret)
++			goto out;
++	}
++
++	if (phy2 && test_bit(MT76_STATE_RUNNING, &phy2->mt76->state)) {
++		ret = mt7996_run(phy2->mt76->hw);
++		if (ret)
++			goto out;
++	}
++
++	if (phy3 && test_bit(MT76_STATE_RUNNING, &phy3->mt76->state)) {
++		ret = mt7996_run(phy3->mt76->hw);
++		if (ret)
++			goto out;
++	}
++
++out:
++	/* reset done */
++	clear_bit(MT76_RESET, &dev->mphy.state);
++	if (phy2)
++		clear_bit(MT76_RESET, &phy2->mt76->state);
++	if (phy3)
++		clear_bit(MT76_RESET, &phy3->mt76->state);
++
++	local_bh_disable();
++	napi_enable(&dev->mt76.tx_napi);
++	napi_schedule(&dev->mt76.tx_napi);
++	local_bh_enable();
++
++	mt76_worker_enable(&dev->mt76.tx_worker);
++	return ret;
+ }
+ 
+-void mt7996_tx_token_put(struct mt7996_dev *dev)
++static void
++mt7996_mac_full_reset(struct mt7996_dev *dev)
+ {
+-	struct mt76_txwi_cache *txwi;
+-	int id;
++	struct mt7996_phy *phy2, *phy3;
++	int i;
+ 
+-	spin_lock_bh(&dev->mt76.token_lock);
+-	idr_for_each_entry(&dev->mt76.token, txwi, id) {
+-		mt7996_txwi_free(dev, txwi, NULL, NULL);
+-		dev->mt76.token_count--;
++	phy2 = mt7996_phy2(dev);
++	phy3 = mt7996_phy3(dev);
++	dev->recovery.hw_full_reset = true;
++
++	wake_up(&dev->mt76.mcu.wait);
++	ieee80211_stop_queues(mt76_hw(dev));
++	if (phy2)
++		ieee80211_stop_queues(phy2->mt76->hw);
++	if (phy3)
++		ieee80211_stop_queues(phy3->mt76->hw);
++
++	cancel_delayed_work_sync(&dev->mphy.mac_work);
++	if (phy2)
++		cancel_delayed_work_sync(&phy2->mt76->mac_work);
++	if (phy3)
++		cancel_delayed_work_sync(&phy3->mt76->mac_work);
++
++	mutex_lock(&dev->mt76.mutex);
++	for (i = 0; i < 10; i++) {
++		if (!mt7996_mac_restart(dev))
++			break;
+ 	}
+-	spin_unlock_bh(&dev->mt76.token_lock);
+-	idr_destroy(&dev->mt76.token);
++	mutex_unlock(&dev->mt76.mutex);
++
++	if (i == 10)
++		dev_err(dev->mt76.dev, "chip full reset failed\n");
++
++	ieee80211_restart_hw(mt76_hw(dev));
++	if (phy2)
++		ieee80211_restart_hw(phy2->mt76->hw);
++	if (phy3)
++		ieee80211_restart_hw(phy3->mt76->hw);
++
++	ieee80211_wake_queues(mt76_hw(dev));
++	if (phy2)
++		ieee80211_wake_queues(phy2->mt76->hw);
++	if (phy3)
++		ieee80211_wake_queues(phy3->mt76->hw);
++
++	dev->recovery.hw_full_reset = false;
++	ieee80211_queue_delayed_work(mt76_hw(dev),
++				     &dev->mphy.mac_work,
++				     MT7996_WATCHDOG_TIME);
++	if (phy2)
++		ieee80211_queue_delayed_work(phy2->mt76->hw,
++					     &phy2->mt76->mac_work,
++					     MT7996_WATCHDOG_TIME);
++	if (phy3)
++		ieee80211_queue_delayed_work(phy3->mt76->hw,
++					     &phy3->mt76->mac_work,
++					     MT7996_WATCHDOG_TIME);
+ }
+ 
+-/* system error recovery */
+ void mt7996_mac_reset_work(struct work_struct *work)
+ {
+ 	struct mt7996_phy *phy2, *phy3;
+@@ -1845,9 +1985,36 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	phy2 = mt7996_phy2(dev);
+ 	phy3 = mt7996_phy3(dev);
+ 
+-	if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA))
++	/* chip full reset */
++	if (dev->recovery.restart) {
++		/* disable WA/WM WDT */
++		mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA,
++			   MT_MCU_CMD_WDT_MASK);
++
++		if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
++			dev->recovery.wa_reset_count++;
++		else
++			dev->recovery.wm_reset_count++;
++
++		mt7996_mac_full_reset(dev);
++
++		/* enable mcu irq */
++		mt7996_irq_enable(dev, MT_INT_MCU_CMD);
++		mt7996_irq_disable(dev, 0);
++
++		/* enable WA/WM WDT */
++		mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
++
++		dev->recovery.state = MT_MCU_CMD_NORMAL_STATE;
++		dev->recovery.restart = false;
+ 		return;
++	}
+ 
++	if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
++		return;
++
++	dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
++		 wiphy_name(dev->mt76.hw->wiphy));
+ 	ieee80211_stop_queues(mt76_hw(dev));
+ 	if (phy2)
+ 		ieee80211_stop_queues(phy2->mt76->hw);
+@@ -1876,7 +2043,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
+ 
+ 	if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
+-		mt7996_dma_reset(dev);
++		mt7996_dma_reset(dev, false);
+ 
+ 		mt7996_tx_token_put(dev);
+ 		idr_init(&dev->mt76.token);
+@@ -1931,6 +2098,32 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 		ieee80211_queue_delayed_work(phy3->mt76->hw,
+ 					     &phy3->mt76->mac_work,
+ 					     MT7996_WATCHDOG_TIME);
++	dev_info(dev->mt76.dev,"\n%s L1 SER recovery completed.",
++		 wiphy_name(dev->mt76.hw->wiphy));
++}
++
++void mt7996_reset(struct mt7996_dev *dev)
++{
++	if (!dev->recovery.hw_init_done)
++		return;
++
++	if (dev->recovery.hw_full_reset)
++		return;
++
++	/* wm/wa exception: do full recovery */
++	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK) {
++		dev->recovery.restart = true;
++		dev_info(dev->mt76.dev,
++			 "%s indicated firmware crash, attempting recovery\n",
++			 wiphy_name(dev->mt76.hw->wiphy));
++
++		mt7996_irq_disable(dev, MT_INT_MCU_CMD);
++		queue_work(dev->mt76.wq, &dev->reset_work);
++		return;
++	}
++
++	queue_work(dev->mt76.wq, &dev->reset_work);
++	wake_up(&dev->reset_wait);
+ }
+ 
+ void mt7996_mac_update_stats(struct mt7996_phy *phy)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index d8d578c0..cb0e0d31 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -22,17 +22,13 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
+ 	return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ }
+ 
+-static int mt7996_start(struct ieee80211_hw *hw)
++int mt7996_run(struct ieee80211_hw *hw)
+ {
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 	bool running;
+ 	int ret;
+ 
+-	flush_work(&dev->init_work);
+-
+-	mutex_lock(&dev->mt76.mutex);
+-
+ 	running = mt7996_dev_running(dev);
+ 	if (!running) {
+ 		ret = mt7996_mcu_set_hdr_trans(dev, true);
+@@ -71,6 +67,18 @@ static int mt7996_start(struct ieee80211_hw *hw)
+ 		mt7996_mac_reset_counters(phy);
+ 
+ out:
++	return ret;
++}
++
++static int mt7996_start(struct ieee80211_hw *hw)
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	int ret;
++
++	flush_work(&dev->init_work);
++
++	mutex_lock(&dev->mt76.mutex);
++	ret = mt7996_run(hw);
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+ 	return ret;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0dbe2e01..a949ff76 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2629,17 +2629,10 @@ mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev)
+ 				     MCU_WM_UNI_CMD(VOW), true);
+ }
+ 
+-int mt7996_mcu_init(struct mt7996_dev *dev)
++int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
+ {
+-	static const struct mt76_mcu_ops mt7996_mcu_ops = {
+-		.headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */
+-		.mcu_skb_send_msg = mt7996_mcu_send_message,
+-		.mcu_parse_response = mt7996_mcu_parse_response,
+-	};
+ 	int ret;
+ 
+-	dev->mt76.mcu_ops = &mt7996_mcu_ops;
+-
+ 	/* force firmware operation mode into normal state,
+ 	 * which should be set before firmware download stage.
+ 	 */
+@@ -2680,6 +2673,19 @@ int mt7996_mcu_init(struct mt7996_dev *dev)
+ 				 MCU_WA_PARAM_RED, 0, 0);
+ }
+ 
++int mt7996_mcu_init(struct mt7996_dev *dev)
++{
++	static const struct mt76_mcu_ops mt7996_mcu_ops = {
++		.headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */
++		.mcu_skb_send_msg = mt7996_mcu_send_message,
++		.mcu_parse_response = mt7996_mcu_parse_response,
++	};
++
++	dev->mt76.mcu_ops = &mt7996_mcu_ops;
++
++	return mt7996_mcu_init_firmware(dev);
++}
++
+ void mt7996_mcu_exit(struct mt7996_dev *dev)
+ {
+ 	mt7996_mcu_restart(&dev->mt76);
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index ad66a1f8..778deedf 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -692,23 +692,21 @@ enum {
+ };
+ 
+ enum {
+-	UNI_CMD_SER_QUERY = 0x0,
+-	UNI_CMD_SER_SET = 0x2,
+-	UNI_CMD_SER_TRIGGER = 0x3,
+-};
+-
+-enum {
+-	SER_QUERY,
++	UNI_CMD_SER_QUERY,
+ 	/* recovery */
+-	SER_SET_RECOVER_L1,
+-	SER_SET_RECOVER_L2,
+-	SER_SET_RECOVER_L3_RX_ABORT,
+-	SER_SET_RECOVER_L3_TX_ABORT,
+-	SER_SET_RECOVER_L3_TX_DISABLE,
+-	SER_SET_RECOVER_L3_BF,
++	UNI_CMD_SER_SET_RECOVER_L1,
++	UNI_CMD_SER_SET_RECOVER_L2,
++	UNI_CMD_SER_SET_RECOVER_L3_RX_ABORT,
++	UNI_CMD_SER_SET_RECOVER_L3_TX_ABORT,
++	UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE,
++	UNI_CMD_SER_SET_RECOVER_L3_BF,
++	UNI_CMD_SER_SET_RECOVER_L4_MDP,
++	UNI_CMD_SER_SET_RECOVER_FULL,
++	UNI_CMD_SER_SET_SYSTEM_ASSERT,
+ 	/* action */
+-	SER_ENABLE = 2,
+-	SER_RECOVER
++	UNI_CMD_SER_ENABLE = 1,
++	UNI_CMD_SER_SET,
++	UNI_CMD_SER_TRIGGER
+ };
+ 
+ enum {
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 6610cc45..0e11f398 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -289,10 +289,9 @@ static void mt7996_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) {
+-			dev->reset_state = val;
+-			ieee80211_queue_work(mt76_hw(dev), &dev->reset_work);
+-			wake_up(&dev->reset_wait);
++		if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) {
++			dev->recovery.state = val;
++			mt7996_reset(dev);
+ 		}
+ 	}
+ }
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 25b20fa6..923e6fc9 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -268,7 +268,14 @@ struct mt7996_dev {
+ 	struct work_struct rc_work;
+ 	struct work_struct reset_work;
+ 	wait_queue_head_t reset_wait;
+-	u32 reset_state;
++	struct {
++		u32 state;
++		u32 wa_reset_count;
++		u32 wm_reset_count;
++		bool hw_full_reset:1;
++		bool hw_init_done:1;
++		bool restart:1;
++	} recovery;
+ 
+ 	struct list_head sta_rc_list;
+ 	struct list_head sta_poll_list;
+@@ -401,9 +408,16 @@ int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
+ 				   struct ieee80211_channel *chan);
+ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band);
+ int mt7996_dma_init(struct mt7996_dev *dev);
++void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
+ void mt7996_dma_prefetch(struct mt7996_dev *dev);
+ void mt7996_dma_cleanup(struct mt7996_dev *dev);
++void mt7996_init_txpower(struct mt7996_dev *dev,
++		    struct ieee80211_supported_band *sband);
++int mt7996_txbf_init(struct mt7996_dev *dev);
++void mt7996_reset(struct mt7996_dev *dev);
++int mt7996_run(struct ieee80211_hw *hw);
+ int mt7996_mcu_init(struct mt7996_dev *dev);
++int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
+ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ 			       struct mt7996_vif *mvif,
+ 			       struct mt7996_twt_flow *flow,
+@@ -496,6 +510,7 @@ static inline void mt7996_irq_disable(struct mt7996_dev *dev, u32 mask)
+ 		mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0);
+ }
+ 
++void mt7996_mac_init(struct mt7996_dev *dev);
+ u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw);
+ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask);
+ void mt7996_mac_reset_counters(struct mt7996_phy *phy);
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 7a28cae3..0775ca58 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -317,6 +317,8 @@ enum base_rev {
+ #define MT_WFDMA0_RX_INT_PCIE_SEL		MT_WFDMA0(0x154)
+ #define MT_WFDMA0_RX_INT_SEL_RING3		BIT(3)
+ 
++#define MT_WFDMA0_MCU_HOST_INT_ENA		MT_WFDMA0(0x1f4)
++
+ #define MT_WFDMA0_GLO_CFG			MT_WFDMA0(0x208)
+ #define MT_WFDMA0_GLO_CFG_TX_DMA_EN		BIT(0)
+ #define MT_WFDMA0_GLO_CFG_RX_DMA_EN		BIT(2)
+@@ -444,6 +446,10 @@ enum base_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)
++
+ /* l1/l2 remap */
+ #define MT_HIF_REMAP_L1				0x155024
+ #define MT_HIF_REMAP_L1_MASK			GENMASK(31, 16)
+@@ -468,8 +474,27 @@ enum base_rev {
+ #define MT_INFRA_MCU_END			0x7c3fffff
+ 
+ /* FW MODE SYNC */
+-#define MT_SWDEF_MODE				0x9143c
++#define MT_SWDEF_BASE				0x00401400
++
++#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_STATS			MT_SWDEF(0x040)
++#define MT_SWDEF_PLE_STATS			MT_SWDEF(0x044)
++#define MT_SWDEF_PLE1_STATS			MT_SWDEF(0x048)
++#define MT_SWDEF_PLE_AMSDU_STATS		MT_SWDEF(0x04C)
++#define MT_SWDEF_PSE_STATS			MT_SWDEF(0x050)
++#define MT_SWDEF_PSE1_STATS			MT_SWDEF(0x054)
++#define MT_SWDEF_LAMC_WISR6_BN0_STATS		MT_SWDEF(0x058)
++#define MT_SWDEF_LAMC_WISR6_BN1_STATS		MT_SWDEF(0x05C)
++#define MT_SWDEF_LAMC_WISR6_BN2_STATS		MT_SWDEF(0x060)
++#define MT_SWDEF_LAMC_WISR7_BN0_STATS		MT_SWDEF(0x064)
++#define MT_SWDEF_LAMC_WISR7_BN1_STATS		MT_SWDEF(0x068)
++#define MT_SWDEF_LAMC_WISR7_BN2_STATS		MT_SWDEF(0x06C)
++#define WF_SWDEF_WFDMA_STATUS_ADDR		MT_SWDEF(0x090)
+ 
+ /* LED */
+ #define MT_LED_TOP_BASE				0x18013000
+@@ -506,7 +531,7 @@ enum base_rev {
+ #define MT_TOP_MISC_FW_STATE			GENMASK(2, 0)
+ 
+ #define MT_HW_REV				0x70010204
+-#define MT_WF_SUBSYS_RST			0x70002600
++#define MT_WF_SUBSYS_RST			0x70028600
+ 
+ /* PCIE MAC */
+ #define MT_PCIE_MAC_BASE			0x74030000
+@@ -539,4 +564,11 @@ enum base_rev {
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY	BIT(18)
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR		BIT(29)
+ 
++#define MT_MCU_WM_CIRQ_BASE			0x89010000
++#define MT_MCU_WM_CIRQ(ofs)			(MT_MCU_WM_CIRQ_BASE + (ofs))
++#define MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR	MT_MCU_WM_CIRQ(0x80)
++#define MT_MCU_WM_CIRQ_IRQ_SOFT_ADDR		MT_MCU_WM_CIRQ(0xc0)
++#define MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR	MT_MCU_WM_CIRQ(0x108)
++#define MT_MCU_WM_CIRQ_EINT_SOFT_ADDR		MT_MCU_WM_CIRQ(0x118)
++
+ #endif
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-add-dsp-firmware-download.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-add-dsp-firmware-download.patch
new file mode 100644
index 0000000..8193a5a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-add-dsp-firmware-download.patch
@@ -0,0 +1,194 @@
+From 2d6ca0f6974ad4f6dcf110ccbbcbbc7185aa7655 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 17 Feb 2023 14:13:38 +0800
+Subject: [PATCH 14/19] wifi: mt76: mt7996: add dsp firmware download
+
+Add DSP firmware for phy related control. Without this patch,the
+firmware state would not be ready.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/mcu.c      | 94 +++++++++++++++++++++--------------------------
+ mt7996/mt7996.h   |  7 ++++
+ mt7996/pci.c      |  1 +
+ 4 files changed, 50 insertions(+), 53 deletions(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index fa10d823..79dde31a 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -22,6 +22,7 @@
+ 
+ #define FW_START_OVERRIDE		BIT(0)
+ #define FW_START_WORKING_PDA_CR4	BIT(2)
++#define FW_START_WORKING_PDA_DSP	BIT(3)
+ 
+ #define PATCH_SEC_NOT_SUPPORT		GENMASK(31, 0)
+ #define PATCH_SEC_TYPE_MASK		GENMASK(15, 0)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index a949ff76..09800ff2 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2353,7 +2353,7 @@ out:
+ static int
+ mt7996_mcu_send_ram_firmware(struct mt7996_dev *dev,
+ 			     const struct mt7996_fw_trailer *hdr,
+-			     const u8 *data, bool is_wa)
++			     const u8 *data, enum mt7996_ram_type type)
+ {
+ 	int i, offset = 0;
+ 	u32 override = 0, option = 0;
+@@ -2365,8 +2365,10 @@ mt7996_mcu_send_ram_firmware(struct mt7996_dev *dev,
+ 
+ 		region = (const struct mt7996_fw_region *)((const u8 *)hdr -
+ 			 (hdr->n_region - i) * sizeof(*region));
++		/* DSP and WA use same mode */
+ 		mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76,
+-						   region->feature_set, is_wa);
++						   region->feature_set,
++						   type != MT7996_RAM_TYPE_WM);
+ 		len = le32_to_cpu(region->len);
+ 		addr = le32_to_cpu(region->addr);
+ 
+@@ -2393,8 +2395,10 @@ mt7996_mcu_send_ram_firmware(struct mt7996_dev *dev,
+ 	if (override)
+ 		option |= FW_START_OVERRIDE;
+ 
+-	if (is_wa)
++	if (type == MT7996_RAM_TYPE_WA)
+ 		option |= FW_START_WORKING_PDA_CR4;
++	else if (type == MT7996_RAM_TYPE_DSP)
++		option |= FW_START_WORKING_PDA_DSP;
+ 
+ 	return mt76_connac_mcu_start_firmware(&dev->mt76, override, option);
+ }
+@@ -2405,56 +2409,40 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
+ 	const struct firmware *fw;
+ 	int ret;
+ 
+-	ret = request_firmware(&fw, MT7996_FIRMWARE_WM, dev->mt76.dev);
+-	if (ret)
+-		return ret;
+-
+-	if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+-		dev_err(dev->mt76.dev, "Invalid firmware\n");
+-		ret = -EINVAL;
+-		goto out;
+-	}
+-
+-	hdr = (const struct mt7996_fw_trailer *)(fw->data + fw->size - sizeof(*hdr));
+-
+-	dev_info(dev->mt76.dev, "WM Firmware Version: %.10s, Build Time: %.15s\n",
+-		 hdr->fw_ver, hdr->build_date);
+-
+-	ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, false);
+-	if (ret) {
+-		dev_err(dev->mt76.dev, "Failed to start WM firmware\n");
+-		goto out;
+-	}
+-
+-	release_firmware(fw);
+-
+-	ret = request_firmware(&fw, MT7996_FIRMWARE_WA, dev->mt76.dev);
+-	if (ret)
+-		return ret;
+-
+-	if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+-		dev_err(dev->mt76.dev, "Invalid firmware\n");
+-		ret = -EINVAL;
+-		goto out;
+-	}
+-
+-	hdr = (const struct mt7996_fw_trailer *)(fw->data + fw->size - sizeof(*hdr));
+-
+-	dev_info(dev->mt76.dev, "WA Firmware Version: %.10s, Build Time: %.15s\n",
+-		 hdr->fw_ver, hdr->build_date);
+-
+-	ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, true);
+-	if (ret) {
+-		dev_err(dev->mt76.dev, "Failed to start WA firmware\n");
+-		goto out;
+-	}
+-
+-	snprintf(dev->mt76.hw->wiphy->fw_version,
+-		 sizeof(dev->mt76.hw->wiphy->fw_version),
+-		 "%.10s-%.15s", hdr->fw_ver, hdr->build_date);
+-
+-out:
+-	release_firmware(fw);
++#define LOAD_RAM(_type) \
++	do {									\
++		ret = request_firmware(&fw, MT7996_FIRMWARE_##_type, dev->mt76.dev);	\
++		if (ret)							\
++			return ret;						\
++										\
++		if (!fw || !fw->data || fw->size < sizeof(*hdr)) {		\
++			dev_err(dev->mt76.dev, "Invalid firmware\n");		\
++			release_firmware(fw);					\
++			return -EINVAL;						\
++		}								\
++										\
++		hdr = (const struct mt7996_fw_trailer *)			\
++				(fw->data + fw->size - sizeof(*hdr));		\
++										\
++		dev_info(dev->mt76.dev,						\
++			 "%s Firmware Version: %.10s, Build Time: %.15s\n",	\
++			 #_type, hdr->fw_ver, hdr->build_date);			\
++										\
++		ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data,		\
++						   MT7996_RAM_TYPE_##_type);    \
++		if (ret) {							\
++			dev_err(dev->mt76.dev, "Failed to start %s firmware\n", #_type);\
++			release_firmware(fw);					\
++			return ret;						\
++		}								\
++										\
++		release_firmware(fw);						\
++	} while (0)
++
++	LOAD_RAM(WM);
++	LOAD_RAM(DSP);
++	LOAD_RAM(WA);
++#undef LOAD_RAM
+ 
+ 	return ret;
+ }
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 923e6fc9..c2f8900c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -29,6 +29,7 @@
+ 
+ #define MT7996_FIRMWARE_WA		"mediatek/mt7996/mt7996_wa.bin"
+ #define MT7996_FIRMWARE_WM		"mediatek/mt7996/mt7996_wm.bin"
++#define MT7996_FIRMWARE_DSP		"mediatek/mt7996/mt7996_dsp.bin"
+ #define MT7996_ROM_PATCH		"mediatek/mt7996/mt7996_rom_patch.bin"
+ 
+ #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
+@@ -55,6 +56,12 @@ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+ struct mt7996_dfs_pattern;
+ 
++enum mt7996_ram_type {
++	MT7996_RAM_TYPE_WM = 0,
++	MT7996_RAM_TYPE_WA,
++	MT7996_RAM_TYPE_DSP,
++};
++
+ enum mt7996_txq_id {
+ 	MT7996_TXQ_FWDL = 16,
+ 	MT7996_TXQ_MCU_WM,
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index 64aee3fb..c5301050 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -219,4 +219,5 @@ MODULE_DEVICE_TABLE(pci, mt7996_pci_device_table);
+ MODULE_DEVICE_TABLE(pci, mt7996_hif_device_table);
+ MODULE_FIRMWARE(MT7996_FIRMWARE_WA);
+ MODULE_FIRMWARE(MT7996_FIRMWARE_WM);
++MODULE_FIRMWARE(MT7996_FIRMWARE_DSP);
+ MODULE_FIRMWARE(MT7996_ROM_PATCH);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-fix-eeprom-antenna-bitfield-mask.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-fix-eeprom-antenna-bitfield-mask.patch
new file mode 100644
index 0000000..236cada
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-fix-eeprom-antenna-bitfield-mask.patch
@@ -0,0 +1,33 @@
+From 763303e5fb846c92c22dd19ffec95d844103e425 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 23 Feb 2023 19:18:45 +0800
+Subject: [PATCH 15/19] wifi: mt76: mt7996: fix eeprom antenna bitfield mask
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/eeprom.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 8da599e0..cfc48698 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -31,11 +31,11 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CONF2_BAND_SEL		GENMASK(2, 0)
+ 
+ #define MT_EE_WIFI_CONF1_TX_PATH_BAND0		GENMASK(5, 3)
+-#define MT_EE_WIFI_CONF2_TX_PATH_BAND1		GENMASK(5, 3)
+-#define MT_EE_WIFI_CONF2_TX_PATH_BAND2		GENMASK(2, 0)
++#define MT_EE_WIFI_CONF2_TX_PATH_BAND1		GENMASK(2, 0)
++#define MT_EE_WIFI_CONF2_TX_PATH_BAND2		GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF4_STREAM_NUM_BAND0	GENMASK(5, 3)
+-#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND1	GENMASK(5, 3)
+-#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND2	GENMASK(2, 0)
++#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND1	GENMASK(2, 0)
++#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND2	GENMASK(5, 3)
+ 
+ #define MT_EE_RATE_DELTA_MASK			GENMASK(5, 0)
+ #define MT_EE_RATE_DELTA_SIGN			BIT(6)
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-mt7996-init-mpdu-density.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-mt7996-init-mpdu-density.patch
new file mode 100644
index 0000000..74992ab
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-mt7996-init-mpdu-density.patch
@@ -0,0 +1,53 @@
+From 322307272a4c4ccb627d6596220b0ab5bd4aed45 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 13 Feb 2023 09:46:40 +0800
+Subject: [PATCH 16/19] wifi: mt76: mt7996: init mpdu density
+
+Init mpdu density based on the hardware capability to
+prevent hardware drop.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/init.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index ced38ac8..479b2cee 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -374,10 +374,13 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ 
+ 	hw->max_tx_fragments = 4;
+ 
+-	if (phy->mt76->cap.has_2ghz)
++	if (phy->mt76->cap.has_2ghz) {
+ 		phy->mt76->sband_2g.sband.ht_cap.cap |=
+ 			IEEE80211_HT_CAP_LDPC_CODING |
+ 			IEEE80211_HT_CAP_MAX_AMSDU;
++		phy->mt76->sband_2g.sband.ht_cap.ampdu_density =
++			IEEE80211_HT_MPDU_DENSITY_2;
++	}
+ 
+ 	if (phy->mt76->cap.has_5ghz) {
+ 		phy->mt76->sband_5g.sband.ht_cap.cap |=
+@@ -389,6 +392,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ 			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
+ 			IEEE80211_VHT_CAP_SHORT_GI_160 |
+ 			IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
++		phy->mt76->sband_5g.sband.ht_cap.ampdu_density =
++			IEEE80211_HT_MPDU_DENSITY_1;
+ 	}
+ 
+ 	mt76_set_stream_caps(phy->mt76, true);
+@@ -873,7 +878,7 @@ mt7996_init_he_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 		u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS |
+ 			  IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS;
+ 
+-		cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_2,
++		cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_0_5,
+ 				       IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) |
+ 		       u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K,
+ 				       IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) |
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-fix-icv-error-when-enable-AP-and-ST.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-fix-icv-error-when-enable-AP-and-ST.patch
new file mode 100644
index 0000000..0b179f4
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-fix-icv-error-when-enable-AP-and-ST.patch
@@ -0,0 +1,71 @@
+From b4af2c843368e7a787fc02ebde5a8ff41edf0a76 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 2 Mar 2023 15:44:52 +0800
+Subject: [PATCH 17/19] wifi: mt76: mt7996: fix icv error when enable AP and
+ STA simultaneously
+
+Fix mcu command content to prevent ICV error
+1. The legacy mld index needs to start from 16.
+2. The bmc_tx_wlan_idx needs to be the vif index rather
+than peer AP's index.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/mcu.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 09800ff2..829f7be6 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -664,6 +664,7 @@ mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en)
+ static void
+ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+ {
++#define MT7996_LEGACY_MLD_IDX_START 16
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct bss_mld_tlv *mld;
+ 	struct tlv *tlv;
+@@ -672,8 +673,12 @@ mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+ 
+ 	mld = (struct bss_mld_tlv *)tlv;
+ 	mld->group_mld_id = 0xff;
+-	mld->own_mld_id = mvif->mt76.idx;
++	mld->own_mld_id = MT7996_LEGACY_MLD_IDX_START + mvif->mt76.idx;
+ 	mld->remap_idx = 0xff;
++	if (vif->type == NL80211_IFTYPE_AP) {
++		mld->group_mld_id = MT7996_LEGACY_MLD_IDX_START + mvif->mt76.idx;
++		memcpy(mld->mac_addr, vif->bss_conf.bssid, ETH_ALEN);
++	}
+ }
+ 
+ static void
+@@ -744,6 +749,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ 	struct cfg80211_chan_def *chandef = &phy->chandef;
+ 	struct mt76_connac_bss_basic_tlv *bss;
+ 	u32 type = CONNECTION_INFRA_AP;
++	u16 sta_wlan_idx = wlan_idx;
+ 	struct tlv *tlv;
+ 	int idx;
+ 
+@@ -763,7 +769,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ 				struct mt76_wcid *wcid;
+ 
+ 				wcid = (struct mt76_wcid *)sta->drv_priv;
+-				wlan_idx = wcid->idx;
++				sta_wlan_idx = wcid->idx;
+ 			}
+ 			rcu_read_unlock();
+ 		}
+@@ -783,7 +789,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ 	bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+ 	bss->dtim_period = vif->bss_conf.dtim_period;
+ 	bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
+-	bss->sta_idx = cpu_to_le16(wlan_idx);
++	bss->sta_idx = cpu_to_le16(sta_wlan_idx);
+ 	bss->conn_type = cpu_to_le32(type);
+ 	bss->omac_idx = mvif->omac_idx;
+ 	bss->band_idx = mvif->band_idx;
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-set-wcid-in-txp.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-set-wcid-in-txp.patch
new file mode 100644
index 0000000..74f4e9e
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-set-wcid-in-txp.patch
@@ -0,0 +1,35 @@
+From 68649746f1862d6c9b3e06492789aadde8c4d2e5 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 6 Mar 2023 15:52:26 +0800
+Subject: [PATCH 18/19] wifi: mt76: mt7996: set wcid in txp
+
+Set correct wcid in txp for SDO to get wtbl.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7996/mac.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 4c0c8f1e..bb23f531 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1168,10 +1168,12 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	}
+ 
+ 	txp->fw.token = cpu_to_le16(id);
+-	if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags))
+-		txp->fw.rept_wds_wcid = cpu_to_le16(wcid->idx);
+-	else
++	if ((is_8023 && is_multicast_ether_addr(tx_info->skb->data)) ||
++	     is_multicast_ether_addr(hdr->addr1))
+ 		txp->fw.rept_wds_wcid = cpu_to_le16(0xfff);
++	else
++		txp->fw.rept_wds_wcid = cpu_to_le16(wcid->idx);
++
+ 	tx_info->skb = DMA_DUMMY_DATA;
+ 
+ 	/* pass partial skb header to fw */
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0007-mt76-revert-page-pool-changes.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0019-mt76-revert-page-pool-changes.patch
similarity index 92%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0007-mt76-revert-page-pool-changes.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0019-mt76-revert-page-pool-changes.patch
index ab1161f..20feead 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0007-mt76-revert-page-pool-changes.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0019-mt76-revert-page-pool-changes.patch
@@ -1,7 +1,7 @@
-From 60c4e64c0198c828fafd5e7b96027ad5fd633213 Mon Sep 17 00:00:00 2001
+From 175bf122ac5790e7e28dadecd9410370364bc16a Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Mon, 6 Feb 2023 15:34:43 +0800
-Subject: [PATCH 7/7] mt76: revert page pool changes
+Subject: [PATCH 19/19] mt76: revert page pool changes
 
 ---
  dma.c         | 72 ++++++++++++++++++++++++++-------------------------
@@ -10,11 +10,11 @@
  mt7915/main.c | 26 +++++++------------
  mt7915/mmio.c | 55 ++++++++++++++++++++++++---------------
  mt7921/main.c | 31 +++-------------------
- usb.c         | 42 ++++++++++++++++--------------
- 7 files changed, 108 insertions(+), 197 deletions(-)
+ usb.c         | 43 +++++++++++++++---------------
+ 7 files changed, 108 insertions(+), 198 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index e3fa4f39..50a7a689 100644
+index df2ca73f..7357b398 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -173,7 +173,7 @@ mt76_free_pending_rxwi(struct mt76_dev *dev)
@@ -39,7 +39,7 @@
  
  		buf = t->ptr;
  		t->dma_addr = 0;
-@@ -428,9 +428,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+@@ -430,9 +430,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
  	} else {
  		buf = e->buf;
  		e->buf = NULL;
@@ -52,7 +52,7 @@
  	}
  
  	return buf;
-@@ -582,11 +582,11 @@ free_skb:
+@@ -584,11 +584,11 @@ free_skb:
  }
  
  static int
@@ -67,7 +67,7 @@
  
  	if (!q->ndesc)
  		return 0;
-@@ -594,25 +594,26 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -596,25 +596,26 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
  	spin_lock_bh(&q->lock);
  
  	while (q->queued < q->ndesc - 1) {
@@ -105,7 +105,7 @@
  			break;
  		}
  		frames++;
-@@ -656,7 +657,7 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+@@ -658,7 +659,7 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
  		/* WED txfree queue needs ring to be initialized before setup */
  		q->flags = 0;
  		mt76_dma_queue_reset(dev, q);
@@ -114,7 +114,7 @@
  		q->flags = flags;
  
  		ret = mtk_wed_device_txfree_ring_setup(wed, q->regs);
-@@ -704,10 +705,6 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -706,10 +707,6 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
  	if (!q->entry)
  		return -ENOMEM;
  
@@ -125,7 +125,7 @@
  	ret = mt76_dma_wed_setup(dev, q, false);
  	if (ret)
  		return ret;
-@@ -721,6 +718,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -723,6 +720,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
  static void
  mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
  {
@@ -133,7 +133,7 @@
  	void *buf;
  	bool more;
  
-@@ -734,7 +732,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -736,7 +734,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
  		if (!buf)
  			break;
  
@@ -142,7 +142,7 @@
  	} while (1);
  
  	if (q->rx_head) {
-@@ -743,6 +741,13 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -745,6 +743,13 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
  	}
  
  	spin_unlock_bh(&q->lock);
@@ -156,7 +156,7 @@
  }
  
  static void
-@@ -763,7 +768,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+@@ -765,7 +770,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
  	mt76_dma_wed_setup(dev, q, true);
  	if (q->flags != MT_WED_Q_TXFREE) {
  		mt76_dma_sync_idx(dev, q);
@@ -165,7 +165,7 @@
  	}
  }
  
-@@ -781,7 +786,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+@@ -783,7 +788,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
  
  		skb_add_rx_frag(skb, nr_frags, page, offset, len, q->buf_size);
  	} else {
@@ -174,7 +174,7 @@
  	}
  
  	if (more)
-@@ -854,7 +859,6 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -856,7 +861,6 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
  			goto free_frag;
  
  		skb_reserve(skb, q->buf_offset);
@@ -182,7 +182,7 @@
  
  		*(u32 *)skb->cb = info;
  
-@@ -870,10 +874,10 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -872,10 +876,10 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
  		continue;
  
  free_frag:
@@ -195,7 +195,7 @@
  	return done;
  }
  
-@@ -918,7 +922,7 @@ mt76_dma_init(struct mt76_dev *dev,
+@@ -920,7 +924,7 @@ mt76_dma_init(struct mt76_dev *dev,
  
  	mt76_for_each_q_rx(dev, i) {
  		netif_napi_add(&dev->napi_dev, &dev->napi[i], poll);
@@ -204,7 +204,7 @@
  		napi_enable(&dev->napi[i]);
  	}
  
-@@ -969,8 +973,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+@@ -971,8 +975,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
  
  		netif_napi_del(&dev->napi[i]);
  		mt76_dma_rx_cleanup(dev, q);
@@ -214,7 +214,7 @@
  
  	mt76_free_pending_txwi(dev);
 diff --git a/mac80211.c b/mac80211.c
-index b117e446..8a5f21e8 100644
+index e53166fc..d69e7214 100644
 --- a/mac80211.c
 +++ b/mac80211.c
 @@ -4,7 +4,6 @@
@@ -225,7 +225,7 @@
  #include "mt76.h"
  
  #define CHAN2G(_idx, _freq) {			\
-@@ -557,47 +556,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
+@@ -562,47 +561,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
  }
  EXPORT_SYMBOL_GPL(mt76_unregister_phy);
  
@@ -273,7 +273,7 @@
  struct mt76_dev *
  mt76_alloc_device(struct device *pdev, unsigned int size,
  		  const struct ieee80211_ops *ops,
-@@ -1732,21 +1690,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
+@@ -1746,21 +1704,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
  }
  EXPORT_SYMBOL_GPL(mt76_ethtool_worker);
  
@@ -296,7 +296,7 @@
  {
  	struct ieee80211_hw *hw = phy->hw;
 diff --git a/mt76.h b/mt76.h
-index ccca0162..f6a32fe3 100644
+index c3d1313e..49da2caa 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -202,7 +202,7 @@ struct mt76_queue {
@@ -308,7 +308,7 @@
  };
  
  struct mt76_mcu_ops {
-@@ -1318,7 +1318,6 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len,
+@@ -1329,7 +1329,6 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len,
  	return usb_bulk_msg(udev, pipe, data, len, actual_len, timeout);
  }
  
@@ -316,7 +316,7 @@
  void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
  			 struct mt76_sta_stats *stats, bool eht);
  int mt76_skb_adjust_pad(struct sk_buff *skb, int pad);
-@@ -1430,25 +1429,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+@@ -1441,25 +1440,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
  struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
  int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
  			  struct mt76_txwi_cache *r, dma_addr_t phys);
@@ -401,7 +401,7 @@
  
  static void
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 225a1960..cbe6e2cd 100644
+index 6f0c0e2a..5ef43c44 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -596,9 +596,13 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed)
@@ -505,7 +505,7 @@
  	return -ENOMEM;
  }
 diff --git a/mt7921/main.c b/mt7921/main.c
-index 75eaf86c..1434ca79 100644
+index a72964e7..4c400223 100644
 --- a/mt7921/main.c
 +++ b/mt7921/main.c
 @@ -1090,34 +1090,17 @@ static void
@@ -571,7 +571,7 @@
  
  static u64
 diff --git a/usb.c b/usb.c
-index b88959ef..3e281715 100644
+index 5e5c7bf5..3e281715 100644
 --- a/usb.c
 +++ b/usb.c
 @@ -319,27 +319,29 @@ mt76u_set_endpoints(struct usb_interface *intf,
@@ -688,11 +688,12 @@
  	int i;
  
  	for (i = 0; i < q->ndesc; i++) {
-@@ -705,7 +701,13 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -705,8 +701,13 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
  		mt76u_urb_free(q->entry[i].urb);
  		q->entry[i].urb = NULL;
  	}
 -	page_pool_destroy(q->page_pool);
+-	q->page_pool = NULL;
 +
 +	if (!q->rx_page.va)
 +		return;
@@ -704,5 +705,5 @@
  
  static void mt76u_free_rx(struct mt76_dev *dev)
 -- 
-2.25.1
+2.39.2
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0999-mt76-mt7996-for-build-pass.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0999-mt76-mt7996-for-build-pass.patch
index dd28dc9..2f9a0c3 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0999-mt76-mt7996-for-build-pass.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0999-mt76-mt7996-for-build-pass.patch
@@ -1,4 +1,4 @@
-From e66fbcb72e09e9e6a88fcedc84f4eda0d53ef65d Mon Sep 17 00:00:00 2001
+From a126ad08af718c71b4b05261caefb68996044330 Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Thu, 3 Nov 2022 00:27:17 +0800
 Subject: [PATCH] mt76: mt7996: for build pass
@@ -6,16 +6,15 @@
 Change-Id: Ieb44c33ee6e6a2e6058c1ef528404c1a1cbcfdaf
 ---
  debugfs.c         | 3 +++
- dma.c             | 2 +-
  eeprom.c          | 8 +++++++-
  mcu.c             | 1 +
  mt7615/mcu.c      | 1 +
  mt76_connac_mcu.c | 1 +
  mt7915/mcu.c      | 1 +
  mt7996/dma.c      | 4 ++--
- mt7996/eeprom.c   | 1 +
+ mt7996/eeprom.c   | 2 ++
  mt7996/mcu.c      | 1 +
- 10 files changed, 19 insertions(+), 4 deletions(-)
+ 9 files changed, 19 insertions(+), 3 deletions(-)
 
 diff --git a/debugfs.c b/debugfs.c
 index 79064a4d..e10d4cbc 100644
@@ -33,19 +32,6 @@
  
  	return 0;
  }
-diff --git a/dma.c b/dma.c
-index 50a7a689..beb9f4a4 100644
---- a/dma.c
-+++ b/dma.c
-@@ -854,7 +854,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
- 		    !(dev->drv->rx_check(dev, data, len)))
- 			goto free_frag;
- 
--		skb = napi_build_skb(data, q->buf_size);
-+		skb = build_skb(data, q->buf_size);
- 		if (!skb)
- 			goto free_frag;
- 
 diff --git a/eeprom.c b/eeprom.c
 index ea54b7af..90d36c8d 100644
 --- a/eeprom.c
@@ -92,7 +78,7 @@
  static bool prefer_offload_fw = true;
  module_param(prefer_offload_fw, bool, 0644);
 diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index efb9bfaa..4a650721 100644
+index 4e4f6b35..e5815227 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -4,6 +4,7 @@
@@ -104,7 +90,7 @@
  int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option)
  {
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index f151ce86..20d81f05 100644
+index d08907f5..99ef8c9c 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -6,6 +6,7 @@
@@ -116,7 +102,7 @@
  #define fw_name(_dev, name, ...)	({			\
  	char *_fw;						\
 diff --git a/mt7996/dma.c b/mt7996/dma.c
-index c09fe427..8c2e060d 100644
+index 18ea758c..3e2967f7 100644
 --- a/mt7996/dma.c
 +++ b/mt7996/dma.c
 @@ -343,8 +343,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
@@ -131,19 +117,20 @@
  
  	mt7996_dma_enable(dev);
 diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 2e48c5a4..dd322f62 100644
+index 2e48c5a4..e747cb9f 100644
 --- a/mt7996/eeprom.c
 +++ b/mt7996/eeprom.c
-@@ -98,6 +98,7 @@ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
+@@ -98,6 +98,8 @@ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
  	if (ret)
  		return ret;
  
-+	cap = 0x4b249248;	/* internal hardcode */
++	/* for internal testing */
++	cap = 0x4b249248;
  	if (cap) {
  		dev->has_eht = !(cap & MODE_HE_ONLY);
  		dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 1e47b0ae..db715195 100644
+index 829f7be6..0d9d309f 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
 @@ -5,6 +5,7 @@
@@ -155,5 +142,5 @@
  #include "mcu.h"
  #include "mac.h"
 -- 
-2.25.1
+2.39.2
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc b/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
index cf59945..51df096 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
@@ -1,12 +1,23 @@
 #patch patches (come from openwrt/lede/target/linux/mediatek)
 SRC_URI_append = " \
-    file://0000-sync-to-master.patch \
-    file://0001-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_tx_compl.patch \
-    file://0002-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_txp_skb_.patch \
-    file://0003-Revert-wifi-mt76-mt7996-rely-on-mt76_connac_txp_comm.patch \
-    file://0004-wifi-mt76-mt7996-add-muru-support.patch \
-    file://0005-wifi-mt76-mt7996-set-txd-v1.patch \
-    file://0006-wifi-mt76-mt7996-add-802.11s-mesh-amsdu-de-amsdu-sup.patch \
-    file://0007-mt76-revert-page-pool-changes.patch \
+    file://0001-wifi-mt76-mt7996-add-eht-rx-rate-support.patch \
+    file://0002-wifi-mt76-mt7996-let-non-bufferable-MMPDUs-use-corre.patch \
+    file://0003-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch \
+    file://0004-wifi-mt76-mt7996-move-radio-enable-command-to-mt7996.patch \
+    file://0005-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch \
+    file://0006-wifi-mt76-mt7996-fix-pointer-calculation-in-ie-count.patch \
+    file://0007-wifi-mt76-mt7996-remove-mt7996_mcu_set_pm.patch \
+    file://0008-wifi-mt76-mt7996-add-muru-support.patch \
+    file://0009-wifi-mt76-mt7996-set-txd-v1.patch \
+    file://0010-wifi-mt76-mt7996-add-thermal-protection-support.patch \
+    file://0011-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch \
+    file://0012-wifi-mt76-mt7996-add-802.11s-mesh-amsdu-de-amsdu-sup.patch \
+    file://0013-wifi-mt76-mt7996-add-L0.5-system-error-recovery-supp.patch \
+    file://0014-wifi-mt76-mt7996-add-dsp-firmware-download.patch \
+    file://0015-wifi-mt76-mt7996-fix-eeprom-antenna-bitfield-mask.patch \
+    file://0016-wifi-mt76-mt7996-init-mpdu-density.patch \
+    file://0017-wifi-mt76-mt7996-fix-icv-error-when-enable-AP-and-ST.patch \
+    file://0018-wifi-mt76-mt7996-set-wcid-in-txp.patch \
+    file://0019-mt76-revert-page-pool-changes.patch \
     file://0999-mt76-mt7996-for-build-pass.patch \
     "