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

[Description]
86825828 [MAC80211][misc][update mt7996 to 20230616 trunk FW]
deb5be37 [MAC80211][hnat][Revert Flowblock framework patch]
aa832cd1 [MAC80211][misc][correct feeds.conf.default-21.02]
94f66edf [MAC80211][hnat][Sync Flowblock framework to the OpenWRT_trunk_20230525]
ac633261 [MAC80211][misc][update autobuild folder for WiFi7]
c087498b [MAC80211][core][Add WFA TGac VHT-4.2.16h-DFS required patch to Wi-Fi 7 codebase]
619f5d08 [MAC80211][hostapd][update hostapd patches for WiFi 7]
aff83495 [MAC80211][core][update mac80211 patches for WiFi 7]
8aa09e02 [MAC80211][mt76][update mt76 patches for WiFi 7]
5d52889f [MAC80211][hostapd][Add HE capabilities check]
43d9a75b [MAC80211][core][Add sta-assisted DFS state update mechanism in eagle]
7b658b94 [MAC80211][Rebase Patches][Fix patch fail]
4fdaa231 [MAC80211][mt76][Fix connac2 testmode issues]
1a3ecccc [MAC80211][hostapd][Fix hostapd auto ht patch misplaced issue]

[Release-log]

Change-Id: I9901dada2bbc330f99bdd7c95b4dfed52802d57b
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0018-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0018-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
index c53b55a..bf91686 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0018-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0018-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
@@ -1,19 +1,18 @@
-From 3237a993233da052219018eec10ca82d79225fdb Mon Sep 17 00:00:00 2001
+From 705e1a59381e7bbd92043ad4338834aad504f232 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Mon, 20 Feb 2023 16:58:20 +0800
-Subject: [PATCH 18/28] hostapd: mtk: Fix auto ht issue when switching to DFS
- channel
+Subject: [PATCH] hostapd: mtk: Fix auto ht issue when switching to DFS channel
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
- hostapd/ctrl_iface.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
+ hostapd/ctrl_iface.c | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 07de0ad..be86f6c 100644
+index 07de0ad..3c38df5 100644
 --- a/hostapd/ctrl_iface.c
 +++ b/hostapd/ctrl_iface.c
-@@ -2773,6 +2773,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+@@ -2773,6 +2773,13 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
  		break;
  	}
  
@@ -21,12 +20,13 @@
 +		settings.freq_params.ht_enabled = iface->conf->ieee80211n;
 +		settings.freq_params.vht_enabled = iface->conf->ieee80211ac;
 +		settings.freq_params.he_enabled = iface->conf->ieee80211ax;
++		settings.freq_params.eht_enabled = iface->conf->ieee80211be;
 +	}
 +
  	if (settings.freq_params.center_freq1)
  		dfs_range += hostapd_is_dfs_overlap(
  			iface, bandwidth, settings.freq_params.center_freq1);
-@@ -2810,12 +2816,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+@@ -2810,12 +2817,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
  		return 0;
  	}
  
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch
new file mode 100644
index 0000000..1c5cacc
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch
@@ -0,0 +1,53 @@
+From 43c8934074a4f6fd1e98143b3bd011e71fe69fdb Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri Jun 9 09:03:05 2023 +0800
+Subject: hostapd: mtk: Add HE capabilities check
+
+Add HE capabilities check.
+Since "HE capabilities" check has been removed by driver,
+add the support for "HE capabilities" check in hostapd.
+
+---
+ src/ap/hw_features.c         | 26 ++++++++++++++++++++++++++
+ 1 files changed, 26 insertions(+)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 9967494..309f2d5 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -680,6 +680,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+ #ifdef CONFIG_IEEE80211AX
+ static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+ {
++	struct hostapd_hw_modes *mode = iface->current_mode;
++	struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
++	struct hostapd_config *conf = iface->conf;
++
++#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap)					\
++	do {									\
++		if (cfg_cap && !(hw_cap[phy_idx] & field)) {	\
++			wpa_printf(MSG_ERROR, "Driver does not support configured" \
++				     " HE capability [%s]", #field);		\
++			return 0;						\
++		}								\
++	} while (0)
++
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
++		     HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
++		     conf->he_phy_capab.he_ldpc);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
++		     HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
++		     conf->he_phy_capab.he_su_beamformer);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
++		     HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
++		     conf->he_phy_capab.he_su_beamformee);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
++		     HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
++		     conf->he_phy_capab.he_mu_beamformer);
++
+ 	return 1;
+ }
+ #endif /* CONFIG_IEEE80211AX */
+-- 
+2.39.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0104-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0104-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch
new file mode 100644
index 0000000..01a134e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0104-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch
@@ -0,0 +1,44 @@
+From 0aaec4ebc72e40da76a279d817763f4655f45d21 Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 26 May 2023 14:52:35 +0800
+Subject: [PATCH] hostapd: mtk: Add support for gtk rekeying in hostapd cli
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ hostapd/hostapd_cli.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 02f8546..d529bbc 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1256,6 +1256,15 @@ static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
++#ifdef CONFIG_TESTING_OPTIONS
++static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
++				      char *argv[])
++{
++	return wpa_ctrl_command(ctrl, "REKEY_GTK");
++}
++#endif
++
++
+ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ 	char cmd[256];
+@@ -1761,6 +1770,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  "= disable hostapd on current interface" },
+ 	{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
+ 	  "= update Beacon frame contents\n"},
++#ifdef CONFIG_TESTING_OPTIONS
++	{ "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
++	  "= rekey gtk\n"},
++#endif
+ 	{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
+ 	  "= drop all ERP keys"},
+ 	{ "log_level", hostapd_cli_cmd_log_level, NULL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc b/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
index 38ca4d8..a0c06b1 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
@@ -88,6 +88,8 @@
     file://mtk-0028-hostapd-mtk-Add-muru-user-number-debug-command.patch \
     file://mtk-0029-hostapd-mtk-Fix-CCA-issue.patch \
     file://mtk-0030-hostapd-mtk-Fix-unexpected-AP-beacon-state-transitio.patch \
+    file://mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch \
     file://mtk-0100-hostapd-mtk-update-eht-operation-element.patch \
     file://mtk-0103-hostapd-mtk-Add-BW320-channel-switch-command.patch \
+    file://mtk-0104-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch \
     "
diff --git a/recipes-wifi/hostapd/files/patches/mtk-0019-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch b/recipes-wifi/hostapd/files/patches/mtk-0019-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
index f89da87..d5f0dac 100644
--- a/recipes-wifi/hostapd/files/patches/mtk-0019-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
+++ b/recipes-wifi/hostapd/files/patches/mtk-0019-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
@@ -1,19 +1,18 @@
-From 0aa1200534c41279f5f05e1919040a86f003ca0a Mon Sep 17 00:00:00 2001
+From a71a78bc51b74d331aeb3f900c03480d058d5233 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Mon, 20 Feb 2023 16:58:20 +0800
-Subject: [PATCH 19/29] hostapd: mtk: Fix auto ht issue when switching to DFS
- channel
+Subject: [PATCH] hostapd: mtk: Fix auto ht issue when switching to DFS channel
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
- hostapd/ctrl_iface.c | 13 +++++++------
- 1 file changed, 7 insertions(+), 6 deletions(-)
+ hostapd/ctrl_iface.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 61c9e80..06cbea1 100644
+index 61c9e80..c33b7a4 100644
 --- a/hostapd/ctrl_iface.c
 +++ b/hostapd/ctrl_iface.c
-@@ -2698,6 +2698,13 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+@@ -2698,6 +2698,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
  		break;
  	}
  
@@ -21,13 +20,12 @@
 +		settings.freq_params.ht_enabled = iface->conf->ieee80211n;
 +		settings.freq_params.vht_enabled = iface->conf->ieee80211ac;
 +		settings.freq_params.he_enabled = iface->conf->ieee80211ax;
-+		settings.freq_params.eht_enabled = iface->conf->ieee80211be;
 +	}
 +
  	if (settings.freq_params.center_freq1)
  		dfs_range += hostapd_is_dfs_overlap(
  			iface, bandwidth, settings.freq_params.center_freq1);
-@@ -2735,12 +2742,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+@@ -2735,12 +2741,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
  		return 0;
  	}
  
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0016-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0016-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch
new file mode 100644
index 0000000..f14111c
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0016-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch
@@ -0,0 +1,180 @@
+From e545285e24de95d0d620bb7e9c97abba39e363b9 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:25:24 +0800
+Subject: [PATCH] mac80211: mtk: add sta-assisted DFS state update mechanism
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h       | 14 +++++++++
+ include/uapi/linux/nl80211.h |  6 ++++
+ net/mac80211/mlme.c          | 12 ++++++++
+ net/wireless/chan.c          | 60 ++++++++++++++++++++++++++++++++++++
+ 4 files changed, 92 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 84564bd..06d1567 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -8180,6 +8180,20 @@ void cfg80211_cac_event(struct net_device *netdev,
+ 			const struct cfg80211_chan_def *chandef,
+ 			enum nl80211_radar_event event, gfp_t gfp);
+ 
++/**
++ * cfg80211_sta_update_dfs_state - Update channel's DFS state during STA channel switch,
++ *				   association, and disassociation
++ * @wdev: the wireless device
++ * @bss_chandef: the current BSS channel definition
++ * @csa_chandef: the CSA channel definition
++ * @associated: whether STA is during association or disassociation process
++ *
++ */
++void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
++				   const struct cfg80211_chan_def *bss_chandef,
++				   const struct cfg80211_chan_def *csa_chandef,
++				   bool associated);
++
+ /**
+  * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
+  * @wiphy: the wiphy
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index bd26a06..e453d64 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -6565,6 +6565,10 @@ enum nl80211_smps_mode {
+  *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
+  * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+  *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
++ *	when receiving CSA/assoc resp
++ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
++ *	when STA is disconnected or leaving the channel
+  */
+ enum nl80211_radar_event {
+ 	NL80211_RADAR_DETECTED,
+@@ -6573,6 +6577,8 @@ enum nl80211_radar_event {
+ 	NL80211_RADAR_NOP_FINISHED,
+ 	NL80211_RADAR_PRE_CAC_EXPIRED,
+ 	NL80211_RADAR_CAC_STARTED,
++	NL80211_RADAR_STA_CAC_SKIPPED,
++	NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+ 
+ /**
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 72d153f..5a3dd31 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -1987,6 +1987,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
+ 					  IEEE80211_QUEUE_STOP_REASON_CSA);
+ 	mutex_unlock(&local->mtx);
+ 
++	cfg80211_sta_update_dfs_state(&sdata->wdev,
++				      &sdata->vif.bss_conf.chandef,
++				      &link->csa_chandef,
++				      sdata->vif.cfg.assoc);
++
+ 	cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, 0,
+ 					  csa_ie.count, csa_ie.mode, 0);
+ 
+@@ -3072,6 +3077,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+ 	       sizeof(sdata->vif.bss_conf.tx_pwr_env));
+ 
+ 	ieee80211_vif_set_links(sdata, 0);
++
++	cfg80211_sta_update_dfs_state(&sdata->wdev,
++				      &sdata->vif.bss_conf.chandef,
++				      NULL, sdata->vif.cfg.assoc);
+ }
+ 
+ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
+@@ -5276,6 +5285,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
+ 		event.u.mlme.status = MLME_SUCCESS;
+ 		drv_event_callback(sdata->local, sdata, &event);
+ 		sdata_info(sdata, "associated\n");
++		cfg80211_sta_update_dfs_state(&sdata->wdev,
++					      &sdata->vif.bss_conf.chandef,
++					      NULL, sdata->vif.cfg.assoc);
+ 
+ 		info.success = 1;
+ 	}
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index fef0ba5..13276bc 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -14,6 +14,7 @@
+ #include <net/cfg80211.h>
+ #include "core.h"
+ #include "rdev-ops.h"
++#include "nl80211.h"
+ 
+ static bool cfg80211_valid_60g_freq(u32 freq)
+ {
+@@ -1436,6 +1437,65 @@ bool cfg80211_any_usable_channels(struct wiphy *wiphy,
+ }
+ EXPORT_SYMBOL(cfg80211_any_usable_channels);
+ 
++static void cfg80211_sta_radar_notify(struct wiphy *wiphy,
++				      const struct cfg80211_chan_def *chandef,
++				      enum nl80211_radar_event event)
++{
++	struct wireless_dev *wdev;
++
++	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++		if (cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) {
++			nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef,
++					     event, wdev->netdev, GFP_KERNEL);
++			return;
++		}
++	}
++}
++
++void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
++				   const struct cfg80211_chan_def *bss_chandef,
++				   const struct cfg80211_chan_def *csa_chandef,
++				   bool associated)
++{
++	bool csa_active = !!csa_chandef;
++	enum nl80211_dfs_state dfs_state = NL80211_DFS_USABLE;
++	enum nl80211_radar_event event = NL80211_RADAR_STA_CAC_EXPIRED;
++
++	if (!bss_chandef)
++		return;
++
++	/* assume csa channel is cac completed */
++	if (csa_active &&
++	    (cfg80211_chandef_dfs_usable(wdev->wiphy, csa_chandef) ||
++	    cfg80211_chandef_dfs_available(wdev->wiphy, csa_chandef))) {
++		cfg80211_set_dfs_state(wdev->wiphy, csa_chandef, NL80211_DFS_AVAILABLE);
++		cfg80211_sta_radar_notify(wdev->wiphy, csa_chandef,
++					  NL80211_RADAR_STA_CAC_SKIPPED);
++		netdev_info(wdev->netdev, "Set CSA channel's DFS state to available\n");
++	}
++
++	/* avoid updating the dfs state during nop */
++	if (!cfg80211_chandef_dfs_usable(wdev->wiphy, bss_chandef) &&
++	    !cfg80211_chandef_dfs_available(wdev->wiphy, bss_chandef))
++		return;
++
++	if (associated && !csa_active) {
++		dfs_state = NL80211_DFS_AVAILABLE;
++		event = NL80211_RADAR_STA_CAC_SKIPPED;
++	}
++
++	cfg80211_set_dfs_state(wdev->wiphy, bss_chandef, dfs_state);
++	cfg80211_sta_radar_notify(wdev->wiphy, bss_chandef, event);
++
++	if (csa_active)
++		netdev_info(wdev->netdev, "Set origin channel's DFS state to usable\n");
++	else
++		netdev_info(wdev->netdev, "Set BSS channel's DFS state to %s due to %s\n",
++			    (dfs_state == NL80211_DFS_USABLE) ? "usable" : "available",
++			    associated ? "association" : "disassociation");
++}
++EXPORT_SYMBOL(cfg80211_sta_update_dfs_state);
++
+ struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev,
+ 				       unsigned int link_id)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0017-nl80211-mtk-Mark-DFS-channel-as-available-for-CSA.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0017-nl80211-mtk-Mark-DFS-channel-as-available-for-CSA.patch
new file mode 100644
index 0000000..cdc6290
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0017-nl80211-mtk-Mark-DFS-channel-as-available-for-CSA.patch
@@ -0,0 +1,28 @@
+From 4c78e6469da8de9c4584d029231938063545b031 Mon Sep 17 00:00:00 2001
+From: "himanshu.goyal" <himanshu.goyal@mediatek.com>
+Date: Fri, 17 Mar 2023 17:36:01 +0800
+Subject: [PATCH] nl80211: mtk: Mark DFS channel as available for CSA.
+
+---
+ net/wireless/nl80211.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index af43c22..53f32c6 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -10079,6 +10079,11 @@ skip_beacons:
+ 	if (err)
+ 		goto free;
+ 
++	/* Use RADAR_BACKGROUND attribute here for skipping CAC */
++	if (info->attrs[NL80211_ATTR_RADAR_BACKGROUND]) {
++		cfg80211_set_dfs_state(&rdev->wiphy, &params.chandef, NL80211_DFS_AVAILABLE);
++	}
++
+ 	if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
+ 					   wdev->iftype)) {
+ 		err = -EINVAL;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0100-mac80211-mtk-add-EHT-BA1024-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1000-mac80211-mtk-add-EHT-BA1024-support.patch
similarity index 92%
rename from recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0100-mac80211-mtk-add-EHT-BA1024-support.patch
rename to recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1000-mac80211-mtk-add-EHT-BA1024-support.patch
index 5493895..fe7b1af 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0100-mac80211-mtk-add-EHT-BA1024-support.patch
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1000-mac80211-mtk-add-EHT-BA1024-support.patch
@@ -1,7 +1,7 @@
-From 21b39d41faf3a67127778d85a79029002ed65291 Mon Sep 17 00:00:00 2001
+From 5a82834ed7eb4cbf0f4f5abc8665eeef023b67ff Mon Sep 17 00:00:00 2001
 From: MeiChia Chiu <meichia.chiu@mediatek.com>
 Date: Sun, 25 Dec 2022 22:43:46 +0800
-Subject: [PATCH 100/102] mac80211: mtk: add EHT BA1024 support
+Subject: [PATCH 1000/1003] mac80211: mtk: add EHT BA1024 support
 
 ---
  include/linux/ieee80211.h |  2 ++
@@ -9,10 +9,10 @@
  2 files changed, 45 insertions(+), 2 deletions(-)
 
 diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
-index 6f70394..27321f2 100644
+index 00d381e..0781b76 100644
 --- a/include/linux/ieee80211.h
 +++ b/include/linux/ieee80211.h
-@@ -1270,6 +1270,8 @@ struct ieee80211_mgmt {
+@@ -1256,6 +1256,8 @@ struct ieee80211_mgmt {
  					__le16 status;
  					__le16 capab;
  					__le16 timeout;
@@ -22,7 +22,7 @@
  				struct{
  					u8 action_code;
 diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-index 56cd1fc..8daf292 100644
+index 752ad09..7f0e72a 100644
 --- a/net/mac80211/agg-tx.c
 +++ b/net/mac80211/agg-tx.c
 @@ -66,10 +66,17 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
@@ -73,7 +73,7 @@
  	} else {
  		/*
  		 * We really should use what the driver told us it will
-@@ -978,13 +997,35 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
+@@ -980,13 +999,35 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
  {
  	struct tid_ampdu_tx *tid_tx;
  	struct ieee80211_txq *txq;
@@ -110,5 +110,5 @@
  
  	txq = sta->sta.txq[tid];
 -- 
-2.18.0
+2.39.2
 
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0101-mac80211-mtk-add-rate-duration-for-EHT-rate.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1001-mac80211-mtk-add-rate-duration-for-EHT-rate.patch
similarity index 98%
rename from recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0101-mac80211-mtk-add-rate-duration-for-EHT-rate.patch
rename to recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1001-mac80211-mtk-add-rate-duration-for-EHT-rate.patch
index 27ed002..f20cb61 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0101-mac80211-mtk-add-rate-duration-for-EHT-rate.patch
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1001-mac80211-mtk-add-rate-duration-for-EHT-rate.patch
@@ -1,7 +1,7 @@
-From a1d426d8bb909612b66c31d450fd717177862d2f Mon Sep 17 00:00:00 2001
+From 857debd24af093da4e85f148767fd16fb9eaf975 Mon Sep 17 00:00:00 2001
 From: Bo Jiao <Bo.Jiao@mediatek.com>
 Date: Sun, 25 Dec 2022 22:43:46 +0800
-Subject: [PATCH 101/102] mac80211: mtk: add rate duration for EHT rate.
+Subject: [PATCH 1001/1003] mac80211: mtk: add rate duration for EHT rate.
 
 ---
  net/mac80211/airtime.c | 349 ++++++++++++++++++++++++++++++++++++++++-
@@ -436,5 +436,5 @@
  	if (stat->encoding != RX_ENC_LEGACY)
  		return true;
 -- 
-2.18.0
+2.39.2
 
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0103-mac80211-mtk-add-send-bar-on-addbarsp_handle.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1002-mac80211-mtk-add-send-bar-action-when-recieve-addba-.patch
similarity index 68%
rename from recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0103-mac80211-mtk-add-send-bar-on-addbarsp_handle.patch
rename to recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1002-mac80211-mtk-add-send-bar-action-when-recieve-addba-.patch
index 054d623..099071c 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0103-mac80211-mtk-add-send-bar-on-addbarsp_handle.patch
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1002-mac80211-mtk-add-send-bar-action-when-recieve-addba-.patch
@@ -1,21 +1,19 @@
-From 64b7f68018adbbbe33c6edb602c05dc19f8c5ea8 Mon Sep 17 00:00:00 2001
+From a9b8e0e62b19fbd7c1dd865330ceb5a943e5cbb2 Mon Sep 17 00:00:00 2001
 From: ye he <ye.he@mediatek.com>
 Date: Wed, 22 Feb 2023 16:09:32 +0800
-Subject: [PATCH] add send bar action when recieve addba rsp
+Subject: [PATCH 1002/1003] mac80211: mtk: add send bar action when recieve
+ addba rsp
 
 Signed-off-by: ye he <ye.he@mediatek.com>
 ---
  net/mac80211/agg-tx.c | 3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)
- mode change 100755 => 100644 net/mac80211/agg-tx.c
 
 diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
-old mode 100755
-new mode 100644
-index 7f750c1..4292258
+index 7f0e72a..3ce2226 100644
 --- a/net/mac80211/agg-tx.c
 +++ b/net/mac80211/agg-tx.c
-@@ -1060,7 +1060,8 @@ next:
+@@ -1080,7 +1080,8 @@ next:
  
  		tid_tx->buf_size = buf_size;
  		tid_tx->amsdu = amsdu;
@@ -26,5 +24,5 @@
  			ieee80211_agg_tx_operational(local, sta, tid);
  
 -- 
-2.18.0
+2.39.2
 
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1003-mac80211-mtk-inrease-beacon-loss-count.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1003-mac80211-mtk-inrease-beacon-loss-count.patch
new file mode 100644
index 0000000..1def860
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1003-mac80211-mtk-inrease-beacon-loss-count.patch
@@ -0,0 +1,33 @@
+From 8175f5cdf45728cdfcf34893696f994c6f1e6cc1 Mon Sep 17 00:00:00 2001
+From: Amit Khatri <amit.khatri@mediatek.com>
+Date: Thu, 6 Apr 2023 21:37:33 +0800
+Subject: [PATCH 1003/1003] mac80211: mtk: inrease beacon loss count
+
+as per eagle code beacone loss time out is
+4 seconds.
+in 2G connection getting beacon loss logs in routed client
+scenario.
+
+so increasing beacon loss count from 7 to 20
+
+Signed-off-by: Amit Khatri <amit.khatri@mediatek.com>
+---
+ net/mac80211/mlme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 72d153f..458609c 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -61,7 +61,7 @@ MODULE_PARM_DESC(max_probe_tries,
+  * probe on beacon miss before declaring the connection lost
+  * default to what we want.
+  */
+-static int beacon_loss_count = 7;
++static int beacon_loss_count = 20;
+ module_param(beacon_loss_count, int, 0644);
+ MODULE_PARM_DESC(beacon_loss_count,
+ 		 "Number of beacon intervals before we decide beacon was lost.");
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1004-mac80211-mtk-add-disabling-cca-when-stopping-AP.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1004-mac80211-mtk-add-disabling-cca-when-stopping-AP.patch
new file mode 100644
index 0000000..c8fc398
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-1004-mac80211-mtk-add-disabling-cca-when-stopping-AP.patch
@@ -0,0 +1,24 @@
+From 79837bcb22a80a4c95cbba77b23f16352d4e791d Mon Sep 17 00:00:00 2001
+From: Michael Lee <michael-cy.lee@mediatek.com>
+Date: Mon, 24 Apr 2023 09:59:24 +0800
+Subject: [PATCH] mac80211: mtk: add disabling cca when stopping AP.
+
+---
+ net/mac80211/cfg.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index 343ce2b..611c29e 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1540,6 +1540,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ 	/* abort any running channel switch */
+ 	mutex_lock(&local->mtx);
+ 	link_conf->csa_active = false;
++	link_conf->color_change_active = false;
+ 	if (link->csa_block_tx) {
+ 		ieee80211_wake_vif_queues(local, sdata,
+ 					  IEEE80211_QUEUE_STOP_REASON_CSA);
+-- 
+2.25.1
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
index 42dbee0..cea85f7 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
@@ -64,7 +64,11 @@
     file://mtk-0013-mac80211-mtk-ageout-color-bitmap.patch \
     file://mtk-0014-mac80211-mtk-update-max_bssid_indicator-based-on-rea.patch \
     file://mtk-0015-mac80211-support-configurable-addba-resp-time.patch \
-    file://mtk-0100-mac80211-mtk-add-EHT-BA1024-support.patch \
-    file://mtk-0101-mac80211-mtk-add-rate-duration-for-EHT-rate.patch \
-    file://mtk-0103-mac80211-mtk-add-send-bar-on-addbarsp_handle.patch \
+    file://mtk-0016-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch \
+    file://mtk-0017-nl80211-mtk-Mark-DFS-channel-as-available-for-CSA.patch \
+    file://mtk-1000-mac80211-mtk-add-EHT-BA1024-support.patch \
+    file://mtk-1001-mac80211-mtk-add-rate-duration-for-EHT-rate.patch \
+    file://mtk-1002-mac80211-mtk-add-send-bar-action-when-recieve-addba-.patch \
+    file://mtk-1003-mac80211-mtk-inrease-beacon-loss-count.patch \
+    file://mtk-1004-mac80211-mtk-add-disabling-cca-when-stopping-AP.patch \
     "
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
index c927e1a..91d65d7 100644
--- 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
@@ -1,7 +1,7 @@
-From 31a7471ec1e183deafd9b28fd62cca3c25f79633 Mon Sep 17 00:00:00 2001
+From 15d734c77b25451efdf3bb1bcd5e687bc429a852 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/22] wifi: mt76: mt7996: add eht rx rate support
+Subject: [PATCH 01/11] wifi: mt76: mt7996: add eht rx rate support
 
 Add support to report eht rx rate.
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-move-radio-enable-command-to-mt7996.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-move-radio-ctrl-commands-to-proper-.patch
similarity index 77%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-move-radio-enable-command-to-mt7996.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-move-radio-ctrl-commands-to-proper-.patch
index d723e62..8ab501f 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-mt7996-move-radio-enable-command-to-mt7996.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-move-radio-ctrl-commands-to-proper-.patch
@@ -1,20 +1,19 @@
-From 61c13ad2aacdc077bc3729090702821d4777530a Mon Sep 17 00:00:00 2001
+From 83d30a89d61ee914b23d77256e993e9521de5cbc 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 03/22] wifi: mt76: mt7996: move radio enable command to
- mt7996_start()
+Subject: [PATCH 02/11] wifi: mt76: mt7996: move radio ctrl commands to proper
+ functions
 
-The radio enable and disable commands are used for per-phy radio, so
-move them into mt7996_start() and mt7996_stop(), respectively.
+Move radio enable/disable commands into functions for configuring
+per-phy radio.
 
 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 136a0c28..28b63d44 100644
+index f306e9c5..e7c97d2f 100644
 --- a/mt7996/main.c
 +++ b/mt7996/main.c
 @@ -43,6 +43,10 @@ int mt7996_run(struct ieee80211_hw *hw)
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-connac-add-support-for-dsp-firmware-downlo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-connac-add-support-for-dsp-firmware-downlo.patch
new file mode 100644
index 0000000..4342d73
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0003-wifi-mt76-connac-add-support-for-dsp-firmware-downlo.patch
@@ -0,0 +1,198 @@
+From 20c8d7bfeb91be51129e7e98213db441a62d6d95 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 31 May 2023 18:31:41 +0800
+Subject: [PATCH 03/11] wifi: mt76: connac: add support for dsp firmware
+ download
+
+Add FW_START_WORKING_PDA_DSP for the indication of starting DSP
+firmware download, which is for phy-related control.
+The firmware is transparent to the driver, but it's necessary for the
+firmware download process.
+
+Reviewed-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: I35666504cfe7bf213c8f8f0c0223b3089372f2ab
+---
+v2:
+  - merge two commits
+  - move main load ram code to a regular function
+v3:
+  - remove all macros to directly call __mt7996_load_ram()
+  - add back missing code which records fw_version to wiphy
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/mcu.c      | 67 +++++++++++++++++++++++------------------------
+ mt7996/mt7996.h   |  7 +++++
+ mt7996/pci.c      |  1 +
+ 4 files changed, 42 insertions(+), 34 deletions(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 91d98eff..d2a3d56b 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 88e2f9d0..545cc987 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2155,7 +2155,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;
+@@ -2167,8 +2167,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);
+ 
+@@ -2195,19 +2197,22 @@ 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);
+ }
+ 
+-static int mt7996_load_ram(struct mt7996_dev *dev)
++static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type,
++			     const char *fw_file, enum mt7996_ram_type ram_type)
+ {
+ 	const struct mt7996_fw_trailer *hdr;
+ 	const struct firmware *fw;
+ 	int ret;
+ 
+-	ret = request_firmware(&fw, MT7996_FIRMWARE_WM, dev->mt76.dev);
++	ret = request_firmware(&fw, fw_file, dev->mt76.dev);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -2217,37 +2222,13 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
+ 		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);
++	hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
++	dev_info(dev->mt76.dev, "%s Firmware Version: %.10s, Build Time: %.15s\n",
++		 fw_type, hdr->fw_ver, hdr->build_date);
+ 
+-	ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, false);
++	ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, ram_type);
+ 	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");
++		dev_err(dev->mt76.dev, "Failed to start %s firmware\n", fw_type);
+ 		goto out;
+ 	}
+ 
+@@ -2261,6 +2242,24 @@ out:
+ 	return ret;
+ }
+ 
++static int mt7996_load_ram(struct mt7996_dev *dev)
++{
++	int ret;
++
++	ret = __mt7996_load_ram(dev, "WM", MT7996_FIRMWARE_WM,
++				MT7996_RAM_TYPE_WM);
++	if (ret)
++		return ret;
++
++	ret = __mt7996_load_ram(dev, "DSP", MT7996_FIRMWARE_DSP,
++				MT7996_RAM_TYPE_DSP);
++	if (ret)
++		return ret;
++
++	return __mt7996_load_ram(dev, "WA", MT7996_FIRMWARE_WA,
++				 MT7996_RAM_TYPE_WA);
++}
++
+ static int
+ mt7996_firmware_state(struct mt7996_dev *dev, bool wa)
+ {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 4d7dcb95..7dfdc738 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"
+@@ -52,6 +53,12 @@ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+ struct mt7996_dfs_pattern;
+ 
++enum mt7996_ram_type {
++	MT7996_RAM_TYPE_WM,
++	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/0004-wifi-mt76-mt7996-fix-bss-wlan_idx-when-sending-bss_i.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-fix-bss-wlan_idx-when-sending-bss_i.patch
new file mode 100644
index 0000000..a4e17a9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-mt7996-fix-bss-wlan_idx-when-sending-bss_i.patch
@@ -0,0 +1,51 @@
+From 3c7a5592ea211f5ddb2f6bbd6ba8720c74faf459 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 04/11] wifi: mt76: mt7996: fix bss wlan_idx when sending
+ bss_info command
+
+The bmc_tx_wlan_idx should be the wlan_idx of the current bss rather
+than peer AP's wlan_idx, otherwise there will appear some frame
+decryption problems on station mode.
+
+Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
+Reviewed-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mcu.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 545cc987..6706d38c 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -712,6 +712,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;
+ 
+@@ -731,7 +732,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();
+ 		}
+@@ -751,7 +752,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/0005-wifi-mt76-mt7996-init-he-and-eht-cap-for-AP_VLAN.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-init-he-and-eht-cap-for-AP_VLAN.patch
new file mode 100644
index 0000000..d4968dd
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-init-he-and-eht-cap-for-AP_VLAN.patch
@@ -0,0 +1,29 @@
+From 56d84272851f6c77cf68186bc8906de0558d2e5b Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Fri, 17 Mar 2023 11:08:04 +0800
+Subject: [PATCH 05/11] wifi: mt76: mt7996: init he and eht cap for AP_VLAN
+
+Init he and eht capabilities for AP_VLAN type. Without this patch, the
+BA response from AP_VLAN will not include the ADDBA extension tag.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/init.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f1b48cdd..004575a0 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -808,6 +808,7 @@ __mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy,
+ 		switch (i) {
+ 		case NL80211_IFTYPE_STATION:
+ 		case NL80211_IFTYPE_AP:
++		case NL80211_IFTYPE_AP_VLAN:
+ #ifdef CONFIG_MAC80211_MESH
+ 		case NL80211_IFTYPE_MESH_POINT:
+ #endif
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-enable-VHT-extended-NSS-BW-feature.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-enable-VHT-extended-NSS-BW-feature.patch
new file mode 100644
index 0000000..348a04a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-enable-VHT-extended-NSS-BW-feature.patch
@@ -0,0 +1,30 @@
+From 1e7bbf8c04d60eb6cd234990f94da73bccd73118 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Mon, 27 Mar 2023 14:30:25 +0800
+Subject: [PATCH 06/11] wifi: mt76: mt7996: enable VHT extended NSS BW feature
+
+Set SUPPORTS_VHT_EXT_NSS_BW to let the max BW capability correctly be
+parsed by different devices.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/init.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 004575a0..8247153d 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -217,6 +217,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ 			IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ 		phy->mt76->sband_5g.sband.ht_cap.ampdu_density =
+ 			IEEE80211_HT_MPDU_DENSITY_1;
++
++		ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
+ 	}
+ 
+ 	mt76_set_stream_caps(phy->mt76, true);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-set-txd-v1.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-set-txd-v1.patch
deleted file mode 100644
index f9cdd1a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0006-wifi-mt76-mt7996-set-txd-v1.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From a25714fa7b610430f9aa3d4ec24647eaea505d35 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 06/22] 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 23cbfdde..420c7403 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1110,6 +1110,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))
-@@ -1141,6 +1142,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 		mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
- 				      pid, qid, 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 bc4e6c55..9ab8e8d2 100644
---- a/mt7996/mac.h
-+++ b/mt7996/mac.h
-@@ -173,7 +173,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/0007-wifi-mt76-connac-add-support-to-set-ifs-time-by-mcu-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-connac-add-support-to-set-ifs-time-by-mcu-.patch
new file mode 100644
index 0000000..8021427
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-connac-add-support-to-set-ifs-time-by-mcu-.patch
@@ -0,0 +1,261 @@
+From edca876e34fc2696e8f855c2d05036fa79a05f8f Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Thu, 1 Jun 2023 12:01:10 +0800
+Subject: [PATCH 07/11] wifi: mt76: connac: add support to set ifs time by mcu
+ command
+
+There's a race between driver and fw on some tx/rx control registers
+when setting ifs, which will cause accidental hw queue pause problems.
+Avoid this by setting ifs time with bss_info mcu command.
+
+Reviewed-by: Shayne Chen <shayne.chen@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+Change-Id: Ib6477462a35df84a89f113a4db0e6aef5154c6a8
+---
+v2:
+  - merge two commits
+  - change bool a_band to use is_2ghz
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/mac.c      | 27 ++-------------------------
+ mt7996/main.c     |  5 ++---
+ mt7996/mcu.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 17 +++++++++++++++++
+ mt7996/mt7996.h   |  3 ++-
+ 6 files changed, 70 insertions(+), 29 deletions(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index d2a3d56b..b91262ee 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1288,6 +1288,7 @@ enum {
+ 	UNI_BSS_INFO_UAPSD = 19,
+ 	UNI_BSS_INFO_PS = 21,
+ 	UNI_BSS_INFO_BCNFT = 22,
++	UNI_BSS_INFO_IFS_TIME = 23,
+ 	UNI_BSS_INFO_OFFLOAD = 25,
+ 	UNI_BSS_INFO_MLD = 26,
+ };
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 23cbfdde..2da61d2e 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1612,20 +1612,19 @@ void mt7996_mac_reset_counters(struct mt7996_phy *phy)
+ 	mt7996_mcu_get_chan_mib_info(phy, true);
+ }
+ 
+-void mt7996_mac_set_timing(struct mt7996_phy *phy)
++void mt7996_mac_set_coverage_class(struct mt7996_phy *phy)
+ {
+ 	s16 coverage_class = phy->coverage_class;
+ 	struct mt7996_dev *dev = phy->dev;
+ 	struct mt7996_phy *phy2 = mt7996_phy2(dev);
+ 	struct mt7996_phy *phy3 = mt7996_phy3(dev);
+-	u32 val, reg_offset;
++	u32 reg_offset;
+ 	u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) |
+ 		  FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48);
+ 	u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) |
+ 		   FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28);
+ 	u8 band_idx = phy->mt76->band_idx;
+ 	int offset;
+-	bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ);
+ 
+ 	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
+ 		return;
+@@ -1638,34 +1637,12 @@ void mt7996_mac_set_timing(struct mt7996_phy *phy)
+ 		coverage_class = max_t(s16, coverage_class,
+ 				       phy3->coverage_class);
+ 
+-	mt76_set(dev, MT_ARB_SCR(band_idx),
+-		 MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE);
+-	udelay(1);
+-
+ 	offset = 3 * coverage_class;
+ 	reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) |
+ 		     FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset);
+ 
+ 	mt76_wr(dev, MT_TMAC_CDTR(band_idx), cck + reg_offset);
+ 	mt76_wr(dev, MT_TMAC_ODTR(band_idx), ofdm + reg_offset);
+-	mt76_wr(dev, MT_TMAC_ICR0(band_idx),
+-		FIELD_PREP(MT_IFS_EIFS_OFDM, a_band ? 84 : 78) |
+-		FIELD_PREP(MT_IFS_RIFS, 2) |
+-		FIELD_PREP(MT_IFS_SIFS, 10) |
+-		FIELD_PREP(MT_IFS_SLOT, phy->slottime));
+-
+-	if (!a_band)
+-		mt76_wr(dev, MT_TMAC_ICR1(band_idx),
+-			FIELD_PREP(MT_IFS_EIFS_CCK, 314));
+-
+-	if (phy->slottime < 20 || a_band)
+-		val = MT7996_CFEND_RATE_DEFAULT;
+-	else
+-		val = MT7996_CFEND_RATE_11B;
+-
+-	mt76_rmw_field(dev, MT_RATE_HRCR0(band_idx), MT_RATE_HRCR0_CFEND_RATE, val);
+-	mt76_clear(dev, MT_ARB_SCR(band_idx),
+-		   MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE);
+ }
+ 
+ void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index e7c97d2f..786c3fbc 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -287,7 +287,6 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+ 	if (ret)
+ 		goto out;
+ 
+-	mt7996_mac_set_timing(phy);
+ 	ret = mt7996_dfs_init_radar_detector(phy);
+ 	mt7996_mac_cca_stats_reset(phy);
+ 
+@@ -564,7 +563,7 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ 
+ 		if (slottime != phy->slottime) {
+ 			phy->slottime = slottime;
+-			mt7996_mac_set_timing(phy);
++			mt7996_mcu_set_timing(phy, vif);
+ 		}
+ 	}
+ 
+@@ -904,7 +903,7 @@ mt7996_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class)
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 	phy->coverage_class = max_t(s16, coverage_class, 0);
+-	mt7996_mac_set_timing(phy);
++	mt7996_mac_set_coverage_class(phy);
+ 	mutex_unlock(&dev->mt76.mutex);
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 6706d38c..0ede9769 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -701,6 +701,34 @@ mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 				 sizeof(req), true);
+ }
+ 
++static void
++mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_phy *phy = mvif->phy;
++	struct bss_ifs_time_tlv *ifs_time;
++	struct tlv *tlv;
++	bool is_2ghz = phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ;
++
++	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_IFS_TIME, sizeof(*ifs_time));
++
++	ifs_time = (struct bss_ifs_time_tlv *)tlv;
++	ifs_time->slot_valid = true;
++	ifs_time->sifs_valid = true;
++	ifs_time->rifs_valid = true;
++	ifs_time->eifs_valid = true;
++
++	ifs_time->slot_time = cpu_to_le16(phy->slottime);
++	ifs_time->sifs_time = cpu_to_le16(10);
++	ifs_time->rifs_time = cpu_to_le16(2);
++	ifs_time->eifs_time = cpu_to_le16(is_2ghz ? 78 : 84);
++
++	if (is_2ghz) {
++		ifs_time->eifs_cck_valid = true;
++		ifs_time->eifs_cck_time = cpu_to_le16(314);
++	}
++}
++
+ static int
+ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ 			 struct ieee80211_vif *vif,
+@@ -826,6 +854,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ 		mt7996_mcu_bss_bmc_tlv(skb, vif, phy);
+ 		mt7996_mcu_bss_ra_tlv(skb, vif, phy);
+ 		mt7996_mcu_bss_txcmd_tlv(skb, true);
++		mt7996_mcu_bss_ifs_timing_tlv(skb, vif);
+ 
+ 		if (vif->bss_conf.he_support)
+ 			mt7996_mcu_bss_he_tlv(skb, vif, phy);
+@@ -838,6 +867,23 @@ out:
+ 				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+ }
+ 
++int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_dev *dev = phy->dev;
++	struct sk_buff *skb;
++
++	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
++					 MT7996_BSS_UPDATE_MAX_SIZE);
++	if (IS_ERR(skb))
++		return PTR_ERR(skb);
++
++	mt7996_mcu_bss_ifs_timing_tlv(skb, vif);
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
++}
++
+ static int
+ mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
+ 		  struct ieee80211_ampdu_params *params,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index d7075a4d..078f8285 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -317,6 +317,22 @@ struct bss_sec_tlv {
+ 	u8 __rsv2[1];
+ } __packed;
+ 
++struct bss_ifs_time_tlv {
++	__le16 tag;
++	__le16 len;
++	u8 slot_valid;
++	u8 sifs_valid;
++	u8 rifs_valid;
++	u8 eifs_valid;
++	__le16 slot_time;
++	__le16 sifs_time;
++	__le16 rifs_time;
++	__le16 eifs_time;
++	u8 eifs_cck_valid;
++	u8 rsv;
++	__le16 eifs_cck_time;
++} __packed;
++
+ struct bss_power_save {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -552,6 +568,7 @@ enum {
+ 					 sizeof(struct bss_txcmd_tlv) +		\
+ 					 sizeof(struct bss_power_save) +	\
+ 					 sizeof(struct bss_sec_tlv) +		\
++					 sizeof(struct bss_ifs_time_tlv) +	\
+ 					 sizeof(struct bss_mld_tlv))
+ 
+ #define MT7996_STA_UPDATE_MAX_SIZE	(sizeof(struct sta_req_hdr) +		\
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 7dfdc738..42892f06 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -463,6 +463,7 @@ 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);
+ int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
++int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif);
+ 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,
+ 		       u8 rx_sel, u8 val);
+@@ -526,7 +527,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 			   struct sk_buff *skb, struct mt76_wcid *wcid,
+ 			   struct ieee80211_key_conf *key, int pid,
+ 			   enum mt76_txq_id qid, u32 changed);
+-void mt7996_mac_set_timing(struct mt7996_phy *phy);
++void mt7996_mac_set_coverage_class(struct mt7996_phy *phy);
+ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 		       struct ieee80211_sta *sta);
+ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-use-correct-phy-for-background-rada.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-use-correct-phy-for-background-rada.patch
new file mode 100644
index 0000000..24d2bfd
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-use-correct-phy-for-background-rada.patch
@@ -0,0 +1,37 @@
+From 9cf96fa56a20d30f7e46e96d9c48c3568fbf11af Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 7 Mar 2023 17:05:01 +0800
+Subject: [PATCH 08/11] wifi: mt76: mt7996: use correct phy for background
+ radar event
+
+If driver directly uses the band_idx reported from the radar event to
+access mt76_phy array, it will get the wrong phy for background radar.
+Fix this by adjusting the statement.
+
+Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mcu.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0ede9769..20519bff 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -339,7 +339,11 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	if (r->band_idx >= ARRAY_SIZE(dev->mt76.phys))
+ 		return;
+ 
+-	mphy = dev->mt76.phys[r->band_idx];
++	if (dev->rdd2_phy && r->band_idx == MT_RX_SEL2)
++		mphy = dev->rdd2_phy->mt76;
++	else
++		mphy = dev->mt76.phys[r->band_idx];
++
+ 	if (!mphy)
+ 		return;
+ 
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0009-wifi-mt76-mt7996-add-dsp-firmware-download.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0009-wifi-mt76-mt7996-add-dsp-firmware-download.patch
deleted file mode 100644
index eb65330..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0009-wifi-mt76-mt7996-add-dsp-firmware-download.patch
+++ /dev/null
@@ -1,194 +0,0 @@
-From 9b6e04ff1ac32161c6aacb939b2ff51bdced9629 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 09/22] 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 c5c48349..fbb1206f 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 f3fd2fd4..73d5dedf 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2241,7 +2241,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;
-@@ -2253,8 +2253,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);
- 
-@@ -2281,8 +2283,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);
- }
-@@ -2293,56 +2297,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 36337808..ab4521a4 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"
-@@ -59,6 +60,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/0009-wifi-mt76-mt7996-fix-WA-event-ring-size.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0009-wifi-mt76-mt7996-fix-WA-event-ring-size.patch
new file mode 100644
index 0000000..6ffa018
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0009-wifi-mt76-mt7996-fix-WA-event-ring-size.patch
@@ -0,0 +1,44 @@
+From f9ac23ac488c0dafceab97c8d39a22904cf78f77 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 23 Mar 2023 15:16:14 +0800
+Subject: [PATCH 09/11] wifi: mt76: mt7996: fix WA event ring size
+
+Fix rx ring size of WA event to get rid of event loss and queue overflow
+problems.
+
+Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/dma.c    | 2 +-
+ mt7996/mt7996.h | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 53414346..fbedaacf 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -293,7 +293,7 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	/* event from WA */
+ 	ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
+ 			       MT_RXQ_ID(MT_RXQ_MCU_WA),
+-			       MT7996_RX_MCU_RING_SIZE,
++			       MT7996_RX_MCU_RING_SIZE_WA,
+ 			       MT_RX_BUF_SIZE,
+ 			       MT_RXQ_RING_BASE(MT_RXQ_MCU_WA));
+ 	if (ret)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 42892f06..a3bd85d3 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -26,6 +26,7 @@
+ 
+ #define MT7996_RX_RING_SIZE		1536
+ #define MT7996_RX_MCU_RING_SIZE		512
++#define MT7996_RX_MCU_RING_SIZE_WA	1024
+ 
+ #define MT7996_FIRMWARE_WA		"mediatek/mt7996/mt7996_wa.bin"
+ #define MT7996_FIRMWARE_WM		"mediatek/mt7996/mt7996_wm.bin"
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-add-muru-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-add-muru-support.patch
similarity index 83%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-add-muru-support.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-add-muru-support.patch
index 1854421..c0f61b7 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0005-wifi-mt76-mt7996-add-muru-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-add-muru-support.patch
@@ -1,23 +1,22 @@
-From 85fb9bc9f85a5e64d88db85fbfdef968d037fada Mon Sep 17 00:00:00 2001
+From 8a284ffc268754a8e941888ce6f1b506f3c7564c 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 05/22] wifi: mt76: mt7996: add muru support
+Subject: [PATCH 10/11] wifi: mt76: mt7996: add muru support
 
-Add sta_rec_muru() and related phy cap for MU and RU support.
+Add sta_rec_muru() fw command to support MU-MIMO and OFDMA features.
 
 Signed-off-by: MeiChia Chiu <meichia.chiu@mediatek.com>
 Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
-Change-Id: I2206a9bb6fd6e50f4bf1380a8bea19920f1b7bfd
 ---
  mt76_connac_mcu.h |  3 ++-
- mt7996/mcu.c      | 57 ++++++++++++++++++++++++++++++++++++++++++++++-
- 2 files changed, 58 insertions(+), 2 deletions(-)
+ mt7996/mcu.c      | 56 ++++++++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 57 insertions(+), 2 deletions(-)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 91d98eff..8580ca59 100644
+index b91262ee..6249de57 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -518,7 +518,8 @@ struct sta_rec_muru {
+@@ -519,7 +519,8 @@ struct sta_rec_muru {
  		u8 uo_ra;
  		u8 he_2x996_tone;
  		u8 rx_t_frame_11ac;
@@ -28,10 +27,10 @@
  
  	struct {
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 88e2f9d0..6812a47b 100644
+index 20519bff..611f6450 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
-@@ -1050,6 +1050,60 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+@@ -1101,6 +1101,59 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
  	}
  }
  
@@ -50,7 +49,6 @@
 +	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
 +
 +	muru = (struct sta_rec_muru *)tlv;
-+
 +	muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer ||
 +			       vif->bss_conf.he_mu_beamformer ||
 +			       vif->bss_conf.vht_mu_beamformer ||
@@ -92,7 +90,7 @@
  static inline bool
  mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
  			struct ieee80211_sta *sta, bool bfee)
-@@ -1727,7 +1781,8 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+@@ -1778,7 +1831,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);
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-fix-icv-error-when-enable-AP-and-ST.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-fix-icv-error-when-enable-AP-and-ST.patch
deleted file mode 100644
index caa9b1a..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0010-wifi-mt76-mt7996-fix-icv-error-when-enable-AP-and-ST.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From ee3a2aa33145d17d3be17af85583437f079e91fb 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 10/22] wifi: mt76: mt7996: fix icv error when enable AP and
- STA simultaneously
-
-Fix mcu command content to prevent ICV error
-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 | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 73d5dedf..6d11bc1a 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -743,6 +743,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;
- 
-@@ -762,7 +763,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();
- 		}
-@@ -782,7 +783,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/0011-wifi-mt76-mt7996-increase-tx-token-size.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0011-wifi-mt76-mt7996-increase-tx-token-size.patch
new file mode 100644
index 0000000..aa039ce
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0011-wifi-mt76-mt7996-increase-tx-token-size.patch
@@ -0,0 +1,30 @@
+From 0cc8d7827510e36978175d84d63cced5f842ff61 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 17 Apr 2023 09:49:53 +0800
+Subject: [PATCH 11/11] wifi: mt76: mt7996: increase tx token size
+
+Align tx token size to proprietary driver, which can improve peak
+throughput under MU performance tests.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/mt7996.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index a3bd85d3..651f53aa 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -36,7 +36,7 @@
+ #define MT7996_EEPROM_DEFAULT		"mediatek/mt7996/mt7996_eeprom.bin"
+ #define MT7996_EEPROM_SIZE		7680
+ #define MT7996_EEPROM_BLOCK_SIZE	16
+-#define MT7996_TOKEN_SIZE		8192
++#define MT7996_TOKEN_SIZE		16384
+ 
+ #define MT7996_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
+ #define MT7996_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0012-wifi-mt76-mt7996-init-he-and-eht-cap-for-AP_VLAN.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0012-wifi-mt76-mt7996-init-he-and-eht-cap-for-AP_VLAN.patch
deleted file mode 100644
index f05fe30..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0012-wifi-mt76-mt7996-init-he-and-eht-cap-for-AP_VLAN.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 9af2057cec3d77aafb4f92b8d1542d88c8ac5efc Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Fri, 17 Mar 2023 11:08:04 +0800
-Subject: [PATCH 12/22] wifi: mt76: mt7996: init he and eht cap for AP_VLAN
-
-Add AP_VLAN types in __mt7996_set_stream_he_eht_caps to
-initialize the ht and eht caps. Without this patch, the
-BA response from VLAN AP would not include the ADDBA
-extension tag.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- mt7996/init.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/mt7996/init.c b/mt7996/init.c
-index 9c5d20ad..d44e3ae9 100644
---- a/mt7996/init.c
-+++ b/mt7996/init.c
-@@ -993,6 +993,7 @@ __mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy,
- 		switch (i) {
- 		case NL80211_IFTYPE_STATION:
- 		case NL80211_IFTYPE_AP:
-+		case NL80211_IFTYPE_AP_VLAN:
- #ifdef CONFIG_MAC80211_MESH
- 		case NL80211_IFTYPE_MESH_POINT:
- #endif
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-Fix-using-the-wrong-phy-for-backgro.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-Fix-using-the-wrong-phy-for-backgro.patch
deleted file mode 100644
index 461236e..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-Fix-using-the-wrong-phy-for-backgro.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From b1296ffe7596adf514bc2b5397c946e276e30176 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Tue, 7 Mar 2023 17:05:01 +0800
-Subject: [PATCH 14/22] wifi: mt76: mt7996: Fix using the wrong phy for
- background radar event
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/mcu.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index df1ae639..cd86209b 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -339,7 +339,11 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
- 	if (r->band_idx >= ARRAY_SIZE(dev->mt76.phys))
- 		return;
- 
--	mphy = dev->mt76.phys[r->band_idx];
-+	if (dev->rdd2_phy && r->band_idx == MT_RX_SEL2)
-+		mphy = dev->rdd2_phy->mt76;
-+	else
-+		mphy = dev->mt76.phys[r->band_idx];
-+
- 	if (!mphy)
- 		return;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0011-wifi-mt76-mt7996-set-wcid-in-txp.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-set-wcid-in-txp.patch
similarity index 70%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0011-wifi-mt76-mt7996-set-wcid-in-txp.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-set-wcid-in-txp.patch
index d612bdf..8103127 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0011-wifi-mt76-mt7996-set-wcid-in-txp.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0014-wifi-mt76-mt7996-set-wcid-in-txp.patch
@@ -1,20 +1,21 @@
-From 952869c0b481651e9c125d5cd7c4ea2b255521c5 Mon Sep 17 00:00:00 2001
+From 5e2177e77b9609185f748708332aa6a6fdf4d1f9 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 11/22] wifi: mt76: mt7996: set wcid in txp
+Subject: [PATCH 14/39] 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>
+Change-Id: Ie715a659ff52f2d85332158f273d0ee4fe9f4051
 ---
  mt7996/mac.c | 8 +++++---
  1 file changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/mt7996/mac.c b/mt7996/mac.c
-index 420c7403..ca163969 100644
+index 2da61d2..bddb84f 100644
 --- a/mt7996/mac.c
 +++ b/mt7996/mac.c
-@@ -1169,10 +1169,12 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+@@ -1166,10 +1166,12 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
  	}
  
  	txp->fw.token = cpu_to_le16(id);
@@ -22,7 +23,7 @@
 -		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))
++	    (!is_8023 && 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);
@@ -31,5 +32,5 @@
  
  	/* pass partial skb header to fw */
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch
similarity index 90%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch
index 50f83e7..ea01cf0 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0002-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch
@@ -1,7 +1,7 @@
-From 1a55033a534847f9c10ffd52e277378c1ba3e45a Mon Sep 17 00:00:00 2001
+From 5580f05c864aadfad4092fc97a937869dc08eca8 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 02/22] wifi: mt76: mt7996: reduce repeated bss_info and
+Subject: [PATCH 15/39] 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
@@ -14,10 +14,10 @@
  1 file changed, 6 insertions(+), 15 deletions(-)
 
 diff --git a/mt7996/main.c b/mt7996/main.c
-index f306e9c5..136a0c28 100644
+index 786c3fb..02a33b8 100644
 --- a/mt7996/main.c
 +++ b/mt7996/main.c
-@@ -246,8 +246,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
+@@ -248,8 +248,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw,
  	struct mt7996_phy *phy = mt7996_hw_phy(hw);
  	int idx = msta->wcid.idx;
  
@@ -63,5 +63,5 @@
  	if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
  		mt7996_mcu_set_tx(dev, vif);
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch
similarity index 86%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch
index 3f55f11..e6a4f75 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0004-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch
@@ -1,7 +1,7 @@
-From fb2659a8aa12346cdda4010737f63178040fa513 Mon Sep 17 00:00:00 2001
+From eec83d4410d7c669d9a05bba1a69a742a6ccd490 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 04/22] wifi: mt76: connac: set correct muar_idx for connac3
+Subject: [PATCH 16/39] 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
@@ -15,7 +15,7 @@
  2 files changed, 8 insertions(+)
 
 diff --git a/mt76_connac.h b/mt76_connac.h
-index 77ca8f05..02acac64 100644
+index 77ca8f0..02acac6 100644
 --- a/mt76_connac.h
 +++ b/mt76_connac.h
 @@ -240,6 +240,11 @@ static inline bool is_connac_v1(struct mt76_dev *dev)
@@ -31,7 +31,7 @@
  {
  	switch (mt76_chip(dev)) {
 diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 46f69aa8..5fab6772 100644
+index 46f69aa..5fab677 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,
@@ -45,5 +45,5 @@
  				     &hdr.wlan_idx_hi);
  	skb = mt76_mcu_msg_alloc(dev, NULL, len);
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-mt7996-fill-txwi-by-SW-temporarily.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-mt7996-fill-txwi-by-SW-temporarily.patch
deleted file mode 100644
index c756ccd..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0016-wifi-mt76-mt7996-fill-txwi-by-SW-temporarily.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From aa539aaae8a95f3e970ad8e1f5a7381bb249ad7e Mon Sep 17 00:00:00 2001
-From: Peter Chiu <chui-hao.chiu@mediatek.com>
-Date: Fri, 17 Mar 2023 11:16:44 +0800
-Subject: [PATCH 16/22] wifi: mt76: mt7996: fill txwi by SW temporarily
-
-If use WA to fill TXD, it cannot ping pass.
-Remove this patch after bug fix.
-
-Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
----
- mt7996/mac.c | 8 +++-----
- 1 file changed, 3 insertions(+), 5 deletions(-)
-
-diff --git a/mt7996/mac.c b/mt7996/mac.c
-index ca163969..7059a4e1 100644
---- a/mt7996/mac.c
-+++ b/mt7996/mac.c
-@@ -1138,9 +1138,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 	pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
- 	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, key,
--				      pid, qid, 0);
-+	mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key,
-+			      pid, qid, 0);
- 
- 	txd[0] |= le32_encode_bits(1, MT_TXD0_VER);
- 
-@@ -1153,8 +1152,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
- 
- 	txp->fw.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->fw.flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD);
- 
- 	if (!key)
- 		txp->fw.flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME);
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7996-add-thermal-protection-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-add-thermal-protection-support.patch
similarity index 91%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7996-add-thermal-protection-support.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-add-thermal-protection-support.patch
index 526edc7..bcc5d51 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0007-wifi-mt76-mt7996-add-thermal-protection-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-add-thermal-protection-support.patch
@@ -1,7 +1,7 @@
-From 7d85212987815786ceff28379015a6bb23012dc3 Mon Sep 17 00:00:00 2001
+From 1fa1645ef7621981c7e87d27b9a7816f3485d3db 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 07/22] wifi: mt76: mt7996: add thermal protection support
+Subject: [PATCH 17/39] wifi: mt76: mt7996: add thermal protection support
 
 This commit includes the following changes:
 1. implement MTK thermal protection driver API
@@ -18,10 +18,10 @@
  6 files changed, 277 insertions(+)
 
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 8580ca59..c5c48349 100644
+index 6249de5..30c9a5d 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
-@@ -1009,6 +1009,7 @@ enum {
+@@ -1010,6 +1010,7 @@ enum {
  	MCU_UNI_EVENT_FW_LOG_2_HOST = 0x04,
  	MCU_UNI_EVENT_IE_COUNTDOWN = 0x09,
  	MCU_UNI_EVENT_RDD_REPORT = 0x11,
@@ -30,7 +30,7 @@
  
  #define MCU_UNI_CMD_EVENT			BIT(1)
 diff --git a/mt7996/init.c b/mt7996/init.c
-index f1b48cdd..53852ffc 100644
+index 8247153..c072b09 100644
 --- a/mt7996/init.c
 +++ b/mt7996/init.c
 @@ -42,6 +42,98 @@ static const struct ieee80211_iface_combination if_comb[] = {
@@ -132,7 +132,7 @@
  static void mt7996_led_set_config(struct led_classdev *led_cdev,
  				  u8 delay_on, u8 delay_off)
  {
-@@ -389,6 +481,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+@@ -391,6 +483,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
  	if (ret)
  		goto error;
  
@@ -143,7 +143,7 @@
  	ret = mt7996_init_debugfs(phy);
  	if (ret)
  		goto error;
-@@ -409,6 +505,8 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+@@ -411,6 +507,8 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
  	if (!phy)
  		return;
  
@@ -152,7 +152,7 @@
  	mphy = phy->dev->mt76.phys[band];
  	mt76_unregister_phy(mphy);
  	ieee80211_free_hw(mphy->hw);
-@@ -879,6 +977,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
+@@ -882,6 +980,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
  	if (ret)
  		return ret;
  
@@ -163,7 +163,7 @@
  	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
  
  	ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1);
-@@ -902,6 +1004,7 @@ void mt7996_unregister_device(struct mt7996_dev *dev)
+@@ -905,6 +1007,7 @@ void mt7996_unregister_device(struct mt7996_dev *dev)
  {
  	mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
  	mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
@@ -172,7 +172,7 @@
  	mt76_unregister_device(&dev->mt76);
  	mt7996_mcu_exit(dev);
 diff --git a/mt7996/main.c b/mt7996/main.c
-index 28b63d44..fbb7270d 100644
+index 02a33b8..9c80839 100644
 --- a/mt7996/main.c
 +++ b/mt7996/main.c
 @@ -51,6 +51,14 @@ int mt7996_run(struct ieee80211_hw *hw)
@@ -191,10 +191,10 @@
  
  	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6812a47b..325051bd 100644
+index c50dcce..c308d86 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)
+@@ -447,6 +447,34 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
  	}
  }
  
@@ -229,7 +229,7 @@
  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)
+@@ -491,6 +519,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;
@@ -239,7 +239,7 @@
  	default:
  		break;
  	}
-@@ -3178,6 +3209,81 @@ out:
+@@ -3217,6 +3248,81 @@ out:
  	return 0;
  }
  
@@ -322,7 +322,7 @@
  {
  	struct {
 diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index d7075a4d..778deedf 100644
+index 078f828..f235175 100644
 --- a/mt7996/mcu.h
 +++ b/mt7996/mcu.h
 @@ -30,6 +30,28 @@ struct mt7996_mcu_uni_event {
@@ -377,7 +377,7 @@
  enum mt7996_chan_mib_offs {
  	UNI_MIB_OBSS_AIRTIME = 26,
  	UNI_MIB_NON_WIFI_TIME = 27,
-@@ -642,6 +680,12 @@ enum{
+@@ -659,6 +697,12 @@ enum{
  	UNI_CMD_SR_SET_SIGA = 0xd0,
  };
  
@@ -391,10 +391,10 @@
  	UNI_CMD_ACCESS_REG_BASIC = 0x0,
  	UNI_CMD_ACCESS_RF_REG_BASIC,
 diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 4d7dcb95..36337808 100644
+index 651f53a..071031b 100644
 --- a/mt7996/mt7996.h
 +++ b/mt7996/mt7996.h
-@@ -47,6 +47,13 @@
+@@ -49,6 +49,13 @@
  #define MT7996_BASIC_RATES_TBL		11
  #define MT7996_BEACON_RATES_TBL		25
  
@@ -408,7 +408,7 @@
  struct mt7996_vif;
  struct mt7996_sta;
  struct mt7996_dfs_pulse;
-@@ -209,6 +216,11 @@ struct mt7996_phy {
+@@ -217,6 +224,11 @@ struct mt7996_phy {
  
  	struct ieee80211_vif *monitor_vif;
  
@@ -420,9 +420,9 @@
  	u32 rxfilter;
  	u64 omac_mask;
  
-@@ -457,6 +469,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);
+@@ -466,6 +478,9 @@ 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_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif);
  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);
@@ -431,5 +431,5 @@
  		       u8 rx_sel, u8 val);
  int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch
similarity index 95%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch
index 9e0668a..effc63b 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0008-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch
@@ -1,7 +1,7 @@
-From 72f33a06b35ae981db88d12cd8db267ce68b9e08 Mon Sep 17 00:00:00 2001
+From db282c765c1d9cd2a5eec6a095069fd4bcf627b4 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 08/22] wifi: mt76: mt7996: add thermal sensor device support
+Subject: [PATCH 18/39] wifi: mt76: mt7996: add thermal sensor device support
 
 ---
  mt7996/init.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -9,7 +9,7 @@
  2 files changed, 128 insertions(+)
 
 diff --git a/mt7996/init.c b/mt7996/init.c
-index 53852ffc..9c5d20ad 100644
+index c072b09..0319e7f 100644
 --- a/mt7996/init.c
 +++ b/mt7996/init.c
 @@ -4,6 +4,8 @@
@@ -128,10 +128,10 @@
  }
  
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 325051bd..f3fd2fd4 100644
+index c308d86..349c20e 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
-@@ -3209,6 +3209,47 @@ out:
+@@ -3248,6 +3248,47 @@ out:
  	return 0;
  }
  
@@ -180,5 +180,5 @@
  int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state)
  {
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0013-wifi-mt76-mt7996-fix-beamform-mcu-cmd-configuration.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0019-wifi-mt76-mt7996-fix-beamform-mcu-cmd-configuration.patch
similarity index 75%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0013-wifi-mt76-mt7996-fix-beamform-mcu-cmd-configuration.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0019-wifi-mt76-mt7996-fix-beamform-mcu-cmd-configuration.patch
index b2ed6a1..1fef46a 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0013-wifi-mt76-mt7996-fix-beamform-mcu-cmd-configuration.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0019-wifi-mt76-mt7996-fix-beamform-mcu-cmd-configuration.patch
@@ -1,7 +1,7 @@
-From 854db11781bd8f9fa7cb45ed529223a4784de9d9 Mon Sep 17 00:00:00 2001
+From 2fe477b8db9ab494a975a565a28e35fff76505d1 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Thu, 16 Mar 2023 16:09:51 +0800
-Subject: [PATCH 13/22] wifi: mt76: mt7996: fix beamform mcu cmd configuration
+Subject: [PATCH 19/39] wifi: mt76: mt7996: fix beamform mcu cmd configuration
 
 bf_num means how many band can support beamform, so the value shall be 3.
 bf_bitmap represents which band can support beamform.
@@ -10,10 +10,10 @@
  1 file changed, 2 insertions(+), 2 deletions(-)
 
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 6d11bc1a..df1ae639 100644
+index 349c20e..62e4869 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
-@@ -3394,8 +3394,8 @@ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
+@@ -3444,8 +3444,8 @@ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
  
  		tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en));
  		req_mod_en = (struct bf_mod_en_ctrl *)tlv;
@@ -25,5 +25,5 @@
  	}
  	default:
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-support-more-options-in-.set_bitrat.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0020-wifi-mt76-mt7996-support-more-options-in-.set_bitrat.patch
similarity index 87%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-support-more-options-in-.set_bitrat.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0020-wifi-mt76-mt7996-support-more-options-in-.set_bitrat.patch
index 1e2ec3b..14855c7 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0015-wifi-mt76-mt7996-support-more-options-in-.set_bitrat.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0020-wifi-mt76-mt7996-support-more-options-in-.set_bitrat.patch
@@ -1,21 +1,26 @@
-From c1d0d0a15d4cdafa1ed0a61606fd5fefa85a6bb7 Mon Sep 17 00:00:00 2001
+From bfb2498bbef4bcfd44658fad8acf82730c56ede6 Mon Sep 17 00:00:00 2001
 From: Howard Hsu <howard-yh.hsu@mediatek.com>
 Date: Tue, 20 Dec 2022 09:47:31 +0800
-Subject: [PATCH 15/22] wifi: mt76: mt7996: support more options in
+Subject: [PATCH] wifi: mt76: mt7996: support more options in
  .set_bitrate_mask()
 
 With this patch, driver can support runtime configuration for single
 rate, (HE)GI and HE_Ltf through .set_bitrate_mask(). Please noted that
 currently we do not support to fix any single parameter for EHT mode.
+
+Co-developed-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+Co-developed-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
 ---
- mt7996/mcu.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++-
- 1 file changed, 137 insertions(+), 2 deletions(-)
+ mt7996/mcu.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 135 insertions(+), 2 deletions(-)
 
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index cd86209b..8a7487ba 100644
+index 62e4869..b7f378a 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
-@@ -1610,6 +1610,136 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+@@ -1656,6 +1656,134 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
  				     MCU_WM_UNI_CMD(RA), true);
  }
  
@@ -75,10 +80,8 @@
 +	do {									\
 +		u8 i, gi = mask->control[band]._gi;				\
 +		gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI;		\
-+		for (i = 0; i <= sta->deflink.bandwidth; i++) {			\
-+			phy.sgi |= gi << (i << (_he));				\
-+			phy.he_ltf |= mask->control[band].he_ltf << (i << (_he));\
-+		}								\
++		phy.sgi = gi;							\
++		phy.he_ltf = mask->control[band].he_ltf;			\
 +		for (i = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) {	\
 +			if (!mask->control[band]._mcs[i])			\
 +				continue;					\
@@ -152,7 +155,7 @@
  static void
  mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
  			     struct ieee80211_vif *vif, struct ieee80211_sta *sta)
-@@ -1719,6 +1849,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+@@ -1765,6 +1893,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
  	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
  	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
  	struct sk_buff *skb;
@@ -160,7 +163,7 @@
  
  	skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
  					      &msta->wcid,
-@@ -1738,8 +1869,12 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+@@ -1784,8 +1913,12 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
  	 */
  	mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta);
  
@@ -176,5 +179,5 @@
  
  static int
 -- 
-2.39.2
+2.39.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0021-mt76-testmode-add-chainmask-hacking-for-eagle-band-2.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0021-mt76-testmode-add-chainmask-hacking-for-eagle-band-2.patch
deleted file mode 100644
index a3a0633..0000000
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0021-mt76-testmode-add-chainmask-hacking-for-eagle-band-2.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 59c4b77798c213a1766b3dac36a9de08145f063e Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Thu, 9 Mar 2023 18:45:04 +0800
-Subject: [PATCH 21/22] mt76: testmode: add chainmask hacking for eagle band 2
- 4T5R
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- mt7996/testmode.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/mt7996/testmode.c b/mt7996/testmode.c
-index 6d7cdbd5..0d4d9138 100644
---- a/mt7996/testmode.c
-+++ b/mt7996/testmode.c
-@@ -463,6 +463,7 @@ mt7996_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
- 		return 0;
- 
- 	chainmask = chainmask >> dev->chainshift[band_idx];
-+	chainmask = 0x1f;	/* hacking for eagle band2 4T5R */
- 	if (td->tx_antenna_mask > chainmask)
- 		return -EINVAL;
- 
--- 
-2.39.2
-
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-update-wmm-queue-mapping.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0021-wifi-mt76-mt7996-update-wmm-queue-mapping.patch
similarity index 83%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-update-wmm-queue-mapping.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0021-wifi-mt76-mt7996-update-wmm-queue-mapping.patch
index 659e580..6b1d684 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0017-wifi-mt76-mt7996-update-wmm-queue-mapping.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0021-wifi-mt76-mt7996-update-wmm-queue-mapping.patch
@@ -1,7 +1,7 @@
-From c3611ca1b4d8260dee8893cb922ad2ad0a8eb8d7 Mon Sep 17 00:00:00 2001
+From 88577bcf928a15c2e8e78b7684ccb75dfc693eac Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Mon, 20 Mar 2023 19:09:59 +0800
-Subject: [PATCH 17/22] wifi: mt76: mt7996: update wmm queue mapping
+Subject: [PATCH 21/39] wifi: mt76: mt7996: update wmm queue mapping
 
 The mac80211 use mac80211 queue (MQ) and the firmware
 use access class index (ACI) so convert the MQ to ACI
@@ -14,7 +14,7 @@
  2 files changed, 10 insertions(+), 3 deletions(-)
 
 diff --git a/mt7996/main.c b/mt7996/main.c
-index fbb7270d..059cc420 100644
+index 9c80839..8e38ebc 100644
 --- a/mt7996/main.c
 +++ b/mt7996/main.c
 @@ -198,7 +198,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw,
@@ -26,7 +26,7 @@
  
  	ret = mt7996_mcu_add_dev_info(phy, vif, true);
  	if (ret)
-@@ -423,9 +423,16 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+@@ -422,9 +422,16 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
  	       const struct ieee80211_tx_queue_params *params)
  {
  	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
@@ -45,10 +45,10 @@
  
  	return 0;
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 8a7487ba..43a4f939 100644
+index b27b88c..4359ede 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
-@@ -2789,7 +2789,7 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
+@@ -2835,7 +2835,7 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
  
  		e = (struct edca *)tlv;
  		e->set = WMM_PARAM_SET;
@@ -58,5 +58,5 @@
  		e->txop = cpu_to_le16(q->txop);
  
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-enable-IDS-debug-log.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0022-wifi-mt76-mt7996-enable-IDS-debug-log.patch
similarity index 84%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-enable-IDS-debug-log.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0022-wifi-mt76-mt7996-enable-IDS-debug-log.patch
index ca2e812..659edd4 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0018-wifi-mt76-mt7996-enable-IDS-debug-log.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0022-wifi-mt76-mt7996-enable-IDS-debug-log.patch
@@ -1,14 +1,14 @@
-From 6e972b77bd615df709c8ce4401ea6e120ccdf758 Mon Sep 17 00:00:00 2001
+From 7e3ab3b90fc75ff41a503f4632a669c8cba2ae90 Mon Sep 17 00:00:00 2001
 From: Peter Chiu <chui-hao.chiu@mediatek.com>
 Date: Tue, 21 Mar 2023 15:04:45 +0800
-Subject: [PATCH 18/22] wifi: mt76: mt7996: enable IDS debug log
+Subject: [PATCH 22/39] wifi: mt76: mt7996: enable IDS debug log
 
 ---
  mt7996/debugfs.c | 10 ++++++++--
  1 file changed, 8 insertions(+), 2 deletions(-)
 
 diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 513ab4ba..04220180 100644
+index 513ab4b..0422018 100644
 --- a/mt7996/debugfs.c
 +++ b/mt7996/debugfs.c
 @@ -290,6 +290,12 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
@@ -36,5 +36,5 @@
  
  		if (debug == DEBUG_RPT_RX)
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0019-mt76-testmode-add-atenl-support-in-mt7996.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0023-wifi-mt76-testmode-add-atenl-support-in-mt7996.patch
similarity index 87%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0019-mt76-testmode-add-atenl-support-in-mt7996.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0023-wifi-mt76-testmode-add-atenl-support-in-mt7996.patch
index 1cbaf0e..686576d 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0019-mt76-testmode-add-atenl-support-in-mt7996.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0023-wifi-mt76-testmode-add-atenl-support-in-mt7996.patch
@@ -1,7 +1,7 @@
-From e119dd8edebc8baa1e30945db3d0a7bc192bfc4b Mon Sep 17 00:00:00 2001
+From 33211d380fbf3b0892e560db99ac8dbc5c4e8cd5 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Wed, 28 Dec 2022 22:24:25 +0800
-Subject: [PATCH 19/22] mt76: testmode: add atenl support in mt7996
+Subject: [PATCH 23/39] wifi: mt76: testmode: add atenl support in mt7996
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -10,7 +10,7 @@
  2 files changed, 4 insertions(+), 1 deletion(-)
 
 diff --git a/testmode.c b/testmode.c
-index 0accc71a..0d2bae9f 100644
+index 0accc71..0d2bae9 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -612,7 +612,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
@@ -24,7 +24,7 @@
  
  	if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
 diff --git a/testmode.h b/testmode.h
-index 5e2792d8..a40cd74b 100644
+index 5e2792d..a40cd74 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -17,6 +17,7 @@
@@ -44,5 +44,5 @@
  	MT76_TM_ATTR_TX_COUNT,
  	MT76_TM_ATTR_TX_LENGTH,
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0020-mt76-testmode-add-basic-testmode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0024-wifi-mt76-testmode-add-basic-testmode-support.patch
similarity index 94%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0020-mt76-testmode-add-basic-testmode-support.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0024-wifi-mt76-testmode-add-basic-testmode-support.patch
index 30eef7a..6facdc3 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0020-mt76-testmode-add-basic-testmode-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0024-wifi-mt76-testmode-add-basic-testmode-support.patch
@@ -1,7 +1,7 @@
-From 058747d35c83a796712bec098cf6509965cf28cc Mon Sep 17 00:00:00 2001
+From 826ab7312fa42110844329a333df76dec614e48f Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Wed, 1 Mar 2023 11:59:16 +0800
-Subject: [PATCH 20/22] mt76: testmode: add basic testmode support
+Subject: [PATCH] wifi: mt76: testmode: add basic testmode support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -13,21 +13,21 @@
  mt7996/eeprom.c   |  35 ++-
  mt7996/eeprom.h   |   1 +
  mt7996/init.c     |   7 +
- mt7996/main.c     |  15 ++
- mt7996/mcu.c      |  39 ++-
+ mt7996/main.c     |  16 ++
+ mt7996/mcu.c      |  42 ++-
  mt7996/mcu.h      |  27 ++
  mt7996/mt7996.h   |  22 ++
- mt7996/testmode.c | 632 ++++++++++++++++++++++++++++++++++++++++++++++
- mt7996/testmode.h | 295 ++++++++++++++++++++++
- testmode.c        |  73 ++++--
+ mt7996/testmode.c | 658 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/testmode.h | 295 +++++++++++++++++++++
+ testmode.c        |  73 +++--
  testmode.h        |  60 +++++
  tools/fields.c    |  92 +++++++
- 17 files changed, 1315 insertions(+), 29 deletions(-)
+ 17 files changed, 1344 insertions(+), 30 deletions(-)
  create mode 100644 mt7996/testmode.c
  create mode 100644 mt7996/testmode.h
 
 diff --git a/eeprom.c b/eeprom.c
-index ea54b7af..263e5089 100644
+index ea54b7a..263e508 100644
 --- a/eeprom.c
 +++ b/eeprom.c
 @@ -89,8 +89,10 @@ int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int offset, int len)
@@ -44,7 +44,7 @@
  
  out_put_node:
 diff --git a/mac80211.c b/mac80211.c
-index 467afef9..d1cdaee8 100644
+index 467afef..d1cdaee 100644
 --- a/mac80211.c
 +++ b/mac80211.c
 @@ -826,7 +826,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
@@ -58,7 +58,7 @@
  		if (status->flag & RX_FLAG_FAILED_FCS_CRC)
  			phy->test.rx_stats.fcs_error[q]++;
 diff --git a/mt76.h b/mt76.h
-index 6b07b8fa..31d5dc37 100644
+index 6b07b8f..31d5dc3 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -652,8 +652,12 @@ struct mt76_testmode_ops {
@@ -133,7 +133,7 @@
  {
  #ifdef CONFIG_NL80211_TESTMODE
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index fbb1206f..17cb8128 100644
+index 30c9a5d..724a48a 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
 @@ -1218,10 +1218,12 @@ enum {
@@ -150,7 +150,7 @@
  	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
  	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
 diff --git a/mt7996/Makefile b/mt7996/Makefile
-index 07c8b555..bed9efd6 100644
+index 07c8b55..bed9efd 100644
 --- a/mt7996/Makefile
 +++ b/mt7996/Makefile
 @@ -6,3 +6,5 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
@@ -160,7 +160,7 @@
 +
 +mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
 diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 544b6c6f..f5e95460 100644
+index 544b6c6..f5e9546 100644
 --- a/mt7996/eeprom.c
 +++ b/mt7996/eeprom.c
 @@ -6,6 +6,11 @@
@@ -230,7 +230,7 @@
  		if (ret < 0)
  			return ret;
 diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 0c749774..0f8f0cd8 100644
+index 0c74977..0f8f0cd 100644
 --- a/mt7996/eeprom.h
 +++ b/mt7996/eeprom.h
 @@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
@@ -242,10 +242,10 @@
  	MT_EE_RATE_DELTA_2G =	0x1400,
  	MT_EE_RATE_DELTA_5G =	0x147d,
 diff --git a/mt7996/init.c b/mt7996/init.c
-index d44e3ae9..36ead6db 100644
+index 0319e7f..fecd0d3 100644
 --- a/mt7996/init.c
 +++ b/mt7996/init.c
-@@ -639,6 +639,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+@@ -641,6 +641,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
  
  	set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
  
@@ -256,7 +256,7 @@
  	ret = mt7996_mcu_init(dev);
  	if (ret)
  		return ret;
-@@ -1054,6 +1058,9 @@ int mt7996_register_device(struct mt7996_dev *dev)
+@@ -1056,6 +1060,9 @@ int mt7996_register_device(struct mt7996_dev *dev)
  
  	mt7996_init_wiphy(hw);
  
@@ -267,10 +267,10 @@
  	if (IS_ENABLED(CONFIG_MT76_LEDS)) {
  		dev->mphy.leds.cdev.brightness_set = mt7996_led_set_brightness;
 diff --git a/mt7996/main.c b/mt7996/main.c
-index 059cc420..3820b235 100644
+index 8e38ebc..6c38993 100644
 --- a/mt7996/main.c
 +++ b/mt7996/main.c
-@@ -23,6 +23,17 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
+@@ -23,6 +23,18 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
  	return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
  }
  
@@ -281,14 +281,15 @@
 +
 +	for (i = 0; i < __MT_MAX_BAND; i++) {
 +		phy = __mt7996_phy(dev, i);
-+		mt76_testmode_set_state(phy->mt76, MT76_TM_STATE_OFF);
++		if (phy)
++			mt76_testmode_set_state(phy->mt76, MT76_TM_STATE_OFF);
 +	}
 +}
 +
  int mt7996_run(struct ieee80211_hw *hw)
  {
  	struct mt7996_dev *dev = mt7996_hw_dev(hw);
-@@ -37,6 +48,8 @@ int mt7996_run(struct ieee80211_hw *hw)
+@@ -37,6 +49,8 @@ int mt7996_run(struct ieee80211_hw *hw)
  			goto out;
  	}
  
@@ -297,7 +298,7 @@
  	mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
  
  	ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
-@@ -1390,6 +1403,8 @@ const struct ieee80211_ops mt7996_ops = {
+@@ -1389,6 +1403,8 @@ const struct ieee80211_ops mt7996_ops = {
  	.sta_set_decap_offload = mt7996_sta_set_decap_offload,
  	.add_twt_setup = mt7996_mac_add_twt_setup,
  	.twt_teardown_request = mt7996_twt_teardown_request,
@@ -307,22 +308,25 @@
  	.sta_add_debugfs = mt7996_sta_add_debugfs,
  #endif
 diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index 43a4f939..24adeb12 100644
+index 665457a..0d2053c 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
-@@ -2467,7 +2467,10 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
- 		release_firmware(fw);						\
- 	} while (0)
+@@ -2515,8 +2515,12 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
+ {
+ 	int ret;
  
--	LOAD_RAM(WM);
+-	ret = __mt7996_load_ram(dev, "WM", MT7996_FIRMWARE_WM,
+-				MT7996_RAM_TYPE_WM);
 +	if (dev->testmode_enable)
-+		LOAD_RAM(WM_TM);
++		ret = __mt7996_load_ram(dev, "WM_TM", MT7996_FIRMWARE_WM_TM,
++					MT7996_RAM_TYPE_WM_TM);
 +	else
-+		LOAD_RAM(WM);
- 	LOAD_RAM(DSP);
- 	LOAD_RAM(WA);
- #undef LOAD_RAM
-@@ -4012,3 +4015,37 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val)
++		ret = __mt7996_load_ram(dev, "WM", MT7996_FIRMWARE_WM,
++					MT7996_RAM_TYPE_WM);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -4066,3 +4070,37 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val)
  	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req,
  				 sizeof(req), true);
  }
@@ -361,10 +365,10 @@
 +				 &req, sizeof(req), false);
 +}
 diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index 778deedf..ebc62713 100644
+index f235175..4ba06d9 100644
 --- a/mt7996/mcu.h
 +++ b/mt7996/mcu.h
-@@ -686,6 +686,33 @@ enum {
+@@ -703,6 +703,33 @@ enum {
  	UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
  };
  
@@ -399,10 +403,10 @@
  	UNI_CMD_ACCESS_REG_BASIC = 0x0,
  	UNI_CMD_ACCESS_RF_REG_BASIC,
 diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index ab4521a4..9bf3bf1a 100644
+index 071031b..f7d6580 100644
 --- a/mt7996/mt7996.h
 +++ b/mt7996/mt7996.h
-@@ -30,9 +30,11 @@
+@@ -31,9 +31,11 @@
  #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"
@@ -413,16 +417,16 @@
 +#define MT7996_EEPROM_DEFAULT_TM	"mediatek/mt7996/mt7996_eeprom_tm.bin"
  #define MT7996_EEPROM_SIZE		7680
  #define MT7996_EEPROM_BLOCK_SIZE	16
- #define MT7996_TOKEN_SIZE		8192
-@@ -62,6 +64,7 @@ struct mt7996_dfs_pattern;
+ #define MT7996_TOKEN_SIZE		16384
+@@ -63,6 +65,7 @@ struct mt7996_dfs_pattern;
  
  enum mt7996_ram_type {
- 	MT7996_RAM_TYPE_WM = 0,
+ 	MT7996_RAM_TYPE_WM,
 +	MT7996_RAM_TYPE_WM_TM = MT7996_RAM_TYPE_WM,
  	MT7996_RAM_TYPE_WA,
  	MT7996_RAM_TYPE_DSP,
  };
-@@ -243,6 +246,20 @@ struct mt7996_phy {
+@@ -244,6 +247,20 @@ struct mt7996_phy {
  
  	struct mib_stats mib;
  	struct mt76_channel_state state_ts;
@@ -443,7 +447,7 @@
  };
  
  struct mt7996_dev {
-@@ -302,6 +319,8 @@ struct mt7996_dev {
+@@ -303,6 +320,8 @@ struct mt7996_dev {
  	bool flash_mode:1;
  	bool has_eht:1;
  
@@ -452,7 +456,7 @@
  	bool ibf;
  	u8 fw_debug_wm;
  	u8 fw_debug_wa;
-@@ -407,6 +426,7 @@ mt7996_phy3(struct mt7996_dev *dev)
+@@ -408,6 +427,7 @@ mt7996_phy3(struct mt7996_dev *dev)
  extern const struct ieee80211_ops mt7996_ops;
  extern struct pci_driver mt7996_pci_driver;
  extern struct pci_driver mt7996_hif_driver;
@@ -460,7 +464,7 @@
  
  struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
  				     void __iomem *mem_base, u32 device_id);
-@@ -416,6 +436,7 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+@@ -417,6 +437,7 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
  int mt7996_register_device(struct mt7996_dev *dev);
  void mt7996_unregister_device(struct mt7996_dev *dev);
  int mt7996_eeprom_init(struct mt7996_dev *dev);
@@ -468,7 +472,7 @@
  int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
  int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
  				   struct ieee80211_channel *chan);
-@@ -492,6 +513,7 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
+@@ -494,6 +515,7 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
  int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
  void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
  void mt7996_mcu_exit(struct mt7996_dev *dev);
@@ -478,10 +482,10 @@
  {
 diff --git a/mt7996/testmode.c b/mt7996/testmode.c
 new file mode 100644
-index 00000000..6d7cdbd5
+index 0000000..43eca4e
 --- /dev/null
 +++ b/mt7996/testmode.c
-@@ -0,0 +1,632 @@
+@@ -0,0 +1,658 @@
 +// SPDX-License-Identifier: ISC
 +/*
 + * Copyright (C) 2022 MediaTek Inc.
@@ -554,6 +558,27 @@
 +}
 +
 +static int
++mt7996_tm_check_antenna(struct mt7996_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_dev *dev = phy->dev;
++	u8 band_idx = phy->mt76->band_idx;
++	u32 chainmask = phy->mt76->chainmask;
++	u32 aux_rx_mask;
++
++	chainmask = chainmask >> dev->chainshift[band_idx];
++	aux_rx_mask = BIT(fls(chainmask)) * phy->has_aux_rx;
++	if (td->tx_antenna_mask & ~(chainmask | aux_rx_mask)) {
++		dev_err(dev->mt76.dev,
++			"tx antenna mask 0x%x exceeds hw limit (chainmask 0x%x, has aux rx: %s)\n",
++			td->tx_antenna_mask, chainmask, phy->has_aux_rx ? "yes" : "no");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int
 +mt7996_tm_set(struct mt7996_dev *dev, u32 func_idx, u32 data)
 +{
 +	struct mt7996_tm_req req = {
@@ -603,6 +628,7 @@
 +mt7996_tm_set_antenna(struct mt7996_phy *phy, u32 func_idx)
 +{
 +#define SPE_INDEX_MASK		BIT(31)
++#define TX_ANTENNA_MASK		GENMASK(3, 0)
 +#define RX_ANTENNA_MASK		GENMASK(20, 16)		/* RX antenna mask at most 5 bit */
 +	struct mt7996_dev *dev = phy->dev;
 +	struct mt76_testmode_data *td = &phy->mt76->test;
@@ -613,7 +639,7 @@
 +
 +	if (func_idx == SET_ID(TX_PATH))
 +		antenna_mask = td->tx_spe_idx ? (SPE_INDEX_MASK | td->tx_spe_idx) :
-+						td->tx_antenna_mask;
++						td->tx_antenna_mask & TX_ANTENNA_MASK;
 +	else if (func_idx == SET_ID(RX_PATH))
 +		antenna_mask = u32_encode_bits(td->tx_antenna_mask, RX_ANTENNA_MASK);
 +	else
@@ -758,6 +784,7 @@
 +		}
 +
 +		mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
++		mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
 +		mt7996_tm_set(dev, SET_ID(STBC), td->tx_rate_stbc);
 +		mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
 +		mt7996_tm_set(dev, SET_ID(IBF_ENABLE), td->ibf);
@@ -914,7 +941,7 @@
 +	    (state == MT76_TM_STATE_OFF &&
 +	     prev_state == MT76_TM_STATE_IDLE)) {
 +		u32 changed = 0;
-+		int i;
++		int i, ret;
 +
 +		for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
 +			u16 cur = tm_change_map[i];
@@ -923,6 +950,10 @@
 +				changed |= BIT(i);
 +		}
 +
++		ret = mt7996_tm_check_antenna(phy);
++		if (ret)
++			return ret;
++
 +		mt7996_tm_update_params(phy, changed);
 +	}
 +
@@ -936,9 +967,8 @@
 +	struct mt76_testmode_data *td = &mphy->test;
 +	struct mt7996_phy *phy = mphy->priv;
 +	struct mt7996_dev *dev = phy->dev;
-+	u32 chainmask = mphy->chainmask, changed = 0;
-+	u8 band_idx = phy->mt76->band_idx;
-+	int i;
++	u32 changed = 0;
++	int i, ret;
 +
 +	BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
 +
@@ -946,9 +976,9 @@
 +	    td->state == MT76_TM_STATE_OFF)
 +		return 0;
 +
-+	chainmask = chainmask >> dev->chainshift[band_idx];
-+	if (td->tx_antenna_mask > chainmask)
-+		return -EINVAL;
++	ret = mt7996_tm_check_antenna(phy);
++	if (ret)
++		return ret;
 +
 +	for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
 +		if (tb[tm_change_map[i]])
@@ -1116,7 +1146,7 @@
 +};
 diff --git a/mt7996/testmode.h b/mt7996/testmode.h
 new file mode 100644
-index 00000000..f00e51f4
+index 0000000..f00e51f
 --- /dev/null
 +++ b/mt7996/testmode.h
 @@ -0,0 +1,295 @@
@@ -1416,7 +1446,7 @@
 +
 +#endif
 diff --git a/testmode.c b/testmode.c
-index 0d2bae9f..fc68c2af 100644
+index 0d2bae9..fc68c2a 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -2,6 +2,7 @@
@@ -1590,7 +1620,7 @@
  	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
  	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
 diff --git a/testmode.h b/testmode.h
-index a40cd74b..8d0b9702 100644
+index a40cd74..8d0b970 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -39,6 +39,11 @@
@@ -1710,7 +1740,7 @@
  	/* keep last */
  	NUM_MT76_TM_TX_MODES,
 diff --git a/tools/fields.c b/tools/fields.c
-index e3f69089..e5cf7c53 100644
+index e3f6908..e5cf7c5 100644
 --- a/tools/fields.c
 +++ b/tools/fields.c
 @@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
@@ -1872,5 +1902,5 @@
  
  const struct tm_field msg_field = {
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0026-wifi-mt76-mt7996-add-led-feature-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0026-wifi-mt76-mt7996-add-led-feature-support.patch
new file mode 100644
index 0000000..06e273a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0026-wifi-mt76-mt7996-add-led-feature-support.patch
@@ -0,0 +1,105 @@
+From 030159528eff349db01b6b47b6fff8112b4282a4 Mon Sep 17 00:00:00 2001
+From: mtk25577 <jen-hao.cheng@mediatek.com>
+Date: Tue, 28 Mar 2023 18:23:00 +0800
+Subject: [PATCH 26/39] wifi: mt76: mt7996: add led feature support
+
+Signed-off-by: mtk25577 <jen-hao.cheng@mediatek.com>
+---
+ mt7996/Makefile |  1 +
+ mt7996/init.c   | 33 +++++++++++++++++++++------------
+ mt7996/regs.h   |  1 +
+ 3 files changed, 23 insertions(+), 12 deletions(-)
+
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index bed9efd..7c2514a 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -1,4 +1,5 @@
+ # SPDX-License-Identifier: ISC
++EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
+ 
+ obj-$(CONFIG_MT7996E) += mt7996e.o
+ 
+diff --git a/mt7996/init.c b/mt7996/init.c
+index fecd0d3..192af3f 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -232,23 +232,31 @@ static void mt7996_led_set_config(struct led_classdev *led_cdev,
+ 	dev = container_of(mphy->dev, struct mt7996_dev, mt76);
+ 
+ 	/* select TX blink mode, 2: only data frames */
+-	mt76_rmw_field(dev, MT_TMAC_TCR0(0), MT_TMAC_TCR0_TX_BLINK, 2);
++	mt76_rmw_field(dev, MT_TMAC_TCR0(mphy->band_idx), MT_TMAC_TCR0_TX_BLINK, 2);
+ 
+ 	/* enable LED */
+-	mt76_wr(dev, MT_LED_EN(0), 1);
++	mt76_wr(dev, MT_LED_EN(mphy->band_idx), 1);
+ 
+ 	/* set LED Tx blink on/off time */
+ 	val = FIELD_PREP(MT_LED_TX_BLINK_ON_MASK, delay_on) |
+ 	      FIELD_PREP(MT_LED_TX_BLINK_OFF_MASK, delay_off);
+-	mt76_wr(dev, MT_LED_TX_BLINK(0), val);
++	mt76_wr(dev, MT_LED_TX_BLINK(mphy->band_idx), val);
++
++	/* turn LED off */
++	if (delay_off == 0xff && delay_on == 0x0)
++		val = MT_LED_CTRL_POLARITY | MT_LED_CTRL_KICK;
++	else {
++		/* control LED */
++		val = MT_LED_CTRL_BLINK_MODE | MT_LED_CTRL_KICK;
++		if (mphy->band_idx == MT_BAND1)
++			val |= MT_LED_CTRL_BLINK_BAND_SEL;
++	}
+ 
+-	/* control LED */
+-	val = MT_LED_CTRL_BLINK_MODE | MT_LED_CTRL_KICK;
+ 	if (mphy->leds.al)
+ 		val |= MT_LED_CTRL_POLARITY;
+ 
+-	mt76_wr(dev, MT_LED_CTRL(0), val);
+-	mt76_clear(dev, MT_LED_CTRL(0), MT_LED_CTRL_KICK);
++	mt76_wr(dev, MT_LED_CTRL(mphy->band_idx), val);
++	mt76_clear(dev, MT_LED_CTRL(mphy->band_idx), MT_LED_CTRL_KICK);
+ }
+ 
+ static int mt7996_led_set_blink(struct led_classdev *led_cdev,
+@@ -400,6 +408,12 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ 		ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
+ 	}
+ 
++	/* init led callbacks */
++	if (IS_ENABLED(CONFIG_MT76_LEDS)) {
++		phy->mt76->leds.cdev.brightness_set = mt7996_led_set_brightness;
++		phy->mt76->leds.cdev.blink_set = mt7996_led_set_blink;
++	}
++
+ 	mt76_set_stream_caps(phy->mt76, true);
+ 	mt7996_set_stream_vht_txbf_caps(phy);
+ 	mt7996_set_stream_he_eht_caps(phy);
+@@ -1063,11 +1077,6 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	dev->mt76.test_ops = &mt7996_testmode_ops;
+ #endif
+-	/* init led callbacks */
+-	if (IS_ENABLED(CONFIG_MT76_LEDS)) {
+-		dev->mphy.leds.cdev.brightness_set = mt7996_led_set_brightness;
+-		dev->mphy.leds.cdev.blink_set = mt7996_led_set_blink;
+-	}
+ 
+ 	ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+ 				   ARRAY_SIZE(mt76_rates));
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index d1d3d15..86da1bf 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -509,6 +509,7 @@ enum base_rev {
+ 
+ #define MT_LED_CTRL(_n)				MT_LED_PHYS(0x00 + ((_n) * 4))
+ #define MT_LED_CTRL_KICK			BIT(7)
++#define MT_LED_CTRL_BLINK_BAND_SEL		BIT(4)
+ #define MT_LED_CTRL_BLINK_MODE			BIT(2)
+ #define MT_LED_CTRL_POLARITY			BIT(1)
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0027-wifi-mt76-mt7996-fix-twt-mcu-command.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0027-wifi-mt76-mt7996-fix-twt-mcu-command.patch
new file mode 100644
index 0000000..2e938b7
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0027-wifi-mt76-mt7996-fix-twt-mcu-command.patch
@@ -0,0 +1,60 @@
+From e503dbe84bc2b46907a792e11c196018b274647a Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Tue, 28 Mar 2023 20:20:57 +0800
+Subject: [PATCH 27/39] wifi: mt76: mt7996: fix twt mcu command
+
+Update unified command for twt.
+
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+---
+ mt7915/mcu.h | 1 -
+ mt7996/mcu.c | 7 +++++--
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/mt7915/mcu.h b/mt7915/mcu.h
+index b9ea297..e7a5395 100644
+--- a/mt7915/mcu.h
++++ b/mt7915/mcu.h
+@@ -264,7 +264,6 @@ enum {
+ 	MCU_TWT_AGRT_MODIFY,
+ 	MCU_TWT_AGRT_DELETE,
+ 	MCU_TWT_AGRT_TEARDOWN,
+-	MCU_TWT_AGRT_GET_TSF,
+ };
+ 
+ enum {
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index a6d8235..6bcb33e 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3823,7 +3823,9 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ 			       int cmd)
+ {
+ 	struct {
+-		u8 _rsv[4];
++		/* fixed field */
++		u8 bss;
++		u8 _rsv[3];
+ 
+ 		__le16 tag;
+ 		__le16 len;
+@@ -3841,7 +3843,7 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ 		u8 exponent;
+ 		u8 is_ap;
+ 		u8 agrt_params;
+-		u8 __rsv2[135];
++		u8 __rsv2[23];
+ 	} __packed req = {
+ 		.tag = cpu_to_le16(UNI_CMD_TWT_ARGT_UPDATE),
+ 		.len = cpu_to_le16(sizeof(req) - 4),
+@@ -3851,6 +3853,7 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ 		.flowid = flow->id,
+ 		.peer_id = cpu_to_le16(flow->wcid),
+ 		.duration = flow->duration,
++		.bss = mvif->mt76.idx,
+ 		.bss_idx = mvif->mt76.idx,
+ 		.start_tsf = cpu_to_le64(flow->tsf),
+ 		.mantissa = flow->mantissa,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0028-wifi-mt76-mt7996-add-11v-mbss-support-for-mt76.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0028-wifi-mt76-mt7996-add-11v-mbss-support-for-mt76.patch
new file mode 100644
index 0000000..bf724c8
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0028-wifi-mt76-mt7996-add-11v-mbss-support-for-mt76.patch
@@ -0,0 +1,181 @@
+From 0449694e4e963d0b48354ae2c52016c34899fba6 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Wed, 8 Mar 2023 14:18:29 +0800
+Subject: [PATCH 28/39] wifi: mt76: mt7996: add 11v mbss support for mt76
+
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ mt76_connac_mcu.h | 10 ++++++
+ mt7996/init.c     |  2 ++
+ mt7996/mcu.c      | 79 ++++++++++++++++++++++++++++++++++++++++++++++-
+ 3 files changed, 90 insertions(+), 1 deletion(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 724a48a..97f874b 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1281,6 +1281,7 @@ enum {
+ 	UNI_BSS_INFO_RLM = 2,
+ 	UNI_BSS_INFO_BSS_COLOR = 4,
+ 	UNI_BSS_INFO_HE_BASIC = 5,
++	UNI_BSS_INFO_11V_MBSSID = 6,
+ 	UNI_BSS_INFO_BCN_CONTENT = 7,
+ 	UNI_BSS_INFO_BCN_CSA = 8,
+ 	UNI_BSS_INFO_BCN_BCC = 9,
+@@ -1551,6 +1552,15 @@ struct bss_info_uni_he {
+ 	u8 rsv[2];
+ } __packed;
+ 
++struct bss_info_uni_mbssid {
++	__le16 tag;
++	__le16 len;
++	u8 max_indicator;
++	u8 mbss_idx;
++	u8 tx_bss_omac_idx;
++	u8 rsv[1];
++} __packed;
++
+ struct mt76_connac_gtk_rekey_tlv {
+ 	__le16 tag;
+ 	__le16 len;
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 192af3f..0562439 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -359,6 +359,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ 	wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ 	wiphy->reg_notifier = mt7996_regd_notifier;
+ 	wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
++	wiphy->mbssid_max_interfaces = 16;
+ 
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+@@ -381,6 +382,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ 	ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+ 	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ 	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
++	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ 
+ 	hw->max_tx_fragments = 4;
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 6bcb33e..a369a08 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -631,6 +631,24 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 	he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80;
+ }
+ 
++static void
++mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
++		      struct mt7996_phy *phy)
++{
++	struct bss_info_uni_mbssid *mbssid;
++	struct tlv *tlv;
++
++	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid));
++
++	mbssid = (struct bss_info_uni_mbssid *)tlv;
++
++	mbssid->max_indicator = vif->bss_conf.bssid_indicator;
++	mbssid->mbss_idx = vif->bss_conf.bssid_index;
++	mbssid->tx_bss_omac_idx = 0;
++
++	return;
++}
++
+ static void
+ mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ 		       struct mt7996_phy *phy)
+@@ -895,6 +913,9 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ 		if (vif->bss_conf.he_support)
+ 			mt7996_mcu_bss_he_tlv(skb, vif, phy);
+ 
++		if (vif->bss_conf.bssid_indicator)
++			mt7996_mcu_bss_mbssid_tlv(skb, vif, phy);
++
+ 		/* this tag is necessary no matter if the vif is MLD */
+ 		mt7996_mcu_bss_mld_tlv(skb, vif);
+ 	}
+@@ -2162,6 +2183,59 @@ mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
+ 	info->cnt = skb->data[offs->cntdwn_counter_offs[0]];
+ }
+ 
++static void
++mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
++			struct ieee80211_vif *vif, struct bss_bcn_content_tlv *bcn,
++			struct ieee80211_mutable_offsets *offs)
++{
++	struct bss_bcn_mbss_tlv *mbss;
++	const struct element *elem;
++	struct tlv *tlv;
++
++	if (!vif->bss_conf.bssid_indicator)
++		return;
++
++	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_MBSSID, sizeof(*mbss));
++
++	mbss = (struct bss_bcn_mbss_tlv *)tlv;
++	mbss->offset[0] = cpu_to_le16(offs->tim_offset);
++	mbss->bitmap = cpu_to_le32(1);
++
++	for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID,
++			&skb->data[offs->mbssid_off],
++			skb->len - offs->mbssid_off) {
++		const struct element *sub_elem;
++
++		if (elem->datalen < 2)
++			continue;
++
++		for_each_element(sub_elem, elem->data + 1, elem->datalen - 1) {
++			const struct ieee80211_bssid_index *idx;
++			const u8 *idx_ie;
++
++			if (sub_elem->id || sub_elem->datalen < 4)
++				 continue; /* not a valid BSS profile */
++
++			/* Find WLAN_EID_MULTI_BSSID_IDX
++			 * in the merged nontransmitted profile
++			 */
++			idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX,
++						sub_elem->data,
++						sub_elem->datalen);
++			if (!idx_ie || idx_ie[1] < sizeof(*idx))
++				 continue;
++
++			idx = (void *)(idx_ie + 2);
++			if (!idx->bssid_index || idx->bssid_index > 31)
++				continue;
++
++			mbss->offset[idx->bssid_index] =
++				cpu_to_le16(idx_ie - skb->data);
++			mbss->bitmap |= cpu_to_le32(BIT(idx->bssid_index));
++		}
++	}
++}
++
+ static void
+ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 		       struct sk_buff *rskb, struct sk_buff *skb,
+@@ -2202,6 +2276,9 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	struct tlv *tlv;
+ 	struct bss_bcn_content_tlv *bcn;
+ 
++	if (vif->bss_conf.nontransmitted)
++		return 0;
++
+ 	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+ 					  MT7996_BEACON_UPDATE_SIZE);
+ 	if (IS_ERR(rskb))
+@@ -2229,7 +2306,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
+ 
+ 	mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
+-	/* TODO: subtag - 11v MBSSID */
++	mt7996_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs);
+ 	mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs);
+ 	dev_kfree_skb(skb);
+ out:
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0029-wifi-mt76-mt7996-Update-beacon-size-limitation-for-1.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0029-wifi-mt76-mt7996-Update-beacon-size-limitation-for-1.patch
new file mode 100644
index 0000000..67e5c6c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0029-wifi-mt76-mt7996-Update-beacon-size-limitation-for-1.patch
@@ -0,0 +1,188 @@
+From 8b591f425b1bb71ebde4a1eb9c926f0c9017fe28 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Mon, 27 Mar 2023 09:47:44 +0800
+Subject: [PATCH 29/39] wifi: mt76: mt7996: Update beacon size limitation for
+ 11v
+
+The TLV size limitation for these two has been expanded to
+accommodate 11v MBSSID IE.
+
+Co-developed-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
+Co-developed-by: Money Wang <Money.Wang@mediatek.com>
+Signed-off-by: Money Wang <Money.Wang@mediatek.com>
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ mt7996/main.c |  4 ++--
+ mt7996/mcu.c  | 39 +++++++++++++++++++++++----------------
+ mt7996/mcu.h  | 11 ++++-------
+ 3 files changed, 29 insertions(+), 25 deletions(-)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 6c38993..520f250 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -618,8 +618,8 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ 		mt7996_mcu_add_beacon(hw, vif, info->enable_beacon);
+ 	}
+ 
+-	if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP ||
+-	    changed & BSS_CHANGED_FILS_DISCOVERY)
++	if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
++		       BSS_CHANGED_FILS_DISCOVERY))
+ 		mt7996_mcu_beacon_inband_discov(dev, vif, changed);
+ 
+ 	mutex_unlock(&dev->mt76.mutex);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index a369a08..7b8f883 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2257,7 +2257,7 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ 			bcn->bcc_ie_pos = cpu_to_le16(offset - 3);
+ 	}
+ 
+-	buf = (u8 *)bcn + sizeof(*bcn) - MAX_BEACON_SIZE;
++	buf = (u8 *)bcn + sizeof(*bcn);
+ 	mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0,
+ 			      BSS_CHANGED_BEACON);
+ 
+@@ -2275,28 +2275,21 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	struct sk_buff *skb, *rskb;
+ 	struct tlv *tlv;
+ 	struct bss_bcn_content_tlv *bcn;
++	int len;
+ 
+ 	if (vif->bss_conf.nontransmitted)
+ 		return 0;
+ 
+ 	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+-					  MT7996_BEACON_UPDATE_SIZE);
++					  MT7996_MAX_BSS_OFFLOAD_SIZE);
+ 	if (IS_ERR(rskb))
+ 		return PTR_ERR(rskb);
+ 
+-	tlv = mt7996_mcu_add_uni_tlv(rskb,
+-				     UNI_BSS_INFO_BCN_CONTENT, sizeof(*bcn));
+-	bcn = (struct bss_bcn_content_tlv *)tlv;
+-	bcn->enable = en;
+-
+-	if (!en)
+-		goto out;
+-
+ 	skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
+ 	if (!skb)
+ 		return -EINVAL;
+ 
+-	if (skb->len > MAX_BEACON_SIZE - MT_TXD_SIZE) {
++	if (skb->len > MT7996_MAX_BEACON_SIZE) {
+ 		dev_err(dev->mt76.dev, "Bcn size limit exceed\n");
+ 		dev_kfree_skb(skb);
+ 		return -EINVAL;
+@@ -2305,11 +2298,19 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 	info = IEEE80211_SKB_CB(skb);
+ 	info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
+ 
++	len = sizeof(*bcn) + MT_TXD_SIZE + skb->len;
++	tlv = mt7996_mcu_add_uni_tlv(rskb,
++				     UNI_BSS_INFO_BCN_CONTENT, len);
++	bcn = (struct bss_bcn_content_tlv *)tlv;
++	bcn->enable = en;
++	if (!en)
++		goto out;
++
+ 	mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
+ 	mt7996_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs);
+ 	mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs);
+-	dev_kfree_skb(skb);
+ out:
++	dev_kfree_skb(skb);
+ 	return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+ 				     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+ }
+@@ -2330,9 +2331,13 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	struct sk_buff *rskb, *skb = NULL;
+ 	struct tlv *tlv;
+ 	u8 *buf, interval;
++	int len;
++
++	if (vif->bss_conf.nontransmitted)
++		return 0;
+ 
+ 	rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+-					  MT7996_INBAND_FRAME_SIZE);
++					  MT7996_MAX_BSS_OFFLOAD_SIZE);
+ 	if (IS_ERR(rskb))
+ 		return PTR_ERR(rskb);
+ 
+@@ -2349,7 +2354,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	if (!skb)
+ 		return -EINVAL;
+ 
+-	if (skb->len > MAX_INBAND_FRAME_SIZE - MT_TXD_SIZE) {
++	if (skb->len > MT7996_MAX_BEACON_SIZE) {
+ 		dev_err(dev->mt76.dev, "inband discovery size limit exceed\n");
+ 		dev_kfree_skb(skb);
+ 		return -EINVAL;
+@@ -2360,7 +2365,9 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	info->band = band;
+ 	info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
+ 
+-	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, sizeof(*discov));
++	len = sizeof(*discov) + MT_TXD_SIZE + skb->len;
++
++	tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, len);
+ 
+ 	discov = (struct bss_inband_discovery_tlv *)tlv;
+ 	discov->tx_mode = OFFLOAD_TX_MODE_SU;
+@@ -2371,7 +2378,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	discov->enable = true;
+ 	discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED);
+ 
+-	buf = (u8 *)tlv + sizeof(*discov) - MAX_INBAND_FRAME_SIZE;
++	buf = (u8 *)tlv + sizeof(*discov);
+ 
+ 	mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, changed);
+ 
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 4ba06d9..eed7371 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -308,8 +308,6 @@ struct bss_inband_discovery_tlv {
+ 	u8 enable;
+ 	__le16 wcid;
+ 	__le16 prob_rsp_len;
+-#define MAX_INBAND_FRAME_SIZE 512
+-	u8 pkt[MAX_INBAND_FRAME_SIZE];
+ } __packed;
+ 
+ struct bss_bcn_content_tlv {
+@@ -321,8 +319,6 @@ struct bss_bcn_content_tlv {
+ 	u8 enable;
+ 	u8 type;
+ 	__le16 pkt_len;
+-#define MAX_BEACON_SIZE 512
+-	u8 pkt[MAX_BEACON_SIZE];
+ } __packed;
+ 
+ struct bss_bcn_cntdwn_tlv {
+@@ -629,13 +625,14 @@ enum {
+ 					 sizeof(struct sta_rec_hdr_trans) +	\
+ 					 sizeof(struct tlv))
+ 
++#define MT7996_MAX_BEACON_SIZE		1342
+ #define MT7996_BEACON_UPDATE_SIZE	(sizeof(struct bss_req_hdr) +		\
+ 					 sizeof(struct bss_bcn_content_tlv) +	\
++					 MT_TXD_SIZE +				\
+ 					 sizeof(struct bss_bcn_cntdwn_tlv) +	\
+ 					 sizeof(struct bss_bcn_mbss_tlv))
+-
+-#define MT7996_INBAND_FRAME_SIZE	(sizeof(struct bss_req_hdr) +		\
+-					 sizeof(struct bss_inband_discovery_tlv))
++#define MT7996_MAX_BSS_OFFLOAD_SIZE	(MT7996_MAX_BEACON_SIZE +		\
++					 MT7996_BEACON_UPDATE_SIZE)
+ 
+ enum {
+ 	UNI_BAND_CONFIG_RADIO_ENABLE,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0030-wifi-mt76-mt7996-add-support-for-auxiliary-path.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0030-wifi-mt76-mt7996-add-support-for-auxiliary-path.patch
new file mode 100644
index 0000000..798eb69
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0030-wifi-mt76-mt7996-add-support-for-auxiliary-path.patch
@@ -0,0 +1,138 @@
+From 7390e5db3745febd580026e723b2ca806e308008 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 14 Apr 2023 16:51:59 +0800
+Subject: [PATCH 30/39] wifi: mt76: mt7996: add support for auxiliary path
+
+Add support to correctly configure the setting of variants that have
+additional TX or RX path.
+
+Change-Id: I1312c193beab5e16aae7161a7e3bdda100b72f8d
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/eeprom.c | 21 +++++++++++++++++----
+ mt7996/eeprom.h |  3 +++
+ mt7996/mcu.c    |  2 +-
+ mt7996/mt7996.h | 14 ++++++++++++++
+ 4 files changed, 35 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index f5e9546..9840c77 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -171,36 +171,49 @@ static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+ 
+ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ {
+-	u8 path, nss, band_idx = phy->mt76->band_idx;
++	u8 path, rx_path, nss, band_idx = phy->mt76->band_idx;
+ 	u8 *eeprom = dev->mt76.eeprom.data;
+ 	struct mt76_phy *mphy = phy->mt76;
++	int max_path = 5, max_nss = 4;
+ 	int ret;
+ 
+ 	switch (band_idx) {
+ 	case MT_BAND1:
+ 		path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
+ 				 eeprom[MT_EE_WIFI_CONF + 2]);
++		rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1,
++				    eeprom[MT_EE_WIFI_CONF + 3]);
+ 		nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
+ 				eeprom[MT_EE_WIFI_CONF + 5]);
+ 		break;
+ 	case MT_BAND2:
+ 		path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
+ 				 eeprom[MT_EE_WIFI_CONF + 2]);
++		rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2,
++				    eeprom[MT_EE_WIFI_CONF + 4]);
+ 		nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
+ 				eeprom[MT_EE_WIFI_CONF + 5]);
+ 		break;
+ 	default:
+ 		path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
+ 				 eeprom[MT_EE_WIFI_CONF + 1]);
++		rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0,
++				    eeprom[MT_EE_WIFI_CONF + 3]);
+ 		nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
+ 				eeprom[MT_EE_WIFI_CONF + 4]);
+ 		break;
+ 	}
+ 
+-	if (!path || path > 4)
+-		path = 4;
++	if (!path || path > max_path)
++		path = max_path;
+ 
+-	nss = min_t(u8, min_t(u8, 4, nss), path);
++	if (!nss || nss > max_nss)
++		nss = max_nss;
++
++	nss = min_t(u8, nss, path);
++
++	if (path != rx_path)
++		phy->has_aux_rx = true;
+ 
+ 	mphy->antenna_mask = BIT(nss) - 1;
+ 	mphy->chainmask = (BIT(path) - 1) << dev->chainshift[band_idx];
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 0f8f0cd..9ea3667 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -34,6 +34,9 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CONF1_TX_PATH_BAND0		GENMASK(5, 3)
+ #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_CONF3_RX_PATH_BAND0		GENMASK(2, 0)
++#define MT_EE_WIFI_CONF3_RX_PATH_BAND1		GENMASK(5, 3)
++#define MT_EE_WIFI_CONF4_RX_PATH_BAND2		GENMASK(2, 0)
+ #define MT_EE_WIFI_CONF4_STREAM_NUM_BAND0	GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF5_STREAM_NUM_BAND1	GENMASK(2, 0)
+ #define MT_EE_WIFI_CONF5_STREAM_NUM_BAND2	GENMASK(5, 3)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 7b8f883..a2c1e43 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3198,7 +3198,7 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+ 		.center_ch = ieee80211_frequency_to_channel(freq1),
+ 		.bw = mt76_connac_chan_bw(chandef),
+ 		.tx_path_num = hweight16(phy->mt76->chainmask),
+-		.rx_path = phy->mt76->chainmask >> dev->chainshift[band_idx],
++		.rx_path = mt7996_rx_chainmask(phy) >> dev->chainshift[band_idx],
+ 		.band_idx = band_idx,
+ 		.channel_band = ch_band[chandef->chan->band],
+ 	};
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index f7d6580..8e5b3c3 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -248,6 +248,8 @@ struct mt7996_phy {
+ 	struct mib_stats mib;
+ 	struct mt76_channel_state state_ts;
+ 
++	bool has_aux_rx;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	struct {
+ 		u32 *reg_backup;
+@@ -551,6 +553,18 @@ static inline void mt7996_irq_disable(struct mt7996_dev *dev, u32 mask)
+ void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset,
+ 			  size_t len);
+ 
++static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy)
++{
++	int max_nss = hweight8(phy->mt76->hw->wiphy->available_antennas_tx);
++	int cur_nss = hweight8(phy->mt76->antenna_mask);
++	u16 tx_chainmask = phy->mt76->chainmask;
++
++	if (cur_nss != max_nss)
++		return tx_chainmask;
++
++	return tx_chainmask | (BIT(fls(tx_chainmask)) * phy->has_aux_rx);
++}
++
+ 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);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0031-wifi-mt76-mt7996-fix-memory-leak.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0031-wifi-mt76-mt7996-fix-memory-leak.patch
new file mode 100644
index 0000000..0669836
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0031-wifi-mt76-mt7996-fix-memory-leak.patch
@@ -0,0 +1,49 @@
+From 80a4f15fa3077ef5a340a4af5b12fda8d958c337 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Tue, 18 Apr 2023 19:49:45 +0800
+Subject: [PATCH 31/39] wifi: mt76: mt7996: fix memory leak.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mt7996/mcu.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index a2c1e43..f98a48d 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2286,11 +2286,14 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ 		return PTR_ERR(rskb);
+ 
+ 	skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
+-	if (!skb)
++	if (!skb) {
++		dev_kfree_skb(rskb);
+ 		return -EINVAL;
++	}
+ 
+ 	if (skb->len > MT7996_MAX_BEACON_SIZE) {
+ 		dev_err(dev->mt76.dev, "Bcn size limit exceed\n");
++		dev_kfree_skb(rskb);
+ 		dev_kfree_skb(skb);
+ 		return -EINVAL;
+ 	}
+@@ -2351,11 +2354,14 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 		skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
+ 	}
+ 
+-	if (!skb)
++	if (!skb) {
++		dev_kfree_skb(rskb);
+ 		return -EINVAL;
++	}
+ 
+ 	if (skb->len > MT7996_MAX_BEACON_SIZE) {
+ 		dev_err(dev->mt76.dev, "inband discovery size limit exceed\n");
++		dev_kfree_skb(rskb);
+ 		dev_kfree_skb(skb);
+ 		return -EINVAL;
+ 	}
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0032-wifi-mt76-mt7996-add-eht-mode-tx-stats.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0032-wifi-mt76-mt7996-add-eht-mode-tx-stats.patch
new file mode 100644
index 0000000..ba0e378
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0032-wifi-mt76-mt7996-add-eht-mode-tx-stats.patch
@@ -0,0 +1,39 @@
+From fd7730026ccef8b0d5a52ac07ddb21af48def17d Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Thu, 20 Apr 2023 16:34:47 +0800
+Subject: [PATCH 32/39] wifi: mt76: mt7996: add eht mode tx stats
+
+Add eht mode bf fbk stats and bw320 through debugfs tx_stats command
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/debugfs.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 0422018..ca4d615 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -481,7 +481,7 @@ static void
+ mt7996_txbf_stat_read_phy(struct mt7996_phy *phy, struct seq_file *s)
+ {
+ 	static const char * const bw[] = {
+-		"BW20", "BW40", "BW80", "BW160"
++		"BW20", "BW40", "BW80", "BW160", "BW320"
+ 	};
+ 	struct mib_stats *mib = &phy->mib;
+ 
+@@ -495,8 +495,9 @@ mt7996_txbf_stat_read_phy(struct mt7996_phy *phy, struct seq_file *s)
+ 	/* Tx Beamformer Rx feedback monitor */
+ 	seq_puts(s, "Tx Beamformer Rx feedback statistics: ");
+ 
+-	seq_printf(s, "All: %d, HE: %d, VHT: %d, HT: %d, ",
++	seq_printf(s, "All: %d, EHT: %d, HE: %d, VHT: %d, HT: %d, ",
+ 		   mib->tx_bf_rx_fb_all_cnt,
++		   mib->tx_bf_rx_fb_eht_cnt,
+ 		   mib->tx_bf_rx_fb_he_cnt,
+ 		   mib->tx_bf_rx_fb_vht_cnt,
+ 		   mib->tx_bf_rx_fb_ht_cnt);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0033-wifi-mt76-mt7996-disable-wfdma-tx-rx-during-SER.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0033-wifi-mt76-mt7996-disable-wfdma-tx-rx-during-SER.patch
new file mode 100644
index 0000000..a88d36a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0033-wifi-mt76-mt7996-disable-wfdma-tx-rx-during-SER.patch
@@ -0,0 +1,219 @@
+From d246d8300bcbf7dea19919d61df9d7553e21bd1b Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 26 Apr 2023 15:37:23 +0800
+Subject: [PATCH 33/39] wifi: mt76: mt7996: disable wfdma tx/rx during SER
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ dma.c           |  6 ++++
+ mt7996/dma.c    | 79 ++++++++++++++++++++++++++++++-------------------
+ mt7996/mac.c    | 13 ++++++--
+ mt7996/mt7996.h |  1 +
+ 4 files changed, 65 insertions(+), 34 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 465190e..f2b1b2a 100644
+--- a/dma.c
++++ b/dma.c
+@@ -466,6 +466,9 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
+ 	struct mt76_queue_buf buf = {};
+ 	dma_addr_t addr;
+ 
++	if (test_bit(MT76_MCU_RESET, &dev->phy.state))
++		goto error;
++
+ 	if (q->queued + 1 >= q->ndesc - 1)
+ 		goto error;
+ 
+@@ -507,6 +510,9 @@ mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
+ 	dma_addr_t addr;
+ 	u8 *txwi;
+ 
++	if (test_bit(MT76_MCU_RESET, &dev->phy.state))
++		goto free_skb;
++
+ 	t = mt76_get_txwi(dev);
+ 	if (!t)
+ 		goto free_skb;
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index fbedaac..6a21e3e 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -128,11 +128,55 @@ static void mt7996_dma_disable(struct mt7996_dev *dev, bool reset)
+ 	}
+ }
+ 
+-static int mt7996_dma_enable(struct mt7996_dev *dev)
++void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ {
+ 	u32 hif1_ofs = 0;
+ 	u32 irq_mask;
+ 
++	if (dev->hif2)
++		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++
++	/* enable wpdma tx/rx */
++	if (!reset) {
++		mt76_set(dev, MT_WFDMA0_GLO_CFG,
++			 MT_WFDMA0_GLO_CFG_TX_DMA_EN |
++			 MT_WFDMA0_GLO_CFG_RX_DMA_EN |
++			 MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
++			 MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
++
++		if (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 |
++				 MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
++				 MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
++	}
++
++	/* enable interrupts for TX/RX rings */
++	irq_mask = MT_INT_MCU_CMD;
++	if (reset)
++		goto done;
++
++	irq_mask |= (MT_INT_RX_DONE_MCU | MT_INT_TX_DONE_MCU);
++
++	if (!dev->mphy.band_idx)
++		irq_mask |= MT_INT_BAND0_RX_DONE;
++
++	if (dev->dbdc_support)
++		irq_mask |= MT_INT_BAND1_RX_DONE;
++
++	if (dev->tbtc_support)
++		irq_mask |= MT_INT_BAND2_RX_DONE;
++
++done:
++	mt7996_irq_enable(dev, irq_mask);
++	mt7996_irq_disable(dev, 0);
++}
++
++static int mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
++{
++	u32 hif1_ofs = 0;
++
+ 	if (dev->hif2)
+ 		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+ 
+@@ -170,13 +214,6 @@ static int mt7996_dma_enable(struct mt7996_dev *dev)
+ 	mt76_poll(dev, MT_WFDMA_EXT_CSR_HIF_MISC,
+ 		  MT_WFDMA_EXT_CSR_HIF_MISC_BUSY, 0, 1000);
+ 
+-	/* set WFDMA Tx/Rx */
+-	mt76_set(dev, MT_WFDMA0_GLO_CFG,
+-		 MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+-		 MT_WFDMA0_GLO_CFG_RX_DMA_EN |
+-		 MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+-		 MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+-
+ 	/* GLO_CFG_EXT0 */
+ 	mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0,
+ 		 WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD |
+@@ -187,12 +224,6 @@ static int mt7996_dma_enable(struct mt7996_dev *dev)
+ 		 WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE);
+ 
+ 	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 |
+-			 MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+-			 MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+-
+ 		/* GLO_CFG_EXT0 */
+ 		mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
+ 			 WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD |
+@@ -216,21 +247,7 @@ static int mt7996_dma_enable(struct mt7996_dev *dev)
+ 		/* TODO: redirect rx ring6 interrupt to pcie0 for wed function */
+ 	}
+ 
+-	/* enable interrupts for TX/RX rings */
+-	irq_mask = MT_INT_RX_DONE_MCU |
+-		   MT_INT_TX_DONE_MCU |
+-		   MT_INT_MCU_CMD;
+-
+-	if (!dev->mphy.band_idx)
+-		irq_mask |= MT_INT_BAND0_RX_DONE;
+-
+-	if (dev->dbdc_support)
+-		irq_mask |= MT_INT_BAND1_RX_DONE;
+-
+-	if (dev->tbtc_support)
+-		irq_mask |= MT_INT_BAND2_RX_DONE;
+-
+-	mt7996_irq_enable(dev, irq_mask);
++	__mt7996_dma_enable(dev, reset);
+ 
+ 	return 0;
+ }
+@@ -347,7 +364,7 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 			  mt7996_poll_tx);
+ 	napi_enable(&dev->mt76.tx_napi);
+ 
+-	mt7996_dma_enable(dev);
++	mt7996_dma_enable(dev, false);
+ 
+ 	return 0;
+ }
+@@ -413,7 +430,7 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 	mt76_for_each_q_rx(&dev->mt76, i)
+ 		mt76_queue_rx_reset(dev, i);
+ 
+-	mt7996_dma_enable(dev);
++	mt7996_dma_enable(dev, !force);
+ }
+ 
+ void mt7996_dma_cleanup(struct mt7996_dev *dev)
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index bddb84f..986031f 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2028,6 +2028,12 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 		mt7996_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE);
+ 	}
+ 
++	mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE);
++	mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
++
++	/* enable dma tx/rx and interrupt */
++	__mt7996_dma_enable(dev, false);
++
+ 	clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+ 	clear_bit(MT76_RESET, &dev->mphy.state);
+ 	if (phy2)
+@@ -2044,9 +2050,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 
+ 	tasklet_schedule(&dev->mt76.irq_tasklet);
+ 
+-	mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE);
+-	mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
+-
+ 	mt76_worker_enable(&dev->mt76.tx_worker);
+ 
+ 	local_bh_disable();
+@@ -2149,6 +2152,10 @@ skip_coredump:
+ 
+ void mt7996_reset(struct mt7996_dev *dev)
+ {
++	dev_info(dev->mt76.dev, "%s SER recovery state: 0x%08x\n",
++			 wiphy_name(dev->mt76.hw->wiphy),
++			 READ_ONCE(dev->recovery.state));
++
+ 	if (!dev->recovery.hw_init_done)
+ 		return;
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 8e5b3c3..561c1cd 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -448,6 +448,7 @@ int mt7996_dma_init(struct mt7996_dev *dev);
+ void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
+ void mt7996_dma_prefetch(struct mt7996_dev *dev);
+ void mt7996_dma_cleanup(struct mt7996_dev *dev);
++void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset);
+ void mt7996_init_txpower(struct mt7996_dev *dev,
+ 			 struct ieee80211_supported_band *sband);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0034-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0034-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch
new file mode 100644
index 0000000..3d4d1c0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0034-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch
@@ -0,0 +1,598 @@
+From cf66e6fc90e7a12209bd0a3d9b687b37f7324718 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 19 May 2023 14:16:50 +0800
+Subject: [PATCH] wifi: mt76: mt7996: add firmware WA's coredump.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Change-Id: I51f115b4ae15bc0f871f93652570d72511dbf880
+---
+ mt7996/coredump.c | 180 ++++++++++++++++++++++++++++++----------------
+ mt7996/coredump.h |  35 ++++++---
+ mt7996/mac.c      |  31 +++++---
+ mt7996/mcu.c      |   5 ++
+ mt7996/mt7996.h   |   7 +-
+ mt7996/regs.h     |   7 +-
+ 6 files changed, 182 insertions(+), 83 deletions(-)
+
+diff --git a/mt7996/coredump.c b/mt7996/coredump.c
+index ccab0d7b..60b88085 100644
+--- a/mt7996/coredump.c
++++ b/mt7996/coredump.c
+@@ -7,11 +7,11 @@
+ #include <linux/utsname.h>
+ #include "coredump.h"
+ 
+-static bool coredump_memdump;
++static bool coredump_memdump = true;
+ module_param(coredump_memdump, bool, 0644);
+ MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
+ 
+-static const struct mt7996_mem_region mt7996_mem_regions[] = {
++static const struct mt7996_mem_region mt7996_wm_mem_regions[] = {
+ 	{
+ 		.start = 0x00800000,
+ 		.len = 0x0004ffff,
+@@ -44,27 +44,55 @@ static const struct mt7996_mem_region mt7996_mem_regions[] = {
+ 	},
+ };
+ 
++static const struct mt7996_mem_region mt7996_wa_mem_regions[] = {
++	{
++		.start = 0xE0000000,
++		.len = 0x0000ffff,
++		.name = "CRAM",
++	},
++	{
++		.start = 0xE0010000,
++		.len = 0x000117ff,
++		.name = "CRAM2",
++	},
++	{
++		.start = 0x10000000,
++		.len = 0x0001bfff,
++		.name = "ILM",
++	},
++	{
++		.start = 0x10200000,
++		.len = 0x00063fff,
++		.name = "DLM",
++	},
++};
++
+ const struct mt7996_mem_region*
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ {
+ 	switch (mt76_chip(&dev->mt76)) {
+ 	case 0x7990:
+ 	case 0x7991:
+-		*num = ARRAY_SIZE(mt7996_mem_regions);
+-		return &mt7996_mem_regions[0];
++		if (type == MT7996_RAM_TYPE_WA) {
++			*num = ARRAY_SIZE(mt7996_wa_mem_regions);
++			return &mt7996_wa_mem_regions[0];
++		}
++
++		*num = ARRAY_SIZE(mt7996_wm_mem_regions);
++		return &mt7996_wm_mem_regions[0];
+ 	default:
+ 		return NULL;
+ 	}
+ }
+ 
+-static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
++static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev, u8 type)
+ {
+ 	const struct mt7996_mem_region *mem_region;
+ 	size_t size = 0;
+ 	u32 num;
+ 	int i;
+ 
+-	mem_region = mt7996_coredump_get_mem_layout(dev, &num);
++	mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
+ 	if (!mem_region)
+ 		return 0;
+ 
+@@ -81,14 +109,13 @@ static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
+ 	return size;
+ }
+ 
+-struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
++struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
+-	struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
++	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+ 
+ 	lockdep_assert_held(&dev->dump_mutex);
+ 
+-	if (coredump_memdump &&
+-	    !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500))
++	if (!coredump_memdump)
+ 		return NULL;
+ 
+ 	guid_gen(&crash_data->guid);
+@@ -98,12 +125,15 @@ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
+ }
+ 
+ static void
+-mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
++mt7996_coredump_fw_state(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
+ 			 bool *exception)
+ {
+-	u32 count;
++	u32 count, reg = MT_FW_WM_DUMP_STATE;
++
++	if (type == MT7996_RAM_TYPE_WA)
++		reg = MT_FW_WA_DUMP_STATE;
+ 
+-	count = mt76_rr(dev, MT_FW_ASSERT_CNT);
++	count = mt76_rr(dev, reg);
+ 
+ 	/* normal mode: driver can manually trigger assert for detail info */
+ 	if (!count)
+@@ -115,53 +145,59 @@ mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
+ }
+ 
+ static void
+-mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump,
++mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump *dump,
+ 			 bool exception)
+ {
+-	u32 oldest, i, idx;
++	u32 reg, i, offset = 0, val = MT7996_RAM_TYPE_WM;
+ 
+-	strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current));
++	if (type == MT7996_RAM_TYPE_WA) {
++		offset = MT_MCU_WA_EXCP_BASE - MT_MCU_WM_EXCP_BASE;
++		val = MT7996_RAM_TYPE_WA;
++	}
+ 
+-	/* 0: WM PC log output */
+-	mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0);
++	/* 0: WM PC log output, 1: WA PC log output  */
++	mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, val);
+ 	/* choose 33th PC log buffer to read current PC index */
+ 	mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f);
+ 
+ 	/* read current PC */
+-	dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
++	for (i = 0; i < 10; i++)
++		dump->pc_cur[i] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
+ 
+ 	/* stop call stack record */
+ 	if (!exception) {
+-		mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
+-		mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
++		mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
++		mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
+ 	}
+ 
+-	oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL,
+-				     GENMASK(20, 16)) + 2;
+-	for (i = 0; i < 16; i++) {
+-		idx = ((oldest + 2 * i + 1) % 32);
+-		dump->pc_stack[i + 1] =
+-			mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4);
++	/* read PC log */
++	dump->pc_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_PC_CTRL + offset);
++	dump->pc_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS,
++				     dump->pc_dbg_ctrl);
++	for (i = 0; i < 32; i++) {
++		reg = MT_MCU_WM_EXCP_PC_LOG + i * 4 + offset;
++		dump->pc_stack[i] = mt76_rr(dev, reg);
+ 	}
+ 
+-	oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL,
+-				     GENMASK(20, 16)) + 2;
+-	for (i = 0; i < 16; i++) {
+-		idx = ((oldest + 2 * i + 1) % 32);
+-		dump->lr_stack[i] =
+-			mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4);
++	/* read LR log */
++	dump->lr_dbg_ctrl = mt76_rr(dev, MT_MCU_WM_EXCP_LR_CTRL + offset);
++	dump->lr_cur_idx = FIELD_GET(MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS,
++				     dump->lr_dbg_ctrl);
++	for (i = 0; i < 32; i++) {
++		reg = MT_MCU_WM_EXCP_LR_LOG + i * 4 + offset;
++		dump->lr_stack[i] = mt76_rr(dev, reg);
+ 	}
+ 
+ 	/* start call stack record */
+ 	if (!exception) {
+-		mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
+-		mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
++		mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL + offset, BIT(0));
++		mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL + offset, BIT(0));
+ 	}
+ }
+ 
+-static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
++static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
+ {
+-	struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
++	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+ 	struct mt7996_coredump *dump;
+ 	struct mt7996_coredump_mem *dump_mem;
+ 	size_t len, sofar = 0, hdr_len = sizeof(*dump);
+@@ -186,20 +222,31 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
+ 
+ 	dump = (struct mt7996_coredump *)(buf);
+ 	dump->len = len;
++	dump->hdr_len = hdr_len;
+ 
+ 	/* plain text */
+ 	strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
+ 	strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
++	strscpy(dump->fw_type, ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"),
++		sizeof(dump->fw_type));
+ 	strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
+ 		sizeof(dump->fw_ver));
++	strscpy(dump->fw_patch_date, dev->patch_build_date,
++		sizeof(dump->fw_patch_date));
++	strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WM],
++		dev->ram_build_date[MT7996_RAM_TYPE_WM],
++		MT7996_BUILD_TIME_LEN);
++	strscpy(dump->fw_ram_date[MT7996_RAM_TYPE_WA],
++		dev->ram_build_date[MT7996_RAM_TYPE_WA],
++		MT7996_BUILD_TIME_LEN);
+ 
+ 	guid_copy(&dump->guid, &crash_data->guid);
+ 	dump->tv_sec = crash_data->timestamp.tv_sec;
+ 	dump->tv_nsec = crash_data->timestamp.tv_nsec;
+ 	dump->device_id = mt76_chip(&dev->mt76);
+ 
+-	mt7996_coredump_fw_state(dev, dump, &exception);
+-	mt7996_coredump_fw_stack(dev, dump, exception);
++	mt7996_coredump_fw_state(dev, type, dump, &exception);
++	mt7996_coredump_fw_stack(dev, type, dump, exception);
+ 
+ 	/* gather memory content */
+ 	dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
+@@ -213,17 +260,19 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
+ 	return dump;
+ }
+ 
+-int mt7996_coredump_submit(struct mt7996_dev *dev)
++int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ 	struct mt7996_coredump *dump;
+ 
+-	dump = mt7996_coredump_build(dev);
++	dump = mt7996_coredump_build(dev, type);
+ 	if (!dump) {
+ 		dev_warn(dev->mt76.dev, "no crash dump data found\n");
+ 		return -ENODATA;
+ 	}
+ 
+ 	dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
++	dev_info(dev->mt76.dev, "%s coredump completed\n",
++		 wiphy_name(dev->mt76.hw->wiphy));
+ 
+ 	return 0;
+ }
+@@ -231,23 +280,26 @@ int mt7996_coredump_submit(struct mt7996_dev *dev)
+ int mt7996_coredump_register(struct mt7996_dev *dev)
+ {
+ 	struct mt7996_crash_data *crash_data;
++	int i;
+ 
+-	crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
+-	if (!crash_data)
+-		return -ENOMEM;
++	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
++		crash_data = vzalloc(sizeof(*dev->coredump.crash_data[i]));
++		if (!crash_data)
++			return -ENOMEM;
+ 
+-	dev->coredump.crash_data = crash_data;
++		dev->coredump.crash_data[i] = crash_data;
+ 
+-	if (coredump_memdump) {
+-		crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev);
+-		if (!crash_data->memdump_buf_len)
+-			/* no memory content */
+-			return 0;
++		if (coredump_memdump) {
++			crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev, i);
++			if (!crash_data->memdump_buf_len)
++				/* no memory content */
++				return 0;
+ 
+-		crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
+-		if (!crash_data->memdump_buf) {
+-			vfree(crash_data);
+-			return -ENOMEM;
++			crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
++			if (!crash_data->memdump_buf) {
++				vfree(crash_data);
++				return -ENOMEM;
++			}
+ 		}
+ 	}
+ 
+@@ -256,13 +308,17 @@ int mt7996_coredump_register(struct mt7996_dev *dev)
+ 
+ void mt7996_coredump_unregister(struct mt7996_dev *dev)
+ {
+-	if (dev->coredump.crash_data->memdump_buf) {
+-		vfree(dev->coredump.crash_data->memdump_buf);
+-		dev->coredump.crash_data->memdump_buf = NULL;
+-		dev->coredump.crash_data->memdump_buf_len = 0;
+-	}
++	int i;
+ 
+-	vfree(dev->coredump.crash_data);
+-	dev->coredump.crash_data = NULL;
++	for (i = 0; i < MT7996_COREDUMP_MAX; i++) {
++		if (dev->coredump.crash_data[i]->memdump_buf) {
++			vfree(dev->coredump.crash_data[i]->memdump_buf);
++			dev->coredump.crash_data[i]->memdump_buf = NULL;
++			dev->coredump.crash_data[i]->memdump_buf_len = 0;
++		}
++
++		vfree(dev->coredump.crash_data[i]);
++		dev->coredump.crash_data[i] = NULL;
++	}
+ }
+ 
+diff --git a/mt7996/coredump.h b/mt7996/coredump.h
+index af2ba219..01ed3731 100644
+--- a/mt7996/coredump.h
++++ b/mt7996/coredump.h
+@@ -6,10 +6,13 @@
+ 
+ #include "mt7996.h"
+ 
++#define MT7996_COREDUMP_MAX	(MT7996_RAM_TYPE_WA + 1)
++
+ struct mt7996_coredump {
+ 	char magic[16];
+ 
+ 	u32 len;
++	u32 hdr_len;
+ 
+ 	guid_t guid;
+ 
+@@ -21,17 +24,28 @@ struct mt7996_coredump {
+ 	char kernel[64];
+ 	/* firmware version */
+ 	char fw_ver[ETHTOOL_FWVERS_LEN];
++	char fw_patch_date[MT7996_BUILD_TIME_LEN];
++	char fw_ram_date[MT7996_COREDUMP_MAX][MT7996_BUILD_TIME_LEN];
+ 
+ 	u32 device_id;
+ 
++	/* fw type */
++	char fw_type[8];
++
+ 	/* exception state */
+ 	char fw_state[12];
+ 
+ 	/* program counters */
+-	char pc_current[16];
+-	u32 pc_stack[17];
+-	/* link registers */
+-	u32 lr_stack[16];
++	u32 pc_dbg_ctrl;
++	u32 pc_cur_idx;
++	u32 pc_cur[10];
++	/* PC registers */
++	u32 pc_stack[32];
++
++	u32 lr_dbg_ctrl;
++	u32 lr_cur_idx;
++	/* LR registers */
++	u32 lr_stack[32];
+ 
+ 	/* memory content */
+ 	u8 data[];
+@@ -43,6 +57,7 @@ struct mt7996_coredump_mem {
+ } __packed;
+ 
+ struct mt7996_mem_hdr {
++	char name[64];
+ 	u32 start;
+ 	u32 len;
+ 	u8 data[];
+@@ -58,27 +73,27 @@ struct mt7996_mem_region {
+ #ifdef CONFIG_DEV_COREDUMP
+ 
+ const struct mt7996_mem_region *
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num);
+-struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev);
+-int mt7996_coredump_submit(struct mt7996_dev *dev);
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
++struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
++int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
+ int mt7996_coredump_register(struct mt7996_dev *dev);
+ void mt7996_coredump_unregister(struct mt7996_dev *dev);
+ 
+ #else /* CONFIG_DEV_COREDUMP */
+ 
+ static inline const struct mt7996_mem_region *
+-mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
++mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num)
+ {
+ 	return NULL;
+ }
+ 
+-static inline int mt7996_coredump_submit(struct mt7996_dev *dev)
++static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ 	return 0;
+ }
+ 
+ static inline struct
+-mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
++mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
+ 	return NULL;
+ }
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 986031f5..724af82a 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2082,28 +2082,25 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ }
+ 
+ /* firmware coredump */
+-void mt7996_mac_dump_work(struct work_struct *work)
++void mt7996_mac_fw_coredump(struct mt7996_dev *dev, u8 type)
+ {
+ 	const struct mt7996_mem_region *mem_region;
+ 	struct mt7996_crash_data *crash_data;
+-	struct mt7996_dev *dev;
+ 	struct mt7996_mem_hdr *hdr;
+ 	size_t buf_len;
+ 	int i;
+ 	u32 num;
+ 	u8 *buf;
+ 
+-	dev = container_of(work, struct mt7996_dev, dump_work);
+-
+ 	mutex_lock(&dev->dump_mutex);
+ 
+-	crash_data = mt7996_coredump_new(dev);
++	crash_data = mt7996_coredump_new(dev, type);
+ 	if (!crash_data) {
+ 		mutex_unlock(&dev->dump_mutex);
+-		goto skip_coredump;
++		return;
+ 	}
+ 
+-	mem_region = mt7996_coredump_get_mem_layout(dev, &num);
++	mem_region = mt7996_coredump_get_mem_layout(dev, type, &num);
+ 	if (!mem_region || !crash_data->memdump_buf_len) {
+ 		mutex_unlock(&dev->dump_mutex);
+ 		goto skip_memdump;
+@@ -2113,6 +2110,9 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ 	buf_len = crash_data->memdump_buf_len;
+ 
+ 	/* dumping memory content... */
++	dev_info(dev->mt76.dev, "%s start coredump for %s\n",
++		 wiphy_name(dev->mt76.hw->wiphy),
++		 ((type == MT7996_RAM_TYPE_WA) ? "WA" : "WM"));
+ 	memset(buf, 0, buf_len);
+ 	for (i = 0; i < num; i++) {
+ 		if (mem_region->len > buf_len) {
+@@ -2129,6 +2129,7 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ 		mt7996_memcpy_fromio(dev, buf, mem_region->start,
+ 				     mem_region->len);
+ 
++		strscpy(hdr->name, mem_region->name, sizeof(mem_region->name));
+ 		hdr->start = mem_region->start;
+ 		hdr->len = mem_region->len;
+ 
+@@ -2145,8 +2146,20 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ 	mutex_unlock(&dev->dump_mutex);
+ 
+ skip_memdump:
+-	mt7996_coredump_submit(dev);
+-skip_coredump:
++	mt7996_coredump_submit(dev, type);
++}
++
++void mt7996_mac_dump_work(struct work_struct *work)
++{
++	struct mt7996_dev *dev;
++
++	dev = container_of(work, struct mt7996_dev, dump_work);
++	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
++		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
++
++	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
++		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
++
+ 	queue_work(dev->mt76.wq, &dev->reset_work);
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 38292da3..14f7a43f 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2458,6 +2458,8 @@ static int mt7996_load_patch(struct mt7996_dev *dev)
+ 
+ 	dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
+ 		 be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
++	memcpy(dev->patch_build_date, hdr->build_date,
++	       sizeof(dev->patch_build_date));
+ 
+ 	for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
+ 		struct mt7996_patch_sec *sec;
+@@ -2584,6 +2586,9 @@ static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type,
+ 	}
+ 
+ 	hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
++	memcpy(dev->ram_build_date[ram_type],
++	       hdr->build_date,
++	       sizeof(dev->ram_build_date[ram_type]));
+ 	dev_info(dev->mt76.dev, "%s Firmware Version: %.10s, Build Time: %.15s\n",
+ 		 fw_type, hdr->fw_ver, hdr->build_date);
+ 
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 561c1cdc..f59dce77 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -58,6 +58,8 @@
+ #define MT7996_CRIT_TEMP		110
+ #define MT7996_MAX_TEMP			120
+ 
++#define MT7996_BUILD_TIME_LEN		24
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -68,6 +70,7 @@ enum mt7996_ram_type {
+ 	MT7996_RAM_TYPE_WM_TM = MT7996_RAM_TYPE_WM,
+ 	MT7996_RAM_TYPE_WA,
+ 	MT7996_RAM_TYPE_DSP,
++	__MT7996_RAM_TYPE_MAX,
+ };
+ 
+ enum mt7996_txq_id {
+@@ -306,9 +309,11 @@ struct mt7996_dev {
+ 	struct mutex dump_mutex;
+ #ifdef CONFIG_DEV_COREDUMP
+ 	struct {
+-		struct mt7996_crash_data *crash_data;
++		struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
+ 	} coredump;
+ #endif
++	char patch_build_date[MT7996_BUILD_TIME_LEN];
++	char ram_build_date[__MT7996_RAM_TYPE_MAX][MT7996_BUILD_TIME_LEN];
+ 
+ 	struct list_head sta_rc_list;
+ 	struct list_head sta_poll_list;
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 86da1bf8..c054586d 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -482,7 +482,8 @@ enum base_rev {
+ 
+ /* FW MODE SYNC */
+ #define MT_FW_ASSERT_CNT			0x02208274
+-#define MT_FW_DUMP_STATE			0x02209e90
++#define MT_FW_WM_DUMP_STATE			0x02209e90
++#define MT_FW_WA_DUMP_STATE			0x7C05B080
+ 
+ #define MT_SWDEF_BASE				0x00401400
+ 
+@@ -580,11 +581,15 @@ enum base_rev {
+ #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR		BIT(29)
+ 
+ /* CONN MCU EXCP CON */
++#define MT_MCU_WA_EXCP_BASE			0x890d0000
+ #define MT_MCU_WM_EXCP_BASE			0x89050000
++
+ #define MT_MCU_WM_EXCP(ofs)			(MT_MCU_WM_EXCP_BASE + (ofs))
+ #define MT_MCU_WM_EXCP_PC_CTRL			MT_MCU_WM_EXCP(0x100)
++#define MT_MCU_WM_EXCP_PC_CTRL_IDX_STATUS	GENMASK(20, 16)
+ #define MT_MCU_WM_EXCP_PC_LOG			MT_MCU_WM_EXCP(0x104)
+ #define MT_MCU_WM_EXCP_LR_CTRL			MT_MCU_WM_EXCP(0x200)
++#define MT_MCU_WM_EXCP_LR_CTRL_IDX_STATUS	GENMASK(20, 16)
+ #define MT_MCU_WM_EXCP_LR_LOG			MT_MCU_WM_EXCP(0x204)
+ 
+ #endif
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0035-wifi-mt76-mt7996-make-band-capability-init-flexible.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0035-wifi-mt76-mt7996-make-band-capability-init-flexible.patch
new file mode 100644
index 0000000..6864e26
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0035-wifi-mt76-mt7996-make-band-capability-init-flexible.patch
@@ -0,0 +1,184 @@
+From 18ed969cf7bfdaa059ed946f5bacd62a2ebf9ffe Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Tue, 23 May 2023 15:49:03 +0800
+Subject: [PATCH 35/39] wifi: mt76: mt7996: make band capability init flexible
+
+There're some variations of mt7996 chipset which only support two-band,
+so parse the adie combination to correctly set band capability.
+
+Change-Id: Ifcb49504f02f5cc6a23c626e30b4f0e1360fe157
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt7996/dma.c    |  8 ++++----
+ mt7996/init.c   | 29 ++++++++++++++++++-----------
+ mt7996/mcu.c    | 13 +++++--------
+ mt7996/mt7996.h | 11 +++++++++++
+ mt7996/regs.h   |  3 +++
+ 5 files changed, 41 insertions(+), 23 deletions(-)
+
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 6a21e3e..f01cea5 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -159,13 +159,13 @@ void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 
+ 	irq_mask |= (MT_INT_RX_DONE_MCU | MT_INT_TX_DONE_MCU);
+ 
+-	if (!dev->mphy.band_idx)
++	if (mt7996_band_valid(dev, MT_BAND0))
+ 		irq_mask |= MT_INT_BAND0_RX_DONE;
+ 
+-	if (dev->dbdc_support)
++	if (mt7996_band_valid(dev, MT_BAND1))
+ 		irq_mask |= MT_INT_BAND1_RX_DONE;
+ 
+-	if (dev->tbtc_support)
++	if (mt7996_band_valid(dev, MT_BAND2))
+ 		irq_mask |= MT_INT_BAND2_RX_DONE;
+ 
+ done:
+@@ -334,7 +334,7 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
+-	if (dev->tbtc_support || dev->mphy.band_idx == MT_BAND2) {
++	if (mt7996_band_valid(dev, MT_BAND2)) {
+ 		/* rx data queue for band2 */
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
+ 				       MT_RXQ_ID(MT_RXQ_BAND2),
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 0562439..0825e0b 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -529,11 +529,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	u32 mac_ofs, hif1_ofs = 0;
+ 	int ret;
+ 
+-	if (band != MT_BAND1 && band != MT_BAND2)
+-		return 0;
+-
+-	if ((band == MT_BAND1 && !dev->dbdc_support) ||
+-	    (band == MT_BAND2 && !dev->tbtc_support))
++	if (!mt7996_band_valid(dev, band) || band == MT_BAND0)
+ 		return 0;
+ 
+ 	if (phy)
+@@ -648,8 +644,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ 
+ 	INIT_WORK(&dev->init_work, mt7996_init_work);
+ 
+-	dev->dbdc_support = true;
+-	dev->tbtc_support = true;
++	dev->dbdc_support = mt7996_band_valid(dev, MT_BAND1) ||
++			    mt7996_band_valid(dev, MT_BAND2);
++	dev->tbtc_support = mt7996_band_valid(dev, MT_BAND1) &&
++			    mt7996_band_valid(dev, MT_BAND2);
+ 
+ 	ret = mt7996_dma_init(dev);
+ 	if (ret)
+@@ -1089,8 +1087,6 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
+-	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
+-
+ 	ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1);
+ 	if (ret)
+ 		return ret;
+@@ -1099,13 +1095,24 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
++
+ 	dev->recovery.hw_init_done = true;
+ 
+ 	ret = mt7996_init_debugfs(&dev->phy);
+ 	if (ret)
+-		return ret;
++		goto error;
+ 
+-	return mt7996_coredump_register(dev);
++	ret = mt7996_coredump_register(dev);
++	if (ret)
++		goto error;
++
++	return 0;
++
++error:
++	cancel_work_sync(&dev->init_work);
++
++	return ret;
+ }
+ 
+ void mt7996_unregister_device(struct mt7996_dev *dev)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index c647979..9fb800a 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2767,7 +2767,7 @@ mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev)
+ {
+ 	struct uni_header hdr = {};
+ 	struct sk_buff *skb;
+-	int len, num;
++	int len, num, i;
+ 
+ 	num = 2 + 2 * (dev->dbdc_support + dev->tbtc_support);
+ 	len = sizeof(hdr) + num * sizeof(struct vow_rx_airtime);
+@@ -2777,13 +2777,10 @@ mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev)
+ 
+ 	skb_put_data(skb, &hdr, sizeof(hdr));
+ 
+-	mt7996_add_rx_airtime_tlv(skb, dev->mt76.phy.band_idx);
+-
+-	if (dev->dbdc_support)
+-		mt7996_add_rx_airtime_tlv(skb, MT_BAND1);
+-
+-	if (dev->tbtc_support)
+-		mt7996_add_rx_airtime_tlv(skb, MT_BAND2);
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		if (mt7996_band_valid(dev, i))
++			mt7996_add_rx_airtime_tlv(skb, i);
++	}
+ 
+ 	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ 				     MCU_WM_UNI_CMD(VOW), true);
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index f59dce7..488f59c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -431,6 +431,17 @@ mt7996_phy3(struct mt7996_dev *dev)
+ 	return __mt7996_phy(dev, MT_BAND2);
+ }
+ 
++static inline bool
++mt7996_band_valid(struct mt7996_dev *dev, u8 band)
++{
++	/* tri-band support */
++	if (band <= MT_BAND2 &&
++	    mt76_get_field(dev, MT_PAD_GPIO, MT_PAD_GPIO_ADIE_COMB) <= 1)
++		return true;
++
++	return band == MT_BAND0 || band == MT_BAND2;
++}
++
+ extern const struct ieee80211_ops mt7996_ops;
+ extern struct pci_driver mt7996_pci_driver;
+ extern struct pci_driver mt7996_hif_driver;
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index c054586..3a5914c 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -546,6 +546,9 @@ enum base_rev {
+ #define MT_TOP_MISC				MT_TOP(0xf0)
+ #define MT_TOP_MISC_FW_STATE			GENMASK(2, 0)
+ 
++#define MT_PAD_GPIO				0x700056f0
++#define MT_PAD_GPIO_ADIE_COMB			GENMASK(16, 15)
++
+ #define MT_HW_REV				0x70010204
+ #define MT_WF_SUBSYS_RST			0x70028600
+ 
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0036-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-drive.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0036-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-drive.patch
new file mode 100644
index 0000000..c0ce85d
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0036-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-drive.patch
@@ -0,0 +1,272 @@
+From 18f990de206c3301d3ae72cfeef40dfb3b361fb0 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 8 May 2023 09:03:50 +0800
+Subject: [PATCH 36/39] wifi: mt76: mt7996: enable SCS feature for mt7996
+ driver
+
+Enable Smart Carrier Sense algorithn by default to improve performance
+in a noisy environment.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt76_connac_mcu.h |   1 +
+ mt7996/init.c     |   1 +
+ mt7996/main.c     |   7 +++
+ mt7996/mcu.c      | 123 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      |   6 +++
+ mt7996/mt7996.h   |  16 ++++++
+ 6 files changed, 154 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 97f874b..bfec420 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1213,6 +1213,7 @@ enum {
+ 	MCU_UNI_CMD_GET_MIB_INFO = 0x22,
+ 	MCU_UNI_CMD_SNIFFER = 0x24,
+ 	MCU_UNI_CMD_SR = 0x25,
++	MCU_UNI_CMD_SCS = 0x26,
+ 	MCU_UNI_CMD_ROC = 0x27,
+ 	MCU_UNI_CMD_TXPOWER = 0x2b,
+ 	MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 0825e0b..1072874 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -1058,6 +1058,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	dev->mt76.phy.priv = &dev->phy;
+ 	INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
+ 	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
++	INIT_DELAYED_WORK(&dev->scs_work, mt7996_mcu_scs_sta_poll);
+ 	INIT_LIST_HEAD(&dev->sta_rc_list);
+ 	INIT_LIST_HEAD(&dev->sta_poll_list);
+ 	INIT_LIST_HEAD(&dev->twt_list);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 520f250..20b89a7 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -73,11 +73,17 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 	if (ret)
+ 		goto out;
+ 
++	ret = mt7996_mcu_set_scs(phy, SCS_ENABLE);
++	if (ret)
++		goto out;
++
+ 	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+ 	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+ 				     MT7996_WATCHDOG_TIME);
+ 
++	ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++
+ 	if (!running)
+ 		mt7996_mac_reset_counters(phy);
+ 
+@@ -105,6 +111,7 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 
+ 	cancel_delayed_work_sync(&phy->mt76->mac_work);
++	cancel_delayed_work_sync(&dev->scs_work);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 9fb800a..a5c473a 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4190,3 +4190,126 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
+ 	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
+ 				 &req, sizeof(req), false);
+ }
++
++int mt7996_mcu_set_scs_stats(struct mt7996_phy *phy)
++{
++	struct mt7996_scs_ctrl ctrl = phy->scs_ctrl;
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 _rsv2[6];
++		s8 min_rssi;
++		u8 _rsv3;
++	} __packed req = {
++		.band_idx = phy->mt76->band_idx,
++		.tag = cpu_to_le16(UNI_CMD_SCS_SEND_DATA),
++		.len = cpu_to_le16(sizeof(req) - 4),
++
++		.min_rssi = ctrl.sta_min_rssi,
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
++				 &req, sizeof(req), false);
++}
++
++void mt7996_sta_rssi_work(void *data, struct ieee80211_sta *sta)
++{
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_dev *dev = (struct mt7996_dev *)data;
++	struct mt7996_phy *poll_phy;
++	u8 band_idx = msta->wcid.phy_idx;
++
++	switch (band_idx) {
++	case MT_BAND0:
++		poll_phy = dev->mphy.priv;
++		break;
++	case MT_BAND1:
++		poll_phy = mt7996_phy2(dev);
++		break;
++	case MT_BAND2:
++		poll_phy = mt7996_phy3(dev);
++		break;
++	default:
++		poll_phy = NULL;
++		break;
++	}
++
++	if (!poll_phy->scs_ctrl.scs_enable)
++		return;
++
++	if (poll_phy->scs_ctrl.sta_min_rssi > msta->ack_signal)
++		poll_phy->scs_ctrl.sta_min_rssi = msta->ack_signal;
++}
++
++void mt7996_mcu_scs_sta_poll(struct work_struct *work)
++{
++	struct mt7996_dev *dev = container_of(work, struct mt7996_dev,
++				 scs_work.work);
++	bool scs_enable_flag = false;
++	u8 i;
++
++	ieee80211_iterate_stations_atomic(dev->mphy.hw, mt7996_sta_rssi_work, dev);
++
++	for (i = 0; i < __MT_MAX_BAND; i++) {
++		struct mt7996_phy *phy;
++
++		switch (i) {
++		case MT_BAND0:
++			phy = dev->mphy.priv;
++			break;
++		case MT_BAND1:
++			phy = mt7996_phy2(dev);
++			break;
++		case MT_BAND2:
++			phy = mt7996_phy3(dev);
++			break;
++		default:
++			phy = NULL;
++			break;
++		}
++
++		if (phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state) &&
++		    phy->scs_ctrl.scs_enable) {
++			scs_enable_flag = true;
++			if (mt7996_mcu_set_scs_stats(phy))
++				dev_err(dev->mt76.dev, "Failed to send scs mcu cmd\n");
++			phy->scs_ctrl.sta_min_rssi = 0;
++		}
++	}
++
++	if (scs_enable_flag)
++		ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++}
++
++
++int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct {
++		u8 band_idx;
++		u8 _rsv[3];
++
++		__le16 tag;
++		__le16 len;
++
++		u8 scs_enable;
++		u8 _rsv2[3];
++	} __packed req = {
++		.band_idx = phy->mt76->band_idx,
++		.tag = cpu_to_le16(UNI_CMD_SCS_ENABLE),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.scs_enable = enable,
++	};
++
++	phy->scs_ctrl.scs_enable = enable;
++
++	if (enable == SCS_ENABLE)
++		ieee80211_queue_delayed_work(mt76_hw(dev), &dev->scs_work, HZ);
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
++				 &req, sizeof(req), false);
++}
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index eed7371..eb63441 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -757,6 +757,12 @@ enum {
+ 	MT7996_SEC_MODE_MAX,
+ };
+ 
++enum {
++	UNI_CMD_SCS_SEND_DATA,
++	UNI_CMD_SCS_SET_PD_THR_RANGE = 2,
++	UNI_CMD_SCS_ENABLE,
++};
++
+ #define MT7996_PATCH_SEC		GENMASK(31, 24)
+ #define MT7996_PATCH_SCRAMBLE_KEY	GENMASK(15, 8)
+ #define MT7996_PATCH_AES_KEY		GENMASK(7, 0)
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 488f59c..f78f1fd 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -222,6 +222,17 @@ struct mt7996_hif {
+ 	int irq;
+ };
+ 
++
++struct mt7996_scs_ctrl {
++	u8 scs_enable;
++	s8 sta_min_rssi;
++};
++
++enum {
++	SCS_DISABLE = 0,
++	SCS_ENABLE,
++};
++
+ struct mt7996_phy {
+ 	struct mt76_phy *mt76;
+ 	struct mt7996_dev *dev;
+@@ -253,6 +264,8 @@ struct mt7996_phy {
+ 
+ 	bool has_aux_rx;
+ 
++	struct mt7996_scs_ctrl scs_ctrl;
++
+ #ifdef CONFIG_NL80211_TESTMODE
+ 	struct {
+ 		u32 *reg_backup;
+@@ -295,6 +308,7 @@ struct mt7996_dev {
+ 	struct work_struct rc_work;
+ 	struct work_struct dump_work;
+ 	struct work_struct reset_work;
++	struct delayed_work scs_work;
+ 	wait_queue_head_t reset_wait;
+ 	struct {
+ 		u32 state;
+@@ -535,6 +549,8 @@ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
++int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
++void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0037-wifi-mt76-mt7996-add-beacon-duplicate-tx-mode-suppor.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0037-wifi-mt76-mt7996-add-beacon-duplicate-tx-mode-suppor.patch
new file mode 100644
index 0000000..5dc8109
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0037-wifi-mt76-mt7996-add-beacon-duplicate-tx-mode-suppor.patch
@@ -0,0 +1,237 @@
+From de92f88f5c00cf1069df00bb89f50281b0b2d05e Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 12 May 2023 16:24:53 +0800
+Subject: [PATCH 37/39] wifi: mt76: mt7996: add beacon duplicate tx mode
+ support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76_connac_mcu.h |  1 +
+ mt7996/init.c     |  6 ++++--
+ mt7996/mac.c      | 11 -----------
+ mt7996/main.c     | 18 +++++++++---------
+ mt7996/mcu.c      | 30 ++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 20 ++++++++++++++++++++
+ mt7996/mt7996.h   |  8 +++++---
+ 7 files changed, 69 insertions(+), 25 deletions(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index bfec420..4bb9508 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1224,6 +1224,7 @@ enum {
+ 	MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
+ 	MCU_UNI_CMD_THERMAL = 0x35,
+ 	MCU_UNI_CMD_VOW = 0x37,
++	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 1072874..9eba689 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -351,6 +351,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ 		IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US;
+ 
+ 	phy->slottime = 9;
++	phy->beacon_rate = -1;
+ 
+ 	hw->sta_data_size = sizeof(struct mt7996_sta);
+ 	hw->vif_data_size = sizeof(struct mt7996_vif);
+@@ -459,11 +460,12 @@ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev)
+ 
+ 	for (i = 0; i < ARRAY_SIZE(mt76_rates); i++) {
+ 		u16 rate = mt76_rates[i].hw_value;
+-		u16 idx = MT7996_BASIC_RATES_TBL + i;
++		/* odd index for driver, even index for firmware */
++		u16 idx = MT7996_BASIC_RATES_TBL + 2 * i;
+ 
+ 		rate = FIELD_PREP(MT_TX_RATE_MODE, rate >> 8) |
+ 		       FIELD_PREP(MT_TX_RATE_IDX, rate & GENMASK(7, 0));
+-		mt7996_mac_set_fixed_rate_table(dev, idx, rate);
++		mt7996_mcu_set_fixed_rate_table(&dev->phy, idx, rate, false);
+ 	}
+ }
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 724af82..05269e7 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -252,17 +252,6 @@ void mt7996_mac_enable_rtscts(struct mt7996_dev *dev,
+ 		mt76_clear(dev, addr, BIT(5));
+ }
+ 
+-void mt7996_mac_set_fixed_rate_table(struct mt7996_dev *dev,
+-				     u8 tbl_idx, u16 rate_idx)
+-{
+-	u32 ctrl = MT_WTBL_ITCR_WR | MT_WTBL_ITCR_EXEC | tbl_idx;
+-
+-	mt76_wr(dev, MT_WTBL_ITDR0, rate_idx);
+-	/* use wtbl spe idx */
+-	mt76_wr(dev, MT_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL);
+-	mt76_wr(dev, MT_WTBL_ITCR, ctrl);
+-}
+-
+ static void
+ mt7996_mac_decode_he_radiotap_ru(struct mt76_rx_status *status,
+ 				 struct ieee80211_radiotap_he *he,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 20b89a7..2ed66e6 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -544,24 +544,25 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ 	struct mt76_phy *mphy = hw->priv;
+ 	u16 rate;
+-	u8 i, idx, ht;
++	u8 i, idx;
+ 
+ 	rate = mt76_connac2_mac_tx_rate_val(mphy, vif, beacon, mcast);
+-	ht = FIELD_GET(MT_TX_RATE_MODE, rate) > MT_PHY_TYPE_OFDM;
+ 
+-	if (beacon && ht) {
+-		struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	if (beacon) {
++		struct mt7996_phy *phy = mt7996_hw_phy(hw);
++
++		/* odd index for driver, even index for firmware */
++		idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
++		if (phy->beacon_rate != rate)
++			mt7996_mcu_set_fixed_rate_table(phy, idx, rate, beacon);
+ 
+-		/* must odd index */
+-		idx = MT7996_BEACON_RATES_TBL + 2 * (mvif->mt76.idx % 20);
+-		mt7996_mac_set_fixed_rate_table(dev, idx, rate);
+ 		return idx;
+ 	}
+ 
+ 	idx = FIELD_GET(MT_TX_RATE_IDX, rate);
+ 	for (i = 0; i < ARRAY_SIZE(mt76_rates); i++)
+ 		if ((mt76_rates[i].hw_value & GENMASK(7, 0)) == idx)
+-			return MT7996_BASIC_RATES_TBL + i;
++			return MT7996_BASIC_RATES_TBL + 2 * i;
+ 
+ 	return mvif->basic_rates_idx;
+ }
+@@ -965,7 +966,6 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+ 	mt7996_set_stream_vht_txbf_caps(phy);
+ 	mt7996_set_stream_he_eht_caps(phy);
+ 
+-	/* TODO: update bmc_wtbl spe_idx when antenna changes */
+ 	mutex_unlock(&dev->mt76.mutex);
+ 
+ 	return 0;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index a5c473a..b34c6b7 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4056,6 +4056,36 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+ 
++int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
++				    u16 rate_idx, bool beacon)
++{
++#define UNI_FIXED_RATE_TABLE_SET	0
++#define SPE_IXD_SELECT_TXD		0
++#define SPE_IXD_SELECT_BMC_WTBL		1
++	struct mt7996_dev *dev = phy->dev;
++	struct fixed_rate_table_ctrl req = {
++		.tag = cpu_to_le16(UNI_FIXED_RATE_TABLE_SET),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.table_idx = table_idx,
++		.rate_idx = cpu_to_le16(rate_idx),
++		.gi = 1,
++		.he_ltf = 1,
++	};
++	u8 band_idx = phy->mt76->band_idx;
++
++	if (beacon) {
++		req.spe_idx_sel = SPE_IXD_SELECT_TXD;
++		req.spe_idx = 24 + band_idx;
++		phy->beacon_rate = rate_idx;
++	} else {
++		req.spe_idx_sel = SPE_IXD_SELECT_BMC_WTBL;
++		req.spe_idx = 0;
++	}
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(FIXED_RATE_TABLE),
++				 &req, sizeof(req), false);
++}
++
+ int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set)
+ {
+ 	struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index eb63441..e32767e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -771,4 +771,24 @@ enum {
+ #define MT7996_SEC_KEY_IDX		GENMASK(2, 1)
+ #define MT7996_SEC_IV			BIT(3)
+ 
++struct fixed_rate_table_ctrl {
++	u8 _rsv[4];
++
++	__le16 tag;
++	__le16 len;
++
++	u8 table_idx;
++	u8 antenna_idx;
++	__le16 rate_idx;
++	u8 spe_idx_sel;
++	u8 spe_idx;
++	u8 gi;
++	u8 he_ltf;
++	bool ldpc;
++	bool txbf;
++	bool dynamic_bw;
++
++	u8 rsv[1];
++} __packed;
++
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index f78f1fd..286fc1e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -48,7 +48,7 @@
+ #define MT7996_MAX_QUEUE		(__MT_RXQ_MAX +	__MT_MCUQ_MAX + 3)
+ 
+ /* NOTE: used to map mt76_rates. idx may change if firmware expands table */
+-#define MT7996_BASIC_RATES_TBL		11
++#define MT7996_BASIC_RATES_TBL		31
+ #define MT7996_BEACON_RATES_TBL		25
+ 
+ #define MT7996_THERMAL_THROTTLE_MAX	100
+@@ -256,6 +256,8 @@ struct mt7996_phy {
+ 
+ 	u8 rdd_state;
+ 
++	u16 beacon_rate;
++
+ 	u32 rx_ampdu_ts;
+ 	u32 ampdu_ref;
+ 
+@@ -539,6 +541,8 @@ 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,
+ 				     struct cfg80211_chan_def *chandef);
++int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
++				    u16 rate_idx, bool beacon);
+ int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set);
+ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
+ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val);
+@@ -606,8 +610,6 @@ void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy);
+ 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_set_fixed_rate_table(struct mt7996_dev *dev,
+-				     u8 tbl_idx, u16 rate_idx);
+ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ 			   struct sk_buff *skb, struct mt76_wcid *wcid,
+ 			   struct ieee80211_key_conf *key, int pid,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0038-wifi-mt76-mt7996-fix-DFS-CAC-tx-emission-issue-after.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0038-wifi-mt76-mt7996-fix-DFS-CAC-tx-emission-issue-after.patch
new file mode 100644
index 0000000..10bbe3a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0038-wifi-mt76-mt7996-fix-DFS-CAC-tx-emission-issue-after.patch
@@ -0,0 +1,40 @@
+From 39893cc863213984a9ab0ae279dbfe433a6fe90e Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 23 May 2023 21:18:59 +0800
+Subject: [PATCH 38/39] wifi: mt76: mt7996: fix DFS CAC tx emission issue after
+ 2nd interface up
+
+FW's channel state is set during the first wifi interface setup. If the switch reason for
+setting the tx/rx path during second-time wifi interface setup is CH_SWITCH_NORMAL,
+then the FW would perform runtime dpd channel calibration during DFS CAC, which leads to
+tx emission. Therefore, in order to bypass tx calibration during DFS CAC, set the switch reason
+to CH_SWITCH_DFS whenever chandef is set to DFS channel.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt7996/mcu.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index b34c6b7..be5c908 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3211,12 +3211,12 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+ 		.channel_band = ch_band[chandef->chan->band],
+ 	};
+ 
+-	if (tag == UNI_CHANNEL_RX_PATH ||
+-	    dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
++	if (dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
+ 		req.switch_reason = CH_SWITCH_NORMAL;
+ 	else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+ 		req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
+-	else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
++	else if (cfg80211_chandef_valid(chandef) &&
++		 !cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
+ 					  NL80211_IFTYPE_AP))
+ 		req.switch_reason = CH_SWITCH_DFS;
+ 	else
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0039-wifi-mt76-mt7996-fix-bss-rate-tlv-to-sync-firmware-c.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0039-wifi-mt76-mt7996-fix-bss-rate-tlv-to-sync-firmware-c.patch
new file mode 100644
index 0000000..c2fed58
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0039-wifi-mt76-mt7996-fix-bss-rate-tlv-to-sync-firmware-c.patch
@@ -0,0 +1,27 @@
+From affb48f4b09fca1e4df1e2291c78ee01b877e40f Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Tue, 30 May 2023 11:27:01 +0800
+Subject: [PATCH 39/39] wifi: mt76: mt7996: fix bss rate tlv to sync firmware
+ change
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ mt7996/mcu.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index e32767e..549007f 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -259,7 +259,7 @@ struct bss_rate_tlv {
+ 	u8 short_preamble;
+ 	u8 bc_fixed_rate;
+ 	u8 mc_fixed_rate;
+-	u8 __rsv2[1];
++	u8 __rsv2[9];
+ } __packed;
+ 
+ struct bss_ra_tlv {
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0040-wifi-mt76-mt7996-fix-beamformee-ss-subfield-in-EHT-P.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0040-wifi-mt76-mt7996-fix-beamformee-ss-subfield-in-EHT-P.patch
new file mode 100644
index 0000000..b5a49a5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0040-wifi-mt76-mt7996-fix-beamformee-ss-subfield-in-EHT-P.patch
@@ -0,0 +1,43 @@
+From da1c93b8c6480cfcd605cd8c19111a6df8c9f8b4 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 2 Jun 2023 15:12:34 +0800
+Subject: [PATCH] wifi: mt76: mt7996: fix beamformee ss subfield in EHT PHY
+ caps IE
+
+According to P802.11be_D2.1 Table 9-401I, the minimum value of Beamformee SS shall
+be 3. Fix it to ensure that the value of Beamformee SS subfield is at least 3.
+
+Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
+---
+ mt7996/init.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 9eba689a..96c4bb01 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -936,16 +936,17 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
+ 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
+ 
++	val = max_t(u8, sts - 1, 3);
+ 	eht_cap_elem->phy_cap_info[0] |=
+-		u8_encode_bits(u8_get_bits(sts - 1, BIT(0)),
++		u8_encode_bits(u8_get_bits(val, BIT(0)),
+ 			       IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK);
+ 
+ 	eht_cap_elem->phy_cap_info[1] =
+-		u8_encode_bits(u8_get_bits(sts - 1, GENMASK(2, 1)),
++		u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)),
+ 			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) |
+-		u8_encode_bits(sts - 1,
++		u8_encode_bits(val,
+ 			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK) |
+-		u8_encode_bits(sts - 1,
++		u8_encode_bits(val,
+ 			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
+ 
+ 	eht_cap_elem->phy_cap_info[2] =
+-- 
+2.18.0
+
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-wifi-mt76-mt7996-for-build-pass.patch
similarity index 90%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0999-mt76-mt7996-for-build-pass.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/0999-wifi-mt76-mt7996-for-build-pass.patch
index 89d1cdd..cb5d3bb 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-wifi-mt76-mt7996-for-build-pass.patch
@@ -1,7 +1,7 @@
-From c187058bfc83a80b3cfbfed4ce0c73ece6efb1fe Mon Sep 17 00:00:00 2001
+From 83bb8ec37f85161b08f4eb2abf6a9a0530cfc189 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 0999/1001] mt76: mt7996: for build pass
+Subject: [PATCH 0999/1015] wifi: mt76: mt7996: for build pass
 
 Change-Id: Ieb44c33ee6e6a2e6058c1ef528404c1a1cbcfdaf
 ---
@@ -34,10 +34,10 @@
  	return 0;
  }
 diff --git a/dma.c b/dma.c
-index f560d37d..ffc5b553 100644
+index f2b1b2ac..e1e9062b 100644
 --- a/dma.c
 +++ b/dma.c
-@@ -858,7 +858,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -859,7 +859,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;
  
@@ -116,10 +116,10 @@
  #define fw_name(_dev, name, ...)	({			\
  	char *_fw;						\
 diff --git a/mt7996/dma.c b/mt7996/dma.c
-index 53414346..733d1228 100644
+index f01cea5e..b8f253d0 100644
 --- a/mt7996/dma.c
 +++ b/mt7996/dma.c
-@@ -343,8 +343,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+@@ -360,8 +360,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
  	if (ret < 0)
  		return ret;
  
@@ -129,9 +129,9 @@
 +			  mt7996_poll_tx, NAPI_POLL_WEIGHT);
  	napi_enable(&dev->mt76.tx_napi);
  
- 	mt7996_dma_enable(dev);
+ 	mt7996_dma_enable(dev, false);
 diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index f5e95460..50f2227f 100644
+index 9840c77d..b81ed64c 100644
 --- a/mt7996/eeprom.c
 +++ b/mt7996/eeprom.c
 @@ -121,6 +121,7 @@ static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
@@ -143,7 +143,7 @@
  		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 24adeb12..f2bfbd8a 100644
+index 871d32a4..58893348 100644
 --- a/mt7996/mcu.c
 +++ b/mt7996/mcu.c
 @@ -5,6 +5,7 @@
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1000-mt76-mt7996-add-debug-tool.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1000-wifi-mt76-mt7996-add-debug-tool.patch
similarity index 97%
rename from recipes-wifi/linux-mt76/files/patches-3.x/1000-mt76-mt7996-add-debug-tool.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/1000-wifi-mt76-mt7996-add-debug-tool.patch
index 018c7b9..7179a65 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1000-mt76-mt7996-add-debug-tool.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1000-wifi-mt76-mt7996-add-debug-tool.patch
@@ -1,43 +1,119 @@
-From 9cf11ae2bfdf56babbdfe4fe03f61f492c06ce1a Mon Sep 17 00:00:00 2001
+From e7b3aa46df08dc89f0ed30c36d56ee4acf50d982 Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Fri, 24 Mar 2023 14:02:32 +0800
-Subject: [PATCH 1000/1001] mt76: mt7996: add debug tool
+Subject: [PATCH 1000/1015] wifi: mt76: mt7996: add debug tool
 
 Change-Id: Ie10390b01f17db893dbfbf3221bf63a4bd1fe38f
 ---
  mt7996/Makefile      |    3 +
- mt7996/debugfs.c     |   31 +-
- mt7996/mcu.c         |    4 +
- mt7996/mt7996.h      |   15 +
- mt7996/mtk_debug.h   | 2166 ++++++++++++++++++++++++++++++++++++++
- mt7996/mtk_debugfs.c | 2344 ++++++++++++++++++++++++++++++++++++++++++
+ mt7996/coredump.c    |   10 +-
+ mt7996/coredump.h    |    7 +
+ mt7996/debugfs.c     |   29 +-
+ mt7996/mt7996.h      |   14 +
+ mt7996/mtk_debug.h   | 2165 ++++++++++++++++++++++++++++++++++++++
+ mt7996/mtk_debugfs.c | 2353 ++++++++++++++++++++++++++++++++++++++++++
  mt7996/mtk_mcu.c     |   18 +
  mt7996/mtk_mcu.h     |   16 +
  tools/fwlog.c        |   25 +-
- 9 files changed, 4606 insertions(+), 16 deletions(-)
+ 10 files changed, 4622 insertions(+), 18 deletions(-)
  create mode 100644 mt7996/mtk_debug.h
  create mode 100644 mt7996/mtk_debugfs.c
  create mode 100644 mt7996/mtk_mcu.c
  create mode 100644 mt7996/mtk_mcu.h
 
 diff --git a/mt7996/Makefile b/mt7996/Makefile
-index bed9efd6..9ef0b824 100644
+index 7c2514a6..df131869 100644
 --- a/mt7996/Makefile
 +++ b/mt7996/Makefile
-@@ -1,4 +1,5 @@
+@@ -1,5 +1,6 @@
  # SPDX-License-Identifier: ISC
+ EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
 +EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
  
  obj-$(CONFIG_MT7996E) += mt7996e.o
  
-@@ -8,3 +9,5 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+@@ -9,3 +10,5 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
  mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
  
  mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
 +
 +mt7996e-y += mtk_debugfs.o mtk_mcu.o
+diff --git a/mt7996/coredump.c b/mt7996/coredump.c
+index 60b88085..a7f91b56 100644
+--- a/mt7996/coredump.c
++++ b/mt7996/coredump.c
+@@ -195,7 +195,7 @@ mt7996_coredump_fw_stack(struct mt7996_dev *dev, u8 type, struct mt7996_coredump
+ 	}
+ }
+ 
+-static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type)
++struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
+ {
+ 	struct mt7996_crash_data *crash_data = dev->coredump.crash_data[type];
+ 	struct mt7996_coredump *dump;
+@@ -206,7 +206,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+ 
+ 	len = hdr_len;
+ 
+-	if (coredump_memdump && crash_data->memdump_buf_len)
++	if (full_dump && coredump_memdump && crash_data->memdump_buf_len)
+ 		len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
+ 
+ 	sofar += hdr_len;
+@@ -248,6 +248,9 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+ 	mt7996_coredump_fw_state(dev, type, dump, &exception);
+ 	mt7996_coredump_fw_stack(dev, type, dump, exception);
+ 
++	if (!full_dump)
++		goto skip_dump_mem;
++
+ 	/* gather memory content */
+ 	dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
+ 	dump_mem->len = crash_data->memdump_buf_len;
+@@ -255,6 +258,7 @@ static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8
+ 		memcpy(dump_mem->data, crash_data->memdump_buf,
+ 		       crash_data->memdump_buf_len);
+ 
++skip_dump_mem:
+ 	mutex_unlock(&dev->dump_mutex);
+ 
+ 	return dump;
+@@ -264,7 +268,7 @@ int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ {
+ 	struct mt7996_coredump *dump;
+ 
+-	dump = mt7996_coredump_build(dev, type);
++	dump = mt7996_coredump_build(dev, type, true);
+ 	if (!dump) {
+ 		dev_warn(dev->mt76.dev, "no crash dump data found\n");
+ 		return -ENODATA;
+diff --git a/mt7996/coredump.h b/mt7996/coredump.h
+index 01ed3731..93cd84a0 100644
+--- a/mt7996/coredump.h
++++ b/mt7996/coredump.h
+@@ -75,6 +75,7 @@ struct mt7996_mem_region {
+ const struct mt7996_mem_region *
+ mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u8 type, u32 *num);
+ struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type);
++struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump);
+ int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type);
+ int mt7996_coredump_register(struct mt7996_dev *dev);
+ void mt7996_coredump_unregister(struct mt7996_dev *dev);
+@@ -92,6 +93,12 @@ static inline int mt7996_coredump_submit(struct mt7996_dev *dev, u8 type)
+ 	return 0;
+ }
+ 
++static inline struct
++mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev, u8 type, bool full_dump)
++{
++	return NULL;
++}
++
+ static inline struct
+ mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev, u8 type)
+ {
 diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
-index 04220180..0bfded17 100644
+index ca4d615d..8a513f46 100644
 --- a/mt7996/debugfs.c
 +++ b/mt7996/debugfs.c
 @@ -301,6 +301,9 @@ mt7996_fw_debug_wm_set(void *data, u64 val)
@@ -76,22 +152,19 @@
  	return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
  }
  
-@@ -821,8 +830,13 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
- 					    mt7996_rdd_monitor);
- 	}
- 
--	if (phy == &dev->phy)
-+	if (phy == &dev->phy) {
+@@ -825,6 +834,11 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 	if (phy == &dev->phy)
  		dev->debugfs_dir = dir;
+ 
 +#ifdef CONFIG_MTK_DEBUG
-+		debugfs_create_u16("wlan_idx", 0600, dir, &dev->wlan_idx);
-+		mt7996_mtk_init_debugfs(phy, dir);
++	debugfs_create_u16("wlan_idx", 0600, dir, &dev->wlan_idx);
++	mt7996_mtk_init_debugfs(phy, dir);
 +#endif
-+	}
- 
++
  	return 0;
  }
-@@ -836,6 +850,12 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
+ 
+@@ -837,6 +851,12 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
  	void *dest;
  
  	spin_lock_irqsave(&lock, flags);
@@ -104,7 +177,7 @@
  	dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4);
  	if (dest) {
  		*(u32 *)dest = hdrlen + len;
-@@ -868,9 +888,6 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
+@@ -869,9 +889,6 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
  		.msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR),
  	};
  
@@ -114,33 +187,11 @@
  	hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++);
  	hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0)));
  	hdr.len = *(__le16 *)data;
-diff --git a/mt7996/mcu.c b/mt7996/mcu.c
-index f2bfbd8a..ef779cf9 100644
---- a/mt7996/mcu.c
-+++ b/mt7996/mcu.c
-@@ -2324,6 +2324,7 @@ static int mt7996_load_patch(struct mt7996_dev *dev)
- 
- 	dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
- 		 be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
-+	memcpy(dev->dbg.patch_build_date, hdr->build_date, sizeof(dev->dbg.patch_build_date));
- 
- 	for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
- 		struct mt7996_patch_sec *sec;
-@@ -2453,6 +2454,9 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
- 		hdr = (const struct mt7996_fw_trailer *)			\
- 				(fw->data + fw->size - sizeof(*hdr));		\
- 										\
-+		memcpy(dev->dbg.ram_build_date[MT7996_RAM_TYPE_##_type],	\
-+		       hdr->build_date,						\
-+		       sizeof(dev->dbg.ram_build_date[MT7996_RAM_TYPE_##_type]));\
- 		dev_info(dev->mt76.dev,						\
- 			 "%s Firmware Version: %.10s, Build Time: %.15s\n",	\
- 			 #_type, hdr->fw_ver, hdr->build_date);			\
 diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 9bf3bf1a..1ac54520 100644
+index 286fc1eb..6c76ec20 100644
 --- a/mt7996/mt7996.h
 +++ b/mt7996/mt7996.h
-@@ -339,6 +339,17 @@ struct mt7996_dev {
+@@ -363,6 +363,16 @@ struct mt7996_dev {
  	u32 reg_l2_backup;
  
  	u8 wtbl_size_group;
@@ -148,8 +199,7 @@
 +#ifdef CONFIG_MTK_DEBUG
 +	u16 wlan_idx;
 +	struct {
-+		char patch_build_date[16];
-+		char ram_build_date[3][15];
++		u8 sku_disable;
 +		u32 fw_dbg_module;
 +		u8 fw_dbg_lv;
 +		u32 bcn_total_cnt[__MT_MAX_BAND];
@@ -158,7 +208,7 @@
  };
  
  enum {
-@@ -608,4 +619,8 @@ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+@@ -659,4 +669,8 @@ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
  			    struct ieee80211_sta *sta, struct dentry *dir);
  #endif
  
@@ -169,10 +219,10 @@
  #endif
 diff --git a/mt7996/mtk_debug.h b/mt7996/mtk_debug.h
 new file mode 100644
-index 00000000..a48bac50
+index 00000000..eb40f9cb
 --- /dev/null
 +++ b/mt7996/mtk_debug.h
-@@ -0,0 +1,2166 @@
+@@ -0,0 +1,2165 @@
 +#ifndef __MTK_DEBUG_H
 +#define __MTK_DEBUG_H
 +
@@ -342,9 +392,8 @@
 +	u16 ring_size;
 +	char *const ring_info;
 +};
++
 +// HOST DMA
-+//#define CONN_INFRA_REMAPPING_OFFSET 0x64000000
-+//#define WF_WFDMA_HOST_DMA0_BASE (0x18024000 + CONN_INFRA_REMAPPING_OFFSET)
 +#define WF_WFDMA_HOST_DMA0_BASE                                0xd4000
 +
 +#define WF_WFDMA_HOST_DMA0_HOST_INT_STA_ADDR                                   \
@@ -2341,10 +2390,10 @@
 +#endif
 diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
 new file mode 100644
-index 00000000..080f756e
+index 00000000..f04c300f
 --- /dev/null
 +++ b/mt7996/mtk_debugfs.c
-@@ -0,0 +1,2344 @@
+@@ -0,0 +1,2353 @@
 +// SPDX-License-Identifier: ISC
 +/*
 + * Copyright (C) 2023 MediaTek Inc.
@@ -2356,6 +2405,7 @@
 +#include "eeprom.h"
 +#include "mtk_debug.h"
 +#include "mtk_mcu.h"
++#include "coredump.h"
 +
 +#ifdef CONFIG_MTK_DEBUG
 +
@@ -2690,83 +2740,85 @@
 +	if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
 +		return 0;
 +
-+	seq_printf(s, "Rom Patch Build Time: %.16s\n", dev->dbg.patch_build_date);
-+	seq_printf(s, "WM Patch Build Time: %.15s\n",
-+		   dev->dbg.ram_build_date[MT7996_RAM_TYPE_WM]);
++	seq_printf(s, "Rom Patch Build Time: %.16s\n", dev->patch_build_date);
++	seq_printf(s, "WM Patch Build Time: %.15s, Mode: %s\n",
++		   dev->ram_build_date[MT7996_RAM_TYPE_WM],
++		   dev->testmode_enable ? "Testmode" : "Normal mode");
 +	seq_printf(s, "WA Patch Build Time: %.15s\n",
-+		   dev->dbg.ram_build_date[MT7996_RAM_TYPE_WA]);
++		   dev->ram_build_date[MT7996_RAM_TYPE_WA]);
 +	seq_printf(s, "DSP Patch Build Time: %.15s\n",
-+		   dev->dbg.ram_build_date[MT7996_RAM_TYPE_DSP]);
++		   dev->ram_build_date[MT7996_RAM_TYPE_DSP]);
 +	return 0;
 +}
 +
-+/* dma info dump */
-+const struct queue_desc mt7902_tx_ring_layout[] = {
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_TX_RING18_CTRL0_ADDR,
-+	 .ring_size = 2048,
-+	 .ring_info = "band0 TXD"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_TX_RING19_CTRL0_ADDR,
-+	 .ring_size = 2048,
-+	 .ring_info = "band1 TXD"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_TX_RING21_CTRL0_ADDR,
-+	 .ring_size = 2048,
-+	 .ring_info = "band2 TXD"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_TX_RING16_CTRL0_ADDR,
-+	 .ring_size = 128,
-+	 .ring_info = "FWDL"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_TX_RING17_CTRL0_ADDR,
-+	 .ring_size = 256,
-+	 .ring_info = "cmd to WM"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_TX_RING20_CTRL0_ADDR,
-+	 .ring_size = 256,
-+	 .ring_info = "cmd to WA"
++/* fw wm call trace info dump */
++void mt7996_show_lp_history(struct seq_file *s, u32 type)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt7996_crash_data *crash_data;
++	struct mt7996_coredump *dump;
++	u64 now = 0;
++	int i = 0;
++	u8 fw_type = !!type;
++
++	mutex_lock(&dev->dump_mutex);
++
++	crash_data = mt7996_coredump_new(dev, fw_type);
++	if (!crash_data) {
++		mutex_unlock(&dev->dump_mutex);
++		seq_printf(s, "the coredump is disable!\n");
++		return;
 +	}
-+};
++	mutex_unlock(&dev->dump_mutex);
 +
-+const struct queue_desc mt7902_rx_ring_layout[] = {
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_RX_RING4_CTRL0_ADDR,
-+	 .ring_size = 1536,
-+	 .ring_info = "band0 RX data"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_RX_RING5_CTRL0_ADDR,
-+	 .ring_size = 1536,
-+	 .ring_info = "band1 RX data"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_RX_RING8_CTRL0_ADDR,
-+	 .ring_size = 1536,
-+	 .ring_info = "band2 RX data"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_RX_RING0_CTRL0_ADDR,
-+	 .ring_size = 512,
-+	 .ring_info = "event from WM"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_RX_RING1_CTRL0_ADDR,
-+	 .ring_size = 1024,
-+	 .ring_info = "event from WA"
-+	},
-+	{
-+	 .hw_desc_base = WF_WFDMA_HOST_DMA0_WPDMA_RX_RING2_CTRL0_ADDR,
-+	 .ring_size = 1024,
-+	 .ring_info = "band0/1/2 tx free done"
-+	},
-+};
++	dump = mt7996_coredump_build(dev, fw_type, false);
++	if (!dump) {
++		seq_printf(s, "no call stack data found!\n");
++		return;
++	}
++
++	seq_printf(s, "\x1b[32m%s log output\x1b[0m\n", dump->fw_type);
++	seq_printf(s, "\x1b[32mfw status: %s\n", dump->fw_state);
++	mt7996_dump_version(s, NULL);
++	/* PC log */
++	now = jiffies;
++	for (i = 0; i < 10; i++)
++		seq_printf(s, "\tCurrent PC=%x\n", dump->pc_cur[i]);
 +
++	seq_printf(s, "PC log contorl=0x%x(T=%llu)(latest PC index = 0x%x)\n",
++		dump->pc_dbg_ctrl, now, dump->pc_cur_idx);
++	for (i = 0; i < 32; i++)
++		seq_printf(s, "\tPC log(%d)=0x%08x\n", i, dump->pc_stack[i]);
++
++	/* LR log */
++	now = jiffies;
++	seq_printf(s, "\nLR log contorl=0x%x(T=%llu)(latest LR index = 0x%x)\n",
++		dump->lr_dbg_ctrl, now, dump->lr_cur_idx);
++	for (i = 0; i < 32; i++)
++		seq_printf(s, "\tLR log(%d)=0x%08x\n", i, dump->lr_stack[i]);
++
++	vfree(dump);
++}
++
++static int mt7996_fw_wa_info_read(struct seq_file *s, void *data)
++{
++	seq_printf(s, "======[ShowPcLpHistory]======\n");
++	mt7996_show_lp_history(s, MT7996_RAM_TYPE_WA);
++	seq_printf(s, "======[End ShowPcLpHistory]==\n");
++
++	return 0;
++}
++
++static int mt7996_fw_wm_info_read(struct seq_file *s, void *data)
++{
++	seq_printf(s, "======[ShowPcLpHistory]======\n");
++	mt7996_show_lp_history(s, MT7996_RAM_TYPE_WM);
++	seq_printf(s, "======[End ShowPcLpHistory]==\n");
++
++	return 0;
++}
++
++/* dma info dump */
 +static void
 +dump_dma_tx_ring_info(struct seq_file *s, struct mt7996_dev *dev,  char *str1, char *str2, u32 ring_base)
 +{
@@ -4668,6 +4720,10 @@
 +	debugfs_create_file("fw_wa_set", 0600, dir, dev, &fops_wa_set);
 +	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_version", dir,
 +				    mt7996_dump_version);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wa_info", dir,
++				    mt7996_fw_wa_info_read);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "fw_wm_info", dir,
++				    mt7996_fw_wm_info_read);
 +
 +	debugfs_create_devm_seqfile(dev->mt76.dev, "mib_info0", dir,
 +				    mt7996_mibinfo_band0);
@@ -4685,6 +4741,8 @@
 +	debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
 +				    mt7996_wtbl_read);
 +
++	debugfs_create_u8("sku_disable", 0600, dir, &dev->dbg.sku_disable);
++
 +	return 0;
 +}
 +
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1001-mt76-mt7996-add-txpower-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1001-wifi-mt76-mt7996-add-txpower-support.patch
similarity index 94%
rename from recipes-wifi/linux-mt76/files/patches-3.x/1001-mt76-mt7996-add-txpower-support.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/1001-wifi-mt76-mt7996-add-txpower-support.patch
index a9a371d..75fa826 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/1001-mt76-mt7996-add-txpower-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1001-wifi-mt76-mt7996-add-txpower-support.patch
@@ -1,7 +1,7 @@
-From 0169bc58ed8089fc7ca07d08a0ce90a02db4a3dd Mon Sep 17 00:00:00 2001
+From b729a28ee5895d921e8df1a371e114dee7724595 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Fri, 24 Mar 2023 23:35:30 +0800
-Subject: [PATCH 1001/1001] mt76: mt7996: add txpower support
+Subject: [PATCH 1001/1015] wifi: mt76: mt7996: add txpower support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Change-Id: Ic3e7b17f3664fa7f774137572f885359fa2ec93b
@@ -17,10 +17,10 @@
  8 files changed, 423 insertions(+), 5 deletions(-)
 
 diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
-index 50f2227f..c8f65b98 100644
+index b81ed64c..be0a34ae 100644
 --- a/mt7996/eeprom.c
 +++ b/mt7996/eeprom.c
-@@ -280,3 +280,37 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
+@@ -293,3 +293,37 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
  
  	return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
  }
@@ -59,10 +59,10 @@
 +	[SKU_EHT3x996_484] = 16,
 +};
 diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
-index 0f8f0cd8..54e180fb 100644
+index 9ea3667f..343e65e1 100644
 --- a/mt7996/eeprom.h
 +++ b/mt7996/eeprom.h
-@@ -72,4 +72,46 @@ mt7996_get_channel_group_6g(int channel)
+@@ -75,4 +75,46 @@ mt7996_get_channel_group_6g(int channel)
  	return DIV_ROUND_UP(channel - 29, 32);
  }
  
@@ -110,10 +110,10 @@
 +
  #endif
 diff --git a/mt7996/mcu.h b/mt7996/mcu.h
-index ebc62713..476e007b 100644
+index 549007f9..5d52b0b7 100644
 --- a/mt7996/mcu.h
 +++ b/mt7996/mcu.h
-@@ -698,6 +698,7 @@ struct tx_power_ctrl {
+@@ -712,6 +712,7 @@ struct tx_power_ctrl {
  		bool ate_mode_enable;
  		bool percentage_ctrl_enable;
  		bool bf_backoff_enable;
@@ -121,7 +121,7 @@
  		u8 power_drop_level;
  	};
  	u8 band_idx;
-@@ -711,6 +712,7 @@ enum {
+@@ -725,6 +726,7 @@ enum {
  	UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
  	UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
  	UNI_TXPOWER_ATE_MODE_CTRL = 6,
@@ -130,31 +130,31 @@
  
  enum {
 diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
-index 1ac54520..9bed13a1 100644
+index 6c76ec20..085307ab 100644
 --- a/mt7996/mt7996.h
 +++ b/mt7996/mt7996.h
-@@ -57,6 +57,8 @@
- #define MT7996_CRIT_TEMP		110
- #define MT7996_MAX_TEMP			120
+@@ -60,6 +60,8 @@
+ 
+ #define MT7996_BUILD_TIME_LEN		24
  
 +#define MT7996_SKU_RATE_NUM		417
 +
  struct mt7996_vif;
  struct mt7996_sta;
  struct mt7996_dfs_pulse;
-@@ -525,6 +527,7 @@ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+@@ -563,6 +565,7 @@ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
  void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
  void mt7996_mcu_exit(struct mt7996_dev *dev);
  int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
 +int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
  
- static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
- {
 diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
-index 080f756e..c05f8465 100644
+index f04c300f..2aee3ab0 100644
 --- a/mt7996/mtk_debugfs.c
 +++ b/mt7996/mtk_debugfs.c
-@@ -2296,6 +2296,232 @@ static int mt7996_sta_info(struct seq_file *s, void *data)
+@@ -2299,6 +2299,232 @@ static int mt7996_sta_info(struct seq_file *s, void *data)
  	return 0;
  }
  
@@ -387,7 +387,7 @@
  int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
  {
  	struct mt7996_dev *dev = phy->dev;
-@@ -2334,6 +2560,9 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+@@ -2341,6 +2567,9 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
  
  	debugfs_create_devm_seqfile(dev->mt76.dev, "tr_info", dir,
  				    mt7996_trinfo_read);
@@ -522,10 +522,10 @@
  #endif
  
 diff --git a/mt7996/regs.h b/mt7996/regs.h
-index d1d3d154..d01cc8c9 100644
+index 3a5914c4..6ef905a9 100644
 --- a/mt7996/regs.h
 +++ b/mt7996/regs.h
-@@ -557,15 +557,22 @@ enum base_rev {
+@@ -562,15 +562,22 @@ enum base_rev {
  
  #define MT_PCIE1_MAC_INT_ENABLE			MT_PCIE1_MAC(0x188)
  
@@ -552,7 +552,7 @@
  /* PHYRX CSD */
  #define MT_WF_PHYRX_CSD_BASE			0x83000000
  #define MT_WF_PHYRX_CSD(_band, _wf, ofs)	(MT_WF_PHYRX_CSD_BASE + \
-@@ -574,7 +581,7 @@ enum base_rev {
+@@ -579,7 +586,7 @@ enum base_rev {
  #define MT_WF_PHYRX_CSD_IRPI(_band, _wf)	MT_WF_PHYRX_CSD(_band, _wf, 0x1000)
  
  /* PHYRX CSD BAND */
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1002-wifi-mt76-mt7996-add-mu-vendor-command-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1002-wifi-mt76-mt7996-add-mu-vendor-command-support.patch
new file mode 100644
index 0000000..fe75034
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1002-wifi-mt76-mt7996-add-mu-vendor-command-support.patch
@@ -0,0 +1,292 @@
+From 55619fa44187b6ed841f94c2bedbc4779457e3f9 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 13 Dec 2022 15:17:43 +0800
+Subject: [PATCH 1002/1015] wifi: mt76: mt7996: add mu vendor command support
+
+Change-Id: I4599bd97917651aaea51d7ff186ffff73a07e4ce
+---
+ mt7996/Makefile |  3 +-
+ mt7996/init.c   |  8 +++++
+ mt7996/mcu.c    | 37 +++++++++++++++++++---
+ mt7996/mcu.h    | 12 +++++++
+ mt7996/mt7996.h |  7 +++++
+ mt7996/vendor.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h | 22 +++++++++++++
+ 7 files changed, 167 insertions(+), 6 deletions(-)
+ create mode 100644 mt7996/vendor.c
+ create mode 100644 mt7996/vendor.h
+
+diff --git a/mt7996/Makefile b/mt7996/Makefile
+index df131869..8dbbc34c 100644
+--- a/mt7996/Makefile
++++ b/mt7996/Makefile
+@@ -1,11 +1,12 @@
+ # SPDX-License-Identifier: ISC
+ EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
+ EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
++EXTRA_CFLAGS += -DCONFIG_MTK_VENDOR
+ 
+ obj-$(CONFIG_MT7996E) += mt7996e.o
+ 
+ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+-	     debugfs.o mmio.o
++	     debugfs.o mmio.o vendor.o
+ 
+ mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
+ 
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 96c4bb01..1d4359f0 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -579,6 +579,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	if (ret)
+ 		goto error;
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_vendor_register(phy);
++#endif
++
+ 	ret = mt76_register_phy(mphy, true, mt76_rates,
+ 				ARRAY_SIZE(mt76_rates));
+ 	if (ret)
+@@ -1082,6 +1086,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 	dev->mt76.test_ops = &mt7996_testmode_ops;
+ #endif
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_vendor_register(&dev->phy);
++#endif
++
+ 	ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+ 				   ARRAY_SIZE(mt76_rates));
+ 	if (ret)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 58893348..91f3103a 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1158,6 +1158,8 @@ static void
+ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 			struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ {
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_phy *phy = mvif->phy;
+ 	struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ 	struct sta_rec_muru *muru;
+ 	struct tlv *tlv;
+@@ -1169,11 +1171,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
+ 
+ 	muru = (struct sta_rec_muru *)tlv;
+-	muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer ||
+-			       vif->bss_conf.he_mu_beamformer ||
+-			       vif->bss_conf.vht_mu_beamformer ||
+-			       vif->bss_conf.vht_mu_beamformee;
+-	muru->cfg.ofdma_dl_en = true;
++	muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
++				vif->bss_conf.he_mu_beamformer ||
++				vif->bss_conf.vht_mu_beamformer ||
++				vif->bss_conf.vht_mu_beamformee) &&
++			       !!(phy->muru_onoff & MUMIMO_DL);
++	muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
++	muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
++	muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
+ 
+ 	if (sta->deflink.vht_cap.vht_supported)
+ 		muru->mimo_dl.vht_mu_bfee =
+@@ -4353,3 +4358,25 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ 	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
+ 				 &req, sizeof(req), false);
+ }
++
++#ifdef CONFIG_MTK_VENDOR
++void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
++{
++	u8 mode, val;
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_phy *phy =  mvif->phy;
++
++	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
++	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
++
++	switch (mode) {
++	case RATE_PARAM_AUTO_MU:
++		if (val < 0 || val > 15) {
++			printk("Wrong value! The value is between 0-15.\n");
++			break;
++		}
++		phy->muru_onoff = val;
++		break;
++	}
++}
++#endif
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 5d52b0b7..baffbcd7 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -568,8 +568,20 @@ enum {
+ 	RATE_PARAM_FIXED_MCS,
+ 	RATE_PARAM_FIXED_GI = 11,
+ 	RATE_PARAM_AUTO = 20,
++#ifdef CONFIG_MTK_VENDOR
++	RATE_PARAM_AUTO_MU = 32,
++#endif
+ };
+ 
++#define RATE_CFG_MODE	GENMASK(15, 8)
++#define RATE_CFG_VAL	GENMASK(7, 0)
++
++/* MURU */
++#define OFDMA_DL                       BIT(0)
++#define OFDMA_UL                       BIT(1)
++#define MUMIMO_DL                      BIT(2)
++#define MUMIMO_UL                      BIT(3)
++
+ enum {
+ 	BF_SOUNDING_ON = 1,
+ 	BF_HW_EN_UPDATE = 17,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 085307ab..b9f3dd8e 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -263,6 +263,8 @@ struct mt7996_phy {
+ 	u32 rx_ampdu_ts;
+ 	u32 ampdu_ref;
+ 
++	u8 muru_onoff;
++
+ 	struct mib_stats mib;
+ 	struct mt76_channel_state state_ts;
+ 
+@@ -672,6 +674,11 @@ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    struct ieee80211_sta *sta, struct dentry *dir);
+ #endif
+ 
++#ifdef CONFIG_MTK_VENDOR
++void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
++void mt7996_vendor_register(struct mt7996_phy *phy);
++#endif
++
+ #ifdef CONFIG_MTK_DEBUG
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
+ #endif
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+new file mode 100644
+index 00000000..08ecc2b3
+--- /dev/null
++++ b/mt7996/vendor.c
+@@ -0,0 +1,84 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2020, MediaTek Inc. All rights reserved.
++ */
++
++#include <net/netlink.h>
++
++#include "mt7996.h"
++#include "mcu.h"
++#include "vendor.h"
++
++static const struct nla_policy
++mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
++	[MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
++};
++
++static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
++				 struct wireless_dev *wdev,
++				 const void *data,
++				 int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
++	int err;
++	u8 val8;
++	u32 val32 = 0;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
++			mu_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
++		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
++			 FIELD_PREP(RATE_CFG_VAL, val8);
++		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++							   mt7996_set_wireless_vif, &val32);
++	}
++
++	return 0;
++}
++
++static int
++mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++			   struct sk_buff *skb, const void *data, int data_len,
++			   unsigned long *storage)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	int len = 0;
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
++		return -ENOMEM;
++	len += 1;
++
++	return len;
++}
++
++static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_MU_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_mu_ctrl,
++		.dumpit = mt7996_vendor_mu_ctrl_dump,
++		.policy = mu_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
++	},
++};
++
++void mt7996_vendor_register(struct mt7996_phy *phy)
++{
++	phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
++	phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
++}
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+new file mode 100644
+index 00000000..8ac3ba8e
+--- /dev/null
++++ b/mt7996/vendor.h
+@@ -0,0 +1,22 @@
++#ifndef __MT7996_VENDOR_H
++#define __MT7996_VENDOR_H
++
++#define MTK_NL80211_VENDOR_ID	0x0ce7
++
++enum mtk_nl80211_vendor_subcmds {
++	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
++};
++
++enum mtk_vendor_attr_mu_ctrl {
++	MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
++	MTK_VENDOR_ATTR_MU_CTRL_DUMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_MU_CTRL,
++	MTK_VENDOR_ATTR_MU_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
++};
++
++#endif
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1003-wifi-mt76-mt7996-Add-air-monitor-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1003-wifi-mt76-mt7996-Add-air-monitor-support.patch
new file mode 100644
index 0000000..d83c3f9
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1003-wifi-mt76-mt7996-Add-air-monitor-support.patch
@@ -0,0 +1,565 @@
+From 2637da59e92d101889cf3680e57f0594c6b349ec Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Wed, 26 Apr 2023 04:40:05 +0800
+Subject: [PATCH 1003/1015] wifi: mt76: mt7996: Add air monitor support
+
+---
+ mt76_connac_mcu.h |   1 +
+ mt7996/mac.c      |   4 +
+ mt7996/main.c     |   4 +
+ mt7996/mt7996.h   |  35 +++++
+ mt7996/vendor.c   | 362 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h   |  39 +++++
+ 6 files changed, 445 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 4bb9508a..e62f17ad 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1206,6 +1206,7 @@ enum {
+ 	MCU_UNI_CMD_REG_ACCESS = 0x0d,
+ 	MCU_UNI_CMD_CHIP_CONFIG = 0x0e,
+ 	MCU_UNI_CMD_POWER_CTRL = 0x0f,
++	MCU_UNI_CMD_CFG_SMESH = 0x10,
+ 	MCU_UNI_CMD_RX_HDR_TRANS = 0x12,
+ 	MCU_UNI_CMD_SER = 0x13,
+ 	MCU_UNI_CMD_TWT = 0x14,
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 05269e7f..3dc5cdae 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -865,6 +865,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+ 			if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
+ 				*qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ 		}
++#ifdef CONFIG_MTK_VENDOR
++		if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
++			mt7996_vendor_amnt_fill_rx(phy, skb);
++#endif
+ 	} else {
+ 		status->flag |= RX_FLAG_8023;
+ 	}
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 2ed66e6c..e5627c96 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -672,6 +672,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	mt7996_mac_wtbl_update(dev, idx,
+ 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
++#endif
++
+ 	ret = mt7996_mcu_add_sta(dev, vif, sta, true);
+ 	if (ret)
+ 		return ret;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index b9f3dd8e..dc44edc1 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -235,6 +235,34 @@ enum {
+ 	SCS_ENABLE,
+ };
+ 
++#ifdef CONFIG_MTK_VENDOR
++#define MT7996_AIR_MONITOR_MAX_ENTRY	16
++#define MT7996_AIR_MONITOR_MAX_GROUP	(MT7996_AIR_MONITOR_MAX_ENTRY >> 1)
++
++struct mt7996_air_monitor_group {
++	bool enable;
++	bool used[2];
++};
++
++struct mt7996_air_monitor_entry {
++	bool enable;
++
++	u8 group_idx;
++	u8 group_used_idx;
++	u8 muar_idx;
++	u8 addr[ETH_ALEN];
++	u32 last_seen;
++	s8 rssi[4];
++	struct ieee80211_sta *sta;
++};
++
++struct mt7996_air_monitor_ctrl {
++	u8 enable;
++	struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
++	struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
++};
++#endif
++
+ struct mt7996_phy {
+ 	struct mt76_phy *mt76;
+ 	struct mt7996_dev *dev;
+@@ -285,6 +313,10 @@ struct mt7996_phy {
+ 		u8 spe_idx;
+ 	} test;
+ #endif
++#ifdef CONFIG_MTK_VENDOR
++	spinlock_t amnt_lock;
++	struct mt7996_air_monitor_ctrl amnt_ctrl;
++#endif
+ };
+ 
+ struct mt7996_dev {
+@@ -677,6 +709,9 @@ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ #ifdef CONFIG_MTK_VENDOR
+ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+ void mt7996_vendor_register(struct mt7996_phy *phy);
++void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
++int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
++				  struct ieee80211_sta *sta);
+ #endif
+ 
+ #ifdef CONFIG_MTK_DEBUG
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 08ecc2b3..8a021324 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -15,6 +15,32 @@ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
+ 	[MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
+ };
+ 
++static const struct nla_policy
++amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
++	[MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
++};
++
++static const struct nla_policy
++amnt_set_policy[NUM_MTK_VENDOR_ATTRS_AMNT_SET] = {
++	[MTK_VENDOR_ATTR_AMNT_SET_INDEX] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_SET_MACADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
++};
++
++static const struct nla_policy
++amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
++	[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
++};
++
++struct mt7996_amnt_data {
++	u8 idx;
++	u8 addr[ETH_ALEN];
++	s8 rssi[4];
++	u32 last_seen;
++};
++
+ static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ 				 struct wireless_dev *wdev,
+ 				 const void *data,
+@@ -62,6 +88,328 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 	return len;
+ }
+ 
++void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
++{
++	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
++	struct mt7996_air_monitor_ctrl *ctrl = &phy->amnt_ctrl;
++	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
++	__le16 fc = hdr->frame_control;
++	u8 addr[ETH_ALEN];
++	int i;
++
++	if (!ieee80211_has_fromds(fc))
++		ether_addr_copy(addr, hdr->addr2);
++	else if (ieee80211_has_tods(fc))
++		ether_addr_copy(addr, hdr->addr4);
++	else
++		ether_addr_copy(addr, hdr->addr3);
++
++	spin_lock_bh(&phy->amnt_lock);
++	for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) {
++		struct mt7996_air_monitor_entry *entry;
++
++		if (ether_addr_equal(addr, ctrl->entry[i].addr)) {
++			entry = &ctrl->entry[i];
++			entry->rssi[0] = status->chain_signal[0];
++			entry->rssi[1] = status->chain_signal[1];
++			entry->rssi[2] = status->chain_signal[2];
++			entry->rssi[3] = status->chain_signal[3];
++			entry->last_seen = jiffies;
++			break;
++		}
++	}
++	spin_unlock_bh(&phy->amnt_lock);
++}
++
++static int
++mt7996_vendor_smesh_ctrl(struct mt7996_phy *phy, u8 write,
++			 u8 enable, u8 *value)
++{
++#define UNI_CMD_SMESH_PARAM  0
++	struct mt7996_dev *dev = phy->dev;
++	struct smesh_param {
++		__le16 tag;
++		__le16 length;
++
++		u8 enable;
++		bool a2;
++		bool a1;
++		bool data;
++		bool mgnt;
++		bool ctrl;
++		u8 padding[2];
++	} req = {
++		.tag = cpu_to_le16(UNI_CMD_SMESH_PARAM),
++		.length = cpu_to_le16(sizeof(req) - 4),
++
++		.enable = enable,
++		.a2 = true,
++		.a1 = true,
++		.data = true,
++		.mgnt = false,
++		.ctrl = false,
++	};
++	struct smesh_param *res;
++	struct sk_buff *skb;
++	int ret = 0;
++
++	if (!value)
++		return -EINVAL;
++
++	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD(CFG_SMESH),
++					&req, sizeof(req), !write, &skb);
++
++	if (ret || write)
++		return ret;
++
++	res = (struct smesh_param *) skb->data;
++
++	*value = res->enable;
++
++	dev_kfree_skb(skb);
++
++	return 0;
++}
++
++static int
++mt7996_vendor_amnt_muar(struct mt7996_phy *phy, u8 muar_idx, u8 *addr)
++{
++#define UNI_CMD_MUAR_ENTRY  2
++	struct mt7996_dev *dev = phy->dev;
++	struct muar_entry {
++		__le16 tag;
++		__le16 length;
++
++		bool smesh;
++		u8 hw_bss_index;
++		u8 muar_idx;
++		u8 entry_add;
++		u8 mac_addr[6];
++		u8 padding[2];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_CMD_MUAR_ENTRY),
++		.length = cpu_to_le16(sizeof(req) - 4),
++
++		.smesh = true,
++		.hw_bss_index = phy != &dev->phy,
++		.muar_idx = muar_idx,
++		.entry_add = 1,
++	};
++
++	ether_addr_copy(req.mac_addr, addr);
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req,
++				 sizeof(req), true);
++}
++
++static int
++mt7996_vendor_amnt_set_en(struct mt7996_phy *phy, u8 enable)
++{
++	u8 status;
++	int ret;
++
++	ret = mt7996_vendor_smesh_ctrl(phy, 0, enable, &status);
++	if (ret)
++		return ret;
++
++	if (status == enable)
++		return 0;
++
++	ret = mt7996_vendor_smesh_ctrl(phy, 1, enable, &status);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static int
++mt7996_vendor_amnt_set_addr(struct mt7996_phy *phy, u8 index, u8 *addr)
++{
++	struct mt7996_air_monitor_ctrl *amnt_ctrl = &phy->amnt_ctrl;
++	struct mt7996_air_monitor_group *group;
++	struct mt7996_air_monitor_entry *entry;
++	int ret, i, j;
++
++	if (index >= MT7996_AIR_MONITOR_MAX_ENTRY)
++		return -1;
++
++	spin_lock_bh(&phy->amnt_lock);
++	entry = &amnt_ctrl->entry[index];
++	if (!is_zero_ether_addr(addr)) {
++		if (entry->enable == false) {
++			for (i = 0; i < MT7996_AIR_MONITOR_MAX_GROUP; i++) {
++				group = &(amnt_ctrl->group[i]);
++				if (group->used[0] == false)
++					j = 0;
++				else if (group->used[1] == false)
++					j = 1;
++				else
++					continue;
++
++				group->enable = true;
++				group->used[j] = true;
++				entry->enable = true;
++				entry->group_idx = i;
++				entry->group_used_idx = j;
++				entry->muar_idx = 32 + 4 * i + 2 * j;
++				break;
++			}
++		}
++	} else {
++		group = &(amnt_ctrl->group[entry->group_idx]);
++
++		group->used[entry->group_used_idx] = false;
++		if (group->used[0] == false && group->used[1] == false)
++			group->enable = false;
++
++		entry->enable = false;
++	}
++	ether_addr_copy(entry->addr, addr);
++	amnt_ctrl->enable &= ~(1 << entry->group_idx);
++	amnt_ctrl->enable |= entry->enable << entry->group_idx;
++	spin_unlock_bh(&phy->amnt_lock);
++
++	ret = mt7996_vendor_amnt_muar(phy, entry->muar_idx, addr);
++	if (ret)
++		return ret;
++
++	return mt7996_vendor_amnt_set_en(phy, amnt_ctrl->enable);
++}
++
++static int
++mt7966_vendor_amnt_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
++			const void *data, int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_SET];
++	u8 index = 0;
++	u8 mac_addr[ETH_ALEN];
++	int err;
++
++	err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
++			amnt_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET])
++		return -EINVAL;
++
++	err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_SET_MAX,
++		tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET], amnt_set_policy, NULL);
++
++	if (!tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX] ||
++		!tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR])
++		return -EINVAL;
++
++	index = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX]);
++	memcpy(mac_addr, nla_data(tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR]), ETH_ALEN);
++
++	return mt7996_vendor_amnt_set_addr(phy, index, mac_addr);
++}
++
++int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
++				  struct ieee80211_sta *sta)
++{
++	u8 zero[ETH_ALEN] = {};
++	int i;
++
++	if (!phy->amnt_ctrl.enable)
++		return 0;
++
++	for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
++		if (ether_addr_equal(sta->addr, phy->amnt_ctrl.entry[i].addr))
++			return mt7996_vendor_amnt_set_addr(phy, i, zero);
++	return 0;
++}
++
++static int
++mt7996_amnt_dump(struct mt7996_phy *phy, struct sk_buff *skb,
++		 u8 amnt_idx, int *attrtype)
++{
++	struct mt7996_air_monitor_entry *entry;
++	struct mt7996_amnt_data data;
++	u32 last_seen = 0;
++
++	spin_lock_bh(&phy->amnt_lock);
++	entry = &phy->amnt_ctrl.entry[amnt_idx];
++	if (entry->enable == 0) {
++		spin_unlock_bh(&phy->amnt_lock);
++		return 0;
++	}
++
++	last_seen = jiffies_to_msecs(jiffies - entry->last_seen);
++	ether_addr_copy(data.addr, entry->addr);
++	data.rssi[0] = entry->rssi[0];
++	data.rssi[1] = entry->rssi[1];
++	data.rssi[2] = entry->rssi[2];
++	data.rssi[3] = entry->rssi[3];
++	spin_unlock_bh(&phy->amnt_lock);
++
++	data.idx = amnt_idx;
++	data.last_seen = last_seen;
++
++	nla_put(skb, (*attrtype)++, sizeof(struct mt7996_amnt_data), &data);
++
++	return 1;
++}
++
++static int
++mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++			     struct sk_buff *skb, const void *data, int data_len,
++			     unsigned long *storage)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
++	struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
++	void *a, *b;
++	int err = 0, attrtype = 0, i, len = 0;
++	u8 amnt_idx;
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
++			amnt_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
++		return -EINVAL;
++
++	err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_DUMP_MAX,
++			       tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP],
++			       amnt_dump_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX])
++		return -EINVAL;
++
++	amnt_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX]);
++
++	a = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP);
++	b = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_DUMP_RESULT);
++
++	if (amnt_idx != 0xff) {
++		len += mt7996_amnt_dump(phy, skb, amnt_idx, &attrtype);
++	} else {
++		for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
++			len += mt7996_amnt_dump(phy, skb, i, &attrtype);
++	}
++
++	nla_nest_end(skb, b);
++
++	nla_put_u8(skb, MTK_VENDOR_ATTR_AMNT_DUMP_LEN, len);
++
++	nla_nest_end(skb, a);
++
++	return len + 1;
++}
++
++
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	{
+ 		.info = {
+@@ -75,10 +423,24 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = mu_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7966_vendor_amnt_ctrl,
++		.dumpit = mt7966_vendor_amnt_ctrl_dump,
++		.policy = amnt_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
++	},
+ };
+ 
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+ {
+ 	phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
+ 	phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
++
++	spin_lock_init(&phy->amnt_lock);
+ }
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 8ac3ba8e..2078cafa 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -4,6 +4,7 @@
+ #define MTK_NL80211_VENDOR_ID	0x0ce7
+ 
+ enum mtk_nl80211_vendor_subcmds {
++	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ };
+ 
+@@ -19,4 +20,42 @@ enum mtk_vendor_attr_mu_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_mnt_ctrl {
++	MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_CTRL_SET,
++	MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
++	MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
++};
++
++enum mtk_vendor_attr_mnt_set {
++	MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_SET_INDEX,
++	MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_SET,
++	MTK_VENDOR_ATTR_AMNT_SET_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
++};
++
++enum mtk_vendor_attr_mnt_dump {
++	MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
++
++	MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
++	MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
++	MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
++	MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
++};
++
++
+ #endif
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1004-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv-and.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1004-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv-and.patch
new file mode 100644
index 0000000..50db576
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1004-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv-and.patch
@@ -0,0 +1,27 @@
+From 75b684d04d569b5e83780d2296bed12d3785836c Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 24 Mar 2023 19:18:53 +0800
+Subject: [PATCH 1004/1015] wifi: mt76: mt7996: add driver support for wpa3 ocv
+ and bp mt76
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ mt7996/init.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 1d4359f0..40d610ae 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -373,6 +373,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw)
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
+ 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+ 
++	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION);
++	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION);
+ 	if (!mdev->dev->of_node ||
+ 	    !of_property_read_bool(mdev->dev->of_node,
+ 				   "mediatek,disable-radar-background"))
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1005-wifi-mt76-mt7996-add-U-NII-4-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1005-wifi-mt76-mt7996-add-U-NII-4-support.patch
new file mode 100644
index 0000000..8eaf2d5
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1005-wifi-mt76-mt7996-add-U-NII-4-support.patch
@@ -0,0 +1,25 @@
+From 09b50cf201ede70688619366c31aa2ddd57d5cb2 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 31 Mar 2023 11:26:27 +0800
+Subject: [PATCH 1005/1015] wifi: mt76: mt7996: add U-NII-4 support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mac80211.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/mac80211.c b/mac80211.c
+index d1cdaee8..501325e9 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -76,6 +76,7 @@ static const struct ieee80211_channel mt76_channels_5ghz[] = {
+ 	CHAN5G(165, 5825),
+ 	CHAN5G(169, 5845),
+ 	CHAN5G(173, 5865),
++	CHAN5G(177, 5885),
+ };
+ 
+ static const struct ieee80211_channel mt76_channels_6ghz[] = {
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1006-wifi-mt76-testmode-add-testmode-pre-calibration-supp.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1006-wifi-mt76-testmode-add-testmode-pre-calibration-supp.patch
new file mode 100644
index 0000000..e185c11
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1006-wifi-mt76-testmode-add-testmode-pre-calibration-supp.patch
@@ -0,0 +1,898 @@
+From 47c64d6cac5ed78fab15cd4173ae8935605a3fca Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 31 Mar 2023 11:27:24 +0800
+Subject: [PATCH 1006/1015] wifi: mt76: testmode: add testmode pre-calibration
+ support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: If8a6cc02fa20e35f079c826e0571e8c04c3f9c7e
+---
+ mac80211.c        |  21 ---
+ mt76.h            |  22 +++
+ mt76_connac_mcu.h |   2 +
+ mt7996/eeprom.c   |  66 +++++++
+ mt7996/eeprom.h   |  47 +++++
+ mt7996/mcu.c      |   5 +
+ mt7996/mt7996.h   |   7 +
+ mt7996/testmode.c | 437 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/testmode.h |  20 ++-
+ testmode.c        |  12 ++
+ testmode.h        |   8 +
+ tools/fields.c    |   8 +
+ 12 files changed, 632 insertions(+), 23 deletions(-)
+
+diff --git a/mac80211.c b/mac80211.c
+index 501325e9..6430e6ee 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -7,27 +7,6 @@
+ #include <net/page_pool.h>
+ #include "mt76.h"
+ 
+-#define CHAN2G(_idx, _freq) {			\
+-	.band = NL80211_BAND_2GHZ,		\
+-	.center_freq = (_freq),			\
+-	.hw_value = (_idx),			\
+-	.max_power = 30,			\
+-}
+-
+-#define CHAN5G(_idx, _freq) {			\
+-	.band = NL80211_BAND_5GHZ,		\
+-	.center_freq = (_freq),			\
+-	.hw_value = (_idx),			\
+-	.max_power = 30,			\
+-}
+-
+-#define CHAN6G(_idx, _freq) {			\
+-	.band = NL80211_BAND_6GHZ,		\
+-	.center_freq = (_freq),			\
+-	.hw_value = (_idx),			\
+-	.max_power = 30,			\
+-}
+-
+ static const struct ieee80211_channel mt76_channels_2ghz[] = {
+ 	CHAN2G(1, 2412),
+ 	CHAN2G(2, 2417),
+diff --git a/mt76.h b/mt76.h
+index 31d5dc37..3341720c 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -18,6 +18,27 @@
+ #include "util.h"
+ #include "testmode.h"
+ 
++#define CHAN2G(_idx, _freq) {			\
++	.band = NL80211_BAND_2GHZ,		\
++	.center_freq = (_freq),			\
++	.hw_value = (_idx),			\
++	.max_power = 30,			\
++}
++
++#define CHAN5G(_idx, _freq) {			\
++	.band = NL80211_BAND_5GHZ,		\
++	.center_freq = (_freq),			\
++	.hw_value = (_idx),			\
++	.max_power = 30,			\
++}
++
++#define CHAN6G(_idx, _freq) {			\
++	.band = NL80211_BAND_6GHZ,		\
++	.center_freq = (_freq),			\
++	.hw_value = (_idx),			\
++	.max_power = 30,			\
++}
++
+ #define MT_MCU_RING_SIZE	32
+ #define MT_RX_BUF_SIZE		2048
+ #define MT_SKB_HEAD_LEN		256
+@@ -654,6 +675,7 @@ struct mt76_testmode_ops {
+ 	int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
+ 	void (*reset_rx_stats)(struct mt76_phy *phy);
+ 	void (*tx_stop)(struct mt76_phy *phy);
++	int (*dump_precal)(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type);
+ };
+ 
+ #define MT_TM_FW_RX_COUNT	BIT(0)
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index e62f17ad..262abf88 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1011,6 +1011,8 @@ enum {
+ 	MCU_UNI_EVENT_IE_COUNTDOWN = 0x09,
+ 	MCU_UNI_EVENT_RDD_REPORT = 0x11,
+ 	MCU_UNI_EVENT_THERMAL = 0x35,
++	MCU_UNI_EVENT_BF = 0x33,
++	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
+ };
+ 
+ #define MCU_UNI_CMD_EVENT			BIT(1)
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index be0a34ae..60e98463 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -12,6 +12,42 @@ static bool testmode_enable;
+ module_param(testmode_enable, bool, 0644);
+ MODULE_PARM_DESC(testmode_enable, "Enable testmode");
+ 
++const struct ieee80211_channel dpd_2g_ch_list_bw20[] = {
++	CHAN2G(3, 2422),
++	CHAN2G(7, 2442),
++	CHAN2G(11, 2462)
++};
++
++const struct ieee80211_channel dpd_5g_ch_list_bw160[] = {
++	CHAN5G(50, 5250),
++	CHAN5G(114, 5570),
++	CHAN5G(163, 5815)
++};
++
++const struct ieee80211_channel dpd_6g_ch_list_bw160[] = {
++	CHAN6G(15, 6025),
++	CHAN6G(47, 6185),
++	CHAN6G(79, 6345),
++	CHAN6G(111, 6505),
++	CHAN6G(143, 6665),
++	CHAN6G(175, 6825),
++	CHAN6G(207, 6985)
++};
++
++const struct ieee80211_channel dpd_6g_ch_list_bw320[] = {
++	CHAN6G(31, 6105),
++	CHAN6G(63, 6265),
++	CHAN6G(95, 6425),
++	CHAN6G(127, 6585),
++	CHAN6G(159, 6745),
++	CHAN6G(191, 6905)
++};
++
++const u32 dpd_2g_bw20_ch_num = ARRAY_SIZE(dpd_2g_ch_list_bw20);
++const u32 dpd_5g_bw160_ch_num = ARRAY_SIZE(dpd_5g_ch_list_bw160);
++const u32 dpd_6g_bw160_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw160);
++const u32 dpd_6g_bw320_ch_num = ARRAY_SIZE(dpd_6g_ch_list_bw320);
++
+ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ {
+ 	u8 *eeprom = dev->mt76.eeprom.data;
+@@ -34,6 +70,36 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ 		return MT7996_EEPROM_DEFAULT;
+ }
+ 
++int
++mt7996_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band band)
++{
++	/* handle different sku */
++	static const u8 band_to_idx[] = {
++		[NL80211_BAND_2GHZ] = MT_BAND0,
++		[NL80211_BAND_5GHZ] = MT_BAND1,
++		[NL80211_BAND_6GHZ] = MT_BAND2,
++	};
++	struct mt7996_phy *phy = __mt7996_phy(dev, band_to_idx[band]);
++	struct mt76_phy *mphy;
++	int dpd_size;
++
++	if (!phy)
++		return 0;
++
++	mphy = phy->mt76;
++
++	if (band == NL80211_BAND_2GHZ)
++		dpd_size = dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
++	else if (band == NL80211_BAND_5GHZ)
++		dpd_size = mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++			   dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++	else
++		dpd_size = mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++			   (dpd_6g_bw160_ch_num + dpd_6g_bw320_ch_num) * DPD_PER_CH_GT_BW20_SIZE;
++
++	return dpd_size;
++}
++
+ static int
+ mt7996_eeprom_load_default(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 343e65e1..7ff290f4 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
+ 	MT_EE_MAC_ADDR =	0x004,
+ 	MT_EE_MAC_ADDR2 =	0x00a,
+ 	MT_EE_WIFI_CONF =	0x190,
++	MT_EE_DO_PRE_CAL =	0x1a5,
+ 	MT_EE_TESTMODE_EN =	0x1af,
+ 	MT_EE_MAC_ADDR3 =	0x2c0,
+ 	MT_EE_RATE_DELTA_2G =	0x1400,
+@@ -31,6 +32,52 @@ enum mt7996_eeprom_field {
+ #define MT_EE_WIFI_CONF1_BAND_SEL		GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF2_BAND_SEL		GENMASK(2, 0)
+ 
++#define MT_EE_WIFI_CAL_GROUP_2G			BIT(0)
++#define MT_EE_WIFI_CAL_GROUP_5G			BIT(1)
++#define MT_EE_WIFI_CAL_GROUP_6G			BIT(2)
++#define MT_EE_WIFI_CAL_GROUP			GENMASK(2, 0)
++#define MT_EE_WIFI_CAL_DPD_2G			BIT(3)
++#define MT_EE_WIFI_CAL_DPD_5G			BIT(4)
++#define MT_EE_WIFI_CAL_DPD_6G			BIT(5)
++#define MT_EE_WIFI_CAL_DPD			GENMASK(5, 3)
++
++#define MT_EE_CAL_UNIT				1024
++#define MT_EE_CAL_GROUP_SIZE_2G			(4 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_GROUP_SIZE_5G			(45 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_GROUP_SIZE_6G			(125 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_ADCDCOC_SIZE_2G		(4 * 4)
++#define MT_EE_CAL_ADCDCOC_SIZE_5G		(4 * 4)
++#define MT_EE_CAL_ADCDCOC_SIZE_6G		(4 * 5)
++#define MT_EE_CAL_GROUP_SIZE			(MT_EE_CAL_GROUP_SIZE_2G + \
++						 MT_EE_CAL_GROUP_SIZE_5G + \
++						 MT_EE_CAL_GROUP_SIZE_6G + \
++						 MT_EE_CAL_ADCDCOC_SIZE_2G + \
++						 MT_EE_CAL_ADCDCOC_SIZE_5G + \
++						 MT_EE_CAL_ADCDCOC_SIZE_6G)
++
++#define DPD_PER_CH_LEGACY_SIZE			(4 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_MEM_SIZE			(13 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_OTFG0_SIZE			(2 * MT_EE_CAL_UNIT)
++#define DPD_PER_CH_BW20_SIZE			(DPD_PER_CH_LEGACY_SIZE + DPD_PER_CH_OTFG0_SIZE)
++#define DPD_PER_CH_GT_BW20_SIZE			(DPD_PER_CH_MEM_SIZE + DPD_PER_CH_OTFG0_SIZE)
++#define MT_EE_CAL_DPD_SIZE			(780 * MT_EE_CAL_UNIT)
++
++extern const struct ieee80211_channel dpd_2g_ch_list_bw20[];
++extern const u32 dpd_2g_bw20_ch_num;
++extern const struct ieee80211_channel dpd_5g_ch_list_bw160[];
++extern const u32 dpd_5g_bw160_ch_num;
++extern const struct ieee80211_channel dpd_6g_ch_list_bw160[];
++extern const u32 dpd_6g_bw160_ch_num;
++extern const struct ieee80211_channel dpd_6g_ch_list_bw320[];
++extern const u32 dpd_6g_bw320_ch_num;
++
++#define RF_DPD_FLAT_CAL				BIT(28)
++#define RF_PRE_CAL				BIT(29)
++#define RF_DPD_FLAT_5G_CAL			GENMASK(29, 28)
++#define RF_DPD_FLAT_5G_MEM_CAL			(BIT(30) | BIT(28))
++#define RF_DPD_FLAT_6G_CAL			GENMASK(30, 28)
++#define RF_DPD_FLAT_6G_MEM_CAL			(BIT(31) | BIT(28))
++
+ #define MT_EE_WIFI_CONF1_TX_PATH_BAND0		GENMASK(5, 3)
+ #define MT_EE_WIFI_CONF2_TX_PATH_BAND1		GENMASK(2, 0)
+ #define MT_EE_WIFI_CONF2_TX_PATH_BAND2		GENMASK(5, 3)
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 91f3103a..1fb7bae1 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -523,6 +523,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_THERMAL:
+ 		mt7996_mcu_rx_thermal_notify(dev, skb);
+ 		break;
++#ifdef CONFIG_NL80211_TESTMODE
++	case MCU_UNI_EVENT_TESTMODE_CTRL:
++		mt7996_tm_rf_test_event(dev, skb);
++		break;
++#endif
+ 	default:
+ 		break;
+ 	}
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index dc44edc1..9ab86eaf 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -390,6 +390,9 @@ struct mt7996_dev {
+ 	struct dentry *debugfs_dir;
+ 	struct rchan *relay_fwlog;
+ 
++	void *cal;
++	u32 cur_prek_offset;
++
+ 	struct {
+ 		u8 table_mask;
+ 		u8 n_agrt;
+@@ -522,6 +525,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+ 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_get_dpd_per_band_size(struct mt7996_dev *dev, enum nl80211_band 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);
+@@ -602,6 +606,9 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
+ int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
++#ifdef CONFIG_NL80211_TESTMODE
++void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb);
++#endif
+ 
+ static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
+ {
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 43eca4ef..7d36902e 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -7,6 +7,8 @@
+ #include "mac.h"
+ #include "mcu.h"
+ #include "testmode.h"
++#include "eeprom.h"
++#include "mtk_mcu.h"
+ 
+ enum {
+ 	TM_CHANGED_TXPOWER,
+@@ -397,6 +399,436 @@ mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
+ 	}
+ }
+ 
++static int
++mt7996_tm_group_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
++{
++	u8 *eeprom;
++	u32 i, group_size, dpd_size, size, offs, *pre_cal;
++	int ret = 0;
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt7996_tm_req req = {
++		.rf_test = {
++			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++			.len = cpu_to_le16(sizeof(req.rf_test)),
++			.action = RF_ACTION_IN_RF_TEST,
++			.icap_len = RF_TEST_ICAP_LEN,
++			.op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
++			.op.rf.param.cal_param.func_data = cpu_to_le32(RF_PRE_CAL),
++			.op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
++		},
++	};
++
++	if (!dev->flash_mode) {
++		dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
++		return -EOPNOTSUPP;
++	}
++
++	eeprom = mdev->eeprom.data;
++	dev->cur_prek_offset = 0;
++	group_size = MT_EE_CAL_GROUP_SIZE;
++	dpd_size = MT_EE_CAL_DPD_SIZE;
++	size = group_size + dpd_size;
++	offs = MT_EE_DO_PRE_CAL;
++
++	switch (state) {
++	case MT76_TM_STATE_GROUP_PREK:
++		if (!dev->cal) {
++			dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++			if (!dev->cal)
++				return -ENOMEM;
++		}
++
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
++					sizeof(req), false);
++		wait_event_timeout(mdev->mcu.wait, dev->cur_prek_offset == group_size,
++				   30 * HZ);
++
++		if (ret) {
++			dev_err(dev->mt76.dev, "Group Pre-cal: mcu send msg failed!\n");
++			return ret;
++		}
++
++		if (!ret)
++			eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
++		break;
++	case MT76_TM_STATE_GROUP_PREK_DUMP:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal) {
++			dev_info(dev->mt76.dev, "Not group pre-cal yet!\n");
++			return ret;
++		}
++		dev_info(dev->mt76.dev, "Group Pre-Cal:\n");
++		for (i = 0; i < (group_size / sizeof(u32)); i += 4) {
++			dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
++				 i * sizeof(u32), pre_cal[i], pre_cal[i + 1],
++				 pre_cal[i + 2], pre_cal[i + 3]);
++		}
++		break;
++	case MT76_TM_STATE_GROUP_PREK_CLEAN:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal)
++			return ret;
++		memset(pre_cal, 0, group_size);
++		eeprom[offs] &= ~MT_EE_WIFI_CAL_GROUP;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return ret;
++}
++
++static int
++mt7996_tm_dpd_prek_send_req(struct mt7996_phy *phy, struct mt7996_tm_req *req,
++			    const struct ieee80211_channel *chan_list, u32 channel_size,
++			    enum nl80211_chan_width width, u32 func_data)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_phy *mphy = phy->mt76;
++	struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
++	struct ieee80211_channel chan_backup;
++	int i, ret;
++
++	if (!chan_list)
++		return -EOPNOTSUPP;
++
++	req->rf_test.op.rf.param.cal_param.func_data = cpu_to_le32(func_data);
++
++	memcpy(&chan_backup, chandef->chan, sizeof(struct ieee80211_channel));
++	memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
++
++	for (i = 0; i < channel_size; i++) {
++		memcpy(chandef->chan, &chan_list[i], sizeof(struct ieee80211_channel));
++		chandef->width = width;
++
++		/* set channel switch reason */
++		mphy->hw->conf.flags |= IEEE80211_CONF_OFFCHANNEL;
++		mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), req,
++					sizeof(*req), false);
++		if (ret) {
++			dev_err(dev->mt76.dev, "DPD Pre-cal: mcu send msg failed!\n");
++			goto out;
++		}
++	}
++
++out:
++	mphy->hw->conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
++	memcpy(chandef, &chandef_backup, sizeof(struct cfg80211_chan_def));
++	memcpy(chandef->chan, &chan_backup, sizeof(struct ieee80211_channel));
++	mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
++
++	return ret;
++}
++
++static int
++mt7996_tm_dpd_prek(struct mt7996_phy *phy, enum mt76_testmode_state state)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt76_phy *mphy = phy->mt76;
++	struct mt7996_tm_req req = {
++		.rf_test = {
++			.tag = cpu_to_le16(UNI_RF_TEST_CTRL),
++			.len = cpu_to_le16(sizeof(req.rf_test)),
++			.action = RF_ACTION_IN_RF_TEST,
++			.icap_len = RF_TEST_ICAP_LEN,
++			.op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
++			.op.rf.param.cal_param.band_idx = phy->mt76->band_idx,
++		},
++	};
++	u32 i, j, group_size, dpd_size, size, offs, *pre_cal;
++	u32 wait_on_prek_offset = 0;
++	u8 do_precal, *eeprom;
++	int ret = 0;
++
++	if (!dev->flash_mode) {
++		dev_err(dev->mt76.dev, "Currently not in FLASH or BIN FILE mode, return!\n");
++		return -EOPNOTSUPP;
++	}
++
++	eeprom = mdev->eeprom.data;
++	dev->cur_prek_offset = 0;
++	group_size = MT_EE_CAL_GROUP_SIZE;
++	dpd_size = MT_EE_CAL_DPD_SIZE;
++	size = group_size + dpd_size;
++	offs = MT_EE_DO_PRE_CAL;
++
++	if (!dev->cal && state < MT76_TM_STATE_DPD_DUMP) {
++		dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++		if (!dev->cal)
++			return -ENOMEM;
++	}
++
++	switch (state) {
++	case MT76_TM_STATE_DPD_2G:
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_2g_ch_list_bw20,
++						  dpd_2g_bw20_ch_num,
++						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_CAL);
++		wait_on_prek_offset += dpd_2g_bw20_ch_num * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		do_precal = MT_EE_WIFI_CAL_DPD_2G;
++		break;
++	case MT76_TM_STATE_DPD_5G:
++		/* 5g channel bw20 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_5g.sband.channels,
++						  mphy->sband_5g.sband.n_channels,
++						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_5G_CAL);
++		if (ret)
++			return ret;
++		wait_on_prek_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		/* 5g channel bw160 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_5g_ch_list_bw160,
++						  dpd_5g_bw160_ch_num,
++						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_5G_MEM_CAL);
++		wait_on_prek_offset += dpd_5g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		do_precal = MT_EE_WIFI_CAL_DPD_5G;
++		break;
++	case MT76_TM_STATE_DPD_6G:
++		/* 6g channel bw20 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, mphy->sband_6g.sband.channels,
++						  mphy->sband_6g.sband.n_channels,
++						  NL80211_CHAN_WIDTH_20, RF_DPD_FLAT_6G_CAL);
++		if (ret)
++			return ret;
++		wait_on_prek_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		/* 6g channel bw160 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw160,
++						  dpd_6g_bw160_ch_num,
++						  NL80211_CHAN_WIDTH_160, RF_DPD_FLAT_6G_MEM_CAL);
++		if (ret)
++			return ret;
++		wait_on_prek_offset += dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		/* 6g channel bw320 calibration */
++		ret = mt7996_tm_dpd_prek_send_req(phy, &req, dpd_6g_ch_list_bw320,
++						  dpd_6g_bw320_ch_num,
++						  NL80211_CHAN_WIDTH_320, RF_DPD_FLAT_6G_MEM_CAL);
++		wait_on_prek_offset += dpd_6g_bw320_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++		wait_event_timeout(mdev->mcu.wait,
++				   dev->cur_prek_offset == wait_on_prek_offset, 30 * HZ);
++
++		do_precal = MT_EE_WIFI_CAL_DPD_6G;
++		break;
++	case MT76_TM_STATE_DPD_DUMP:
++		if (!dev->cal) {
++			dev_info(dev->mt76.dev, "Not DPD pre-cal yet!\n");
++			return ret;
++		}
++		pre_cal = (u32 *)dev->cal;
++		dev_info(dev->mt76.dev, "DPD Pre-Cal:\n");
++		for (i = 0; i < dpd_size / sizeof(u32); i += 4) {
++			j = i + (group_size / sizeof(u32));
++			dev_info(dev->mt76.dev, "[0x%08lx] 0x%8x 0x%8x 0x%8x 0x%8x\n",
++				 j * sizeof(u32), pre_cal[j], pre_cal[j + 1],
++				 pre_cal[j + 2], pre_cal[j + 3]);
++		}
++		return 0;
++	case MT76_TM_STATE_DPD_CLEAN:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal)
++			return ret;
++		memset(pre_cal + (group_size / sizeof(u32)), 0, dpd_size);
++		do_precal = MT_EE_WIFI_CAL_DPD;
++		eeprom[offs] &= ~do_precal;
++		return 0;
++	default:
++		return -EINVAL;
++	}
++
++	if (!ret)
++		eeprom[offs] |= do_precal;
++
++	return ret;
++}
++
++static int
++mt7996_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type)
++{
++#define DPD_PER_CHAN_SIZE_MASK		GENMASK(31, 30)
++#define DPD_2G_RATIO_MASK		GENMASK(29, 20)
++#define DPD_5G_RATIO_MASK		GENMASK(19, 10)
++#define DPD_6G_RATIO_MASK		GENMASK(9, 0)
++	struct mt7996_phy *phy = mphy->priv;
++	struct mt7996_dev *dev = phy->dev;
++	u32 i, group_size, dpd_size, total_size, size, dpd_info = 0;
++	u32 dpd_size_2g, dpd_size_5g, dpd_size_6g;
++	u32 base, offs, transmit_size = 1000;
++	u8 *pre_cal, *eeprom;
++	void *precal;
++	enum prek_ops {
++		PREK_GET_INFO,
++		PREK_SYNC_ALL,
++		PREK_SYNC_GROUP,
++		PREK_SYNC_DPD_2G,
++		PREK_SYNC_DPD_5G,
++		PREK_SYNC_DPD_6G,
++		PREK_CLEAN_GROUP,
++		PREK_CLEAN_DPD,
++	};
++
++	if (!dev->cal) {
++		dev_info(dev->mt76.dev, "Not pre-cal yet!\n");
++		return 0;
++	}
++
++	group_size = MT_EE_CAL_GROUP_SIZE;
++	dpd_size = MT_EE_CAL_DPD_SIZE;
++	total_size = group_size + dpd_size;
++	pre_cal = dev->cal;
++	eeprom = dev->mt76.eeprom.data;
++	offs = MT_EE_DO_PRE_CAL;
++
++	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++
++	switch (type) {
++	case PREK_SYNC_ALL:
++		base = 0;
++		size = total_size;
++		break;
++	case PREK_SYNC_GROUP:
++		base = 0;
++		size = group_size;
++		break;
++	case PREK_SYNC_DPD_2G:
++		base = group_size;
++		size = dpd_size_2g;
++		break;
++	case PREK_SYNC_DPD_5G:
++		base = group_size + dpd_size_2g;
++		size = dpd_size_5g;
++		break;
++	case PREK_SYNC_DPD_6G:
++		base = group_size + dpd_size_2g + dpd_size_5g;
++		size = dpd_size_6g;
++		break;
++	case PREK_GET_INFO:
++		break;
++	default:
++		return 0;
++	}
++
++	if (!flag) {
++		if (eeprom[offs] & MT_EE_WIFI_CAL_DPD) {
++			dpd_info |= u32_encode_bits(1, DPD_PER_CHAN_SIZE_MASK) |
++				    u32_encode_bits(dpd_size_2g / MT_EE_CAL_UNIT,
++						    DPD_2G_RATIO_MASK) |
++				    u32_encode_bits(dpd_size_5g / MT_EE_CAL_UNIT,
++						    DPD_5G_RATIO_MASK) |
++				    u32_encode_bits(dpd_size_6g / MT_EE_CAL_UNIT,
++						    DPD_6G_RATIO_MASK);
++		}
++		dev->cur_prek_offset = 0;
++		precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL_INFO);
++		if (!precal)
++			return -ENOMEM;
++		nla_put_u32(msg, 0, group_size);
++		nla_put_u32(msg, 1, dpd_size);
++		nla_put_u32(msg, 2, dpd_info);
++		nla_put_u32(msg, 3, transmit_size);
++		nla_put_u32(msg, 4, eeprom[offs]);
++		nla_nest_end(msg, precal);
++	} else {
++		precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL);
++		if (!precal)
++			return -ENOMEM;
++
++		transmit_size = (dev->cur_prek_offset + transmit_size < size) ?
++				transmit_size : (size - dev->cur_prek_offset);
++		for (i = 0; i < transmit_size; i++) {
++			if (nla_put_u8(msg, i, pre_cal[base + dev->cur_prek_offset + i]))
++				return -ENOMEM;
++		}
++		dev->cur_prek_offset += transmit_size;
++
++		nla_nest_end(msg, precal);
++	}
++
++	return 0;
++}
++
++static void
++mt7996_tm_re_cal_event(struct mt7996_dev *dev, struct mt7996_tm_rf_test_result *result,
++		       struct mt7996_tm_rf_test_data *data)
++{
++	u32 base, dpd_size_2g, dpd_size_5g, dpd_size_6g, cal_idx, cal_type, len = 0;
++	u8 *pre_cal;
++
++	pre_cal = dev->cal;
++	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++	dpd_size_6g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_6GHZ);
++
++	cal_idx = le32_to_cpu(data->cal_idx);
++	cal_type = le32_to_cpu(data->cal_type);
++	len = le32_to_cpu(result->payload_length);
++	len = len - sizeof(struct mt7996_tm_rf_test_data);
++
++	switch (cal_type) {
++	case RF_PRE_CAL:
++		base = 0;
++		break;
++	case RF_DPD_FLAT_CAL:
++		base = MT_EE_CAL_GROUP_SIZE;
++		break;
++	case RF_DPD_FLAT_5G_CAL:
++	case RF_DPD_FLAT_5G_MEM_CAL:
++		base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g;
++		break;
++	case RF_DPD_FLAT_6G_CAL:
++	case RF_DPD_FLAT_6G_MEM_CAL:
++		base = MT_EE_CAL_GROUP_SIZE + dpd_size_2g + dpd_size_5g;
++		break;
++	default:
++		dev_info(dev->mt76.dev, "Unknown calibration type!\n");
++		return;
++	}
++	pre_cal += (base + dev->cur_prek_offset);
++
++	memcpy(pre_cal, data->cal_data, len);
++	dev->cur_prek_offset += len;
++}
++
++void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_tm_event *event;
++	struct mt7996_tm_rf_test_result *result;
++	struct mt7996_tm_rf_test_data *data;
++	static u32 event_type;
++
++	skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
++	event = (struct mt7996_tm_event *)skb->data;
++	result = (struct mt7996_tm_rf_test_result *)&event->result;
++	data = (struct mt7996_tm_rf_test_data *)result->data;
++
++	event_type = le32_to_cpu(result->func_idx);
++
++	switch (event_type) {
++	case RF_TEST_RE_CAL:
++		mt7996_tm_re_cal_event(dev, result, data);
++		break;
++	default:
++		break;
++	}
++}
++
+ static void
+ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ {
+@@ -447,6 +879,10 @@ mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+ 	else if (prev_state == MT76_TM_STATE_OFF ||
+ 		 state == MT76_TM_STATE_OFF)
+ 		mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
++	else if (state >= MT76_TM_STATE_GROUP_PREK && state <= MT76_TM_STATE_GROUP_PREK_CLEAN)
++		return mt7996_tm_group_prek(phy, state);
++	else if (state >= MT76_TM_STATE_DPD_2G && state <= MT76_TM_STATE_DPD_CLEAN)
++		return mt7996_tm_dpd_prek(phy, state);
+ 
+ 	if ((state == MT76_TM_STATE_IDLE &&
+ 	     prev_state == MT76_TM_STATE_OFF) ||
+@@ -655,4 +1091,5 @@ const struct mt76_testmode_ops mt7996_testmode_ops = {
+ 	.dump_stats = mt7996_tm_dump_stats,
+ 	.reset_rx_stats = mt7996_tm_reset_trx_stats,
+ 	.tx_stop = mt7996_tm_tx_stop,
++	.dump_precal = mt7996_tm_dump_precal,
+ };
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index f00e51f4..778c9bc6 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -34,6 +34,12 @@ enum bw_mapping_method {
+ 	NUM_BW_MAP,
+ };
+ 
++struct tm_cal_param {
++	__le32 func_data;
++	u8 band_idx;
++	u8 rsv[3];
++};
++
+ struct mt7996_tm_rf_test {
+ 	__le16 tag;
+ 	__le16 len;
+@@ -50,7 +56,7 @@ struct mt7996_tm_rf_test {
+ 			union {
+ 				__le32 func_data;
+ 				__le32 cal_dump;
+-
++				struct tm_cal_param cal_param;
+ 				u8 _pad[80];
+ 			} param;
+ 		} rf;
+@@ -63,10 +69,16 @@ struct mt7996_tm_req {
+ 	struct mt7996_tm_rf_test rf_test;
+ } __packed;
+ 
++struct mt7996_tm_rf_test_data {
++	__le32 cal_idx;
++	__le32 cal_type;
++	u8 cal_data[0];
++} __packed;
++
+ struct mt7996_tm_rf_test_result {
+ 	__le32 func_idx;
+ 	__le32 payload_length;
+-	u8 event[0];
++	u8 data[0];
+ };
+ 
+ struct mt7996_tm_event {
+@@ -77,6 +89,8 @@ struct mt7996_tm_event {
+ 	struct mt7996_tm_rf_test_result result;
+ } __packed;
+ 
++#define RF_TEST_RE_CAL		0x01
++
+ enum {
+ 	RF_ACTION_SWITCH_TO_RF_TEST,
+ 	RF_ACTION_IN_RF_TEST,
+@@ -84,6 +98,8 @@ enum {
+ 	RF_ACTION_GET,
+ };
+ 
++#define RF_TEST_ICAP_LEN	120
++
+ enum {
+ 	RF_OPER_NORMAL,
+ 	RF_OPER_RF_TEST,
+diff --git a/testmode.c b/testmode.c
+index fc68c2af..74bb26fa 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -626,6 +626,18 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 
+ 	mutex_lock(&dev->mutex);
+ 
++	if (tb[MT76_TM_ATTR_PRECAL] || tb[MT76_TM_ATTR_PRECAL_INFO]) {
++		int flag, type;
++
++		err = -EINVAL;
++		flag = tb[MT76_TM_ATTR_PRECAL] ? 1 : 0;
++		type = flag ? nla_get_u8(tb[MT76_TM_ATTR_PRECAL_INFO]) : 0;
++		if (dev->test_ops->dump_precal)
++			err = dev->test_ops->dump_precal(phy, msg, flag, type);
++
++		goto out;
++	}
++
+ 	if (tb[MT76_TM_ATTR_STATS]) {
+ 		err = -EINVAL;
+ 
+diff --git a/testmode.h b/testmode.h
+index 8d0b9702..0c3b1393 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -215,6 +215,14 @@ enum mt76_testmode_state {
+ 	MT76_TM_STATE_TX_FRAMES,
+ 	MT76_TM_STATE_RX_FRAMES,
+ 	MT76_TM_STATE_TX_CONT,
++	MT76_TM_STATE_GROUP_PREK,
++	MT76_TM_STATE_GROUP_PREK_DUMP,
++	MT76_TM_STATE_GROUP_PREK_CLEAN,
++	MT76_TM_STATE_DPD_2G,
++	MT76_TM_STATE_DPD_5G,
++	MT76_TM_STATE_DPD_6G,
++	MT76_TM_STATE_DPD_DUMP,
++	MT76_TM_STATE_DPD_CLEAN,
+ 	MT76_TM_STATE_ON,
+ 
+ 	/* keep last */
+diff --git a/tools/fields.c b/tools/fields.c
+index e5cf7c53..b22b3fc8 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -11,6 +11,14 @@ static const char * const testmode_state[] = {
+ 	[MT76_TM_STATE_TX_FRAMES] = "tx_frames",
+ 	[MT76_TM_STATE_RX_FRAMES] = "rx_frames",
+ 	[MT76_TM_STATE_TX_CONT] = "tx_cont",
++	[MT76_TM_STATE_GROUP_PREK] = "group_prek",
++	[MT76_TM_STATE_GROUP_PREK_DUMP] = "group_prek_dump",
++	[MT76_TM_STATE_GROUP_PREK_CLEAN] = "group_prek_clean",
++	[MT76_TM_STATE_DPD_2G] = "dpd_2g",
++	[MT76_TM_STATE_DPD_5G] = "dpd_5g",
++	[MT76_TM_STATE_DPD_6G] = "dpd_6g",
++	[MT76_TM_STATE_DPD_DUMP] = "dpd_dump",
++	[MT76_TM_STATE_DPD_CLEAN] = "dpd_clean",
+ };
+ 
+ static const char * const testmode_tx_mode[] = {
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1007-wifi-mt76-mt7996-add-binfile-mode-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1007-wifi-mt76-mt7996-add-binfile-mode-support.patch
new file mode 100644
index 0000000..549344f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1007-wifi-mt76-mt7996-add-binfile-mode-support.patch
@@ -0,0 +1,260 @@
+From 2cf8a9517919b39318eb1e878ca69418a499126a Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Fri, 31 Mar 2023 11:36:34 +0800
+Subject: [PATCH 1007/1015] wifi: mt76: mt7996: add binfile mode support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ eeprom.c             | 20 ++++++++++++++++++++
+ mt76.h               |  3 +++
+ mt7996/eeprom.c      | 42 +++++++++++++++++++++++++++++++++++++++---
+ mt7996/eeprom.h      |  7 +++++++
+ mt7996/mt7996.h      |  3 +++
+ mt7996/mtk_debugfs.c | 40 ++++++++++++++++++++++++++++++++++++++++
+ 6 files changed, 112 insertions(+), 3 deletions(-)
+
+diff --git a/eeprom.c b/eeprom.c
+index aa889258..412740f0 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -104,6 +104,26 @@ out_put_node:
+ }
+ EXPORT_SYMBOL_GPL(mt76_get_of_eeprom);
+ 
++bool mt76_check_bin_file_mode(struct mt76_dev *dev)
++{
++	struct device_node *np = dev->dev->of_node;
++	const char *bin_file_name = NULL;
++
++	if (!np)
++		return false;
++
++	of_property_read_string(np, "bin_file_name", &bin_file_name);
++
++	dev->bin_file_name = bin_file_name;
++	if (dev->bin_file_name)
++		dev_info(dev->dev, "Using bin file %s\n", dev->bin_file_name);
++
++	of_node_put(np);
++
++	return dev->bin_file_name ? true : false;
++}
++EXPORT_SYMBOL_GPL(mt76_check_bin_file_mode);
++
+ void
+ mt76_eeprom_override(struct mt76_phy *phy)
+ {
+diff --git a/mt76.h b/mt76.h
+index 3341720c..7d6e3241 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -894,6 +894,8 @@ struct mt76_dev {
+ 		struct mt76_usb usb;
+ 		struct mt76_sdio sdio;
+ 	};
++
++	const char *bin_file_name;
+ };
+ 
+ struct mt76_power_limits {
+@@ -1030,6 +1032,7 @@ void mt76_seq_puts_array(struct seq_file *file, const char *str,
+ int mt76_eeprom_init(struct mt76_dev *dev, int len);
+ void mt76_eeprom_override(struct mt76_phy *phy);
+ int mt76_get_of_eeprom(struct mt76_dev *dev, void *data, int offset, int len);
++bool mt76_check_bin_file_mode(struct mt76_dev *dev);
+ 
+ struct mt76_queue *
+ mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 60e98463..85d9e057 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -61,8 +61,11 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
+ 	}
+ }
+ 
+-static char *mt7996_eeprom_name(struct mt7996_dev *dev)
++const char *mt7996_eeprom_name(struct mt7996_dev *dev)
+ {
++	if (dev->bin_file_mode)
++		return dev->mt76.bin_file_name;
++
+ 	/* reserve for future variants */
+ 	if (dev->testmode_enable)
+ 		return MT7996_EEPROM_DEFAULT_TM;
+@@ -112,7 +115,10 @@ mt7996_eeprom_load_default(struct mt7996_dev *dev)
+ 		return ret;
+ 
+ 	if (!fw || !fw->data) {
+-		dev_err(dev->mt76.dev, "Invalid default bin\n");
++		if (dev->bin_file_mode)
++			dev_err(dev->mt76.dev, "Invalid bin (bin file mode)\n");
++		else
++			dev_err(dev->mt76.dev, "Invalid default bin\n");
+ 		ret = -EINVAL;
+ 		goto out;
+ 	}
+@@ -126,18 +132,45 @@ out:
+ 	return ret;
+ }
+ 
++static int mt7996_eeprom_load_flash(struct mt7996_dev *dev)
++{
++	int ret = 1;
++
++	/* return > 0 for load success, return 0 for load failed, return < 0 for non memory */
++	dev->bin_file_mode = mt76_check_bin_file_mode(&dev->mt76);
++	if (dev->bin_file_mode) {
++		dev->mt76.eeprom.size = MT7996_EEPROM_SIZE;
++		dev->mt76.eeprom.data = devm_kzalloc(dev->mt76.dev, dev->mt76.eeprom.size,
++						     GFP_KERNEL);
++		if (!dev->mt76.eeprom.data)
++			return -ENOMEM;
++
++		if (mt7996_eeprom_load_default(dev))
++			return 0;
++
++		if (mt7996_check_eeprom(dev))
++			return 0;
++	} else {
++		ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
++	}
++
++	return ret;
++}
++
+ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
+ {
+ 	u8 *eeprom;
+ 	int ret;
+ 
+ 	/* load eeprom in flash or bin file mode to determine fw mode */
+-	ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
++	ret = mt7996_eeprom_load_flash(dev);
++
+ 	if (ret < 0)
+ 		return ret;
+ 
+ 	if (ret) {
+ 		dev->flash_mode = true;
++		dev->eeprom_mode = dev->bin_file_mode ? BIN_FILE_MODE : FLASH_MODE;
+ 		eeprom = dev->mt76.eeprom.data;
+ 		/* testmode enable priority: eeprom field > module parameter */
+ 		dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
+@@ -171,6 +204,7 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
+ 			if (ret < 0)
+ 				return ret;
+ 		}
++		dev->eeprom_mode = EFUSE_MODE;
+ 	}
+ 
+ 	return mt7996_check_eeprom(dev);
+@@ -306,6 +340,8 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ 			return ret;
+ 
+ 		dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
++		dev->bin_file_mode = false;
++		dev->eeprom_mode = DEFAULT_BIN_MODE;
+ 		ret = mt7996_eeprom_load_default(dev);
+ 		if (ret)
+ 			return ret;
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 7ff290f4..20dd8771 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -99,6 +99,13 @@ enum mt7996_eeprom_band {
+ 	MT_EE_BAND_SEL_6GHZ,
+ };
+ 
++enum mt7915_eeprom_mode {
++	DEFAULT_BIN_MODE,
++	EFUSE_MODE,
++	FLASH_MODE,
++	BIN_FILE_MODE,
++};
++
+ static inline int
+ mt7996_get_channel_group_5g(int channel)
+ {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 9ab86eaf..6ef6bad9 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -380,6 +380,8 @@ struct mt7996_dev {
+ 	bool has_eht:1;
+ 
+ 	bool testmode_enable;
++	bool bin_file_mode;
++	u8 eeprom_mode;
+ 
+ 	bool ibf;
+ 	u8 fw_debug_wm;
+@@ -519,6 +521,7 @@ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance);
+ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
+ int mt7996_register_device(struct mt7996_dev *dev);
+ void mt7996_unregister_device(struct mt7996_dev *dev);
++const char *mt7996_eeprom_name(struct mt7996_dev *dev);
+ int mt7996_eeprom_init(struct mt7996_dev *dev);
+ int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
+ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
+diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
+index 2aee3ab0..2ab2a8a8 100644
+--- a/mt7996/mtk_debugfs.c
++++ b/mt7996/mtk_debugfs.c
+@@ -2525,6 +2525,44 @@ static const struct file_operations mt7996_txpower_sku_fops = {
+ 	.llseek = default_llseek,
+ };
+ 
++static int mt7996_show_eeprom_mode(struct seq_file *s, void *data)
++{
++	struct mt7996_dev *dev = dev_get_drvdata(s->private);
++	struct mt76_dev *mdev = &dev->mt76;
++#ifdef CONFIG_NL80211_TESTMODE
++	const char *mtd_name = mdev->test_mtd.name;
++	u32 mtd_offset = mdev->test_mtd.offset;
++#else
++	const char *mtd_name = NULL;
++	u32 mtd_offset;
++#endif
++
++	seq_printf(s, "Current eeprom mode:\n");
++
++	switch (dev->eeprom_mode) {
++	case DEFAULT_BIN_MODE:
++		seq_printf(s, "   default bin mode\n   filename = %s\n", mt7996_eeprom_name(dev));
++		break;
++	case EFUSE_MODE:
++		seq_printf(s, "   efuse mode\n");
++		break;
++	case FLASH_MODE:
++		if (mtd_name)
++			seq_printf(s, "   flash mode\n   mtd name = %s\n   flash offset = 0x%x\n",
++				   mtd_name, mtd_offset);
++		else
++			seq_printf(s, "   flash mode\n");
++		break;
++	case BIN_FILE_MODE:
++		seq_printf(s, "   bin file mode\n   filename = %s\n", mt7996_eeprom_name(dev));
++		break;
++	default:
++		break;
++	}
++
++	return 0;
++}
++
+ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -2570,6 +2608,8 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir)
+ 	debugfs_create_file("txpower_level", 0600, dir, phy, &fops_txpower_level);
+ 	debugfs_create_file("txpower_info", 0600, dir, phy, &mt7996_txpower_info_fops);
+ 	debugfs_create_file("txpower_sku", 0600, dir, phy, &mt7996_txpower_sku_fops);
++	debugfs_create_devm_seqfile(dev->mt76.dev, "eeprom_mode", dir,
++				    mt7996_show_eeprom_mode);
+ 
+ 	debugfs_create_devm_seqfile(dev->mt76.dev, "wtbl_info", dir,
+ 				    mt7996_wtbl_read);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1008-wifi-mt76-mt7996-add-normal-mode-pre-calibration-sup.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1008-wifi-mt76-mt7996-add-normal-mode-pre-calibration-sup.patch
new file mode 100644
index 0000000..6b18fb0
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1008-wifi-mt76-mt7996-add-normal-mode-pre-calibration-sup.patch
@@ -0,0 +1,310 @@
+From f3ff5d5029a0a589e797dd2536070d3a3e4e30a6 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 1 Mar 2023 12:12:51 +0800
+Subject: [PATCH 1008/1015] wifi: mt76: mt7996: add normal mode pre-calibration
+ support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76_connac_mcu.h |   1 +
+ mt7996/eeprom.c   |  23 +++++++
+ mt7996/eeprom.h   |   2 +
+ mt7996/init.c     |   6 ++
+ mt7996/main.c     |   6 ++
+ mt7996/mcu.c      | 166 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mt7996.h   |   2 +
+ 7 files changed, 206 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 262abf88..42246fb9 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1229,6 +1229,7 @@ enum {
+ 	MCU_UNI_CMD_VOW = 0x37,
+ 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
++	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+ 	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
+index 85d9e057..bee4a4b5 100644
+--- a/mt7996/eeprom.c
++++ b/mt7996/eeprom.c
+@@ -330,6 +330,25 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+ 	return mt7996_eeprom_parse_band_config(phy);
+ }
+ 
++static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
++{
++	struct mt76_dev *mdev = &dev->mt76;
++	u8 *eeprom = mdev->eeprom.data;
++	u32 offs = MT_EE_DO_PRE_CAL;
++	u32 size, val = eeprom[offs];
++
++	if (!dev->flash_mode || !val)
++		return 0;
++
++	size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
++
++	dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++	if (!dev->cal)
++		return -ENOMEM;
++
++	return mt76_get_of_eeprom(mdev, dev->cal, MT_EE_PRECAL, size);
++}
++
+ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ {
+ 	int ret;
+@@ -347,6 +366,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
+ 			return ret;
+ 	}
+ 
++	ret = mt7996_eeprom_load_precal(dev);
++	if (ret)
++		return ret;
++
+ 	ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
+ 	if (ret < 0)
+ 		return ret;
+diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
+index 20dd8771..0f3f31d8 100644
+--- a/mt7996/eeprom.h
++++ b/mt7996/eeprom.h
+@@ -25,6 +25,8 @@ enum mt7996_eeprom_field {
+ 	MT_EE_TX0_POWER_6G =	0x1310,
+ 
+ 	__MT_EE_MAX =	0x1dff,
++	/* 0x1e10 ~ 0x2d644 used to save group cal data */
++	MT_EE_PRECAL =		0x1e10,
+ };
+ 
+ #define MT_EE_WIFI_CONF0_TX_PATH		GENMASK(2, 0)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 40d610ae..31695090 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -675,6 +675,12 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ 	if (ret < 0)
+ 		return ret;
+ 
++	if (dev->flash_mode) {
++		ret = mt7996_mcu_apply_group_cal(dev);
++		if (ret)
++			return ret;
++	}
++
+ 	/* Beacon and mgmt frames should occupy wcid 0 */
+ 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+ 	if (idx)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index e5627c96..d40d3047 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -312,6 +312,12 @@ int mt7996_set_channel(struct mt7996_phy *phy)
+ 
+ 	mt76_set_channel(phy->mt76);
+ 
++	if (dev->flash_mode) {
++		ret = mt7996_mcu_apply_tx_dpd(phy);
++		if (ret)
++			goto out;
++	}
++
+ 	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
+ 	if (ret)
+ 		goto out;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1fb7bae1..6add77da 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -3386,6 +3386,172 @@ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
+ 	return 0;
+ }
+ 
++static int mt7996_mcu_set_pre_cal(struct mt7996_dev *dev, u32 idx,
++				  u8 *cal, u32 len, u32 cal_id)
++{
++#define PRECAL_CMD_PRE_CAL_RESULT	0x0
++	struct {
++		/* fixed field */
++		u8 action;
++		u8 dest;
++		u8 attribute;
++		u8 tag_num;
++
++		__le16 tag;
++		__le16 len;
++
++		__le32 cal_id;
++		s8 precal;
++		u8 band;
++		u8 rsv[2];
++		__le32 idx;
++		__le32 cal_len;
++	} req = {
++		.tag = cpu_to_le16(PRECAL_CMD_PRE_CAL_RESULT),
++		.len = cpu_to_le16(sizeof(req) - 4 + len),
++		.cal_id = cpu_to_le32(cal_id),
++		.idx = cpu_to_le32(idx),
++		.cal_len = cpu_to_le32(len),
++	};
++	struct sk_buff *skb;
++
++	if (!len)
++		return 0;
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + len);
++	if (!skb)
++		return -ENOMEM;
++
++	skb_put_data(skb, &req, sizeof(req));
++	skb_put_data(skb, cal, len);
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(PRECAL_RESULT), false);
++}
++
++int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev)
++{
++	u8 *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
++	u32 idx = 0, total_idx = MT_EE_CAL_GROUP_SIZE / MT_EE_CAL_UNIT;
++	u32 offs = MT_EE_DO_PRE_CAL;
++	int ret = 0;
++
++	if (!(eeprom[offs] & MT_EE_WIFI_CAL_GROUP))
++		return 0;
++
++	for (idx = 0; idx < total_idx; idx++, cal += MT_EE_CAL_UNIT) {
++		ret = mt7996_mcu_set_pre_cal(dev, idx, cal, MT_EE_CAL_UNIT, RF_PRE_CAL);
++		if (ret)
++			goto out;
++	}
++
++	ret = mt7996_mcu_set_pre_cal(dev, total_idx, cal,
++				     MT_EE_CAL_GROUP_SIZE % MT_EE_CAL_UNIT, RF_PRE_CAL);
++
++out:
++	return ret;
++}
++
++int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
++{
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_phy *mphy = phy->mt76;
++	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++	enum nl80211_band band = chandef->chan->band;
++	enum nl80211_chan_width bw = chandef->width;
++	const struct ieee80211_channel *chan_list;
++	u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
++	u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
++	u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
++	u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
++	int idx, i, ret;
++
++	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
++	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
++
++	switch (band) {
++	case NL80211_BAND_2GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_2G;
++		/* channel 14 don't need DPD cal */
++		if (channel >= 1 && channel <= 4)
++			channel = 3;
++		else if (channel >= 5 && channel <= 9)
++			channel = 7;
++		else if (channel >= 10 && channel <= 13)
++			channel = 11;
++		else
++			return 0;
++		cal_id = RF_DPD_FLAT_CAL;
++		chan_list = dpd_2g_ch_list_bw20;
++		chan_list_size = dpd_2g_bw20_ch_num;
++		break;
++	case NL80211_BAND_5GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
++		cal_id = RF_DPD_FLAT_5G_CAL;
++		chan_list = mphy->sband_5g.sband.channels;
++		chan_list_size = mphy->sband_5g.sband.n_channels;
++		base_offset += dpd_size_2g;
++		if (bw == NL80211_CHAN_WIDTH_160) {
++			base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
++			chan_list = dpd_5g_ch_list_bw160;
++			chan_list_size = dpd_5g_bw160_ch_num;
++		} else if (bw > NL80211_CHAN_WIDTH_20) {
++			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
++			channel -= 2;
++		}
++		break;
++	case NL80211_BAND_6GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
++		cal_id = RF_DPD_FLAT_6G_CAL;
++		chan_list = mphy->sband_6g.sband.channels;
++		chan_list_size = mphy->sband_6g.sband.n_channels;
++		base_offset += dpd_size_2g + dpd_size_5g;
++		if (bw == NL80211_CHAN_WIDTH_160) {
++			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
++			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
++			chan_list = dpd_6g_ch_list_bw160;
++			chan_list_size = dpd_6g_bw160_ch_num;
++		} else if (bw == NL80211_CHAN_WIDTH_320) {
++			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
++				       dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
++			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
++			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
++			chan_list = dpd_6g_ch_list_bw320;
++			chan_list_size = dpd_6g_bw320_ch_num;
++		} else if (bw > NL80211_CHAN_WIDTH_20) {
++			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
++			channel -= 2;
++		}
++		break;
++	default:
++		dpd_mask = 0;
++		break;
++	}
++
++	if (!(eeprom[offs] & dpd_mask))
++		return 0;
++
++	for (idx = 0; idx < chan_list_size; idx++)
++		if (channel == chan_list[idx].hw_value)
++			break;
++	if (idx == chan_list_size)
++		return -EINVAL;
++
++	cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
++
++	for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
++		ret = mt7996_mcu_set_pre_cal(dev, i, cal, MT_EE_CAL_UNIT, cal_id);
++		if (ret)
++			return ret;
++
++		cal += MT_EE_CAL_UNIT;
++	}
++
++	return ret;
++}
++
+ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
+ {
+ #define NIC_CAP	3
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 6ef6bad9..c16bc8b4 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -607,6 +607,8 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
+ int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
++int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev);
++int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ #ifdef CONFIG_NL80211_TESTMODE
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1009-wifi-mt76-mt7996-Beacon-protection-feature-added.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1009-wifi-mt76-mt7996-Beacon-protection-feature-added.patch
new file mode 100644
index 0000000..7ddc56f
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1009-wifi-mt76-mt7996-Beacon-protection-feature-added.patch
@@ -0,0 +1,220 @@
+From b99c942620576c63baffd687090febea5ab2973d Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Wed, 26 Apr 2023 20:08:10 +0800
+Subject: [PATCH 1009/1015] wifi: mt76: mt7996: Beacon protection feature added
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+Change-Id: I0149a65f71d844fc395c2827a54f9360492d181e
+---
+ mt76_connac_mcu.h | 16 +++++++++
+ mt7996/main.c     |  4 +++
+ mt7996/mcu.c      | 86 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 11 ++++++
+ mt7996/mt7996.h   |  2 ++
+ 5 files changed, 119 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 42246fb9..a53fa138 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -415,6 +415,14 @@ struct sta_rec_he_6g_capa {
+ 	u8 rsv[2];
+ } __packed;
+ 
++struct sta_rec_pn_info {
++	__le16 tag;
++	__le16 len;
++	u8 pn[6];
++	u8 tsc_type;
++	u8 rsv;
++} __packed;
++
+ struct sec_key {
+ 	u8 cipher_id;
+ 	u8 cipher_len;
+@@ -767,6 +775,7 @@ struct wtbl_raw {
+ 					 sizeof(struct sta_rec_sec) +	\
+ 					 sizeof(struct sta_rec_ra_fixed) + \
+ 					 sizeof(struct sta_rec_he_6g_capa) + \
++					 sizeof(struct sta_rec_pn_info) + \
+ 					 sizeof(struct tlv) +		\
+ 					 MT76_CONNAC_WTBL_UPDATE_MAX_SIZE)
+ 
+@@ -796,6 +805,7 @@ enum {
+ 	STA_REC_HE_6G = 0x17,
+ 	STA_REC_HE_V2 = 0x19,
+ 	STA_REC_EHT = 0x22,
++	STA_REC_PN_INFO = 0x26,
+ 	STA_REC_HDRT = 0x28,
+ 	STA_REC_HDR_TRANS = 0x2B,
+ 	STA_REC_MAX_NUM
+@@ -1077,6 +1087,11 @@ enum mcu_cipher_type {
+ 	MCU_CIPHER_GCMP_256,
+ 	MCU_CIPHER_WAPI,
+ 	MCU_CIPHER_BIP_CMAC_128,
++	MCU_CIPHER_BIP_CMAC_256,
++	MCU_CIPHER_BCN_PROT_CMAC_128,
++	MCU_CIPHER_BCN_PROT_CMAC_256,
++	MCU_CIPHER_BCN_PROT_GMAC_128,
++	MCU_CIPHER_BCN_PROT_GMAC_256,
+ };
+ 
+ enum {
+@@ -1295,6 +1310,7 @@ enum {
+ 	UNI_BSS_INFO_RATE = 11,
+ 	UNI_BSS_INFO_QBSS = 15,
+ 	UNI_BSS_INFO_SEC = 16,
++	UNI_BSS_INFO_BCN_PROT = 17,
+ 	UNI_BSS_INFO_TXCMD = 18,
+ 	UNI_BSS_INFO_UAPSD = 19,
+ 	UNI_BSS_INFO_PS = 21,
+diff --git a/mt7996/main.c b/mt7996/main.c
+index d40d3047..d3d10fab 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -400,6 +400,10 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ 	}
+ 
+ 	mt76_wcid_key_setup(&dev->mt76, wcid, key);
++
++	if (key->keyidx == 6 || key->keyidx == 7)
++		mt7996_mcu_bcn_prot_enable(dev, vif, key);
++
+ 	err = mt7996_mcu_add_key(&dev->mt76, vif, &msta->bip,
+ 				 key, MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+ 				 &msta->wcid, cmd);
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 6add77da..53d2fc73 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2133,6 +2133,92 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ 	return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
+ }
+ 
++static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++			    u8 *pn)
++{
++#define TSC_TYPE_BIGTK_PN 2
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct sta_rec_pn_info *pn_info;
++	struct sk_buff *skb, *rskb;
++	struct tlv *tlv;
++	int ret;
++
++	skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, &mvif->sta.wcid);
++	if (IS_ERR(skb))
++		return PTR_ERR(skb);
++
++	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PN_INFO, sizeof(*pn_info));
++	pn_info = (struct sta_rec_pn_info *)tlv;
++
++	pn_info->tsc_type = TSC_TYPE_BIGTK_PN;
++	ret = mt76_mcu_skb_send_and_get_msg(&dev->mt76, skb,
++				MCU_WM_UNI_CMD_QUERY(STA_REC_UPDATE), true, &rskb);
++	if (ret)
++		return ret;
++
++	skb_pull(rskb, 4);
++
++	pn_info = (struct sta_rec_pn_info *)rskb->data;
++	if (le16_to_cpu(pn_info->tag) == STA_REC_PN_INFO)
++		memcpy(pn, pn_info->pn, 6);
++
++	dev_kfree_skb(rskb);
++	return 0;
++}
++
++int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++		       struct ieee80211_key_conf *key)
++{
++#define WPA_BIGTK_MAX_LEN 32
++	int len = sizeof(struct bss_req_hdr) + sizeof(struct mt7996_mcu_bcn_prot_tlv);
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	int ret;
++	struct mt7996_mcu_bcn_prot_tlv *bcn_prot;
++	struct sk_buff *skb;
++	struct tlv *tlv;
++	u8 pn[6] = {0};
++
++	skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len);
++	if (IS_ERR(skb))
++		return PTR_ERR(skb);
++
++	tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BCN_PROT,
++				sizeof(*bcn_prot));
++
++	bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv;
++
++	ret = mt7996_mcu_get_pn(dev, vif, pn);
++	if (ret) {
++		dev_kfree_skb(skb);
++		return ret;
++	}
++
++	switch(key->cipher){
++	case WLAN_CIPHER_SUITE_AES_CMAC:
++		bcn_prot->cipher_id = MCU_CIPHER_BCN_PROT_CMAC_128;
++		break;
++	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
++		bcn_prot->cipher_id = MCU_CIPHER_BCN_PROT_GMAC_128;
++		break;
++	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
++		bcn_prot->cipher_id = MCU_CIPHER_BCN_PROT_GMAC_256;
++		break;
++	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
++	default:
++		dev_err(dev->mt76.dev, "Not supported Bigtk Cipher\n");
++		dev_kfree_skb(skb);
++		return -EOPNOTSUPP;
++	}
++
++	pn[0]++;
++	memcpy(bcn_prot->pn, pn, 6);
++	bcn_prot->enable = 1;
++	memcpy(bcn_prot->key, key->key, WPA_BIGTK_MAX_LEN);
++	bcn_prot->key_id = key->keyidx;
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++				   MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
++}
+ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ 			    struct ieee80211_vif *vif, bool enable)
+ {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index baffbcd7..f32ac153 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -262,6 +262,17 @@ struct bss_rate_tlv {
+ 	u8 __rsv2[9];
+ } __packed;
+ 
++struct mt7996_mcu_bcn_prot_tlv {
++	__le16 tag;
++	__le16 len;
++	u8 pn[6];
++	u8 enable;
++	u8 cipher_id;
++	u8 key[32];
++	u8 key_id;
++	u8 __rsv[3];
++} __packed;
++
+ struct bss_ra_tlv {
+ 	__le16 tag;
+ 	__le16 len;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index c16bc8b4..94b62211 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -710,6 +710,8 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ 		       struct mt76_connac_sta_key_conf *sta_key_conf,
+ 		       struct ieee80211_key_conf *key, int mcu_cmd,
+ 		       struct mt76_wcid *wcid, enum set_key_cmd cmd);
++int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++		       struct ieee80211_key_conf *key);
+ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ 				     struct ieee80211_vif *vif,
+ 				     struct ieee80211_sta *sta);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1010-wifi-mt76-testmode-add-testmode-ZWDFS-verification-s.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1010-wifi-mt76-testmode-add-testmode-ZWDFS-verification-s.patch
new file mode 100644
index 0000000..999d9ba
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1010-wifi-mt76-testmode-add-testmode-ZWDFS-verification-s.patch
@@ -0,0 +1,490 @@
+From 639385416a573ae5ce631a0323c5541dea3e406c Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 22 Mar 2023 11:19:52 +0800
+Subject: [PATCH 1010/1015] wifi: mt76: testmode: add testmode ZWDFS
+ verification support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ mt76.h            |   8 ++
+ mt7996/mt7996.h   |   1 +
+ mt7996/testmode.c | 248 ++++++++++++++++++++++++++++++++++++++++++++--
+ mt7996/testmode.h |  44 ++++++++
+ testmode.c        |  22 +++-
+ tools/fields.c    |  15 +++
+ 6 files changed, 326 insertions(+), 12 deletions(-)
+
+diff --git a/mt76.h b/mt76.h
+index 7d6e3241..5b442691 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -732,6 +732,14 @@ struct mt76_testmode_data {
+ 	} cfg;
+ 
+ 	u8 aid;
++
++	u8 offchan_ch;
++	u8 offchan_center_ch;
++	u8 offchan_bw;
++
++	u8 ipi_threshold;
++	u32 ipi_period;
++	u8 ipi_reset;
+ };
+ 
+ struct mt76_vif {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 94b62211..abdbb1ef 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -295,6 +295,7 @@ struct mt7996_phy {
+ 
+ 	struct mib_stats mib;
+ 	struct mt76_channel_state state_ts;
++	struct delayed_work ipi_work;
+ 
+ 	bool has_aux_rx;
+ 
+diff --git a/mt7996/testmode.c b/mt7996/testmode.c
+index 7d36902e..ba3cd802 100644
+--- a/mt7996/testmode.c
++++ b/mt7996/testmode.c
+@@ -16,6 +16,12 @@ enum {
+ 	TM_CHANGED_TX_LENGTH,
+ 	TM_CHANGED_TX_TIME,
+ 	TM_CHANGED_CFG,
++	TM_CHANGED_OFF_CHAN_CH,
++	TM_CHANGED_OFF_CHAN_CENTER_CH,
++	TM_CHANGED_OFF_CHAN_BW,
++	TM_CHANGED_IPI_THRESHOLD,
++	TM_CHANGED_IPI_PERIOD,
++	TM_CHANGED_IPI_RESET,
+ 
+ 	/* must be last */
+ 	NUM_TM_CHANGED
+@@ -27,20 +33,31 @@ static const u8 tm_change_map[] = {
+ 	[TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
+ 	[TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
+ 	[TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
++	[TM_CHANGED_OFF_CHAN_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CH,
++	[TM_CHANGED_OFF_CHAN_CENTER_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
++	[TM_CHANGED_OFF_CHAN_BW] = MT76_TM_ATTR_OFF_CH_SCAN_BW,
++	[TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
++	[TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
++	[TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
+ };
+ 
+-static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
++static void mt7996_tm_ipi_work(struct work_struct *work);
++
++static u32 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
+ {
+-	static const u8 width_to_bw[][NUM_BW_MAP] = {
+-		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
+-		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
+-		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
+-		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
+-		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
+-		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
+-		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
+-		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
+-		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
++	static const u32 width_to_bw[][NUM_BW_MAP] = {
++		[NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ, 40,
++					   FIRST_CONTROL_CHAN_BITMAP_BW40},
++		[NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ, 80,
++					   FIRST_CONTROL_CHAN_BITMAP_BW80},
++		[NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ, 80, 0x0},
++		[NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ, 160,
++					    FIRST_CONTROL_CHAN_BITMAP_BW160},
++		[NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ, 5, 0x0},
++		[NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ, 10, 0x0},
++		[NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
++		[NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ, 20, 0x0},
++		[NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ, 320, 0x0},
+ 	};
+ 
+ 	if (width >= ARRAY_SIZE(width_to_bw))
+@@ -217,6 +234,9 @@ mt7996_tm_init(struct mt7996_phy *phy, bool en)
+ 
+ 	/* use firmware counter for RX stats */
+ 	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
++
++	if (en)
++		INIT_DELAYED_WORK(&phy->ipi_work, mt7996_tm_ipi_work);
+ }
+ 
+ static void
+@@ -829,6 +849,204 @@ void mt7996_tm_rf_test_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	}
+ }
+ 
++static u8
++mt7996_tm_get_center_chan(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef)
++{
++	struct mt76_phy *mphy = phy->mt76;
++	const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
++	u32 bitmap, i, offset, width_mhz, size = mphy->sband_5g.sband.n_channels;
++	u16 first_control = 0, control_chan = chandef->chan->hw_value;
++
++	bitmap = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_CONTROL_BITMAP_5G);
++	if (!bitmap)
++		return control_chan;
++
++	width_mhz = mt7996_tm_bw_mapping(chandef->width, BW_MAP_NL_TO_MHZ);
++	offset = width_mhz / 10 - 2;
++
++	for (i = 0; i < size; i++) {
++		if (!((1 << i) & bitmap))
++			continue;
++
++		if (control_chan >= chan[i].hw_value)
++			first_control = chan[i].hw_value;
++		else
++			break;
++	}
++
++	if (i == size || first_control == 0)
++		return control_chan;
++
++	return first_control + offset;
++}
++
++static int
++mt7996_tm_set_offchan(struct mt7996_phy *phy, bool no_center)
++{
++	struct mt76_phy *mphy = phy->mt76;
++	struct mt7996_dev *dev = phy->dev;
++	struct ieee80211_hw *hw = mphy->hw;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct cfg80211_chan_def chandef = {};
++	struct ieee80211_channel *chan;
++	int ret, freq = ieee80211_channel_to_frequency(td->offchan_ch, NL80211_BAND_5GHZ);
++
++	if (!mphy->cap.has_5ghz || !freq) {
++		ret = -EINVAL;
++		dev_info(dev->mt76.dev, "Failed to set offchan (invalid band or channel)!\n");
++		goto out;
++	}
++
++	chandef.width = td->offchan_bw;
++	chan = ieee80211_get_channel(hw->wiphy, freq);
++	chandef.chan = chan;
++	if (no_center)
++		td->offchan_center_ch = mt7996_tm_get_center_chan(phy, &chandef);
++	chandef.center_freq1 = ieee80211_channel_to_frequency(td->offchan_center_ch,
++							      NL80211_BAND_5GHZ);
++	if (!cfg80211_chandef_valid(&chandef)) {
++		ret = -EINVAL;
++		dev_info(dev->mt76.dev, "Failed to set offchan, chandef is invalid!\n");
++		goto out;
++	}
++
++	memset(&dev->rdd2_chandef, 0, sizeof(struct cfg80211_chan_def));
++
++	ret = mt7996_mcu_rdd_background_enable(phy, &chandef);
++
++	if (ret)
++		goto out;
++
++	dev->rdd2_phy = phy;
++	dev->rdd2_chandef = chandef;
++
++	return 0;
++
++out:
++	td->offchan_ch = 0;
++	td->offchan_center_ch = 0;
++	td->offchan_bw = 0;
++
++	return ret;
++}
++
++static void
++mt7996_tm_ipi_hist_ctrl(struct mt7996_phy *phy, struct mt7996_tm_rdd_ipi_ctrl *data, u8 cmd)
++{
++#define MT_IPI_RESET		0x830a5dfc
++#define MT_IPI_RESET_MASK	BIT(28)
++#define MT_IPI_COUNTER_BASE	0x83041000
++#define MT_IPI_COUNTER(idx)	(MT_IPI_COUNTER_BASE + ((idx) * 4))
++	struct mt7996_dev *dev = phy->dev;
++	bool val;
++	int i;
++
++	if (cmd == RDD_SET_IPI_HIST_RESET) {
++		val = mt76_rr(dev, MT_IPI_RESET) & MT_IPI_RESET_MASK;
++		mt76_rmw_field(dev, MT_IPI_RESET, MT_IPI_RESET_MASK, !val);
++		return;
++	}
++
++	for (i = 0; i < POWER_INDICATE_HIST_MAX; i++)
++		data->ipi_hist_val[i] = mt76_rr(dev, MT_IPI_COUNTER(i));
++}
++
++static void
++mt7996_tm_ipi_work(struct work_struct *work)
++{
++#define PRECISION	100
++	struct mt7996_phy *phy = container_of(work, struct mt7996_phy, ipi_work.work);
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7996_tm_rdd_ipi_ctrl data;
++	u32 ipi_idx, ipi_free_count, ipi_percentage;
++	u32 ipi_hist_count_th = 0, ipi_hist_total_count = 0;
++	u32 self_idle_ratio, ipi_idle_ratio, channel_load;
++	u32 *ipi_hist_data;
++	const char *power_lower_bound, *power_upper_bound;
++	static const char * const ipi_idx_to_power_bound[] = {
++		[RDD_IPI_HIST_0] = "-92",
++		[RDD_IPI_HIST_1] = "-89",
++		[RDD_IPI_HIST_2] = "-86",
++		[RDD_IPI_HIST_3] = "-83",
++		[RDD_IPI_HIST_4] = "-80",
++		[RDD_IPI_HIST_5] = "-75",
++		[RDD_IPI_HIST_6] = "-70",
++		[RDD_IPI_HIST_7] = "-65",
++		[RDD_IPI_HIST_8] = "-60",
++		[RDD_IPI_HIST_9] = "-55",
++		[RDD_IPI_HIST_10] = "inf",
++	};
++
++	memset(&data, 0, sizeof(data));
++	mt7996_tm_ipi_hist_ctrl(phy, &data, RDD_IPI_HIST_ALL_CNT);
++
++	ipi_hist_data = data.ipi_hist_val;
++	for (ipi_idx = 0; ipi_idx < POWER_INDICATE_HIST_MAX; ipi_idx++) {
++		power_lower_bound = ipi_idx ? ipi_idx_to_power_bound[ipi_idx - 1] : "-inf";
++		power_upper_bound = ipi_idx_to_power_bound[ipi_idx];
++
++		dev_info(dev->mt76.dev, "IPI %d (power range: (%s, %s] dBm): ipi count = %d\n",
++			 ipi_idx, power_lower_bound, power_upper_bound, ipi_hist_data[ipi_idx]);
++
++		if (td->ipi_threshold <= ipi_idx && ipi_idx <= RDD_IPI_HIST_10)
++			ipi_hist_count_th += ipi_hist_data[ipi_idx];
++
++		ipi_hist_total_count += ipi_hist_data[ipi_idx];
++	}
++
++	ipi_free_count = ipi_hist_data[RDD_IPI_FREE_RUN_CNT];
++
++	dev_info(dev->mt76.dev, "IPI threshold %d: ipi_hist_count_th = %d, ipi_free_count = %d\n",
++		 td->ipi_threshold, ipi_hist_count_th, ipi_free_count);
++	dev_info(dev->mt76.dev, "TX assert time =  %d [ms]\n", data.tx_assert_time / 1000);
++
++	/* calculate channel load = (self idle ratio - idle ratio) / self idle ratio */
++	if (ipi_hist_count_th >= UINT_MAX / (100 * PRECISION))
++		ipi_percentage = 100 * PRECISION *
++				 (ipi_hist_count_th / (100 * PRECISION)) /
++				 (ipi_free_count / (100 * PRECISION));
++	else
++		ipi_percentage = PRECISION * 100 * ipi_hist_count_th / ipi_free_count;
++
++	ipi_idle_ratio = ((100 * PRECISION) - ipi_percentage) / PRECISION;
++
++	self_idle_ratio = PRECISION * 100 *
++			  (td->ipi_period - (data.tx_assert_time / 1000)) /
++			  td->ipi_period / PRECISION;
++
++	if (self_idle_ratio < ipi_idle_ratio)
++		channel_load = 0;
++	else
++		channel_load = self_idle_ratio - ipi_idle_ratio;
++
++	if (self_idle_ratio <= td->ipi_threshold) {
++		dev_info(dev->mt76.dev, "band[%d]: self idle ratio = %d%%, idle ratio = %d%%\n",
++			 phy->mt76->band_idx, self_idle_ratio, ipi_idle_ratio);
++		return;
++	}
++
++	channel_load = (100 * channel_load) / self_idle_ratio;
++	dev_info(dev->mt76.dev,
++		 "band[%d]: chan load = %d%%, self idle ratio = %d%%, idle ratio = %d%%\n",
++		 phy->mt76->band_idx, channel_load, self_idle_ratio, ipi_idle_ratio);
++}
++
++static int
++mt7996_tm_set_ipi(struct mt7996_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
++
++	/* reset IPI CR */
++	mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
++
++	cancel_delayed_work(&phy->ipi_work);
++	ieee80211_queue_delayed_work(phy->mt76->hw, &phy->ipi_work,
++				     msecs_to_jiffies(td->ipi_period));
++
++	return 0;
++}
++
+ static void
+ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ {
+@@ -853,6 +1071,14 @@ mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
+ 
+ 		mt7996_tm_set(dev, func_idx, td->cfg.type);
+ 	}
++	if ((changed & BIT(TM_CHANGED_OFF_CHAN_CH)) &&
++	    (changed & BIT(TM_CHANGED_OFF_CHAN_BW)))
++		mt7996_tm_set_offchan(phy, !(changed & BIT(TM_CHANGED_OFF_CHAN_CENTER_CH)));
++	if ((changed & BIT(TM_CHANGED_IPI_THRESHOLD)) &&
++	    (changed & BIT(TM_CHANGED_IPI_PERIOD)))
++		mt7996_tm_set_ipi(phy);
++	if (changed & BIT(TM_CHANGED_IPI_RESET))
++		mt7996_tm_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET);
+ }
+ 
+ static int
+diff --git a/mt7996/testmode.h b/mt7996/testmode.h
+index 778c9bc6..09f81d39 100644
+--- a/mt7996/testmode.h
++++ b/mt7996/testmode.h
+@@ -27,9 +27,15 @@ enum {
+ 	FW_CDBW_8080MHZ,
+ };
+ 
++#define FIRST_CONTROL_CHAN_BITMAP_BW40		0x5555555
++#define FIRST_CONTROL_CHAN_BITMAP_BW80		0x111111
++#define FIRST_CONTROL_CHAN_BITMAP_BW160		0x100101
++
+ enum bw_mapping_method {
+ 	BW_MAP_NL_TO_FW,
+ 	BW_MAP_NL_TO_TM,
++	BW_MAP_NL_TO_MHZ,
++	BW_MAP_NL_TO_CONTROL_BITMAP_5G,
+ 
+ 	NUM_BW_MAP,
+ };
+@@ -308,4 +314,42 @@ struct mt7996_tm_rx_event {
+ 	};
+ } __packed;
+ 
++enum {
++	RDD_SET_IPI_CR_INIT,		/* CR initialization */
++	RDD_SET_IPI_HIST_RESET,		/* Reset IPI histogram counter */
++	RDD_SET_IDLE_POWER,		/* Idle power info */
++	RDD_SET_IPI_HIST_NUM
++};
++
++enum {
++	RDD_IPI_HIST_0,			/* IPI count for power <= -92 (dBm) */
++	RDD_IPI_HIST_1,			/* IPI count for -92 < power <= -89 (dBm) */
++	RDD_IPI_HIST_2,			/* IPI count for -89 < power <= -86 (dBm) */
++	RDD_IPI_HIST_3,			/* IPI count for -86 < power <= -83 (dBm) */
++	RDD_IPI_HIST_4,			/* IPI count for -83 < power <= -80 (dBm) */
++	RDD_IPI_HIST_5,			/* IPI count for -80 < power <= -75 (dBm) */
++	RDD_IPI_HIST_6,			/* IPI count for -75 < power <= -70 (dBm) */
++	RDD_IPI_HIST_7,			/* IPI count for -70 < power <= -65 (dBm) */
++	RDD_IPI_HIST_8,			/* IPI count for -65 < power <= -60 (dBm) */
++	RDD_IPI_HIST_9,			/* IPI count for -60 < power <= -55 (dBm) */
++	RDD_IPI_HIST_10,		/* IPI count for -55 < power        (dBm) */
++	RDD_IPI_FREE_RUN_CNT,		/* IPI count for counter++ per 8 us */
++	RDD_IPI_HIST_ALL_CNT,		/* Get all IPI */
++	RDD_IPI_HIST_0_TO_10_CNT,	/* Get IPI histogram 0 to 10 */
++	RDD_IPI_HIST_2_TO_10_CNT,	/* Get IPI histogram 2 to 10 */
++	RDD_TX_ASSERT_TIME,		/* Get band 1 TX assert time */
++	RDD_IPI_HIST_NUM
++};
++
++#define POWER_INDICATE_HIST_MAX		RDD_IPI_FREE_RUN_CNT
++#define IPI_HIST_TYPE_NUM		(POWER_INDICATE_HIST_MAX + 1)
++
++struct mt7996_tm_rdd_ipi_ctrl {
++	u8 ipi_hist_idx;
++	u8 band_idx;
++	u8 rsv[2];
++	__le32 ipi_hist_val[IPI_HIST_TYPE_NUM];
++	__le32 tx_assert_time;		/* unit: us */
++} __packed;
++
+ #endif
+diff --git a/testmode.c b/testmode.c
+index 74bb26fa..22d6afd4 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -25,6 +25,13 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+ 	[MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
+ 	[MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED },
++	[MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_OFF_CH_SCAN_PATH] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
++	[MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
++	[MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
+ };
+ EXPORT_SYMBOL_GPL(mt76_tm_policy);
+ 
+@@ -451,6 +458,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 	if (tb[MT76_TM_ATTR_TX_RATE_IDX])
+ 		td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]);
+ 
++	if (tb[MT76_TM_ATTR_IPI_PERIOD])
++		td->ipi_period = nla_get_u32(tb[MT76_TM_ATTR_IPI_PERIOD]);
++
+ 	if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode,
+ 			   0, MT76_TM_TX_MODE_MAX) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss,
+@@ -466,7 +476,14 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			   &td->tx_duty_cycle, 0, 99) ||
+ 	    mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
+ 			   &td->tx_power_control, 0, 1) ||
+-	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CH], &td->offchan_ch, 36, 196) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH], &td->offchan_center_ch,
++			   36, 196) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW],
++			   &td->offchan_bw, NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_160) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_THRESHOLD], &td->ipi_threshold, 0, 10) ||
++	    mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_RESET], &td->ipi_reset, 0, 1))
+ 		goto out;
+ 
+ 	if (tb[MT76_TM_ATTR_TX_LENGTH]) {
+@@ -671,6 +688,9 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
+ 	    nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CH, td->offchan_ch) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH, td->offchan_center_ch) ||
++	    nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_BW, td->offchan_bw) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
+ 	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
+diff --git a/tools/fields.c b/tools/fields.c
+index b22b3fc8..55854a6f 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -35,6 +35,15 @@ static const char * const testmode_tx_mode[] = {
+ 	[MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
+ };
+ 
++static const char * const testmode_offchan_bw[] = {
++	[NL80211_CHAN_WIDTH_20_NOHT] = "NOHT",
++	[NL80211_CHAN_WIDTH_20] = "20",
++	[NL80211_CHAN_WIDTH_40] = "40",
++	[NL80211_CHAN_WIDTH_80] = "80",
++	[NL80211_CHAN_WIDTH_80P80] = "80p80",
++	[NL80211_CHAN_WIDTH_160] = "160",
++};
++
+ static void print_enum(const struct tm_field *field, struct nlattr *attr)
+ {
+ 	unsigned int i = nla_get_u8(attr);
+@@ -387,6 +396,12 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
+ 	FIELD(u8, AID, "aid"),
+ 	FIELD(u8, RU_ALLOC, "ru_alloc"),
+ 	FIELD(u8, RU_IDX, "ru_idx"),
++	FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
++	FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
++	FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
++	FIELD(u8, IPI_THRESHOLD, "ipi_threshold"),
++	FIELD(u32, IPI_PERIOD, "ipi_period"),
++	FIELD(u8, IPI_RESET, "ipi_reset"),
+ 	FIELD_MAC(MAC_ADDRS, "mac_addrs"),
+ 	FIELD_NESTED_RO(STATS, stats, "",
+ 			.print_extra = print_extra_stats),
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1011-wifi-mt76-mt7996-add-single-sku.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1011-wifi-mt76-mt7996-add-single-sku.patch
new file mode 100644
index 0000000..0fcae6a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1011-wifi-mt76-mt7996-add-single-sku.patch
@@ -0,0 +1,337 @@
+From 0d9ff0aa7afe37146c6015e416d9b944b4bb16f5 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Tue, 18 Apr 2023 15:56:22 +0800
+Subject: [PATCH 1011/1015] wifi: mt76: mt7996: add single sku
+
+Add single sku and default enable sku.
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ eeprom.c          | 38 ++++++++++++++++++--
+ mt76.h            | 10 ++++++
+ mt76_connac_mcu.c |  2 +-
+ mt7996/init.c     |  2 ++
+ mt7996/main.c     | 15 ++++++++
+ mt7996/mcu.c      | 92 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 12 +++++++
+ mt7996/mt7996.h   |  2 ++
+ 8 files changed, 169 insertions(+), 4 deletions(-)
+
+diff --git a/eeprom.c b/eeprom.c
+index 412740f0..3abefb5a 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -301,6 +301,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
+ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 			      struct ieee80211_channel *chan,
+ 			      struct mt76_power_limits *dest,
++			      struct mt76_power_path_limits *dest_path,
+ 			      s8 target_power)
+ {
+ 	struct mt76_dev *dev = phy->dev;
+@@ -308,16 +309,17 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 	const __be32 *val;
+ 	char name[16];
+ 	u32 mcs_rates = dev->drv->mcs_rates;
+-	u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
+ 	char band;
+ 	size_t len;
+ 	s8 max_power = 0;
+ 	s8 txs_delta;
+ 
+ 	if (!mcs_rates)
+-		mcs_rates = 10;
++		mcs_rates = 12;
+ 
+ 	memset(dest, target_power, sizeof(*dest));
++	if (dest_path != NULL)
++		memset(dest_path, 0, sizeof(*dest_path));
+ 
+ 	if (!IS_ENABLED(CONFIG_OF))
+ 		return target_power;
+@@ -365,11 +367,41 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 				     ARRAY_SIZE(dest->mcs), val, len,
+ 				     target_power, txs_delta, &max_power);
+ 
+-	val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
++	val = mt76_get_of_array(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1);
+ 	mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
+ 				     ARRAY_SIZE(dest->ru), val, len,
+ 				     target_power, txs_delta, &max_power);
+ 
++	val = mt76_get_of_array(np, "rates-eht-ru", &len, ARRAY_SIZE(dest->eht_ru[0]) + 1);
++	mt76_apply_multi_array_limit(dest->eht_ru[0], ARRAY_SIZE(dest->eht_ru[0]),
++				     ARRAY_SIZE(dest->eht_ru), val, len,
++				     target_power, txs_delta, &max_power);
++
++	if (dest_path == NULL)
++		return max_power;
++
++	val = mt76_get_of_array(np, "paths-cck", &len, ARRAY_SIZE(dest_path->cck));
++	mt76_apply_array_limit(dest_path->cck, ARRAY_SIZE(dest_path->cck), val,
++			       target_power, txs_delta, &max_power);
++
++	val = mt76_get_of_array(np, "paths-ofdm", &len, ARRAY_SIZE(dest_path->ofdm));
++	mt76_apply_array_limit(dest_path->ofdm, ARRAY_SIZE(dest_path->ofdm), val,
++			       target_power, txs_delta, &max_power);
++
++	val = mt76_get_of_array(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest_path->ofdm_bf));
++	mt76_apply_array_limit(dest_path->ofdm_bf, ARRAY_SIZE(dest_path->ofdm_bf), val,
++			       target_power, txs_delta, &max_power);
++
++	val = mt76_get_of_array(np, "paths-ru", &len, ARRAY_SIZE(dest_path->ru[0]) + 1);
++	mt76_apply_multi_array_limit(dest_path->ru[0], ARRAY_SIZE(dest_path->ru[0]),
++				     ARRAY_SIZE(dest_path->ru), val, len,
++				     target_power, txs_delta, &max_power);
++
++	val = mt76_get_of_array(np, "paths-ru-bf", &len, ARRAY_SIZE(dest_path->ru_bf[0]) + 1);
++	mt76_apply_multi_array_limit(dest_path->ru_bf[0], ARRAY_SIZE(dest_path->ru_bf[0]),
++				     ARRAY_SIZE(dest_path->ru_bf), val, len,
++				     target_power, txs_delta, &max_power);
++
+ 	return max_power;
+ }
+ EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits);
+diff --git a/mt76.h b/mt76.h
+index 5b442691..8abb6f41 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -911,6 +911,15 @@ struct mt76_power_limits {
+ 	s8 ofdm[8];
+ 	s8 mcs[4][10];
+ 	s8 ru[7][12];
++	s8 eht_ru[16][16];
++};
++
++struct mt76_power_path_limits {
++	s8 cck[5];
++	s8 ofdm[5];
++	s8 ofdm_bf[4];
++	s8 ru[16][15];
++	s8 ru_bf[16][15];
+ };
+ 
+ struct mt76_ethtool_worker_info {
+@@ -1493,6 +1502,7 @@ void mt76_set_irq_mask(struct mt76_dev *dev, u32 addr, u32 clear, u32 set);
+ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+ 			      struct ieee80211_channel *chan,
+ 			      struct mt76_power_limits *dest,
++			      struct mt76_power_path_limits *dest_path,
+ 			      s8 target_power);
+ 
+ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index c24dac10..ca7b6a6f 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -2235,7 +2235,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
+ 			sar_power = mt76_get_sar_power(phy, &chan, reg_power);
+ 
+ 			mt76_get_rate_power_limits(phy, &chan, &limits,
+-						   sar_power);
++						   NULL, sar_power);
+ 
+ 			tx_power_tlv.last_msg = ch_list[idx] == last_ch;
+ 			sku_tlbv.channel = ch_list[idx];
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 31695090..a6caf4f1 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -294,6 +294,7 @@ void mt7996_init_txpower(struct mt7996_dev *dev,
+ 	int nss_delta = mt76_tx_power_nss_delta(nss);
+ 	int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
+ 	struct mt76_power_limits limits;
++	struct mt76_power_path_limits limits_path;
+ 
+ 	for (i = 0; i < sband->n_channels; i++) {
+ 		struct ieee80211_channel *chan = &sband->channels[i];
+@@ -302,6 +303,7 @@ void mt7996_init_txpower(struct mt7996_dev *dev,
+ 		target_power += pwr_delta;
+ 		target_power = mt76_get_rate_power_limits(&dev->mphy, chan,
+ 							  &limits,
++							  &limits_path,
+ 							  target_power);
+ 		target_power += nss_delta;
+ 		target_power = DIV_ROUND_UP(target_power, 2);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index d3d10fab..71c346cb 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -77,6 +77,15 @@ int mt7996_run(struct ieee80211_hw *hw)
+ 	if (ret)
+ 		goto out;
+ 
++#ifdef CONFIG_MTK_DEBUG
++	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
++					   !dev->dbg.sku_disable);
++#else
++	ret = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL, true);
++#endif
++	if (ret)
++		goto out;
++
+ 	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+ 
+ 	ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+@@ -427,6 +436,12 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+ 		ieee80211_wake_queues(hw);
+ 	}
+ 
++	if (changed & IEEE80211_CONF_CHANGE_POWER) {
++		ret = mt7996_mcu_set_txpower_sku(phy);
++		if (ret)
++			return ret;
++	}
++
+ 	mutex_lock(&dev->mt76.mutex);
+ 
+ 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 53d2fc73..aefbdca6 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -4616,6 +4616,98 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
+ 				 &req, sizeof(req), false);
+ }
+ 
++int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy)
++{
++#define TX_POWER_LIMIT_TABLE_RATE	0
++#define TX_POWER_LIMIT_TABLE_PATH	1
++	struct mt7996_dev *dev = phy->dev;
++	struct mt76_phy *mphy = phy->mt76;
++	struct ieee80211_hw *hw = mphy->hw;
++	struct tx_power_limit_table_ctrl {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++		u8 power_ctrl_id;
++		u8 power_limit_type;
++		u8 band_idx;
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL),
++		.len = cpu_to_le16(sizeof(req) + MT7996_SKU_PATH_NUM - 4),
++		.power_ctrl_id = UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL,
++		.power_limit_type = TX_POWER_LIMIT_TABLE_RATE,
++		.band_idx = phy->mt76->band_idx,
++	};
++
++	int i, ret, tx_power;
++	const u8 *len = mt7996_sku_group_len;
++	struct mt76_power_limits la = {};
++	struct mt76_power_path_limits la_path = {};
++	struct sk_buff *skb;
++
++	tx_power = mt7996_get_power_bound(phy, hw->conf.power_level);
++	tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
++					      &la, &la_path, tx_power);
++	mphy->txpower_cur = tx_power;
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
++				 sizeof(req) + MT7996_SKU_PATH_NUM);
++	if (!skb)
++		return -ENOMEM;
++
++	skb_put_data(skb, &req, sizeof(req));
++	skb_put_data(skb, &la.cck, len[SKU_CCK] + len[SKU_OFDM]);
++
++	skb_put_data(skb, &la.mcs[0], len[SKU_HT20]);
++	skb_put_data(skb, &la.mcs[1], len[SKU_HT40]);
++
++	/* vht */
++	for (i = 0; i < 4; i++) {
++		skb_put_data(skb, &la.mcs[i], sizeof(la.mcs[i]));
++		skb_put_zero(skb, 2);  /* padding */
++	}
++
++	/* he */
++	skb_put_data(skb, &la.ru[0], sizeof(la.ru));
++
++	/* eht */
++	skb_put_data(skb, &la.eht_ru[0], sizeof(la.eht_ru));
++
++	/* padding */
++	skb_put_zero(skb, MT7996_SKU_PATH_NUM - MT7996_SKU_RATE_NUM);
++
++	ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
++				    MCU_WM_UNI_CMD(TXPOWER), true);
++	if (ret)
++		return ret;
++
++	/* only set per-path power table when it's configured */
++	if (!la_path.ofdm[0])
++		return 0;
++
++	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
++				 sizeof(req) + MT7996_SKU_PATH_NUM);
++	if (!skb)
++		return -ENOMEM;
++	req.power_limit_type = TX_POWER_LIMIT_TABLE_PATH;
++
++	skb_put_data(skb, &req, sizeof(req));
++	skb_put_data(skb, &la_path.cck, sizeof(la_path.cck));
++	skb_put_data(skb, &la_path.ofdm, sizeof(la_path.ofdm));
++	skb_put_data(skb, &la_path.ofdm_bf, sizeof(la_path.ofdm_bf));
++
++	for (i = 0; i < 32; i++) {
++		bool bf = i % 2;
++		u8 idx = i / 2;
++		s8 *buf = bf ? la_path.ru_bf[idx] : la_path.ru[idx];
++
++		skb_put_data(skb, buf, sizeof(la_path.ru[0]));
++	}
++
++	return mt76_mcu_skb_send_msg(&dev->mt76, skb,
++				     MCU_WM_UNI_CMD(TXPOWER), true);
++}
++
+ #ifdef CONFIG_MTK_VENDOR
+ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index f32ac153..1d2b7c58 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -657,6 +657,18 @@ enum {
+ #define MT7996_MAX_BSS_OFFLOAD_SIZE	(MT7996_MAX_BEACON_SIZE +		\
+ 					 MT7996_BEACON_UPDATE_SIZE)
+ 
++static inline s8
++mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower)
++{
++	struct mt76_phy *mphy = phy->mt76;
++	int n_chains = hweight8(mphy->antenna_mask);
++
++	txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2);
++	txpower -= mt76_tx_power_nss_delta(n_chains);
++
++	return txpower;
++}
++
+ enum {
+ 	UNI_BAND_CONFIG_RADIO_ENABLE,
+ 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index abdbb1ef..d15bd950 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -61,6 +61,7 @@
+ #define MT7996_BUILD_TIME_LEN		24
+ 
+ #define MT7996_SKU_RATE_NUM		417
++#define MT7996_SKU_PATH_NUM		494
+ 
+ struct mt7996_vif;
+ struct mt7996_sta;
+@@ -610,6 +611,7 @@ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 da
+ int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+ int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev);
+ int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
++int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
+ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
+ void mt7996_mcu_scs_sta_poll(struct work_struct *work);
+ #ifdef CONFIG_NL80211_TESTMODE
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1012-wifi-mt76-mt7996-add-vendor-cmd-to-get-available-col.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1012-wifi-mt76-mt7996-add-vendor-cmd-to-get-available-col.patch
new file mode 100644
index 0000000..c5ea448
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1012-wifi-mt76-mt7996-add-vendor-cmd-to-get-available-col.patch
@@ -0,0 +1,107 @@
+From a048ff567915954ca9cc68ef94c9a7786542b5d9 Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Wed, 3 May 2023 05:08:07 +0800
+Subject: [PATCH 1012/1015] wifi: mt76: mt7996: add vendor cmd to get available
+ color bitmap
+
+Add a vendor cmd to notify user space available color bitmap.
+The OBSS BSS color bitmap is maintained in mac80211, so mt76 will make use of that.
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ mt7996/vendor.c | 36 ++++++++++++++++++++++++++++++++++++
+ mt7996/vendor.h | 11 +++++++++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 8a021324..73f613aa 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -34,6 +34,11 @@ amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ 	[MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
+ };
+ 
++static struct nla_policy
++bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
++	[MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
++};
++
+ struct mt7996_amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+@@ -409,6 +414,26 @@ mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 	return len + 1;
+ }
+ 
++static int
++mt7996_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++				  struct sk_buff *skb, const void *data, int data_len,
++				  unsigned long *storage)
++{
++	struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
++	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
++	int len = 0;
++
++	if (*storage == 1)
++		return -ENOENT;
++	*storage = 1;
++
++	if (nla_put_u64_64bit(skb, MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++			      ~bss_conf->used_color_bitmap, NL80211_ATTR_PAD))
++		return -ENOMEM;
++	len += 1;
++
++	return len;
++}
+ 
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	{
+@@ -435,6 +460,17 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = amnt_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.dumpit = mt7996_vendor_bss_color_ctrl_dump,
++		.policy = bss_color_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
++	},
+ };
+ 
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 2078cafa..eec9e74a 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -6,6 +6,7 @@
+ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
++	MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ };
+ 
+ enum mtk_vendor_attr_mu_ctrl {
+@@ -57,5 +58,15 @@ enum mtk_vendor_attr_mnt_dump {
+ 		NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
+ };
+ 
++enum mtk_vendor_attr_bss_color_ctrl {
++	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
++	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
++};
+ 
+ #endif
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1013-wifi-mt76-mt7996-get-tx_retries-and-tx_fails-from-tx.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1013-wifi-mt76-mt7996-get-tx_retries-and-tx_fails-from-tx.patch
new file mode 100644
index 0000000..c01b398
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1013-wifi-mt76-mt7996-get-tx_retries-and-tx_fails-from-tx.patch
@@ -0,0 +1,109 @@
+From 3a58791cef81709963d654d520fab9f1b7987e7b Mon Sep 17 00:00:00 2001
+From: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+Date: Thu, 11 May 2023 09:14:28 +0800
+Subject: [PATCH 1013/1015] wifi: mt76: mt7996: get tx_retries and tx_fails
+ from txfree
+
+Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
+---
+ mt7996/mac.c  | 20 +++++++++++++++-----
+ mt7996/mac.h  |  6 ++++--
+ mt7996/main.c |  6 ++++++
+ 3 files changed, 25 insertions(+), 7 deletions(-)
+
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 3dc5cdae..bee4a8ae 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1240,6 +1240,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 	struct mt76_phy *phy3 = mdev->phys[MT_BAND2];
+ 	struct mt76_txwi_cache *txwi;
+ 	struct ieee80211_sta *sta = NULL;
++	struct mt76_wcid *wcid;
+ 	LIST_HEAD(free_list);
+ 	struct sk_buff *skb, *tmp;
+ 	void *end = data + len;
+@@ -1258,7 +1259,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 		mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_BE], false);
+ 	}
+ 
+-	if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 4))
++	if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 5))
+ 		return;
+ 
+ 	total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT);
+@@ -1274,10 +1275,9 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 		info = le32_to_cpu(*cur_info);
+ 		if (info & MT_TXFREE_INFO_PAIR) {
+ 			struct mt7996_sta *msta;
+-			struct mt76_wcid *wcid;
+ 			u16 idx;
+ 
+-			idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info);
++			idx = FIELD_GET(MT_TXFREE_INFO_MLD_ID, info);
+ 			wcid = rcu_dereference(dev->mt76.wcid[idx]);
+ 			sta = wcid_to_sta(wcid);
+ 			if (!sta)
+@@ -1289,10 +1289,20 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+ 				list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+ 			spin_unlock_bh(&dev->sta_poll_lock);
+ 			continue;
+-		}
++		} else if (info & MT_TXFREE_INFO_HEADER) {
++			if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) {
++				u32 tx_retries = 0, tx_failed = 0;
++
++				tx_retries =
++					FIELD_GET(MT_TXFREE_INFO_TX_COUNT, info) - 1;
++				tx_failed = tx_retries +
++					!!FIELD_GET(MT_TXFREE_INFO_STAT, info);
+ 
+-		if (info & MT_TXFREE_INFO_HEADER)
++				wcid->stats.tx_retries += tx_retries;
++				wcid->stats.tx_failed += tx_failed;
++			}
+ 			continue;
++		}
+ 
+ 		for (i = 0; i < 2; i++) {
+ 			msdu = (info >> (15 * i)) & MT_TXFREE_INFO_MSDU_ID;
+diff --git a/mt7996/mac.h b/mt7996/mac.h
+index bc4e6c55..74ad1e81 100644
+--- a/mt7996/mac.h
++++ b/mt7996/mac.h
+@@ -256,11 +256,13 @@ enum tx_mgnt_type {
+ #define MT_TXFREE0_MSDU_CNT		GENMASK(25, 16)
+ #define MT_TXFREE0_RX_BYTE		GENMASK(15, 0)
+ 
+-#define MT_TXFREE1_VER			GENMASK(18, 16)
++#define MT_TXFREE1_VER			GENMASK(19, 16)
+ 
+ #define MT_TXFREE_INFO_PAIR		BIT(31)
+ #define MT_TXFREE_INFO_HEADER		BIT(30)
+-#define MT_TXFREE_INFO_WLAN_ID		GENMASK(23, 12)
++#define MT_TXFREE_INFO_TX_COUNT		GENMASK(27, 24)
++#define MT_TXFREE_INFO_STAT		GENMASK(29, 28)
++#define MT_TXFREE_INFO_MLD_ID		GENMASK(23, 12)
+ #define MT_TXFREE_INFO_MSDU_ID		GENMASK(14, 0)
+ 
+ #define MT_TXS0_BW			GENMASK(31, 29)
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 71c346cb..f0bdec6b 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1024,6 +1024,12 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ 	sinfo->txrate.flags = txrate->flags;
+ 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+ 
++	sinfo->tx_failed = msta->wcid.stats.tx_failed;
++	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
++
++	sinfo->tx_retries = msta->wcid.stats.tx_retries;
++	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
++
+ 	sinfo->ack_signal = (s8)msta->ack_signal;
+ 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
+ 
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1014-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1014-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch
new file mode 100644
index 0000000..1b22e9a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1014-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch
@@ -0,0 +1,171 @@
+From 97144fb0d20ba67ec6c3a022ed5a39bc95ed40e9 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Fri, 19 May 2023 14:56:07 +0800
+Subject: [PATCH 1014/1015] wifi: mt76: mt7996: add debugfs for fw coredump.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+---
+ mt7996/debugfs.c | 19 +++++++++++++++++--
+ mt7996/mac.c     | 28 +++++++++++++++++++++++++---
+ mt7996/mcu.h     |  4 ++++
+ mt7996/mt7996.h  | 10 ++++++++++
+ 4 files changed, 56 insertions(+), 5 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index 8a513f46..49c815a5 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -84,6 +84,8 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
+ 	 * 7: trigger & enable system error L4 mdp recovery.
+ 	 * 8: trigger & enable system error full recovery.
+ 	 * 9: trigger firmware crash.
++	 * 10: trigger grab wa firmware coredump.
++	 * 11: trigger grab wm firmware coredump.
+ 	 */
+ 	case UNI_CMD_SER_QUERY:
+ 		ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band);
+@@ -105,15 +107,25 @@ mt7996_sys_recovery_set(struct file *file, const char __user *user_buf,
+ 	/* 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);
+-		dev->recovery.state |= MT_MCU_CMD_WDT_MASK;
++		dev->recovery.state |= MT_MCU_CMD_WM_WDT;
+ 		mt7996_reset(dev);
+ 		break;
+ 
+ 	/* WARNING: trigger firmware crash */
+ 	case UNI_CMD_SER_SET_SYSTEM_ASSERT:
++		// trigger wm assert exception
+ 		ret = mt7996_mcu_trigger_assert(dev);
+ 		if (ret)
+ 			return ret;
++		// trigger wa assert exception
++		mt76_wr(dev, 0x89098108, 0x20);
++		mt76_wr(dev, 0x89098118, 0x20);
++		break;
++	case UNI_CMD_SER_FW_COREDUMP_WA:
++		mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WA);
++		break;
++	case UNI_CMD_SER_FW_COREDUMP_WM:
++		mt7996_coredump(dev, MT7996_COREDUMP_MANUAL_WM);
+ 		break;
+ 	default:
+ 		break;
+@@ -160,7 +172,10 @@ mt7996_sys_recovery_get(struct file *file, char __user *user_buf,
+ 			  "8: trigger system error full recovery\n");
+ 	desc += scnprintf(buff + desc, bufsz - desc,
+ 			  "9: trigger firmware crash\n");
+-
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "10: trigger grab wa firmware coredump\n");
++	desc += scnprintf(buff + desc, bufsz - desc,
++			  "11: trigger grab wm firmware coredump\n");
+ 	/* SER statistics */
+ 	desc += scnprintf(buff + desc, bufsz - desc,
+ 			  "\nlet's dump firmware SER statistics...\n");
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index bee4a8ae..993b43ce 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2157,15 +2157,36 @@ void mt7996_mac_dump_work(struct work_struct *work)
+ 	struct mt7996_dev *dev;
+ 
+ 	dev = container_of(work, struct mt7996_dev, dump_work);
+-	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
++	if (dev->dump_state == MT7996_COREDUMP_MANUAL_WA ||
++	    READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
+ 		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WA);
+ 
+-	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
++	if (dev->dump_state == MT7996_COREDUMP_MANUAL_WM ||
++	    READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WM_WDT)
+ 		mt7996_mac_fw_coredump(dev, MT7996_RAM_TYPE_WM);
+ 
+-	queue_work(dev->mt76.wq, &dev->reset_work);
++	if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK)
++		queue_work(dev->mt76.wq, &dev->reset_work);
++
++	dev->dump_state = MT7996_COREDUMP_IDLE;
+ }
+ 
++void mt7996_coredump(struct mt7996_dev *dev, u8 state)
++{
++	if (state == MT7996_COREDUMP_IDLE ||
++	    state >= __MT7996_COREDUMP_TYPE_MAX)
++		return;
++
++	if (dev->dump_state != MT7996_COREDUMP_IDLE)
++		return;
++
++	dev->dump_state = state;
++	dev_info(dev->mt76.dev, "%s attempting grab coredump\n",
++		 wiphy_name(dev->mt76.hw->wiphy));
++
++	queue_work(dev->mt76.wq, &dev->dump_work);
++ }
++
+ void mt7996_reset(struct mt7996_dev *dev)
+ {
+ 	dev_info(dev->mt76.dev, "%s SER recovery state: 0x%08x\n",
+@@ -2187,6 +2208,7 @@ void mt7996_reset(struct mt7996_dev *dev)
+ 
+ 		mt7996_irq_disable(dev, MT_INT_MCU_CMD);
+ 		queue_work(dev->mt76.wq, &dev->dump_work);
++		mt7996_coredump(dev, MT7996_COREDUMP_AUTO);
+ 		return;
+ 	}
+ 
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 1d2b7c58..a0cbf922 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -780,7 +780,11 @@ enum {
+ 	UNI_CMD_SER_SET_RECOVER_L3_BF,
+ 	UNI_CMD_SER_SET_RECOVER_L4_MDP,
+ 	UNI_CMD_SER_SET_RECOVER_FULL,
++	/* fw assert */
+ 	UNI_CMD_SER_SET_SYSTEM_ASSERT,
++	/* coredump */
++	UNI_CMD_SER_FW_COREDUMP_WA,
++	UNI_CMD_SER_FW_COREDUMP_WM,
+ 	/* action */
+ 	UNI_CMD_SER_ENABLE = 1,
+ 	UNI_CMD_SER_SET,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index d15bd950..e371964b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -76,6 +76,14 @@ enum mt7996_ram_type {
+ 	__MT7996_RAM_TYPE_MAX,
+ };
+ 
++enum mt7996_coredump_state {
++	MT7996_COREDUMP_IDLE = 0,
++	MT7996_COREDUMP_MANUAL_WA,
++	MT7996_COREDUMP_MANUAL_WM,
++	MT7996_COREDUMP_AUTO,
++	__MT7996_COREDUMP_TYPE_MAX,
++};
++
+ enum mt7996_txq_id {
+ 	MT7996_TXQ_FWDL = 16,
+ 	MT7996_TXQ_MCU_WM,
+@@ -361,6 +369,7 @@ struct mt7996_dev {
+ 
+ 	/* protects coredump data */
+ 	struct mutex dump_mutex;
++	u8 dump_state;
+ #ifdef CONFIG_DEV_COREDUMP
+ 	struct {
+ 		struct mt7996_crash_data *crash_data[__MT7996_RAM_TYPE_MAX];
+@@ -540,6 +549,7 @@ 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);
++void mt7996_coredump(struct mt7996_dev *dev, u8 state);
+ int mt7996_run(struct ieee80211_hw *hw);
+ int mt7996_mcu_init(struct mt7996_dev *dev);
+ int mt7996_mcu_init_firmware(struct mt7996_dev *dev);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/1015-wifi-mt76-mt7996-add-support-for-runtime-set-in-band.patch b/recipes-wifi/linux-mt76/files/patches-3.x/1015-wifi-mt76-mt7996-add-support-for-runtime-set-in-band.patch
new file mode 100644
index 0000000..33432ff
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/1015-wifi-mt76-mt7996-add-support-for-runtime-set-in-band.patch
@@ -0,0 +1,44 @@
+From 0d4c8fc47472e0acb7f823f483bc8b83c8a9f235 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 6 Jun 2023 16:57:10 +0800
+Subject: [PATCH 1015/1015] wifi: mt76: mt7996: add support for runtime set
+ in-band discovery
+
+with this patch, AP can runtime set inband discovery via hostapd_cli
+
+Usage:
+Enable FILS: hostapd_cli -i [interface] inband_discovery 2 20
+Enable UBPR: hostapd_cli -i [interface] inband_discovery 1 20
+Disable inband discovery: hostapd_cli -i [interface] inband_discovery 0 0
+
+Signed-off-by: MeiChia Chiu <MeiChia.Chiu@mediatek.com>
+---
+ mt7996/mcu.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index aefbdca6..59f22f6d 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2438,8 +2438,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	if (IS_ERR(rskb))
+ 		return PTR_ERR(rskb);
+ 
+-	if (changed & BSS_CHANGED_FILS_DISCOVERY &&
+-	    vif->bss_conf.fils_discovery.max_interval) {
++	if (changed & BSS_CHANGED_FILS_DISCOVERY) {
+ 		interval = vif->bss_conf.fils_discovery.max_interval;
+ 		skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
+ 	} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
+@@ -2475,7 +2474,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ 	discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY);
+ 	discov->tx_interval = interval;
+ 	discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
+-	discov->enable = true;
++	discov->enable = !!(interval);
+ 	discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED);
+ 
+ 	buf = (u8 *)tlv + sizeof(*discov);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2000-wifi-mt76-rework-wed-rx-flow.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2000-wifi-mt76-rework-wed-rx-flow.patch
new file mode 100644
index 0000000..050c6f2
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2000-wifi-mt76-rework-wed-rx-flow.patch
@@ -0,0 +1,443 @@
+From 06c6ad7b99c07aedc5403506c91cbb8e11cf95df Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Feb 2023 13:37:23 +0800
+Subject: [PATCH 2000/2008] wifi: mt76: rework wed rx flow
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Change-Id: Icd787345c811cb5ad30d9c7c1c5f9e5298bd3be6
+---
+ dma.c           | 89 +++++++++++++++++++++++++------------------------
+ mac80211.c      |  2 +-
+ mt76.h          | 23 ++++++++-----
+ mt7915/dma.c    |  2 --
+ mt7915/mmio.c   | 27 +++++++++++----
+ mt7915/mt7915.h |  1 +
+ tx.c            | 16 ++++-----
+ 7 files changed, 90 insertions(+), 70 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index e1e9062b..35db73b9 100644
+--- a/dma.c
++++ b/dma.c
+@@ -59,17 +59,17 @@ mt76_alloc_txwi(struct mt76_dev *dev)
+ 	return t;
+ }
+ 
+-static struct mt76_txwi_cache *
++static struct mt76_rxwi_cache *
+ mt76_alloc_rxwi(struct mt76_dev *dev)
+ {
+-	struct mt76_txwi_cache *t;
++	struct mt76_rxwi_cache *r;
+ 
+-	t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC);
+-	if (!t)
++	r = kzalloc(L1_CACHE_ALIGN(sizeof(*r)), GFP_ATOMIC);
++	if (!r)
+ 		return NULL;
+ 
+-	t->ptr = NULL;
+-	return t;
++	r->ptr = NULL;
++	return r;
+ }
+ 
+ static struct mt76_txwi_cache *
+@@ -88,20 +88,20 @@ __mt76_get_txwi(struct mt76_dev *dev)
+ 	return t;
+ }
+ 
+-static struct mt76_txwi_cache *
++static struct mt76_rxwi_cache *
+ __mt76_get_rxwi(struct mt76_dev *dev)
+ {
+-	struct mt76_txwi_cache *t = NULL;
++	struct mt76_rxwi_cache *r = NULL;
+ 
+-	spin_lock(&dev->wed_lock);
++	spin_lock(&dev->lock);
+ 	if (!list_empty(&dev->rxwi_cache)) {
+-		t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache,
++		r = list_first_entry(&dev->rxwi_cache, struct mt76_rxwi_cache,
+ 				     list);
+-		list_del(&t->list);
++		list_del(&r->list);
+ 	}
+-	spin_unlock(&dev->wed_lock);
++	spin_unlock(&dev->lock);
+ 
+-	return t;
++	return r;
+ }
+ 
+ static struct mt76_txwi_cache *
+@@ -115,13 +115,13 @@ mt76_get_txwi(struct mt76_dev *dev)
+ 	return mt76_alloc_txwi(dev);
+ }
+ 
+-struct mt76_txwi_cache *
++struct mt76_rxwi_cache *
+ mt76_get_rxwi(struct mt76_dev *dev)
+ {
+-	struct mt76_txwi_cache *t = __mt76_get_rxwi(dev);
++	struct mt76_rxwi_cache *r = __mt76_get_rxwi(dev);
+ 
+-	if (t)
+-		return t;
++	if (r)
++		return r;
+ 
+ 	return mt76_alloc_rxwi(dev);
+ }
+@@ -140,14 +140,14 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+ EXPORT_SYMBOL_GPL(mt76_put_txwi);
+ 
+ void
+-mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
++mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r)
+ {
+-	if (!t)
++	if (!r)
+ 		return;
+ 
+-	spin_lock(&dev->wed_lock);
+-	list_add(&t->list, &dev->rxwi_cache);
+-	spin_unlock(&dev->wed_lock);
++	spin_lock(&dev->lock);
++	list_add(&r->list, &dev->rxwi_cache);
++	spin_unlock(&dev->lock);
+ }
+ EXPORT_SYMBOL_GPL(mt76_put_rxwi);
+ 
+@@ -168,13 +168,13 @@ mt76_free_pending_txwi(struct mt76_dev *dev)
+ void
+ mt76_free_pending_rxwi(struct mt76_dev *dev)
+ {
+-	struct mt76_txwi_cache *t;
++	struct mt76_rxwi_cache *r;
+ 
+ 	local_bh_disable();
+-	while ((t = __mt76_get_rxwi(dev)) != NULL) {
+-		if (t->ptr)
+-			mt76_put_page_pool_buf(t->ptr, false);
+-		kfree(t);
++	while ((r = __mt76_get_rxwi(dev)) != NULL) {
++		if (r->ptr)
++			mt76_put_page_pool_buf(r->ptr, false);
++		kfree(r);
+ 	}
+ 	local_bh_enable();
+ }
+@@ -212,7 +212,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ {
+ 	struct mt76_desc *desc = &q->desc[q->head];
+ 	struct mt76_queue_entry *entry = &q->entry[q->head];
+-	struct mt76_txwi_cache *txwi = NULL;
++	struct mt76_rxwi_cache *rxwi = NULL;
+ 	u32 buf1 = 0, ctrl;
+ 	int idx = q->head;
+ 	int rx_token;
+@@ -220,13 +220,13 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ 	ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
+ 
+ 	if (mt76_queue_is_wed_rx(q)) {
+-		txwi = mt76_get_rxwi(dev);
+-		if (!txwi)
++		rxwi = mt76_get_rxwi(dev);
++		if (!rxwi)
+ 			return -ENOMEM;
+ 
+-		rx_token = mt76_rx_token_consume(dev, data, txwi, buf->addr);
++		rx_token = mt76_rx_token_consume(dev, data, rxwi, buf->addr);
+ 		if (rx_token < 0) {
+-			mt76_put_rxwi(dev, txwi);
++			mt76_put_rxwi(dev, rxwi);
+ 			return -ENOMEM;
+ 		}
+ 
+@@ -241,7 +241,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ 
+ 	entry->dma_addr[0] = buf->addr;
+ 	entry->dma_len[0] = buf->len;
+-	entry->txwi = txwi;
++	entry->rxwi = rxwi;
+ 	entry->buf = data;
+ 	entry->wcid = 0xffff;
+ 	entry->skip_buf1 = true;
+@@ -404,20 +404,20 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 	if (mt76_queue_is_wed_rx(q)) {
+ 		u32 buf1 = le32_to_cpu(desc->buf1);
+ 		u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
+-		struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token);
++		struct mt76_rxwi_cache *r = mt76_rx_token_release(dev, token);
+ 
+-		if (!t)
++		if (!r)
+ 			return NULL;
+ 
+-		dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr,
++		dma_sync_single_for_cpu(dev->dma_dev, r->dma_addr,
+ 				SKB_WITH_OVERHEAD(q->buf_size),
+ 				page_pool_get_dma_dir(q->page_pool));
+ 
+-		buf = t->ptr;
+-		t->dma_addr = 0;
+-		t->ptr = NULL;
++		buf = r->ptr;
++		r->dma_addr = 0;
++		r->ptr = NULL;
+ 
+-		mt76_put_rxwi(dev, t);
++		mt76_put_rxwi(dev, r);
+ 
+ 		if (drop) {
+ 			u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+@@ -977,16 +977,19 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+ 	mt76_for_each_q_rx(dev, i) {
+ 		struct mt76_queue *q = &dev->q_rx[i];
+ 
++		if (mt76_queue_is_wed_rx(q))
++			continue;
++
+ 		netif_napi_del(&dev->napi[i]);
+ 		mt76_dma_rx_cleanup(dev, q);
+ 
+ 		page_pool_destroy(q->page_pool);
+ 	}
+ 
+-	mt76_free_pending_txwi(dev);
+-	mt76_free_pending_rxwi(dev);
+-
+ 	if (mtk_wed_device_active(&dev->mmio.wed))
+ 		mtk_wed_device_detach(&dev->mmio.wed);
++
++	mt76_free_pending_txwi(dev);
++	mt76_free_pending_rxwi(dev);
+ }
+ EXPORT_SYMBOL_GPL(mt76_dma_cleanup);
+diff --git a/mac80211.c b/mac80211.c
+index 6430e6ee..5a203d31 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -613,7 +613,6 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+ 	spin_lock_init(&dev->lock);
+ 	spin_lock_init(&dev->cc_lock);
+ 	spin_lock_init(&dev->status_lock);
+-	spin_lock_init(&dev->wed_lock);
+ 	mutex_init(&dev->mutex);
+ 	init_waitqueue_head(&dev->tx_wait);
+ 
+@@ -644,6 +643,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
+ 	INIT_LIST_HEAD(&dev->txwi_cache);
+ 	INIT_LIST_HEAD(&dev->rxwi_cache);
+ 	dev->token_size = dev->drv->token_size;
++	dev->rx_token_size = dev->drv->rx_token_size;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
+ 		skb_queue_head_init(&dev->rx_skb[i]);
+diff --git a/mt76.h b/mt76.h
+index 8abb6f41..72c3eb8f 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -180,6 +180,7 @@ struct mt76_queue_entry {
+ 	};
+ 	union {
+ 		struct mt76_txwi_cache *txwi;
++		struct mt76_rxwi_cache *rxwi;
+ 		struct urb *urb;
+ 		int buf_sz;
+ 	};
+@@ -371,10 +372,14 @@ struct mt76_txwi_cache {
+ 	struct list_head list;
+ 	dma_addr_t dma_addr;
+ 
+-	union {
+-		struct sk_buff *skb;
+-		void *ptr;
+-	};
++	struct sk_buff *skb;
++};
++
++struct mt76_rxwi_cache {
++	struct list_head list;
++	dma_addr_t dma_addr;
++
++	void *ptr;
+ };
+ 
+ struct mt76_rx_tid {
+@@ -460,6 +465,7 @@ struct mt76_driver_ops {
+ 	u16 txwi_size;
+ 	u16 token_size;
+ 	u8 mcs_rates;
++	u16 rx_token_size;
+ 
+ 	void (*update_survey)(struct mt76_phy *phy);
+ 
+@@ -810,7 +816,6 @@ struct mt76_dev {
+ 
+ 	struct ieee80211_hw *hw;
+ 
+-	spinlock_t wed_lock;
+ 	spinlock_t lock;
+ 	spinlock_t cc_lock;
+ 
+@@ -1360,8 +1365,8 @@ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
+ }
+ 
+ void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+-void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+-struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
++void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_rxwi_cache *r);
++struct mt76_rxwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
+ void mt76_free_pending_rxwi(struct mt76_dev *dev);
+ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
+ 		      struct napi_struct *napi);
+@@ -1515,9 +1520,9 @@ struct mt76_txwi_cache *
+ mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
+ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
+ 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);
++struct mt76_rxwi_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);
++			  struct mt76_rxwi_cache *r, dma_addr_t phys);
+ int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q);
+ static inline void mt76_put_page_pool_buf(void *buf, bool allow_direct)
+ {
+diff --git a/mt7915/dma.c b/mt7915/dma.c
+index 86a93ded..848e9843 100644
+--- a/mt7915/dma.c
++++ b/mt7915/dma.c
+@@ -493,7 +493,6 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
+ 		    mtk_wed_get_rx_capa(&mdev->mmio.wed)) {
+ 			dev->mt76.q_rx[MT_RXQ_MAIN].flags =
+ 				MT_WED_Q_RX(MT7915_RXQ_BAND0);
+-			dev->mt76.rx_token_size += MT7915_RX_RING_SIZE;
+ 		}
+ 
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+@@ -530,7 +529,6 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
+ 		    mtk_wed_get_rx_capa(&mdev->mmio.wed)) {
+ 			dev->mt76.q_rx[MT_RXQ_BAND1].flags =
+ 				MT_WED_Q_RX(MT7915_RXQ_BAND1);
+-			dev->mt76.rx_token_size += MT7915_RX_RING_SIZE;
+ 		}
+ 
+ 		/* rx data queue for band1 */
+diff --git a/mt7915/mmio.c b/mt7915/mmio.c
+index 984b5f60..46256842 100644
+--- a/mt7915/mmio.c
++++ b/mt7915/mmio.c
+@@ -600,16 +600,28 @@ static void mt7915_mmio_wed_release_rx_buf(struct mtk_wed_device *wed)
+ 
+ 	dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+ 	for (i = 0; i < dev->mt76.rx_token_size; i++) {
+-		struct mt76_txwi_cache *t;
++		struct mt76_rxwi_cache *r;
+ 
+-		t = mt76_rx_token_release(&dev->mt76, i);
+-		if (!t || !t->ptr)
++		r = mt76_rx_token_release(&dev->mt76, i);
++		if (!r || !r->ptr)
+ 			continue;
+ 
+-		mt76_put_page_pool_buf(t->ptr, false);
+-		t->ptr = NULL;
++		mt76_put_page_pool_buf(r->ptr, false);
++		r->ptr = NULL;
+ 
+-		mt76_put_rxwi(&dev->mt76, t);
++		mt76_put_rxwi(&dev->mt76, r);
++	}
++
++	mt76_for_each_q_rx(dev, i) {
++		struct mt76_queue *q = &dev->q_rx[i];
++
++		if (!mt76_queue_is_wed_rx(q))
++			continue;
++
++		netif_napi_del(&dev->napi[i]);
++		mt76_dma_rx_cleanup(dev, q);
++
++		page_pool_destroy(q->page_pool);
+ 	}
+ 
+ 	mt76_free_pending_rxwi(&dev->mt76);
+@@ -812,7 +824,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
+ 	wed->wlan.reset = mt7915_mmio_wed_reset;
+ 	wed->wlan.reset_complete = mt7915_mmio_wed_reset_complete;
+ 
+-	dev->mt76.rx_token_size = wed->wlan.rx_npkt;
++	dev->mt76.rx_token_size += wed->wlan.rx_npkt;
+ 
+ 	if (mtk_wed_device_attach(wed))
+ 		return 0;
+@@ -1018,6 +1030,7 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev,
+ 				SURVEY_INFO_TIME_RX |
+ 				SURVEY_INFO_TIME_BSS_RX,
+ 		.token_size = MT7915_TOKEN_SIZE,
++		.rx_token_size = MT7915_RX_TOKEN_SIZE;
+ 		.tx_prepare_skb = mt7915_tx_prepare_skb,
+ 		.tx_complete_skb = mt76_connac_tx_complete_skb,
+ 		.rx_skb = mt7915_queue_rx_skb,
+diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
+index 103cd0d7..8ee62f63 100644
+--- a/mt7915/mt7915.h
++++ b/mt7915/mt7915.h
+@@ -62,6 +62,7 @@
+ #define MT7915_EEPROM_BLOCK_SIZE	16
+ #define MT7915_HW_TOKEN_SIZE		4096
+ #define MT7915_TOKEN_SIZE		8192
++#define MT7915_RX_TOKEN_SIZE		4096
+ 
+ #define MT7915_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
+ #define MT7915_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
+diff --git a/tx.c b/tx.c
+index 72b3ec71..6cb71f34 100644
+--- a/tx.c
++++ b/tx.c
+@@ -761,16 +761,16 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ EXPORT_SYMBOL_GPL(mt76_token_consume);
+ 
+ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+-			  struct mt76_txwi_cache *t, dma_addr_t phys)
++			  struct mt76_rxwi_cache *r, dma_addr_t phys)
+ {
+ 	int token;
+ 
+ 	spin_lock_bh(&dev->rx_token_lock);
+-	token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size,
++	token = idr_alloc(&dev->rx_token, r, 0, dev->rx_token_size,
+ 			  GFP_ATOMIC);
+ 	if (token >= 0) {
+-		t->ptr = ptr;
+-		t->dma_addr = phys;
++		r->ptr = ptr;
++		r->dma_addr = phys;
+ 	}
+ 	spin_unlock_bh(&dev->rx_token_lock);
+ 
+@@ -807,15 +807,15 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
+ }
+ EXPORT_SYMBOL_GPL(mt76_token_release);
+ 
+-struct mt76_txwi_cache *
++struct mt76_rxwi_cache *
+ mt76_rx_token_release(struct mt76_dev *dev, int token)
+ {
+-	struct mt76_txwi_cache *t;
++	struct mt76_rxwi_cache *r;
+ 
+ 	spin_lock_bh(&dev->rx_token_lock);
+-	t = idr_remove(&dev->rx_token, token);
++	r = idr_remove(&dev->rx_token, token);
+ 	spin_unlock_bh(&dev->rx_token_lock);
+ 
+-	return t;
++	return r;
+ }
+ EXPORT_SYMBOL_GPL(mt76_rx_token_release);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0022-mt76-revert-page-pool-changes.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2001-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
similarity index 83%
rename from recipes-wifi/linux-mt76/files/patches-3.x/0022-mt76-revert-page-pool-changes.patch
rename to recipes-wifi/linux-mt76/files/patches-3.x/2001-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
index f0daca8..58605d3 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/0022-mt76-revert-page-pool-changes.patch
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2001-wifi-mt76-revert-page_poll-for-kernel-5.4.patch
@@ -1,44 +1,47 @@
-From d28a3716f729848ef65192c84411b0912afe70c6 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 22/22] mt76: revert page pool changes
+From 15b04fc966aa8f30492726132a6b81466b187581 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Feb 2023 19:49:22 +0800
+Subject: [PATCH 2001/2008] wifi: mt76: revert page_poll for kernel 5.4
 
+This reverts commit e8c10835cf062c577ddf426913788c39d30b4bd7.
+
+Change-Id: I4e5764fc545087f691fb4c2f43e7a9cefd1e1657
 ---
- dma.c         | 72 ++++++++++++++++++++++++++-------------------------
- mac80211.c    | 57 ----------------------------------------
- mt76.h        | 22 +---------------
- mt7915/main.c | 26 +++++++------------
- mt7915/mmio.c | 55 ++++++++++++++++++++++++---------------
- mt7921/main.c | 31 +++-------------------
- usb.c         | 43 +++++++++++++++---------------
- 7 files changed, 108 insertions(+), 198 deletions(-)
+ dma.c         | 78 +++++++++++++++++++++++++++++----------------------
+ mac80211.c    | 57 -------------------------------------
+ mt76.h        | 22 +--------------
+ mt7915/main.c | 26 +++++++----------
+ mt7915/mmio.c | 55 ++++++++++++++++++++++--------------
+ mt7921/main.c | 31 +++-----------------
+ usb.c         | 43 ++++++++++++++--------------
+ 7 files changed, 115 insertions(+), 197 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index 465190eb..f560d37d 100644
+index 35db73b9..7153be47 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -173,7 +173,7 @@ mt76_free_pending_rxwi(struct mt76_dev *dev)
  	local_bh_disable();
- 	while ((t = __mt76_get_rxwi(dev)) != NULL) {
- 		if (t->ptr)
--			mt76_put_page_pool_buf(t->ptr, false);
-+			skb_free_frag(t->ptr);
- 		kfree(t);
+ 	while ((r = __mt76_get_rxwi(dev)) != NULL) {
+ 		if (r->ptr)
+-			mt76_put_page_pool_buf(r->ptr, false);
++			skb_free_frag(r->ptr);
+ 		kfree(r);
  	}
  	local_bh_enable();
 @@ -409,9 +409,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- 		if (!t)
+ 		if (!r)
  			return NULL;
  
--		dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr,
+-		dma_sync_single_for_cpu(dev->dma_dev, r->dma_addr,
 -				SKB_WITH_OVERHEAD(q->buf_size),
 -				page_pool_get_dma_dir(q->page_pool));
-+		dma_unmap_single(dev->dma_dev, t->dma_addr,
++		dma_unmap_single(dev->dma_dev, r->dma_addr,
 +				 SKB_WITH_OVERHEAD(q->buf_size),
 +				 DMA_FROM_DEVICE);
  
- 		buf = t->ptr;
- 		t->dma_addr = 0;
+ 		buf = r->ptr;
+ 		r->dma_addr = 0;
 @@ -430,9 +430,9 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
  	} else {
  		buf = e->buf;
@@ -52,7 +55,7 @@
  	}
  
  	return buf;
-@@ -586,11 +586,11 @@ free_skb:
+@@ -592,11 +592,11 @@ free_skb:
  }
  
  static int
@@ -67,7 +70,7 @@
  
  	if (!q->ndesc)
  		return 0;
-@@ -598,25 +598,26 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -604,25 +604,26 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q,
  	spin_lock_bh(&q->lock);
  
  	while (q->queued < q->ndesc - 1) {
@@ -75,8 +78,7 @@
  		struct mt76_queue_buf qbuf;
 -		dma_addr_t addr;
 -		int offset;
--		void *buf;
-+		void *buf = NULL;
+ 		void *buf;
  
 -		buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
 +		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
@@ -105,7 +107,7 @@
  			break;
  		}
  		frames++;
-@@ -660,7 +661,7 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+@@ -666,7 +667,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 +116,7 @@
  		q->flags = flags;
  
  		ret = mtk_wed_device_txfree_ring_setup(wed, q->regs);
-@@ -708,10 +709,6 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -714,10 +715,6 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
  	if (!q->entry)
  		return -ENOMEM;
  
@@ -125,7 +127,7 @@
  	ret = mt76_dma_wed_setup(dev, q, false);
  	if (ret)
  		return ret;
-@@ -725,6 +722,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -731,6 +728,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,20 +135,28 @@
  	void *buf;
  	bool more;
  
-@@ -738,7 +736,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -744,7 +742,10 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
  		if (!buf)
  			break;
  
 -		mt76_put_page_pool_buf(buf, false);
++		if (q->flags & MT_QFLAG_RRO)
++			continue;
++
 +		skb_free_frag(buf);
  	} while (1);
  
  	if (q->rx_head) {
-@@ -747,6 +745,13 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -753,6 +754,18 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
  	}
  
  	spin_unlock_bh(&q->lock);
 +
++	if (((q->flags & MT_QFLAG_WED) &&
++	     FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) ||
++	    (q->flags & MT_QFLAG_RRO))
++		return;
++
 +	if (!q->rx_page.va)
 +		return;
 +
@@ -156,7 +166,7 @@
  }
  
  static void
-@@ -767,7 +772,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+@@ -773,7 +786,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 +175,7 @@
  	}
  }
  
-@@ -785,7 +790,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+@@ -791,7 +804,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 +184,7 @@
  	}
  
  	if (more)
-@@ -858,7 +863,6 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -864,7 +877,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 +192,7 @@
  
  		*(u32 *)skb->cb = info;
  
-@@ -874,10 +878,10 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -880,10 +892,10 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
  		continue;
  
  free_frag:
@@ -195,7 +205,7 @@
  	return done;
  }
  
-@@ -922,7 +926,7 @@ mt76_dma_init(struct mt76_dev *dev,
+@@ -928,7 +940,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 +214,7 @@
  		napi_enable(&dev->napi[i]);
  	}
  
-@@ -973,8 +977,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+@@ -982,8 +994,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
  
  		netif_napi_del(&dev->napi[i]);
  		mt76_dma_rx_cleanup(dev, q);
@@ -212,9 +222,9 @@
 -		page_pool_destroy(q->page_pool);
  	}
  
- 	mt76_free_pending_txwi(dev);
+ 	if (mtk_wed_device_active(&dev->mmio.wed))
 diff --git a/mac80211.c b/mac80211.c
-index d1cdaee8..4599f697 100644
+index 5a203d31..f7578308 100644
 --- a/mac80211.c
 +++ b/mac80211.c
 @@ -4,7 +4,6 @@
@@ -224,8 +234,8 @@
 -#include <net/page_pool.h>
  #include "mt76.h"
  
- #define CHAN2G(_idx, _freq) {			\
-@@ -562,47 +561,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
+ static const struct ieee80211_channel mt76_channels_2ghz[] = {
+@@ -542,47 +541,6 @@ void mt76_unregister_phy(struct mt76_phy *phy)
  }
  EXPORT_SYMBOL_GPL(mt76_unregister_phy);
  
@@ -273,7 +283,7 @@
  struct mt76_dev *
  mt76_alloc_device(struct device *pdev, unsigned int size,
  		  const struct ieee80211_ops *ops,
-@@ -1748,21 +1706,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
+@@ -1728,21 +1686,6 @@ void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
  }
  EXPORT_SYMBOL_GPL(mt76_ethtool_worker);
  
@@ -296,10 +306,10 @@
  {
  	struct ieee80211_hw *hw = phy->hw;
 diff --git a/mt76.h b/mt76.h
-index 31d5dc37..f012ac0c 100644
+index 72c3eb8f..a0c20d36 100644
 --- a/mt76.h
 +++ b/mt76.h
-@@ -202,7 +202,7 @@ struct mt76_queue {
+@@ -224,7 +224,7 @@ struct mt76_queue {
  
  	dma_addr_t desc_dma;
  	struct sk_buff *rx_head;
@@ -308,7 +318,7 @@
  };
  
  struct mt76_mcu_ops {
-@@ -1363,7 +1363,6 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len,
+@@ -1410,7 +1410,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,10 +326,10 @@
  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);
-@@ -1475,25 +1474,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);
+@@ -1523,25 +1522,6 @@ void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+ struct mt76_rxwi_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);
+ 			  struct mt76_rxwi_cache *r, dma_addr_t phys);
 -int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q);
 -static inline void mt76_put_page_pool_buf(void *buf, bool allow_direct)
 -{
@@ -401,7 +411,7 @@
  
  static void
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 984b5f60..1bb8a4cb 100644
+index 46256842..8ff2c70c 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)
@@ -416,20 +426,20 @@
 +				sizeof(struct skb_shared_info));
 +
  	for (i = 0; i < dev->mt76.rx_token_size; i++) {
- 		struct mt76_txwi_cache *t;
+ 		struct mt76_rxwi_cache *r;
  
 @@ -606,7 +610,9 @@ static void mt7915_mmio_wed_release_rx_buf(struct mtk_wed_device *wed)
- 		if (!t || !t->ptr)
+ 		if (!r || !r->ptr)
  			continue;
  
--		mt76_put_page_pool_buf(t->ptr, false);
-+		dma_unmap_single(dev->mt76.dma_dev, t->dma_addr,
+-		mt76_put_page_pool_buf(r->ptr, false);
++		dma_unmap_single(dev->mt76.dma_dev, r->dma_addr,
 +				 wed->wlan.rx_size, DMA_FROM_DEVICE);
-+		__free_pages(virt_to_page(t->ptr), get_order(length));
- 		t->ptr = NULL;
++		__free_pages(virt_to_page(r->ptr), get_order(length));
+ 		r->ptr = NULL;
  
- 		mt76_put_rxwi(&dev->mt76, t);
-@@ -618,38 +624,47 @@ static void mt7915_mmio_wed_release_rx_buf(struct mtk_wed_device *wed)
+ 		mt76_put_rxwi(&dev->mt76, r);
+@@ -630,38 +636,47 @@ static void mt7915_mmio_wed_release_rx_buf(struct mtk_wed_device *wed)
  static u32 mt7915_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
  {
  	struct mtk_rxbm_desc *desc = wed->rx_buf_ring.desc;
@@ -450,7 +460,7 @@
 -		enum dma_data_direction dir;
 -		dma_addr_t addr;
 -		u32 offset;
-+		struct mt76_txwi_cache *t = mt76_get_rxwi(&dev->mt76);
++		struct mt76_rxwi_cache *r = mt76_get_rxwi(&dev->mt76);
 +		dma_addr_t phy_addr;
 +		struct page *page;
  		int token;
@@ -465,7 +475,7 @@
 -		if (!buf)
 +		page = __dev_alloc_pages(GFP_KERNEL, get_order(length));
 +		if (!page) {
-+			mt76_put_rxwi(&dev->mt76, t);
++			mt76_put_rxwi(&dev->mt76, r);
  			goto unmap;
 +		}
  
@@ -478,24 +488,24 @@
 +					  DMA_TO_DEVICE);
 +		if (unlikely(dma_mapping_error(dev->mt76.dev, phy_addr))) {
 +			__free_pages(page, get_order(length));
-+			mt76_put_rxwi(&dev->mt76, t);
++			mt76_put_rxwi(&dev->mt76, r);
 +			goto unmap;
 +		}
  
 -		desc->buf0 = cpu_to_le32(addr);
 -		token = mt76_rx_token_consume(&dev->mt76, buf, t, addr);
 +		desc->buf0 = cpu_to_le32(phy_addr);
-+		token = mt76_rx_token_consume(&dev->mt76, ptr, t, phy_addr);
++		token = mt76_rx_token_consume(&dev->mt76, ptr, r, phy_addr);
  		if (token < 0) {
 -			mt76_put_page_pool_buf(buf, false);
 +			dma_unmap_single(dev->mt76.dma_dev, phy_addr,
 +					 wed->wlan.rx_size, DMA_TO_DEVICE);
 +			__free_pages(page, get_order(length));
-+			mt76_put_rxwi(&dev->mt76, t);
++			mt76_put_rxwi(&dev->mt76, r);
  			goto unmap;
  		}
  
-@@ -661,8 +676,6 @@ static u32 mt7915_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+@@ -673,8 +688,6 @@ static u32 mt7915_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
  	return 0;
  
  unmap:
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2002-wifi-mt76-wed-change-wed-token-init-size-to-adapt-we.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2002-wifi-mt76-wed-change-wed-token-init-size-to-adapt-we.patch
new file mode 100644
index 0000000..21f5e83
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2002-wifi-mt76-wed-change-wed-token-init-size-to-adapt-we.patch
@@ -0,0 +1,38 @@
+From 2ced3e3d33ef919332226f09a214ef2b04555a6d Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 19 Apr 2023 17:13:41 +0800
+Subject: [PATCH 2002/2008] wifi: mt76: wed: change wed token init size to
+ adapt wed3.0
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ tx.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/tx.c b/tx.c
+index 6cb71f34..618c99a1 100644
+--- a/tx.c
++++ b/tx.c
+@@ -737,12 +737,16 @@ EXPORT_SYMBOL_GPL(__mt76_set_tx_blocked);
+ 
+ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ {
+-	int token;
++	int token, start = 0;
++
++	if (mtk_wed_device_active(&dev->mmio.wed))
++		start = dev->mmio.wed.wlan.nbuf;
+ 
+ 	spin_lock_bh(&dev->token_lock);
+ 
+-	token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC);
+-	if (token >= 0)
++	token = idr_alloc(&dev->token, *ptxwi, start, start + dev->token_size,
++			  GFP_ATOMIC);
++	if (token >= start)
+ 		dev->token_count++;
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2003-wifi-mt76-mt7996-wed-add-wed3.0-tx-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2003-wifi-mt76-mt7996-wed-add-wed3.0-tx-support.patch
new file mode 100644
index 0000000..feb77a6
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2003-wifi-mt76-mt7996-wed-add-wed3.0-tx-support.patch
@@ -0,0 +1,995 @@
+From d9167faacb2a8466e2d19993f29b2c0770c5164e Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Wed, 26 Apr 2023 16:44:57 +0800
+Subject: [PATCH 2003/2008] wifi: mt76: mt7996: wed: add wed3.0 tx support
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ dma.c           |  17 ++-
+ mt76.h          |   7 ++
+ mt7996/dma.c    | 128 ++++++++++++++++++---
+ mt7996/init.c   |  21 +++-
+ mt7996/mac.c    |  29 ++++-
+ mt7996/main.c   |  46 ++++++++
+ mt7996/mmio.c   | 295 +++++++++++++++++++++++++++++++++++++++++++++---
+ mt7996/mt7996.h |   8 +-
+ mt7996/pci.c    |  72 +++++++++---
+ mt7996/regs.h   |   5 +
+ 10 files changed, 567 insertions(+), 61 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 7153be47..930ec768 100644
+--- a/dma.c
++++ b/dma.c
+@@ -13,6 +13,11 @@
+ 	u32 _offset = offsetof(struct mt76_queue_regs, _field);		\
+ 	u32 _val;							\
+ 	if ((_q)->flags & MT_QFLAG_WED)					\
++		if((_q)->flags & MT_QFLAG_WED_EXT)			\
++		_val = mtk_wed_device_reg_read(&(_dev)->mmio.wed_ext,	\
++					       ((_q)->wed_regs +	\
++					        _offset));		\
++		else							\
+ 		_val = mtk_wed_device_reg_read(&(_dev)->mmio.wed,	\
+ 					       ((_q)->wed_regs +	\
+ 					        _offset));		\
+@@ -24,6 +29,11 @@
+ #define Q_WRITE(_dev, _q, _field, _val)	do {				\
+ 	u32 _offset = offsetof(struct mt76_queue_regs, _field);		\
+ 	if ((_q)->flags & MT_QFLAG_WED)					\
++		if((_q)->flags & MT_QFLAG_WED_EXT)			\
++		mtk_wed_device_reg_write(&(_dev)->mmio.wed_ext,		\
++					 ((_q)->wed_regs + _offset),	\
++					 _val);				\
++		else							\
+ 		mtk_wed_device_reg_write(&(_dev)->mmio.wed,		\
+ 					 ((_q)->wed_regs + _offset),	\
+ 					 _val);				\
+@@ -654,6 +664,9 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ 	if (!(q->flags & MT_QFLAG_WED))
+ 		return 0;
+ 
++	if ((q->flags & MT_QFLAG_WED_EXT))
++		wed = &dev->mmio.wed_ext;
++
+ 	type = FIELD_GET(MT_QFLAG_WED_TYPE, q->flags);
+ 	ring = FIELD_GET(MT_QFLAG_WED_RING, q->flags);
+ 
+@@ -719,7 +732,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ 	if (ret)
+ 		return ret;
+ 
+-	if (q->flags != MT_WED_Q_TXFREE)
++	if (!mt76_queue_is_txfree(q))
+ 		mt76_dma_queue_reset(dev, q);
+ 
+ 	return 0;
+@@ -999,6 +1012,8 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+ 	if (mtk_wed_device_active(&dev->mmio.wed))
+ 		mtk_wed_device_detach(&dev->mmio.wed);
+ 
++	if (mtk_wed_device_active(&dev->mmio.wed_ext))
++		mtk_wed_device_detach(&dev->mmio.wed_ext);
+ 	mt76_free_pending_txwi(dev);
+ 	mt76_free_pending_rxwi(dev);
+ }
+diff --git a/mt76.h b/mt76.h
+index a0c20d36..ee0dbdd7 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -51,6 +51,7 @@
+ #define MT_QFLAG_WED_RING	GENMASK(1, 0)
+ #define MT_QFLAG_WED_TYPE	GENMASK(3, 2)
+ #define MT_QFLAG_WED		BIT(4)
++#define MT_QFLAG_WED_EXT	BIT(11)
+ 
+ #define __MT_WED_Q(_type, _n)	(MT_QFLAG_WED | \
+ 				 FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \
+@@ -623,6 +624,7 @@ struct mt76_mmio {
+ 	u32 irqmask;
+ 
+ 	struct mtk_wed_device wed;
++	struct mtk_wed_device wed_ext;
+ 	struct completion wed_reset;
+ 	struct completion wed_reset_complete;
+ };
+@@ -1514,6 +1516,11 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+ 	return (q->flags & MT_QFLAG_WED) &&
+ 	       FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX;
+ }
++static inline bool mt76_queue_is_txfree(struct mt76_queue *q)
++{
++	return (q->flags & MT_QFLAG_WED) &&
++	       FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TXFREE;
++}
+ 
+ struct mt76_txwi_cache *
+ mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index b8f253d0..673b08bb 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -7,6 +7,25 @@
+ #include "../dma.h"
+ #include "mac.h"
+ 
++int
++mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, int n_desc,
++		     int ring_base, struct mtk_wed_device *wed)
++{
++	struct mt7996_dev *dev = phy->dev;
++	u32 flags = 0;
++
++	if (mtk_wed_device_active(wed)) {
++		ring_base += MT_TXQ_ID(0) * MT_RING_SIZE;
++		idx -= MT_TXQ_ID(0);
++		flags = MT_WED_Q_TX(idx);
++		if (phy->mt76->band_idx == MT_BAND2)
++			flags = MT_QFLAG_WED_EXT | MT_WED_Q_TX(0) ;
++	}
++
++	return mt76_connac_init_tx_queues(phy->mt76, idx, n_desc,
++					  ring_base, flags);
++}
++
+ static int mt7996_poll_tx(struct napi_struct *napi, int budget)
+ {
+ 	struct mt7996_dev *dev;
+@@ -128,7 +147,7 @@ static void mt7996_dma_disable(struct mt7996_dev *dev, bool reset)
+ 	}
+ }
+ 
+-void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
++void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ {
+ 	u32 hif1_ofs = 0;
+ 	u32 irq_mask;
+@@ -153,11 +172,9 @@ void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 	}
+ 
+ 	/* enable interrupts for TX/RX rings */
+-	irq_mask = MT_INT_MCU_CMD;
+-	if (reset)
+-		goto done;
+-
+-	irq_mask |= (MT_INT_RX_DONE_MCU | MT_INT_TX_DONE_MCU);
++	irq_mask = MT_INT_MCU_CMD |
++			   MT_INT_RX_DONE_MCU |
++			   MT_INT_TX_DONE_MCU;
+ 
+ 	if (mt7996_band_valid(dev, MT_BAND0))
+ 		irq_mask |= MT_INT_BAND0_RX_DONE;
+@@ -168,7 +185,18 @@ void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 	if (mt7996_band_valid(dev, MT_BAND2))
+ 		irq_mask |= MT_INT_BAND2_RX_DONE;
+ 
+-done:
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed) && wed_reset) {
++		u32 wed_irq_mask = irq_mask;
++
++		wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1;
++
++		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
++
++		mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask);
++	}
++
++	irq_mask = reset ? MT_INT_MCU_CMD : irq_mask;
++
+ 	mt7996_irq_enable(dev, irq_mask);
+ 	mt7996_irq_disable(dev, 0);
+ }
+@@ -241,19 +269,24 @@ static int mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 		/* fix hardware limitation, pcie1's rx ring3 is not available
+ 		 * so, redirect pcie0 rx ring3 interrupt to pcie1
+ 		 */
+-		mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
+-			 MT_WFDMA0_RX_INT_SEL_RING3);
+-
+-		/* TODO: redirect rx ring6 interrupt to pcie0 for wed function */
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed) && dev->rro_support)
++			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
++				 MT_WFDMA0_RX_INT_SEL_RING6);
++		else
++			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
++				 MT_WFDMA0_RX_INT_SEL_RING3);
+ 	}
+ 
+-	__mt7996_dma_enable(dev, reset);
++	__mt7996_dma_enable(dev, reset, true);
+ 
+ 	return 0;
+ }
+ 
+ int mt7996_dma_init(struct mt7996_dev *dev)
+ {
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++	struct mtk_wed_device *wed_ext = &dev->mt76.mmio.wed_ext;
++	u32 rx_base;
+ 	u32 hif1_ofs = 0;
+ 	int ret;
+ 
+@@ -267,10 +300,11 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	mt7996_dma_disable(dev, true);
+ 
+ 	/* init tx queue */
+-	ret = mt76_connac_init_tx_queues(dev->phy.mt76,
+-					 MT_TXQ_ID(dev->mphy.band_idx),
+-					 MT7996_TX_RING_SIZE,
+-					 MT_TXQ_RING_BASE(0), 0);
++	ret = mt7996_init_tx_queues(&dev->phy,
++				    MT_TXQ_ID(dev->mphy.band_idx),
++				    MT7996_TX_RING_SIZE,
++				    MT_TXQ_RING_BASE(0),
++				    wed);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -326,6 +360,9 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 		return ret;
+ 
+ 	/* tx free notify event from WA for band0 */
++	if (mtk_wed_device_active(wed) && !dev->rro_support)
++		dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
++
+ 	ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA],
+ 			       MT_RXQ_ID(MT_RXQ_MAIN_WA),
+ 			       MT7996_RX_MCU_RING_SIZE,
+@@ -336,17 +373,24 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 
+ 	if (mt7996_band_valid(dev, MT_BAND2)) {
+ 		/* rx data queue for band2 */
++		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs;
++		if (mtk_wed_device_active(wed))
++			rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2);
++
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
+ 				       MT_RXQ_ID(MT_RXQ_BAND2),
+ 				       MT7996_RX_RING_SIZE,
+ 				       MT_RX_BUF_SIZE,
+-				       MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs);
++				       rx_base);
+ 		if (ret)
+ 			return ret;
+ 
+ 		/* tx free notify event from WA for band2
+ 		 * use pcie0's rx ring3, but, redirect pcie0 rx ring3 interrupt to pcie1
+ 		 */
++		if (mtk_wed_device_active(wed_ext) && !dev->rro_support)
++			dev->mt76.q_rx[MT_RXQ_BAND2_WA].flags = MT_WED_Q_TXFREE |
++								MT_QFLAG_WED_EXT;
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2_WA],
+ 				       MT_RXQ_ID(MT_RXQ_BAND2_WA),
+ 				       MT7996_RX_MCU_RING_SIZE,
+@@ -356,6 +400,56 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 			return ret;
+ 	}
+ 
++
++	if (dev->rro_support) {
++		/* rx rro data queue for band0 */
++		dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags = MT_RRO_Q_DATA(0);
++		dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags |= MT_QFLAG_MAGIC;
++		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0],
++				       MT_RXQ_ID(MT_RXQ_RRO_BAND0),
++				       MT7996_RX_RING_SIZE,
++				       MT7996_RX_BUF_SIZE,
++				       MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0));
++		if (ret)
++			return ret;
++
++		/* tx free notify event from WA for band0 */
++		if (mtk_wed_device_active(wed))
++			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
++		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
++				       MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
++				       MT7996_RX_MCU_RING_SIZE,
++				       MT7996_RX_BUF_SIZE,
++				       MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0));
++		if (ret)
++			return ret;
++
++		if (mt7996_band_valid(dev, MT_BAND2)) {
++			/* rx rro data queue for band2 */
++			dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags = MT_RRO_Q_DATA(1);
++			dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags |= MT_QFLAG_MAGIC;
++			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2],
++					       MT_RXQ_ID(MT_RXQ_RRO_BAND2),
++					       MT7996_RX_RING_SIZE,
++					       MT7996_RX_BUF_SIZE,
++					       MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND2) + hif1_ofs);
++			if (ret)
++				return ret;
++
++			/* tx free notify event from MAC for band2 */
++			if (mtk_wed_device_active(wed_ext))
++				dev->mt76.q_rx[MT_RXQ_TXFREE_BAND2].flags = MT_WED_Q_TXFREE |
++									    MT_QFLAG_WED_EXT;
++			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND2],
++					       MT_RXQ_ID(MT_RXQ_TXFREE_BAND2),
++					       MT7996_RX_MCU_RING_SIZE,
++					       MT7996_RX_BUF_SIZE,
++					       MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND2) + hif1_ofs);
++			if (ret)
++				return ret;
++		}
++	}
++
+ 	ret = mt76_init_queues(dev, mt76_dma_rx_poll);
+ 	if (ret < 0)
+ 		return ret;
+diff --git a/mt7996/init.c b/mt7996/init.c
+index a6caf4f1..6cfbc50d 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -534,6 +534,7 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	struct mt76_phy *mphy;
+ 	u32 mac_ofs, hif1_ofs = 0;
+ 	int ret;
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 
+ 	if (!mt7996_band_valid(dev, band) || band == MT_BAND0)
+ 		return 0;
+@@ -541,8 +542,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 	if (phy)
+ 		return 0;
+ 
+-	if (band == MT_BAND2 && dev->hif2)
++	if (band == MT_BAND2 && dev->hif2) {
+ 		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++		wed = &dev->mt76.mmio.wed_ext;
++	}
+ 
+ 	mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band);
+ 	if (!mphy)
+@@ -576,10 +579,11 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ 
+ 	/* init wiphy according to mphy and phy */
+ 	mt7996_init_wiphy(mphy->hw);
+-	ret = mt76_connac_init_tx_queues(phy->mt76,
+-					 MT_TXQ_ID(band),
+-					 MT7996_TX_RING_SIZE,
+-					 MT_TXQ_RING_BASE(band) + hif1_ofs, 0);
++	ret = mt7996_init_tx_queues(mphy->priv,
++				    MT_TXQ_ID(band),
++				    MT7996_TX_RING_SIZE,
++				    MT_TXQ_RING_BASE(band) + hif1_ofs,
++				    wed);
+ 	if (ret)
+ 		goto error;
+ 
+@@ -1119,6 +1123,13 @@ int mt7996_register_device(struct mt7996_dev *dev)
+ 
+ 	ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
+ 
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed_ext)) {
++		mt76_wr(dev, MT_INT1_MASK_CSR,
++			dev->mt76.mmio.irqmask|MT_INT_TX_DONE_BAND2);
++		mtk_wed_device_start(&dev->mt76.mmio.wed_ext,
++				     dev->mt76.mmio.irqmask |MT_INT_TX_DONE_BAND2);
++	}
++
+ 	dev->recovery.hw_init_done = true;
+ 
+ 	ret = mt7996_init_debugfs(&dev->phy);
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 993b43ce..fc2d9269 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1175,6 +1175,29 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 	return 0;
+ }
+ 
++u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
++{
++	struct mt76_connac_fw_txp *txp = ptr + MT_TXD_SIZE;
++	__le32 *txwi = ptr;
++	u32 val;
++
++	memset(ptr, 0, MT_TXD_SIZE + sizeof(*txp));
++
++	val = FIELD_PREP(MT_TXD0_TX_BYTES, MT_TXD_SIZE) |
++	      FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CT);
++	txwi[0] = cpu_to_le32(val);
++
++	val = BIT(31) |
++	      FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3);
++	txwi[1] = cpu_to_le32(val);
++
++	txp->token = cpu_to_le16(token_id);
++	txp->nbuf = 1;
++	txp->buf[0] = cpu_to_le32(phys + MT_TXD_SIZE + sizeof(*txp));
++
++	return MT_TXD_SIZE + sizeof(*txp);
++}
++
+ static void
+ mt7996_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+ {
+@@ -1561,6 +1584,10 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 
+ 	switch (type) {
+ 	case PKT_TYPE_TXRX_NOTIFY:
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed_ext) &&
++		    q == MT_RXQ_TXFREE_BAND2)
++		    return;
++
+ 		mt7996_mac_tx_free(dev, skb->data, skb->len);
+ 		napi_consume_skb(skb, 1);
+ 		break;
+@@ -2035,7 +2062,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
+ 
+ 	/* enable dma tx/rx and interrupt */
+-	__mt7996_dma_enable(dev, false);
++	__mt7996_dma_enable(dev, false, false);
+ 
+ 	clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+ 	clear_bit(MT76_RESET, &dev->mphy.state);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index f0bdec6b..50fa6523 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1405,6 +1405,49 @@ out:
+ 	return ret;
+ }
+ 
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++static int
++mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
++			     struct ieee80211_vif *vif,
++			     struct ieee80211_sta *sta,
++			     struct net_device_path_ctx *ctx,
++			     struct net_device_path *path)
++{
++	struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy = mt7996_hw_phy(hw);
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++
++	if(phy != &dev->phy && phy->mt76->band_idx == MT_BAND2)
++		wed = &dev->mt76.mmio.wed_ext;
++
++	if (!mtk_wed_device_active(wed))
++		return -ENODEV;
++
++	if (msta->wcid.idx > MT7996_WTBL_STA)
++		return -EIO;
++
++	path->type = DEV_PATH_MTK_WDMA;
++	path->dev = ctx->dev;
++	path->mtk_wdma.wdma_idx = wed->wdma_idx;
++	path->mtk_wdma.bss = mvif->mt76.idx;
++	path->mtk_wdma.queue = 0;
++	path->mtk_wdma.wcid = msta->wcid.idx;
++
++	/* pao info */
++	if (mtk_wed_device_support_pao(wed)) {
++		path->mtk_wdma.amsdu_en = 1;
++		path->mtk_wdma.is_sp = 0;
++		path->mtk_wdma.is_fixedrate = 0;
++	}
++	ctx->dev = NULL;
++
++	return 0;
++}
++
++#endif
++
+ const struct ieee80211_ops mt7996_ops = {
+ 	.tx = mt7996_tx,
+ 	.start = mt7996_start,
+@@ -1451,4 +1494,7 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.sta_add_debugfs = mt7996_sta_add_debugfs,
+ #endif
+ 	.set_radar_background = mt7996_set_radar_background,
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++	.net_fill_forward_path = mt7996_net_fill_forward_path,
++#endif
+ };
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 3a591a7b..b9e47e73 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -10,6 +10,11 @@
+ #include "mt7996.h"
+ #include "mac.h"
+ #include "../trace.h"
++#include "../dma.h"
++
++
++static bool wed_enable = true;
++module_param(wed_enable, bool, 0644);
+ 
+ static const struct __base mt7996_reg_base[] = {
+ 	[WF_AGG_BASE]		= { { 0x820e2000, 0x820f2000, 0x830e2000 } },
+@@ -191,6 +196,228 @@ static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
+ 	return dev->bus_ops->rmw(mdev, __mt7996_reg_addr(dev, offset), mask, val);
+ }
+ 
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++static void mt7996_mmio_wed_release_rx_buf(struct mtk_wed_device *wed)
++{
++	struct mt7996_dev *dev;
++	struct page *page;
++	int i;
++
++	dev = container_of(wed, struct mt7996_dev, mt76.mmio.wed);
++	for (i = 0; i < dev->mt76.rx_token_size; i++) {
++		struct mt76_rxwi_cache *r;
++
++		r = mt76_rx_token_release(&dev->mt76, i);
++		if (!r || !r->ptr)
++			continue;
++
++		dma_unmap_single(dev->mt76.dma_dev, r->dma_addr,
++				 wed->wlan.rx_size, DMA_FROM_DEVICE);
++		skb_free_frag(r->ptr);
++		r->ptr = NULL;
++
++		mt76_put_rxwi(&dev->mt76, r);
++	}
++
++	mt76_free_pending_rxwi(&dev->mt76);
++
++	mt76_for_each_q_rx(&dev->mt76, i) {
++		struct mt76_queue *q = &dev->mt76.q_rx[i];
++
++		if (mt76_queue_is_wed_rx(q)) {
++			if (!q->rx_page.va)
++				continue;
++
++			page = virt_to_page(q->rx_page.va);
++			__page_frag_cache_drain(page, q->rx_page.pagecnt_bias);
++			memset(&q->rx_page, 0, sizeof(q->rx_page));
++		}
++	}
++
++	if (!wed->rx_buf_ring.rx_page.va)
++		return;
++
++	page = virt_to_page(wed->rx_buf_ring.rx_page.va);
++	__page_frag_cache_drain(page, wed->rx_buf_ring.rx_page.pagecnt_bias);
++	memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page));
++
++}
++
++static u32 mt7996_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
++{
++	struct mtk_rxbm_desc *desc = wed->rx_buf_ring.desc;
++	struct mt7996_dev *dev;
++	u32 length;
++	int i;
++
++	dev = container_of(wed, struct mt7996_dev, mt76.mmio.wed);
++	length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
++				sizeof(struct skb_shared_info));
++
++	for (i = 0; i < size; i++) {
++		struct mt76_rxwi_cache *r = mt76_get_rxwi(&dev->mt76);
++		dma_addr_t phy_addr;
++		int token;
++		void *ptr;
++
++		ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length,
++				      GFP_KERNEL);
++		if (!ptr) {
++			mt76_put_rxwi(&dev->mt76, r);
++			goto unmap;
++		}
++
++		phy_addr = dma_map_single(dev->mt76.dma_dev, ptr,
++					  wed->wlan.rx_size,
++					  DMA_TO_DEVICE);
++		if (unlikely(dma_mapping_error(dev->mt76.dev, phy_addr))) {
++			skb_free_frag(ptr);
++			mt76_put_rxwi(&dev->mt76, r);
++			goto unmap;
++		}
++
++		desc->buf0 = cpu_to_le32(phy_addr);
++		token = mt76_rx_token_consume(&dev->mt76, ptr, r, phy_addr);
++		if (token < 0) {
++			dma_unmap_single(dev->mt76.dma_dev, phy_addr,
++					 wed->wlan.rx_size, DMA_TO_DEVICE);
++			skb_free_frag(ptr);
++			mt76_put_rxwi(&dev->mt76, r);
++			goto unmap;
++		}
++
++		desc->token |= cpu_to_le32(FIELD_PREP(MT_DMA_CTL_TOKEN,
++						      token));
++		desc++;
++	}
++
++	return 0;
++
++unmap:
++	mt7996_mmio_wed_release_rx_buf(wed);
++	return -ENOMEM;
++}
++#endif
++
++int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
++			 bool hif2, int *irq)
++{
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++	struct pci_dev *pci_dev = pdev_ptr;
++	u32 hif1_ofs = 0;
++	int ret;
++
++	if (!wed_enable)
++		return 0;
++
++	dev->rro_support = true;
++
++	hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++
++	if (hif2)
++		wed = &dev->mt76.mmio.wed_ext;
++
++	wed->wlan.pci_dev = pci_dev;
++	wed->wlan.bus_type = MTK_WED_BUS_PCIE;
++
++	wed->wlan.base = devm_ioremap(dev->mt76.dev,
++				      pci_resource_start(pci_dev, 0),
++				      pci_resource_len(pci_dev, 0));
++	wed->wlan.phy_base = pci_resource_start(pci_dev, 0);
++
++	if (hif2) {
++		wed->wlan.wpdma_int = wed->wlan.phy_base +
++				      MT_INT_PCIE1_SOURCE_CSR_EXT;
++		wed->wlan.wpdma_mask = wed->wlan.phy_base +
++				       MT_INT_PCIE1_MASK_CSR;
++		wed->wlan.wpdma_tx = wed->wlan.phy_base + hif1_ofs +
++					     MT_TXQ_RING_BASE(0) +
++					     MT7996_TXQ_BAND2 * MT_RING_SIZE;
++		if (dev->rro_support) {
++			wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
++						 MT_RXQ_RING_BASE(0) +
++						 MT7996_RXQ_TXFREE2 * MT_RING_SIZE;
++			wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1;
++		} else {
++			wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs +
++						 MT_RXQ_RING_BASE(0) +
++						 MT7996_RXQ_MCU_WA_TRI * MT_RING_SIZE;
++			wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_TRI) - 1;
++		}
++
++		wed->wlan.chip_id = 0x7991;
++		wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1;
++	} else {
++		wed->wlan.wpdma_int = wed->wlan.phy_base + MT_INT_SOURCE_CSR;
++		wed->wlan.wpdma_mask = wed->wlan.phy_base + MT_INT_MASK_CSR;
++		wed->wlan.wpdma_tx = wed->wlan.phy_base + MT_TXQ_RING_BASE(0) +
++				     MT7996_TXQ_BAND0 * MT_RING_SIZE;
++
++		wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + MT_WFDMA0_GLO_CFG;
++
++		wed->wlan.wpdma_rx = wed->wlan.phy_base +
++				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
++				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
++
++		wed->wlan.rx_nbuf = 65536;
++		wed->wlan.rx_npkt = 24576;
++		wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE);
++
++		wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1;
++		wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
++
++		wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1;
++		wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1;
++		if (dev->rro_support) {
++			wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
++						 MT7996_RXQ_TXFREE0 * MT_RING_SIZE;
++			wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1;
++		} else {
++			wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1;
++			wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
++						  MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
++		}
++	}
++
++	wed->wlan.nbuf = 16384;
++
++	wed->wlan.token_start = 0;
++
++	wed->wlan.max_amsdu_nums = 8;
++	wed->wlan.max_amsdu_len = 1536;
++
++	wed->wlan.init_buf = mt7996_wed_init_buf;
++	wed->wlan.offload_enable = NULL;
++	wed->wlan.offload_disable = NULL;
++	wed->wlan.init_rx_buf = mt7996_mmio_wed_init_rx_buf;
++	wed->wlan.release_rx_buf = mt7996_mmio_wed_release_rx_buf;
++	wed->wlan.update_wo_rx_stats = NULL;
++
++	dev->mt76.rx_token_size += wed->wlan.rx_npkt;
++
++	if (mtk_wed_device_attach(wed))
++		return 0;
++
++	*irq = wed->irq;
++	dev->mt76.dma_dev = wed->dev;
++
++	dev->mt76.token_size = 1024;
++
++	ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32));
++	if (ret)
++		return ret;
++
++	ret = dma_set_coherent_mask(wed->dev, DMA_BIT_MASK(32));
++	if (ret)
++		return ret;
++
++	return 1;
++#else
++	return 0;
++#endif
++}
++
+ static int mt7996_mmio_init(struct mt76_dev *mdev,
+ 			    void __iomem *mem_base,
+ 			    u32 device_id)
+@@ -241,8 +468,17 @@ void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
+ 	mdev->mmio.irqmask |= set;
+ 
+ 	if (write_reg) {
+-		mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
+-		mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
++		if (mtk_wed_device_active(&mdev->mmio.wed)) {
++			mtk_wed_device_irq_set_mask(&mdev->mmio.wed,
++						    mdev->mmio.irqmask);
++			if (mtk_wed_device_active(&mdev->mmio.wed_ext)) {
++				mtk_wed_device_irq_set_mask(&mdev->mmio.wed_ext,
++							    mdev->mmio.irqmask);
++			}
++		} else {
++			mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
++			mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
++		}
+ 	}
+ 
+ 	spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags);
+@@ -260,22 +496,36 @@ static void mt7996_rx_poll_complete(struct mt76_dev *mdev,
+ static void mt7996_irq_tasklet(struct tasklet_struct *t)
+ {
+ 	struct mt7996_dev *dev = from_tasklet(dev, t, mt76.irq_tasklet);
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++	struct mtk_wed_device *wed_ext = &dev->mt76.mmio.wed_ext;
+ 	u32 i, intr, mask, intr1;
+ 
+-	mt76_wr(dev, MT_INT_MASK_CSR, 0);
+-	if (dev->hif2)
+-		mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+-
+-	intr = mt76_rr(dev, MT_INT_SOURCE_CSR);
+-	intr &= dev->mt76.mmio.irqmask;
+-	mt76_wr(dev, MT_INT_SOURCE_CSR, intr);
+-
+-	if (dev->hif2) {
+-		intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR);
+-		intr1 &= dev->mt76.mmio.irqmask;
+-		mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1);
++	if (dev->hif2 && mtk_wed_device_active(wed_ext)) {
++		mtk_wed_device_irq_set_mask(wed_ext, 0);
++		intr1 = mtk_wed_device_irq_get(wed_ext,
++					       dev->mt76.mmio.irqmask);
++		if (intr1 & MT_INT_RX_TXFREE_EXT)
++			napi_schedule(&dev->mt76.napi[MT_RXQ_TXFREE_BAND2]);
++	}
+ 
+-		intr |= intr1;
++	if (mtk_wed_device_active(wed)) {
++		mtk_wed_device_irq_set_mask(wed, 0);
++		intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask);
++		intr |= (intr1 & ~MT_INT_RX_TXFREE_EXT);
++	} else {
++		mt76_wr(dev, MT_INT_MASK_CSR, 0);
++		if (dev->hif2)
++			mt76_wr(dev, MT_INT1_MASK_CSR, 0);
++
++		intr = mt76_rr(dev, MT_INT_SOURCE_CSR);
++		intr &= dev->mt76.mmio.irqmask;
++		mt76_wr(dev, MT_INT_SOURCE_CSR, intr);
++		if (dev->hif2) {
++			intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR);
++			intr1 &= dev->mt76.mmio.irqmask;
++			mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1);
++			intr |= intr1;
++		}
+ 	}
+ 
+ 	trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask);
+@@ -307,10 +557,19 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
+ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance)
+ {
+ 	struct mt7996_dev *dev = dev_instance;
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 
+-	mt76_wr(dev, MT_INT_MASK_CSR, 0);
+-	if (dev->hif2)
+-		mt76_wr(dev, MT_INT1_MASK_CSR, 0);
++	if (mtk_wed_device_active(wed))
++		mtk_wed_device_irq_set_mask(wed, 0);
++	else
++		mt76_wr(dev, MT_INT_MASK_CSR, 0);
++
++	if (dev->hif2) {
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed_ext))
++			mtk_wed_device_irq_set_mask(&dev->mt76.mmio.wed_ext, 0);
++		else
++			mt76_wr(dev, MT_INT1_MASK_CSR, 0);
++	}
+ 
+ 	if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
+ 		return IRQ_NONE;
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index e371964b..43f20da4 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -544,7 +544,9 @@ int mt7996_dma_init(struct mt7996_dev *dev);
+ void mt7996_dma_reset(struct mt7996_dev *dev, bool force);
+ void mt7996_dma_prefetch(struct mt7996_dev *dev);
+ void mt7996_dma_cleanup(struct mt7996_dev *dev);
+-void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset);
++int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx,
++			  int n_desc, int ring_base, struct mtk_wed_device *wed);
++void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset, bool wed_reset);
+ void mt7996_init_txpower(struct mt7996_dev *dev,
+ 			 struct ieee80211_supported_band *sband);
+ int mt7996_txbf_init(struct mt7996_dev *dev);
+@@ -732,7 +734,9 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			    struct ieee80211_sta *sta, struct dentry *dir);
+ #endif
+-
++int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
++			 bool hif2, int *irq);
++u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
+ #ifdef CONFIG_MTK_VENDOR
+ void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+ void mt7996_vendor_register(struct mt7996_phy *phy);
+diff --git a/mt7996/pci.c b/mt7996/pci.c
+index c5301050..869f32ac 100644
+--- a/mt7996/pci.c
++++ b/mt7996/pci.c
+@@ -125,15 +125,26 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 	mt7996_wfsys_reset(dev);
+ 	hif2 = mt7996_pci_init_hif2(pdev);
+ 
+-	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
++	ret = mt7996_mmio_wed_init(dev, pdev, false, &irq);
+ 	if (ret < 0)
+-		goto free_device;
++		goto free_wed_or_irq_vector;
+ 
+-	irq = pdev->irq;
+-	ret = devm_request_irq(mdev->dev, irq, mt7996_irq_handler,
++	if (!ret) {
++		ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
++		if (ret < 0)
++			goto free_device;
++	}
++	ret = devm_request_irq(mdev->dev, pdev->irq, mt7996_irq_handler,
+ 			       IRQF_SHARED, KBUILD_MODNAME, dev);
+ 	if (ret)
+-		goto free_irq_vector;
++		goto free_wed_or_irq_vector;
++
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
++		ret = devm_request_irq(mdev->dev, irq, mt7996_irq_handler,
++				       IRQF_SHARED, KBUILD_MODNAME "-wed", dev);
++		if (ret)
++			goto free_irq;
++	}
+ 
+ 	mt76_wr(dev, MT_INT_MASK_CSR, 0);
+ 	/* master switch of PCIe tnterrupt enable */
+@@ -143,16 +154,30 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ 		hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
+ 		dev->hif2 = hif2;
+ 
+-		ret = pci_alloc_irq_vectors(hif2_dev, 1, 1, PCI_IRQ_ALL_TYPES);
++		ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &irq);
+ 		if (ret < 0)
+-			goto free_hif2;
++			goto free_irq;
++
++		if (!ret) {
++			ret = pci_alloc_irq_vectors(hif2_dev, 1, 1, PCI_IRQ_ALL_TYPES);
++			if (ret < 0)
++				goto free_hif2;
+ 
+-		dev->hif2->irq = hif2_dev->irq;
+-		ret = devm_request_irq(mdev->dev, dev->hif2->irq,
+-				       mt7996_irq_handler, IRQF_SHARED,
+-				       KBUILD_MODNAME "-hif", dev);
++			dev->hif2->irq = hif2_dev->irq;
++		}
++
++		ret = devm_request_irq(mdev->dev, hif2_dev->irq, mt7996_irq_handler,
++					IRQF_SHARED, KBUILD_MODNAME "-hif", dev);
+ 		if (ret)
+-			goto free_hif2_irq_vector;
++			goto free_hif2;
++
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed_ext)) {
++			ret = devm_request_irq(mdev->dev, irq,
++					       mt7996_irq_handler, IRQF_SHARED,
++					       KBUILD_MODNAME "-wed-hif", dev);
++			if (ret)
++				goto free_hif2_irq_vector;
++		}
+ 
+ 		mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+ 		/* master switch of PCIe tnterrupt enable */
+@@ -168,15 +193,28 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
+ free_hif2_irq:
+ 	if (dev->hif2)
+ 		devm_free_irq(mdev->dev, dev->hif2->irq, dev);
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed_ext))
++		devm_free_irq(mdev->dev, dev->mt76.mmio.wed_ext.irq, dev);
+ free_hif2_irq_vector:
+-	if (dev->hif2)
+-		pci_free_irq_vectors(hif2_dev);
++	if (dev->hif2) {
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed_ext))
++			mtk_wed_device_detach(&dev->mt76.mmio.wed_ext);
++		else
++			pci_free_irq_vectors(hif2_dev);
++	}
+ free_hif2:
+ 	if (dev->hif2)
+ 		put_device(dev->hif2->dev);
+-	devm_free_irq(mdev->dev, irq, dev);
+-free_irq_vector:
+-	pci_free_irq_vectors(pdev);
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++		devm_free_irq(mdev->dev, dev->mt76.mmio.wed.irq, dev);
++free_irq:
++	devm_free_irq(mdev->dev, pdev->irq, dev);
++free_wed_or_irq_vector:
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++		mtk_wed_device_detach(&dev->mt76.mmio.wed);
++	else
++		pci_free_irq_vectors(pdev);
++
+ free_device:
+ 	mt76_free_device(&dev->mt76);
+ 
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 6ef905a9..04658639 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -323,6 +323,7 @@ enum base_rev {
+ 
+ #define MT_WFDMA0_RX_INT_PCIE_SEL		MT_WFDMA0(0x154)
+ #define MT_WFDMA0_RX_INT_SEL_RING3		BIT(3)
++#define MT_WFDMA0_RX_INT_SEL_RING6		BIT(6)
+ 
+ #define MT_WFDMA0_MCU_HOST_INT_ENA		MT_WFDMA0(0x1f4)
+ 
+@@ -367,6 +368,9 @@ enum base_rev {
+ #define MT_WFDMA0_PCIE1_BASE			0xd8000
+ #define MT_WFDMA0_PCIE1(ofs)			(MT_WFDMA0_PCIE1_BASE + (ofs))
+ 
++#define MT_INT_PCIE1_SOURCE_CSR_EXT 		MT_WFDMA0_PCIE1(0x118)
++#define MT_INT_PCIE1_MASK_CSR			MT_WFDMA0_PCIE1(0x11c)
++
+ #define MT_WFDMA0_PCIE1_BUSY_ENA		MT_WFDMA0_PCIE1(0x13c)
+ #define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0	BIT(0)
+ #define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1	BIT(1)
+@@ -412,6 +416,7 @@ enum base_rev {
+ #define MT_INT_RX_TXFREE_MAIN			BIT(17)
+ #define MT_INT_RX_TXFREE_TRI			BIT(15)
+ #define MT_INT_MCU_CMD				BIT(29)
++#define MT_INT_RX_TXFREE_EXT			BIT(26)
+ 
+ #define MT_INT_RX(q)				(dev->q_int_mask[__RXQ(q)])
+ #define MT_INT_TX_MCU(q)			(dev->q_int_mask[(q)])
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2004-wifi-mt76-mt7996-wed-add-wed3.0-rx-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2004-wifi-mt76-mt7996-wed-add-wed3.0-rx-support.patch
new file mode 100644
index 0000000..8452177
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2004-wifi-mt76-mt7996-wed-add-wed3.0-rx-support.patch
@@ -0,0 +1,1465 @@
+From 0eaa67d2a2558d1366c25e18e43475907903dea4 Mon Sep 17 00:00:00 2001
+From: Bo Jiao <Bo.Jiao@mediatek.com>
+Date: Mon, 6 Feb 2023 13:50:56 +0800
+Subject: [PATCH 2004/2008] wifi: mt76: mt7996: wed: add wed3.0 rx support
+
+add hardware rro support, This is the preliminary patch for WED3.0 support.
+
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Change-Id: I7e113b1392bcf085ec02c8a44ffbb7cf7c3fa027
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ dma.c           | 197 +++++++++++++++++++++++++++++++++++++-----------
+ dma.h           |  12 +++
+ mac80211.c      |   1 +
+ mt76.h          |  63 ++++++++++++++--
+ mt7996/dma.c    | 163 +++++++++++++++++++++++++++++++++------
+ mt7996/init.c   | 124 +++++++++++++++++++++++++++++-
+ mt7996/mac.c    |  42 +++++++++--
+ mt7996/mcu.c    |   8 +-
+ mt7996/mmio.c   |  36 +++++++--
+ mt7996/mt7996.h |  58 ++++++++++++++
+ mt7996/regs.h   |  63 +++++++++++++++-
+ 11 files changed, 675 insertions(+), 92 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index 930ec768..e5b4d898 100644
+--- a/dma.c
++++ b/dma.c
+@@ -193,46 +193,65 @@ EXPORT_SYMBOL_GPL(mt76_free_pending_rxwi);
+ static void
+ mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
+ {
++	int ndesc = q->ndesc;
++
++	if (q->flags & MT_QFLAG_MAGIC)
++		ndesc |= MT_DMA_MAGIC_EN;
++
+ 	Q_WRITE(dev, q, desc_base, q->desc_dma);
+-	Q_WRITE(dev, q, ring_size, q->ndesc);
++	Q_WRITE(dev, q, ring_size, ndesc);
+ 	q->head = Q_READ(dev, q, dma_idx);
+ 	q->tail = q->head;
+ }
+ 
+ static void
+-mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
++mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, bool skip)
+ {
+ 	int i;
+ 
+ 	if (!q || !q->ndesc)
+ 		return;
+ 
++	if (!q->desc)
++		goto done;
++
+ 	/* clear descriptors */
+ 	for (i = 0; i < q->ndesc; i++)
+ 		q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+ 
++	if (skip)
++		goto sync;
++
++done:
+ 	Q_WRITE(dev, q, cpu_idx, 0);
+ 	Q_WRITE(dev, q, dma_idx, 0);
++sync:
+ 	mt76_dma_sync_idx(dev, q);
+ }
+ 
+ static int
+ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+-		    struct mt76_queue_buf *buf, void *data)
++		    struct mt76_queue_buf *buf, void *data,
++		    struct mt76_rxwi_cache *rxwi)
+ {
+-	struct mt76_desc *desc = &q->desc[q->head];
++	struct mt76_desc *desc;
+ 	struct mt76_queue_entry *entry = &q->entry[q->head];
+-	struct mt76_rxwi_cache *rxwi = NULL;
+ 	u32 buf1 = 0, ctrl;
+ 	int idx = q->head;
+ 	int rx_token;
+ 
++	if (mt76_queue_is_rro_ind(q))
++		goto done;
++
++	desc = &q->desc[q->head];
+ 	ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
+ 
+ 	if (mt76_queue_is_wed_rx(q)) {
+-		rxwi = mt76_get_rxwi(dev);
+-		if (!rxwi)
+-			return -ENOMEM;
++		if (!rxwi) {
++			rxwi = mt76_get_rxwi(dev);
++			if (!rxwi)
++				return -ENOMEM;
++		}
+ 
+ 		rx_token = mt76_rx_token_consume(dev, data, rxwi, buf->addr);
+ 		if (rx_token < 0) {
+@@ -249,6 +268,7 @@ mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ 	WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
+ 	WRITE_ONCE(desc->info, 0);
+ 
++done:
+ 	entry->dma_addr[0] = buf->addr;
+ 	entry->dma_len[0] = buf->len;
+ 	entry->rxwi = rxwi;
+@@ -396,14 +416,15 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
+ 
+ static void *
+ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+-		 int *len, u32 *info, bool *more, bool *drop)
++		 int *len, u32 *info, bool *more, bool *drop, bool flush)
+ {
+ 	struct mt76_queue_entry *e = &q->entry[idx];
+ 	struct mt76_desc *desc = &q->desc[idx];
+ 	void *buf;
++	u32 ctrl;
+ 
++	ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+ 	if (len) {
+-		u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+ 		*len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl);
+ 		*more = !(ctrl & MT_DMA_CTL_LAST_SEC0);
+ 	}
+@@ -411,6 +432,12 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 	if (info)
+ 		*info = le32_to_cpu(desc->info);
+ 
++	if (drop) {
++		*drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A | MT_DMA_CTL_DROP));
++		if (ctrl & MT_DMA_CTL_VER_MASK)
++			*drop = !!(ctrl & MT_DMA_CTL_PN_CHK_FAIL);
++	}
++
+ 	if (mt76_queue_is_wed_rx(q)) {
+ 		u32 buf1 = le32_to_cpu(desc->buf1);
+ 		u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1);
+@@ -423,20 +450,46 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
+ 				 SKB_WITH_OVERHEAD(q->buf_size),
+ 				 DMA_FROM_DEVICE);
+ 
+-		buf = r->ptr;
+-		r->dma_addr = 0;
+-		r->ptr = NULL;
+-
+-		mt76_put_rxwi(dev, r);
+-
+-		if (drop) {
+-			u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+-
+-			*drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A |
+-					   MT_DMA_CTL_DROP));
++		if (flush) {
++			buf = r->ptr;
++			r->dma_addr = 0;
++			r->ptr = NULL;
++
++			mt76_put_rxwi(dev, r);
++		} else {
++			struct mt76_queue_buf qbuf;
++
++			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
++			if (!buf)
++				return NULL;
++
++			memcpy(buf, r->ptr, SKB_WITH_OVERHEAD(q->buf_size));
++
++			r->dma_addr = dma_map_single(dev->dma_dev, r->ptr,
++						     SKB_WITH_OVERHEAD(q->buf_size),
++						     DMA_FROM_DEVICE);
++			if (unlikely(dma_mapping_error(dev->dma_dev, r->dma_addr))) {
++				skb_free_frag(r->ptr);
++				mt76_put_rxwi(dev, r);
++				return NULL;
++			}
++
++			qbuf.addr = r->dma_addr;
++			qbuf.len = SKB_WITH_OVERHEAD(q->buf_size);
++			qbuf.skip_unmap = false;
++
++			if (mt76_dma_add_rx_buf(dev, q, &qbuf, r->ptr, r) < 0) {
++				dma_unmap_single(dev->dma_dev, r->dma_addr,
++						 SKB_WITH_OVERHEAD(q->buf_size),
++						 DMA_FROM_DEVICE);
++				skb_free_frag(r->ptr);
++				mt76_put_rxwi(dev, r);
++				return NULL;
++			}
++		}
+ 
++		if (drop)
+ 			*drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
+-		}
+ 	} else {
+ 		buf = e->buf;
+ 		e->buf = NULL;
+@@ -458,15 +511,20 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+ 	if (!q->queued)
+ 		return NULL;
+ 
+-	if (flush)
+-		q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+-	else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
+-		return NULL;
++	if (q->flags & MT_QFLAG_RRO) {
++		goto done;
++	} else {
++		if (flush)
++			q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE);
++		else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
++			return NULL;
++	}
+ 
++done:
+ 	q->tail = (q->tail + 1) % q->ndesc;
+ 	q->queued--;
+ 
+-	return mt76_dma_get_buf(dev, q, idx, len, info, more, drop);
++	return mt76_dma_get_buf(dev, q, idx, len, info, more, drop, flush);
+ }
+ 
+ static int
+@@ -615,7 +673,10 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
+ 
+ 	while (q->queued < q->ndesc - 1) {
+ 		struct mt76_queue_buf qbuf;
+-		void *buf;
++		void *buf = NULL;
++
++		if (mt76_queue_is_rro_ind(q))
++			goto done;
+ 
+ 		buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
+ 		if (!buf)
+@@ -627,10 +688,11 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
+ 			break;
+ 		}
+ 
++done:
+ 		qbuf.addr = addr + offset;
+ 		qbuf.len = len - offset;
+ 		qbuf.skip_unmap = false;
+-		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
++		if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf, NULL) < 0) {
+ 			dma_unmap_single(dev->dma_dev, addr, len,
+ 					 DMA_FROM_DEVICE);
+ 			skb_free_frag(buf);
+@@ -639,7 +701,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
+ 		frames++;
+ 	}
+ 
+-	if (frames)
++	if (frames || mt76_queue_is_wed_rx(q))
+ 		mt76_dma_kick_queue(dev, q);
+ 
+ 	spin_unlock_bh(&q->lock);
+@@ -652,7 +714,7 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 	struct mtk_wed_device *wed = &dev->mmio.wed;
+ 	int ret, type, ring;
+-	u8 flags;
++	u16 flags;
+ 
+ 	if (!q || !q->ndesc)
+ 		return -EINVAL;
+@@ -679,7 +741,7 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ 	case MT76_WED_Q_TXFREE:
+ 		/* WED txfree queue needs ring to be initialized before setup */
+ 		q->flags = 0;
+-		mt76_dma_queue_reset(dev, q);
++		mt76_dma_queue_reset(dev, q, false);
+ 		mt76_dma_rx_fill(dev, q);
+ 		q->flags = flags;
+ 
+@@ -688,9 +750,31 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ 			q->wed_regs = wed->txfree_ring.reg_base;
+ 		break;
+ 	case MT76_WED_Q_RX:
+-		ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs, reset);
+-		if (!ret)
+-			q->wed_regs = wed->rx_ring[ring].reg_base;
++		if (q->flags & MT_QFLAG_RRO) {
++			q->flags &= ~0x1f;
++
++			ring = FIELD_GET(MT_QFLAG_RRO_RING, q->flags);
++			type = FIELD_GET(MT_QFLAG_RRO_TYPE, q->flags);
++			if (type == MT76_RRO_Q_DATA) {
++				mt76_dma_queue_reset(dev, q, true);
++				ret = mtk_wed_device_rro_rx_ring_setup(wed, ring, q->regs);
++			} else if (type == MT76_RRO_Q_MSDU_PG) {
++				mt76_dma_queue_reset(dev, q, true);
++				ret = mtk_wed_device_msdu_pg_rx_ring_setup(wed, ring, q->regs);
++			} else if (type == MT76_RRO_Q_IND) {
++				mt76_dma_queue_reset(dev, q, false);
++				mt76_dma_rx_fill(dev, q);
++				ret = mtk_wed_device_ind_rx_ring_setup(wed, q->regs);
++			}
++			if (type != MT76_RRO_Q_IND) {
++				q->head = q->ndesc - 1;
++				q->queued = q->ndesc - 1;
++			}
++		} else {
++			ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs, 0);
++			if (!ret)
++				q->wed_regs = wed->rx_ring[ring].reg_base;
++		}
+ 		break;
+ 	default:
+ 		ret = -EINVAL;
+@@ -719,10 +803,25 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ 	q->hw_idx = idx;
+ 
+ 	size = q->ndesc * sizeof(struct mt76_desc);
++	if (mt76_queue_is_rro_ind(q))
++		size = q->ndesc * sizeof(struct mt76_rro_desc);
++
+ 	q->desc = dmam_alloc_coherent(dev->dma_dev, size, &q->desc_dma, GFP_KERNEL);
+ 	if (!q->desc)
+ 		return -ENOMEM;
+ 
++	if (mt76_queue_is_rro_ind(q)) {
++		struct mt76_rro_ind *cmd;
++		int i;
++
++		q->rro_desc = (struct mt76_rro_desc *)(q->desc);
++		q->desc = NULL;
++		for (i = 0; i < q->ndesc; i++) {
++			cmd = (struct mt76_rro_ind *) &q->rro_desc[i];
++			cmd->magic_cnt = MT_DMA_IND_CMD_MAGIC_CNT - 1;
++		}
++	}
++
+ 	size = q->ndesc * sizeof(*q->entry);
+ 	q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL);
+ 	if (!q->entry)
+@@ -732,8 +831,11 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ 	if (ret)
+ 		return ret;
+ 
+-	if (!mt76_queue_is_txfree(q))
+-		mt76_dma_queue_reset(dev, q);
++	if (!mtk_wed_device_active(&dev->mmio.wed) ||
++	    (!mt76_queue_is_wed_txfree(q) &&
++	     !(mtk_wed_get_rx_capa(&dev->mmio.wed) &&
++	       q->flags & MT_QFLAG_RRO)))
++		mt76_dma_queue_reset(dev, q, false);
+ 
+ 	return 0;
+ }
+@@ -751,13 +853,13 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ 	spin_lock_bh(&q->lock);
+ 
+ 	do {
++		if (q->flags & MT_QFLAG_RRO)
++			break;
++
+ 		buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more, NULL);
+ 		if (!buf)
+ 			break;
+ 
+-		if (q->flags & MT_QFLAG_RRO)
+-			continue;
+-
+ 		skb_free_frag(buf);
+ 	} while (1);
+ 
+@@ -768,8 +870,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+ 
+ 	spin_unlock_bh(&q->lock);
+ 
+-	if (((q->flags & MT_QFLAG_WED) &&
+-	     FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) ||
++	if (mt76_queue_is_wed_rx(q) ||
+ 	    (q->flags & MT_QFLAG_RRO))
+ 		return;
+ 
+@@ -790,9 +891,13 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+ 	if (!q->ndesc)
+ 		return;
+ 
++	if (!q->desc)
++		goto done;
++
+ 	for (i = 0; i < q->ndesc; i++)
+ 		q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+ 
++done:
+ 	mt76_dma_rx_cleanup(dev, q);
+ 
+ 	/* reset WED rx queues */
+@@ -839,8 +944,8 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+ 	bool check_ddone = false;
+ 	bool more;
+ 
+-	if (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
+-	    q->flags == MT_WED_Q_TXFREE) {
++	if ((IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) &&
++	     q->flags == MT_WED_Q_TXFREE)) {
+ 		dma_idx = Q_READ(dev, q, dma_idx);
+ 		check_ddone = true;
+ 	}
+@@ -1002,7 +1107,8 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+ 	mt76_for_each_q_rx(dev, i) {
+ 		struct mt76_queue *q = &dev->q_rx[i];
+ 
+-		if (mt76_queue_is_wed_rx(q))
++		if (mtk_wed_device_active(&dev->mmio.wed) &&
++		    (q->flags & MT_QFLAG_RRO))
+ 			continue;
+ 
+ 		netif_napi_del(&dev->napi[i]);
+@@ -1014,6 +1120,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+ 
+ 	if (mtk_wed_device_active(&dev->mmio.wed_ext))
+ 		mtk_wed_device_detach(&dev->mmio.wed_ext);
++
+ 	mt76_free_pending_txwi(dev);
+ 	mt76_free_pending_rxwi(dev);
+ }
+diff --git a/dma.h b/dma.h
+index 1b090d78..48037092 100644
+--- a/dma.h
++++ b/dma.h
+@@ -25,6 +25,13 @@
+ #define MT_DMA_PPE_ENTRY		GENMASK(30, 16)
+ #define MT_DMA_INFO_PPE_VLD		BIT(31)
+ 
++#define MT_DMA_CTL_PN_CHK_FAIL		BIT(13)
++#define MT_DMA_CTL_VER_MASK		BIT(7)
++
++#define MT_DMA_MAGIC_EN		BIT(13)
++
++#define MT_DMA_IND_CMD_MAGIC_CNT	8
++
+ #define MT_DMA_HDR_LEN			4
+ #define MT_RX_INFO_LEN			4
+ #define MT_FCE_INFO_LEN			4
+@@ -37,6 +44,11 @@ struct mt76_desc {
+ 	__le32 info;
+ } __packed __aligned(4);
+ 
++struct mt76_rro_desc {
++	__le32 buf0;
++	__le32 buf1;
++} __packed __aligned(4);
++
+ enum mt76_qsel {
+ 	MT_QSEL_MGMT,
+ 	MT_QSEL_HCCA,
+diff --git a/mac80211.c b/mac80211.c
+index f7578308..3a5755f9 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -727,6 +727,7 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
+ 			return;
+ 		}
+ 	}
++
+ 	__skb_queue_tail(&dev->rx_skb[q], skb);
+ }
+ 
+diff --git a/mt76.h b/mt76.h
+index ee0dbdd7..e4351338 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -48,6 +48,18 @@
+ 
+ #define MT76_TOKEN_FREE_THR	64
+ 
++#define MT_QFLAG_RRO_RING	GENMASK(6, 5)
++#define MT_QFLAG_RRO_TYPE	GENMASK(8, 7)
++#define MT_QFLAG_RRO		BIT(9)
++#define MT_QFLAG_MAGIC		BIT(10)
++
++#define __MT_RRO_Q(_type, _n)	(MT_QFLAG_RRO | \
++				 FIELD_PREP(MT_QFLAG_RRO_TYPE, _type) | \
++				 FIELD_PREP(MT_QFLAG_RRO_RING, _n))
++#define MT_RRO_Q_DATA(_n)	__MT_RRO_Q(MT76_RRO_Q_DATA, _n)
++#define MT_RRO_Q_MSDU_PG(_n)	__MT_RRO_Q(MT76_RRO_Q_MSDU_PG, _n)
++#define MT_RRO_Q_IND		__MT_RRO_Q(MT76_RRO_Q_IND, 0)
++
+ #define MT_QFLAG_WED_RING	GENMASK(1, 0)
+ #define MT_QFLAG_WED_TYPE	GENMASK(3, 2)
+ #define MT_QFLAG_WED		BIT(4)
+@@ -82,6 +94,12 @@ enum mt76_wed_type {
+ 	MT76_WED_Q_RX,
+ };
+ 
++enum mt76_RRO_type {
++	MT76_RRO_Q_DATA,
++	MT76_RRO_Q_MSDU_PG,
++	MT76_RRO_Q_IND,
++};
++
+ struct mt76_bus_ops {
+ 	u32 (*rr)(struct mt76_dev *dev, u32 offset);
+ 	void (*wr)(struct mt76_dev *dev, u32 offset, u32 val);
+@@ -128,6 +146,16 @@ enum mt76_rxq_id {
+ 	MT_RXQ_MAIN_WA,
+ 	MT_RXQ_BAND2,
+ 	MT_RXQ_BAND2_WA,
++	MT_RXQ_RRO_BAND0,
++	MT_RXQ_RRO_BAND1,
++	MT_RXQ_RRO_BAND2,
++	MT_RXQ_MSDU_PAGE_BAND0,
++	MT_RXQ_MSDU_PAGE_BAND1,
++	MT_RXQ_MSDU_PAGE_BAND2,
++	MT_RXQ_TXFREE_BAND0,
++	MT_RXQ_TXFREE_BAND1,
++	MT_RXQ_TXFREE_BAND2,
++	MT_RXQ_RRO_IND,
+ 	__MT_RXQ_MAX
+ };
+ 
+@@ -206,6 +234,7 @@ struct mt76_queue {
+ 	spinlock_t lock;
+ 	spinlock_t cleanup_lock;
+ 	struct mt76_queue_entry *entry;
++	struct mt76_rro_desc *rro_desc;
+ 	struct mt76_desc *desc;
+ 
+ 	u16 first;
+@@ -219,8 +248,8 @@ struct mt76_queue {
+ 
+ 	u8 buf_offset;
+ 	u8 hw_idx;
+-	u8 flags;
+-
++	u8 magic_cnt;
++	u32 flags;
+ 	u32 wed_regs;
+ 
+ 	dma_addr_t desc_dma;
+@@ -274,7 +303,7 @@ struct mt76_queue_ops {
+ 
+ 	void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
+ 
+-	void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q);
++	void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q, bool skip);
+ };
+ 
+ enum mt76_phy_type {
+@@ -369,6 +398,17 @@ struct mt76_txq {
+ 	bool aggr;
+ };
+ 
++struct mt76_rro_ind {
++	u32 se_id	: 12;
++	u32 rsv		: 4;
++	u32 start_sn	: 12;
++	u32 ind_reason	: 4;
++	u32 ind_cnt	: 13;
++	u32 win_sz	: 3;
++	u32 rsv2	: 13;
++	u32 magic_cnt	: 3;
++};
++
+ struct mt76_txwi_cache {
+ 	struct list_head list;
+ 	dma_addr_t dma_addr;
+@@ -1516,12 +1556,19 @@ static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q)
+ 	return (q->flags & MT_QFLAG_WED) &&
+ 	       FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX;
+ }
+-static inline bool mt76_queue_is_txfree(struct mt76_queue *q)
++
++static inline bool mt76_queue_is_wed_txfree(struct mt76_queue *q)
+ {
+ 	return (q->flags & MT_QFLAG_WED) &&
+ 	       FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TXFREE;
+ }
+ 
++static inline bool mt76_queue_is_rro_ind(struct mt76_queue *q)
++{
++	return (q->flags & MT_QFLAG_RRO) &&
++	       FIELD_GET(MT_QFLAG_RRO_TYPE, q->flags) == MT76_RRO_Q_IND;
++}
++
+ struct mt76_txwi_cache *
+ mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
+ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
+@@ -1540,10 +1587,14 @@ static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
+ static inline int
+ mt76_token_get(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
+ {
+-	int token;
++	int token, start = 0;
++
++	if (mtk_wed_device_active(&dev->mmio.wed))
++		start = dev->mmio.wed.wlan.nbuf;
+ 
+ 	spin_lock_bh(&dev->token_lock);
+-	token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC);
++	token = idr_alloc(&dev->token, *ptxwi, start, start + dev->token_size,
++			  GFP_ATOMIC);
+ 	spin_unlock_bh(&dev->token_lock);
+ 
+ 	return token;
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index 673b08bb..c5c7f160 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -64,6 +64,29 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
+ 	RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2);
+ 	RXQ_CONFIG(MT_RXQ_BAND2_WA, WFDMA0, MT_INT_RX_DONE_WA_TRI, MT7996_RXQ_MCU_WA_TRI);
+ 
++	if (dev->rro_support) {
++		/* band0 */
++		RXQ_CONFIG(MT_RXQ_RRO_BAND0, WFDMA0, MT_INT_RX_DONE_RRO_BAND0,
++			   MT7996_RXQ_RRO_BAND0);
++		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND0, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND0,
++			   MT7996_RXQ_MSDU_PG_BAND0);
++		RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, MT_INT_RX_TXFREE_MAIN,
++			   MT7996_RXQ_TXFREE0);
++		/* band1 */
++		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND1,
++			   MT7996_RXQ_MSDU_PG_BAND1);
++		/* band2 */
++		RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, MT_INT_RX_DONE_RRO_BAND2,
++			   MT7996_RXQ_RRO_BAND2);
++		RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, MT_INT_RX_DONE_MSDU_PG_BAND2,
++			   MT7996_RXQ_MSDU_PG_BAND2);
++		RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, MT_INT_RX_TXFREE_TRI,
++			   MT7996_RXQ_TXFREE2);
++
++		RXQ_CONFIG(MT_RXQ_RRO_IND, WFDMA0, MT_INT_RX_DONE_RRO_IND,
++			   MT7996_RXQ_RRO_IND);
++	}
++
+ 	/* data tx queue */
+ 	TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ 	TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+@@ -91,6 +114,22 @@ static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
+ 	mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2_WA) + ofs, PREFETCH(0x180, 0x2));
+ 	mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x1a0, 0x10));
+ 	mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2) + ofs, PREFETCH(0x2a0, 0x10));
++	if (dev->rro_support) {
++		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND0) + ofs,
++			PREFETCH(0x3a0, 0x10));
++		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs,
++			PREFETCH(0x4a0, 0x10));
++		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs,
++			PREFETCH(0x5a0, 0x4));
++		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs,
++			PREFETCH(0x5e0, 0x4));
++		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND2) + ofs,
++			PREFETCH(0x620, 0x4));
++		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND0) + ofs,
++			PREFETCH(0x660, 0x2));
++		mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND2) + ofs,
++			PREFETCH(0x680, 0x2));
++	}
+ 
+ 	mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + ofs, WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE);
+ }
+@@ -149,6 +188,7 @@ static void mt7996_dma_disable(struct mt7996_dev *dev, bool reset)
+ 
+ void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ {
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 	u32 hif1_ofs = 0;
+ 	u32 irq_mask;
+ 
+@@ -157,11 +197,16 @@ void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ 
+ 	/* enable wpdma tx/rx */
+ 	if (!reset) {
+-		mt76_set(dev, MT_WFDMA0_GLO_CFG,
+-			 MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+-			 MT_WFDMA0_GLO_CFG_RX_DMA_EN |
+-			 MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+-			 MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
++		if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed))
++			mt76_set(dev, MT_WFDMA0_GLO_CFG,
++				 MT_WFDMA0_GLO_CFG_TX_DMA_EN |
++				 MT_WFDMA0_GLO_CFG_OMIT_TX_INFO);
++		else
++			mt76_set(dev, MT_WFDMA0_GLO_CFG,
++				MT_WFDMA0_GLO_CFG_TX_DMA_EN |
++				MT_WFDMA0_GLO_CFG_RX_DMA_EN |
++				MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
++				MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+ 
+ 		if (dev->hif2)
+ 			mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+@@ -173,8 +218,8 @@ void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ 
+ 	/* enable interrupts for TX/RX rings */
+ 	irq_mask = MT_INT_MCU_CMD |
+-			   MT_INT_RX_DONE_MCU |
+-			   MT_INT_TX_DONE_MCU;
++		   MT_INT_RX_DONE_MCU |
++		   MT_INT_TX_DONE_MCU;
+ 
+ 	if (mt7996_band_valid(dev, MT_BAND0))
+ 		irq_mask |= MT_INT_BAND0_RX_DONE;
+@@ -185,14 +230,14 @@ void __mt7996_dma_enable(struct mt7996_dev *dev, bool reset, bool wed_reset)
+ 	if (mt7996_band_valid(dev, MT_BAND2))
+ 		irq_mask |= MT_INT_BAND2_RX_DONE;
+ 
+-	if (mtk_wed_device_active(&dev->mt76.mmio.wed) && wed_reset) {
++	if (mtk_wed_device_active(wed) && wed_reset) {
+ 		u32 wed_irq_mask = irq_mask;
+ 
+ 		wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1;
+ 
+ 		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
+ 
+-		mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask);
++		mtk_wed_device_start(wed, wed_irq_mask);
+ 	}
+ 
+ 	irq_mask = reset ? MT_INT_MCU_CMD : irq_mask;
+@@ -269,7 +314,8 @@ static int mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 		/* fix hardware limitation, pcie1's rx ring3 is not available
+ 		 * so, redirect pcie0 rx ring3 interrupt to pcie1
+ 		 */
+-		if (mtk_wed_device_active(&dev->mt76.mmio.wed) && dev->rro_support)
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++		    dev->rro_support)
+ 			mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs,
+ 				 MT_WFDMA0_RX_INT_SEL_RING6);
+ 		else
+@@ -282,6 +328,78 @@ static int mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
+ 	return 0;
+ }
+ 
++int mt7996_dma_rro_init(struct mt7996_dev *dev)
++{
++	int ret;
++	u32 hif1_ofs = 0;
++	u32 wed_irq_mask;
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++
++	if (dev->hif2)
++		hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
++
++	/* ind cmd */
++	dev->mt76.q_rx[MT_RXQ_RRO_IND].flags = MT_RRO_Q_IND | MT_WED_Q_RX(0);
++	dev->mt76.q_rx[MT_RXQ_RRO_IND].flags |= MT_WED_Q_RX(0);
++	ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_IND],
++			       MT_RXQ_ID(MT_RXQ_RRO_IND),
++			       MT7996_RX_RING_SIZE,
++			       0, MT_RXQ_RRO_IND_RING_BASE);
++	if (ret)
++		return ret;
++
++	/* rx msdu page queue for band0 */
++	dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].flags = MT_RRO_Q_MSDU_PG(0);
++	dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].flags |= MT_QFLAG_MAGIC;
++	dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].flags |= MT_WED_Q_RX(0);
++	ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0],
++			       MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND0),
++			       MT7996_RX_RING_SIZE,
++			       MT7996_RX_MSDU_PAGE_SIZE,
++			       MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND0));
++	if (ret)
++		return ret;
++
++	if (mt7996_band_valid(dev, MT_BAND1)) {
++		/* rx msdu page queue for band1 */
++		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags = MT_RRO_Q_MSDU_PG(1);
++		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags |= MT_QFLAG_MAGIC;
++		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags |= MT_WED_Q_RX(1);
++		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1],
++				       MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND1),
++				       MT7996_RX_RING_SIZE,
++				       MT7996_RX_MSDU_PAGE_SIZE,
++				       MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND1));
++		if (ret)
++			return ret;
++	}
++
++	if (mt7996_band_valid(dev, MT_BAND2)) {
++		/* rx msdu page queue for band2 */
++		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].flags = MT_RRO_Q_MSDU_PG(2);
++		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].flags |= MT_QFLAG_MAGIC;
++		dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].flags |= MT_WED_Q_RX(0);
++		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2],
++				       MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND2),
++				       MT7996_RX_RING_SIZE,
++				       MT7996_RX_MSDU_PAGE_SIZE,
++				       MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND2));
++		if (ret)
++			return ret;
++	}
++
++	wed_irq_mask = dev->mt76.mmio.irqmask |
++		       MT_INT_RRO_RX_DONE |
++		       MT_INT_TX_DONE_BAND2;
++
++	mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
++
++	mtk_wed_device_start_hwrro(wed, wed_irq_mask, false);
++	mt7996_irq_enable(dev, wed_irq_mask);
++
++	return 0;
++}
++
+ int mt7996_dma_init(struct mt7996_dev *dev)
+ {
+ 	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+@@ -351,6 +469,9 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 		return ret;
+ 
+ 	/* rx data queue for band0 and band1 */
++	if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed))
++		dev->mt76.q_rx[MT_RXQ_MAIN].flags = MT_WED_Q_RX(0);
++
+ 	ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+ 			       MT_RXQ_ID(MT_RXQ_MAIN),
+ 			       MT7996_RX_RING_SIZE,
+@@ -374,9 +495,6 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	if (mt7996_band_valid(dev, MT_BAND2)) {
+ 		/* rx data queue for band2 */
+ 		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs;
+-		if (mtk_wed_device_active(wed))
+-			rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2);
+-
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
+ 				       MT_RXQ_ID(MT_RXQ_BAND2),
+ 				       MT7996_RX_RING_SIZE,
+@@ -400,11 +518,12 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 			return ret;
+ 	}
+ 
+-
+-	if (dev->rro_support) {
++	if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed) &&
++	    dev->rro_support) {
+ 		/* rx rro data queue for band0 */
+ 		dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags = MT_RRO_Q_DATA(0);
+ 		dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags |= MT_QFLAG_MAGIC;
++		dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags |= MT_WED_Q_RX(0);
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0],
+ 				       MT_RXQ_ID(MT_RXQ_RRO_BAND0),
+ 				       MT7996_RX_RING_SIZE,
+@@ -414,8 +533,7 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 			return ret;
+ 
+ 		/* tx free notify event from WA for band0 */
+-		if (mtk_wed_device_active(wed))
+-			dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
++		dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0],
+ 				       MT_RXQ_ID(MT_RXQ_TXFREE_BAND0),
+ 				       MT7996_RX_MCU_RING_SIZE,
+@@ -428,6 +546,7 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 			/* rx rro data queue for band2 */
+ 			dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags = MT_RRO_Q_DATA(1);
+ 			dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags |= MT_QFLAG_MAGIC;
++			dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags |= MT_WED_Q_RX(1);
+ 			ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2],
+ 					       MT_RXQ_ID(MT_RXQ_RRO_BAND2),
+ 					       MT7996_RX_RING_SIZE,
+@@ -505,18 +624,18 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 
+ 	/* reset hw queues */
+ 	for (i = 0; i < __MT_TXQ_MAX; i++) {
+-		mt76_queue_reset(dev, dev->mphy.q_tx[i]);
++		mt76_queue_reset(dev, dev->mphy.q_tx[i], false);
+ 		if (phy2)
+-			mt76_queue_reset(dev, phy2->q_tx[i]);
++			mt76_queue_reset(dev, phy2->q_tx[i], false);
+ 		if (phy3)
+-			mt76_queue_reset(dev, phy3->q_tx[i]);
++			mt76_queue_reset(dev, phy3->q_tx[i], false);
+ 	}
+ 
+ 	for (i = 0; i < __MT_MCUQ_MAX; i++)
+-		mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
++		mt76_queue_reset(dev, dev->mt76.q_mcu[i], false);
+ 
+ 	mt76_for_each_q_rx(&dev->mt76, i) {
+-		mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
++		mt76_queue_reset(dev, &dev->mt76.q_rx[i], false);
+ 	}
+ 
+ 	mt76_tx_status_check(&dev->mt76, true);
+diff --git a/mt7996/init.c b/mt7996/init.c
+index 6cfbc50d..d70dcf9f 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -496,8 +496,13 @@ void mt7996_mac_init(struct mt7996_dev *dev)
+ 
+ 	/* rro module init */
+ 	mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2);
+-	mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
+-	mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
++	if (dev->rro_support) {
++		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 1);
++		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 0);
++	} else {
++		mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
++		mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
++	}
+ 
+ 	mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+ 			  MCU_WA_PARAM_HW_PATH_HIF_VER,
+@@ -650,6 +655,114 @@ void mt7996_wfsys_reset(struct mt7996_dev *dev)
+ 	msleep(20);
+ }
+ 
++static int mt7996_rro_init(struct mt7996_dev *dev)
++{
++	struct mt7996_rro_addr *ptr;
++	struct mt7996_rro_cfg *rro = &dev->rro;
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++	u32 size, val = 0, reg = MT_RRO_ADDR_ELEM_SEG_ADDR0;
++	int i, j;
++	void *buf;
++
++	for (i = 0; i < MT7996_RRO_BA_BITMAP_CR_CNT; i++) {
++		buf = dmam_alloc_coherent(dev->mt76.dma_dev,
++					  MT7996_BA_BITMAP_SZ_PER_CR,
++					  &rro->ba_bitmap_cache_pa[i],
++					  GFP_KERNEL);
++		if (!buf)
++			return -ENOMEM;
++
++		rro->ba_bitmap_cache_va[i] = buf;
++	}
++
++	rro->win_sz = MT7996_RRO_WIN_SIZE_MAX;
++	for (i = 0; i < MT7996_RRO_ADDR_ELEM_CR_CNT; i++) {
++		size = MT7996_RRO_SESSION_PER_CR *
++		       rro->win_sz * sizeof(struct mt7996_rro_addr);
++
++		buf = dmam_alloc_coherent(dev->mt76.dma_dev, size,
++					  &rro->addr_elem_alloc_pa[i],
++					  GFP_KERNEL);
++		if (!buf)
++			return -ENOMEM;
++		rro->addr_elem_alloc_va[i] = buf;
++
++		memset(rro->addr_elem_alloc_va[i], 0, size);
++
++		ptr = rro->addr_elem_alloc_va[i];
++		for (j = 0; j < MT7996_RRO_SESSION_PER_CR * rro->win_sz; j++, ptr++)
++			ptr->signature = 0xff;
++
++		wed->wlan.ind_cmd.addr_elem_phys[i] = rro->addr_elem_alloc_pa[i];
++	}
++
++	rro->particular_se_id = MT7996_RRO_SESSION_MAX;
++	size = rro->win_sz * sizeof(struct mt7996_rro_addr);
++	buf = dmam_alloc_coherent(dev->mt76.dma_dev, size,
++				  &rro->particular_session_pa,
++				  GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	rro->particular_session_va = buf;
++	ptr = rro->particular_session_va;
++	for (j = 0; j < rro->win_sz; j++, ptr++)
++		ptr->signature = 0xff;
++
++	INIT_LIST_HEAD(&rro->pg_addr_cache);
++	for (i = 0; i < MT7996_RRO_MSDU_PG_HASH_SIZE; i++)
++		INIT_LIST_HEAD(&rro->pg_hash_head[i]);
++
++	/* rro hw init */
++	/* TODO: remove line after WM has set */
++	mt76_clear(dev, WF_RRO_AXI_MST_CFG, WF_RRO_AXI_MST_CFG_DIDX_OK);
++
++	/* setup BA bitmap cache address */
++	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0,
++		rro->ba_bitmap_cache_pa[0]);
++	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0);
++	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0,
++		rro->ba_bitmap_cache_pa[1]);
++	mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0);
++
++	/* setup Address element address */
++	for (i = 0; i < MT7996_RRO_ADDR_ELEM_CR_CNT; i++) {
++		mt76_wr(dev, reg, rro->addr_elem_alloc_pa[i] >> 4);
++		reg += 4;
++	}
++
++	/* setup Address element address - separate address segment mode */
++	mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1,
++		MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE);
++
++	wed->wlan.ind_cmd.win_size = ffs(rro->win_sz) - 6;
++	wed->wlan.ind_cmd.particular_sid = rro->particular_se_id;
++	wed->wlan.ind_cmd.particular_se_phys = rro->particular_session_pa;
++	wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_CR_CNT;
++	wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL;
++
++	mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00);
++	mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1,
++		 MT_RRO_IND_CMD_SIGNATURE_BASE1_EN);
++
++	/* particular session configure */
++	/* use max session idx + 1 as particular session id */
++	mt76_wr(dev, MT_RRO_PARTICULAR_CFG0,
++		rro->particular_session_pa);
++
++	val = FIELD_PREP(MT_RRO_PARTICULAR_SID,
++			 MT7996_RRO_SESSION_MAX);
++	val |= MT_RRO_PARTICULAR_CONFG_EN;
++	mt76_wr(dev, MT_RRO_PARTICULAR_CFG1, val);
++
++	/* interrupt enable */
++	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
++		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
++
++	/* rro ind cmd queue init */
++	return mt7996_dma_rro_init(dev);
++}
++
+ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ {
+ 	int ret, idx;
+@@ -677,6 +790,13 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++	    dev->rro_support) {
++		ret = mt7996_rro_init(dev);
++		if (ret)
++			return ret;
++	}
++
+ 	ret = mt7996_eeprom_init(dev);
+ 	if (ret < 0)
+ 		return ret;
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index fc2d9269..4fbbc077 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -614,8 +614,37 @@ mt7996_mac_fill_rx_rate(struct mt7996_dev *dev,
+ 	return 0;
+ }
+ 
++static void
++mt7996_wed_check_ppe(struct mt7996_dev *dev, struct mt76_queue *q,
++		     struct mt7996_sta *msta, struct sk_buff *skb,
++		     u32 info)
++{
++	struct ieee80211_vif *vif;
++	struct wireless_dev *wdev;
++
++	if (!msta || !msta->vif)
++		return;
++
++	if (!mt76_queue_is_wed_rx(q))
++		return;
++
++	if (!(info & MT_DMA_INFO_PPE_VLD))
++		return;
++
++	vif = container_of((void *)msta->vif, struct ieee80211_vif,
++			   drv_priv);
++	wdev = ieee80211_vif_to_wdev(vif);
++	skb->dev = wdev->netdev;
++
++	mtk_wed_device_ppe_check(&dev->mt76.mmio.wed, skb,
++				 FIELD_GET(MT_DMA_PPE_CPU_REASON, info),
++				 FIELD_GET(MT_DMA_PPE_ENTRY, info));
++}
++
++
+ static int
+-mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
++mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
++		   struct sk_buff *skb, u32 *info)
+ {
+ 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ 	struct mt76_phy *mphy = &dev->mt76.phy;
+@@ -640,7 +669,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	u16 seq_ctrl = 0;
+ 	__le16 fc = 0;
+ 	int idx;
++	u8 hw_aggr = false;
++	struct mt7996_sta *msta = NULL;
+ 
++	hw_aggr = status->aggr;
+ 	memset(status, 0, sizeof(*status));
+ 
+ 	band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1);
+@@ -667,8 +699,6 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	status->wcid = mt7996_rx_get_wcid(dev, idx, unicast);
+ 
+ 	if (status->wcid) {
+-		struct mt7996_sta *msta;
+-
+ 		msta = container_of(status->wcid, struct mt7996_sta, wcid);
+ 		spin_lock_bh(&dev->sta_poll_lock);
+ 		if (list_empty(&msta->poll_list))
+@@ -871,12 +901,14 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+ #endif
+ 	} else {
+ 		status->flag |= RX_FLAG_8023;
++		mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
++				     *info);
+ 	}
+ 
+ 	if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
+ 		mt7996_mac_decode_he_radiotap(skb, rxv, mode);
+ 
+-	if (!status->wcid || !ieee80211_is_data_qos(fc))
++	if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr)
+ 		return 0;
+ 
+ 	status->aggr = unicast &&
+@@ -1604,7 +1636,7 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 		dev_kfree_skb(skb);
+ 		break;
+ 	case PKT_TYPE_NORMAL:
+-		if (!mt7996_mac_fill_rx(dev, skb)) {
++		if (!mt7996_mac_fill_rx(dev, q, skb, info)) {
+ 			mt76_rx(&dev->mt76, q, skb);
+ 			return;
+ 		}
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 59f22f6d..1891c0d7 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -949,7 +949,7 @@ int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif)
+ static int
+ mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
+ 		  struct ieee80211_ampdu_params *params,
+-		  bool enable, bool tx)
++		  bool enable, bool tx, bool rro_enable)
+ {
+ 	struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv;
+ 	struct sta_rec_ba_uni *ba;
+@@ -970,6 +970,8 @@ mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
+ 	ba->ba_en = enable << params->tid;
+ 	ba->amsdu = params->amsdu;
+ 	ba->tid = params->tid;
++	if (rro_enable && !tx && enable)
++		ba->ba_rdd_rro = true;
+ 
+ 	return mt76_mcu_skb_send_msg(dev, skb,
+ 				     MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+@@ -987,7 +989,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ 		msta->wcid.amsdu = false;
+ 
+ 	return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
+-				 enable, true);
++				 enable, true, dev->rro_support);
+ }
+ 
+ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+@@ -998,7 +1000,7 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ 	struct mt7996_vif *mvif = msta->vif;
+ 
+ 	return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
+-				 enable, false);
++				 enable, false, dev->rro_support);
+ }
+ 
+ static void
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index b9e47e73..9960dca7 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -346,9 +346,15 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 			wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_TRI) - 1;
+ 		}
+ 
++		wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG;
++		wed->wlan.wpdma_rx = wed->wlan.phy_base + hif1_ofs +
++				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
++				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
++
+ 		wed->wlan.chip_id = 0x7991;
+ 		wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1;
+ 	} else {
++		wed->wlan.hwrro = dev->rro_support; /* default on */
+ 		wed->wlan.wpdma_int = wed->wlan.phy_base + MT_INT_SOURCE_CSR;
+ 		wed->wlan.wpdma_mask = wed->wlan.phy_base + MT_INT_MASK_CSR;
+ 		wed->wlan.wpdma_tx = wed->wlan.phy_base + MT_TXQ_RING_BASE(0) +
+@@ -360,13 +366,33 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 				     MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) +
+ 				     MT7996_RXQ_BAND0 * MT_RING_SIZE;
+ 
++		wed->wlan.wpdma_rx_rro[0] = wed->wlan.phy_base +
++					    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND0) +
++					    MT7996_RXQ_RRO_BAND0 * MT_RING_SIZE;
++		wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs +
++					    MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) +
++					    MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE;
++		wed->wlan.wpdma_rx_pg = wed->wlan.phy_base +
++					MT_RXQ_RING_BASE(MT7996_RXQ_MSDU_PG_BAND0) +
++					MT7996_RXQ_MSDU_PG_BAND0 * MT_RING_SIZE;
++
+ 		wed->wlan.rx_nbuf = 65536;
+ 		wed->wlan.rx_npkt = 24576;
++		if (dev->hif2)
++			wed->wlan.rx_npkt += 8192;
++
+ 		wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE);
+ 
+ 		wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1;
+ 		wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1;
+ 
++		wed->wlan.rro_rx_tbit[0] = ffs(MT_INT_RX_DONE_RRO_BAND0) - 1;
++		wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1;
++
++		wed->wlan.rx_pg_tbit[0] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND0) - 1;
++		wed->wlan.rx_pg_tbit[1] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND1) - 1;
++		wed->wlan.rx_pg_tbit[2] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND2) - 1;
++
+ 		wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1;
+ 		wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1;
+ 		if (dev->rro_support) {
+@@ -378,6 +404,8 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 			wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) +
+ 						  MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE;
+ 		}
++
++		dev->mt76.rx_token_size += wed->wlan.rx_npkt;
+ 	}
+ 
+ 	wed->wlan.nbuf = 16384;
+@@ -394,8 +422,6 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 	wed->wlan.release_rx_buf = mt7996_mmio_wed_release_rx_buf;
+ 	wed->wlan.update_wo_rx_stats = NULL;
+ 
+-	dev->mt76.rx_token_size += wed->wlan.rx_npkt;
+-
+ 	if (mtk_wed_device_attach(wed))
+ 		return 0;
+ 
+@@ -557,10 +583,9 @@ static void mt7996_irq_tasklet(struct tasklet_struct *t)
+ irqreturn_t mt7996_irq_handler(int irq, void *dev_instance)
+ {
+ 	struct mt7996_dev *dev = dev_instance;
+-	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ 
+-	if (mtk_wed_device_active(wed))
+-		mtk_wed_device_irq_set_mask(wed, 0);
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++		mtk_wed_device_irq_set_mask(&dev->mt76.mmio.wed, 0);
+ 	else
+ 		mt76_wr(dev, MT_INT_MASK_CSR, 0);
+ 
+@@ -592,6 +617,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ 				SURVEY_INFO_TIME_RX |
+ 				SURVEY_INFO_TIME_BSS_RX,
+ 		.token_size = MT7996_TOKEN_SIZE,
++		.rx_token_size = MT7996_RX_TOKEN_SIZE,
+ 		.tx_prepare_skb = mt7996_tx_prepare_skb,
+ 		.tx_complete_skb = mt76_connac_tx_complete_skb,
+ 		.rx_skb = mt7996_queue_rx_skb,
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 43f20da4..836c7db7 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -39,6 +39,7 @@
+ #define MT7996_EEPROM_SIZE		7680
+ #define MT7996_EEPROM_BLOCK_SIZE	16
+ #define MT7996_TOKEN_SIZE		16384
++#define MT7996_RX_TOKEN_SIZE		16384
+ 
+ #define MT7996_CFEND_RATE_DEFAULT	0x49	/* OFDM 24M */
+ #define MT7996_CFEND_RATE_11B		0x03	/* 11B LP, 11M */
+@@ -63,6 +64,24 @@
+ #define MT7996_SKU_RATE_NUM		417
+ #define MT7996_SKU_PATH_NUM		494
+ 
++#define MT7996_RRO_MSDU_PG_HASH_SIZE	127
++#define MT7996_RRO_SESSION_MAX		1024
++#define MT7996_RRO_WIN_SIZE_MAX		1024
++#define MT7996_RRO_ADDR_ELEM_CR_CNT	128
++#define MT7996_RRO_BA_BITMAP_CR_CNT	2
++#define MT7996_RRO_SESSION_PER_CR	(MT7996_RRO_SESSION_MAX /	\
++					 MT7996_RRO_ADDR_ELEM_CR_CNT)
++#define MT7996_BA_BITMAP_SZ_PER_SESSION	128
++#define MT7996_BA_BITMAP_SZ_PER_CR	((MT7996_RRO_SESSION_MAX *		\
++					 MT7996_BA_BITMAP_SZ_PER_SESSION) /	\
++					 MT7996_RRO_BA_BITMAP_CR_CNT)
++#define MT7996_SKB_TRUESIZE(x)		((x) +	\
++					 SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
++#define MT7996_RX_BUF_SIZE		MT7996_SKB_TRUESIZE(1800)
++#define MT7996_RX_MSDU_PAGE_SIZE	MT7996_SKB_TRUESIZE(128)
++
++#define MT7996_WED_RX_TOKEN_SIZE	32768
++
+ struct mt7996_vif;
+ struct mt7996_sta;
+ struct mt7996_dfs_pulse;
+@@ -102,6 +121,16 @@ enum mt7996_rxq_id {
+ 	MT7996_RXQ_BAND0 = 4,
+ 	MT7996_RXQ_BAND1 = 4,/* unused */
+ 	MT7996_RXQ_BAND2 = 5,
++	MT7996_RXQ_RRO_BAND0 = 8,
++	MT7996_RXQ_RRO_BAND1 = 8,/* unused */
++	MT7996_RXQ_RRO_BAND2 = 6,
++	MT7996_RXQ_MSDU_PG_BAND0 = 10,
++	MT7996_RXQ_MSDU_PG_BAND1 = 11,
++	MT7996_RXQ_MSDU_PG_BAND2 = 12,
++	MT7996_RXQ_TXFREE0 = 9,
++	MT7996_RXQ_TXFREE1 = 9,
++	MT7996_RXQ_TXFREE2 = 7,
++	MT7996_RXQ_RRO_IND = 0,
+ };
+ 
+ struct mt7996_twt_flow {
+@@ -272,6 +301,31 @@ struct mt7996_air_monitor_ctrl {
+ };
+ #endif
+ 
++struct mt7996_rro_addr {
++	u32 head_pkt_l;
++	u32 head_pkt_h	: 4;
++	u32 seg_cnt	: 11;
++	u32 out_of_range: 1;
++	u32 rsv		: 8;
++	u32 signature	: 8;
++};
++
++struct mt7996_rro_cfg {
++	u32 ind_signature;
++	void *ba_bitmap_cache_va[MT7996_RRO_BA_BITMAP_CR_CNT];
++	void *addr_elem_alloc_va[MT7996_RRO_ADDR_ELEM_CR_CNT];
++	void *particular_session_va;
++	u32 particular_se_id;
++	dma_addr_t ba_bitmap_cache_pa[MT7996_RRO_BA_BITMAP_CR_CNT];
++	dma_addr_t addr_elem_alloc_pa[MT7996_RRO_ADDR_ELEM_CR_CNT];
++	dma_addr_t particular_session_pa;
++	u16 win_sz;
++
++	spinlock_t lock;
++	struct list_head pg_addr_cache;
++	struct list_head pg_hash_head[MT7996_RRO_MSDU_PG_HASH_SIZE];
++};
++
+ struct mt7996_phy {
+ 	struct mt76_phy *mt76;
+ 	struct mt7996_dev *dev;
+@@ -390,6 +444,9 @@ struct mt7996_dev {
+ 	bool flash_mode:1;
+ 	bool has_eht:1;
+ 
++	bool rro_support:1;
++	struct mt7996_rro_cfg rro;
++
+ 	bool testmode_enable;
+ 	bool bin_file_mode;
+ 	u8 eeprom_mode;
+@@ -709,6 +766,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 			  struct ieee80211_sta *sta,
+ 			  struct mt76_tx_info *tx_info);
+ void mt7996_tx_token_put(struct mt7996_dev *dev);
++int mt7996_dma_rro_init(struct mt7996_dev *dev);
+ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 			 struct sk_buff *skb, u32 *info);
+ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 04658639..6624685e 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -39,6 +39,40 @@ enum base_rev {
+ 
+ #define __BASE(_id, _band)			(dev->reg.base[(_id)].band_base[(_band)])
+ 
++
++/* RRO TOP */
++#define MT_RRO_TOP_BASE				0xA000
++#define MT_RRO_TOP(ofs)				(MT_RRO_TOP_BASE + (ofs))
++
++#define MT_RRO_BA_BITMAP_BASE0			MT_RRO_TOP(0x8)
++#define MT_RRO_BA_BITMAP_BASE1			MT_RRO_TOP(0xC)
++#define WF_RRO_AXI_MST_CFG			MT_RRO_TOP(0xB8)
++#define WF_RRO_AXI_MST_CFG_DIDX_OK		BIT(12)
++#define MT_RRO_ADDR_ARRAY_BASE1			MT_RRO_TOP(0x34)
++#define MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE	BIT(31)
++
++#define MT_RRO_IND_CMD_SIGNATURE_BASE0		MT_RRO_TOP(0x38)
++#define MT_RRO_IND_CMD_SIGNATURE_BASE1		MT_RRO_TOP(0x3C)
++#define MT_RRO_IND_CMD_0_CTRL0			MT_RRO_TOP(0x40)
++#define MT_RRO_IND_CMD_SIGNATURE_BASE1_EN	BIT(31)
++
++#define MT_RRO_PARTICULAR_CFG0			MT_RRO_TOP(0x5C)
++#define MT_RRO_PARTICULAR_CFG1			MT_RRO_TOP(0x60)
++#define MT_RRO_PARTICULAR_CONFG_EN		BIT(31)
++#define MT_RRO_PARTICULAR_SID			GENMASK(30, 16)
++
++#define MT_RRO_BA_BITMAP_BASE_EXT0		MT_RRO_TOP(0x70)
++#define MT_RRO_BA_BITMAP_BASE_EXT1		MT_RRO_TOP(0x74)
++#define MT_RRO_HOST_INT_ENA			MT_RRO_TOP(0x204)
++#define MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA   BIT(0)
++
++#define MT_RRO_ADDR_ELEM_SEG_ADDR0		MT_RRO_TOP(0x400)
++
++#define MT_RRO_ACK_SN_CTRL			MT_RRO_TOP(0x50)
++#define MT_RRO_ACK_SN_CTRL_SN_MASK		GENMASK(27, 16)
++#define MT_RRO_ACK_SN_CTRL_SESSION_MASK		GENMASK(11, 0)
++
++
+ #define MT_MCU_INT_EVENT			0x2108
+ #define MT_MCU_INT_EVENT_DMA_STOPPED		BIT(0)
+ #define MT_MCU_INT_EVENT_DMA_INIT		BIT(1)
+@@ -391,6 +425,7 @@ enum base_rev {
+ #define MT_MCUQ_RING_BASE(q)			(MT_Q_BASE(q) + 0x300)
+ #define MT_TXQ_RING_BASE(q)			(MT_Q_BASE(__TXQ(q)) + 0x300)
+ #define MT_RXQ_RING_BASE(q)			(MT_Q_BASE(__RXQ(q)) + 0x500)
++#define MT_RXQ_RRO_IND_RING_BASE		MT_RRO_TOP(0x40)
+ 
+ #define MT_MCUQ_EXT_CTRL(q)			(MT_Q_BASE(q) +	0x600 +	\
+ 						 MT_MCUQ_ID(q) * 0x4)
+@@ -418,6 +453,15 @@ enum base_rev {
+ #define MT_INT_MCU_CMD				BIT(29)
+ #define MT_INT_RX_TXFREE_EXT			BIT(26)
+ 
++#define MT_INT_RX_DONE_RRO_BAND0		BIT(16)
++#define MT_INT_RX_DONE_RRO_BAND1		BIT(16)
++#define MT_INT_RX_DONE_RRO_BAND2		BIT(14)
++#define MT_INT_RX_DONE_RRO_IND			BIT(11)
++#define MT_INT_RX_DONE_MSDU_PG_BAND0		BIT(18)
++#define MT_INT_RX_DONE_MSDU_PG_BAND1		BIT(19)
++#define MT_INT_RX_DONE_MSDU_PG_BAND2		BIT(23)
++
++
+ #define MT_INT_RX(q)				(dev->q_int_mask[__RXQ(q)])
+ #define MT_INT_TX_MCU(q)			(dev->q_int_mask[(q)])
+ 
+@@ -425,20 +469,31 @@ enum base_rev {
+ 						 MT_INT_RX(MT_RXQ_MCU_WA))
+ 
+ #define MT_INT_BAND0_RX_DONE			(MT_INT_RX(MT_RXQ_MAIN) |	\
+-						 MT_INT_RX(MT_RXQ_MAIN_WA))
++						 MT_INT_RX(MT_RXQ_MAIN_WA) |	\
++						 MT_INT_RX(MT_RXQ_TXFREE_BAND0))
+ 
+ #define MT_INT_BAND1_RX_DONE			(MT_INT_RX(MT_RXQ_BAND1) |	\
+ 						 MT_INT_RX(MT_RXQ_BAND1_WA) |	\
+-						 MT_INT_RX(MT_RXQ_MAIN_WA))
++						 MT_INT_RX(MT_RXQ_MAIN_WA) |	\
++						 MT_INT_RX(MT_RXQ_TXFREE_BAND0))
+ 
+ #define MT_INT_BAND2_RX_DONE			(MT_INT_RX(MT_RXQ_BAND2) |	\
+ 						 MT_INT_RX(MT_RXQ_BAND2_WA) |	\
+-						 MT_INT_RX(MT_RXQ_MAIN_WA))
++						 MT_INT_RX(MT_RXQ_MAIN_WA) |	\
++						 MT_INT_RX(MT_RXQ_TXFREE_BAND0))
++
++#define MT_INT_RRO_RX_DONE			(MT_INT_RX(MT_RXQ_RRO_BAND0) |		\
++						 MT_INT_RX(MT_RXQ_RRO_BAND1) |		\
++						 MT_INT_RX(MT_RXQ_RRO_BAND2) |		\
++						 MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND0) |	\
++						 MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND1) |	\
++						 MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND2))
+ 
+ #define MT_INT_RX_DONE_ALL			(MT_INT_RX_DONE_MCU |		\
+ 						 MT_INT_BAND0_RX_DONE |		\
+ 						 MT_INT_BAND1_RX_DONE |		\
+-						 MT_INT_BAND2_RX_DONE)
++						 MT_INT_BAND2_RX_DONE |		\
++						 MT_INT_RRO_RX_DONE)
+ 
+ #define MT_INT_TX_DONE_FWDL			BIT(26)
+ #define MT_INT_TX_DONE_MCU_WM			BIT(27)
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2005-wifi-mt76-mt7996-wed-add-mt7996_net_setup_tc-to-supp.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2005-wifi-mt76-mt7996-wed-add-mt7996_net_setup_tc-to-supp.patch
new file mode 100644
index 0000000..7fdee4c
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2005-wifi-mt76-mt7996-wed-add-mt7996_net_setup_tc-to-supp.patch
@@ -0,0 +1,50 @@
+From e5136e5f940adf55f1e7604960dba89e24a187bb Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Thu, 13 Apr 2023 14:12:16 +0800
+Subject: [PATCH 2005/2008] wifi: mt76: mt7996: wed: add mt7996_net_setup_tc to
+ support wifi2wifi offload
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ mt7996/main.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/mt7996/main.c b/mt7996/main.c
+index 50fa6523..cebac4ab 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1446,6 +1446,24 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
+ 	return 0;
+ }
+ 
++static int mt7996_net_setup_tc(struct ieee80211_hw *hw,
++			       struct ieee80211_vif *vif,
++			       struct net_device *ndev,
++			       enum tc_setup_type type,
++			       void *type_data)
++
++{
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++
++	if (!mtk_wed_device_active(wed))
++		return -ENODEV;
++
++	mtk_wed_device_setup_tc(wed, ndev, type, type_data);
++
++	return 0;
++}
++
+ #endif
+ 
+ const struct ieee80211_ops mt7996_ops = {
+@@ -1496,5 +1514,6 @@ const struct ieee80211_ops mt7996_ops = {
+ 	.set_radar_background = mt7996_set_radar_background,
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ 	.net_fill_forward_path = mt7996_net_fill_forward_path,
++	.net_setup_tc = mt7996_net_setup_tc,
+ #endif
+ };
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2006-wifi-mt76-add-random-early-drop-support.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2006-wifi-mt76-add-random-early-drop-support.patch
new file mode 100644
index 0000000..7902639
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2006-wifi-mt76-add-random-early-drop-support.patch
@@ -0,0 +1,144 @@
+From fba98d69dcbbbcbd4cbf61e997637ecead9e55a3 Mon Sep 17 00:00:00 2001
+From: Peter Chiu <chui-hao.chiu@mediatek.com>
+Date: Wed, 19 Apr 2023 18:32:41 +0800
+Subject: [PATCH 2006/2008] wifi: mt76: add random early drop support
+
+---
+ mt7996/mcu.c    | 77 +++++++++++++++++++++++++++++++++++++++++++++++--
+ mt7996/mcu.h    |  4 ++-
+ mt7996/mt7996.h |  1 +
+ 3 files changed, 79 insertions(+), 3 deletions(-)
+
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 1891c0d7..0c01e90b 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -2933,8 +2933,8 @@ int mt7996_mcu_init_firmware(struct mt7996_dev *dev)
+ 	if (ret)
+ 		return ret;
+ 
+-	return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+-				 MCU_WA_PARAM_RED, 0, 0);
++	return mt7996_mcu_red_config(dev,
++			mtk_wed_device_active(&dev->mt76.mmio.wed));
+ }
+ 
+ int mt7996_mcu_init(struct mt7996_dev *dev)
+@@ -2966,6 +2966,79 @@ out:
+ 	skb_queue_purge(&dev->mt76.mcu.res_q);
+ }
+ 
++static int mt7996_mcu_wa_red_config(struct mt7996_dev *dev)
++{
++#define RED_TOKEN_SRC_CNT	4
++#define RED_TOKEN_CONFIG	2
++	struct {
++		__le32 arg0;
++		__le32 arg1;
++		__le32 arg2;
++
++		u8 mode;
++		u8 version;
++		u8 _rsv[4];
++		__le16 len;
++
++		__le16 tcp_offset;
++		__le16 priority_offset;
++		__le16 token_per_src[RED_TOKEN_SRC_CNT];
++		__le16 token_thr_per_src[RED_TOKEN_SRC_CNT];
++
++		u8 _rsv2[604];
++	} __packed req = {
++		.arg0 = cpu_to_le32(MCU_WA_PARAM_RED_CONFIG),
++
++		.mode = RED_TOKEN_CONFIG,
++		.len = cpu_to_le16(sizeof(req) - sizeof(__le32) * 3),
++
++		.tcp_offset = cpu_to_le16(200),
++		.priority_offset = cpu_to_le16(255),
++	};
++	u8 i;
++
++	for (i = 0; i < RED_TOKEN_SRC_CNT; i++) {
++		req.token_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
++		req.token_thr_per_src[i] = cpu_to_le16(MT7996_TOKEN_SIZE);
++	}
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET),
++				 &req, sizeof(req), false);
++}
++
++int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable)
++{
++#define RED_DISABLE		0
++#define RED_BY_WA_ENABLE	2
++	struct {
++		u8 __rsv1[4];
++
++		__le16 tag;
++		__le16 len;
++		u8 enable;
++		u8 __rsv2[3];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_VOW_RED_ENABLE),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.enable = enable ? RED_BY_WA_ENABLE : RED_DISABLE,
++	};
++	int ret;
++
++	ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req,
++				 sizeof(req), true);
++
++	if (ret)
++		return ret;
++
++	ret = mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
++				MCU_WA_PARAM_RED_EN, enable, 0);
++
++	if (ret || !enable)
++		return ret;
++
++	return mt7996_mcu_wa_red_config(dev);
++}
++
+ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
+ {
+ 	struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index a0cbf922..ec074bc6 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -239,8 +239,9 @@ enum {
+ enum {
+ 	MCU_WA_PARAM_PDMA_RX = 0x04,
+ 	MCU_WA_PARAM_CPU_UTIL = 0x0b,
+-	MCU_WA_PARAM_RED = 0x0e,
++	MCU_WA_PARAM_RED_EN = 0x0e,
+ 	MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
++	MCU_WA_PARAM_RED_CONFIG = 0x40,
+ };
+ 
+ enum mcu_mmps_mode {
+@@ -695,6 +696,7 @@ enum {
+ 	UNI_VOW_DRR_CTRL,
+ 	UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
+ 	UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
++	UNI_VOW_RED_ENABLE = 0x18,
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 836c7db7..b239c44c 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -671,6 +671,7 @@ int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set)
+ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
+ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val);
+ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
++int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
+ int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
+ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
+ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2007-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2007-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
new file mode 100644
index 0000000..5897422
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2007-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch
@@ -0,0 +1,473 @@
+From cc7283ecc1da9d4f62803062466fb5420d2ea766 Mon Sep 17 00:00:00 2001
+From: "sujuan.chen" <sujuan.chen@mediatek.com>
+Date: Thu, 18 May 2023 15:01:47 +0800
+Subject: [PATCH 2007/2008] wifi: mt76: mt7996: reset addr_elem when delete ba
+
+The old addr element info may be used when the signature is not equel to
+0xff, and sta will find error SDP cause the SDP/SDL=0 issue.
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ mt76.h            |   1 +
+ mt76_connac_mcu.h |   1 +
+ mt7996/init.c     |   3 +
+ mt7996/mac.c      | 140 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/main.c     |   7 +++
+ mt7996/mcu.c      |  64 +++++++++++++++++++++
+ mt7996/mcu.h      |  34 +++++++++++
+ mt7996/mt7996.h   |  32 +++++++++++
+ mt7996/regs.h     |   5 ++
+ 9 files changed, 287 insertions(+)
+
+diff --git a/mt76.h b/mt76.h
+index e4351338..7ebcf432 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -436,6 +436,7 @@ struct mt76_rx_tid {
+ 	u16 nframes;
+ 
+ 	u8 num;
++	u8 partial_id;
+ 
+ 	u8 started:1, stopped:1, timer_pending:1;
+ 
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index a53fa138..d74fd2dd 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1023,6 +1023,7 @@ enum {
+ 	MCU_UNI_EVENT_THERMAL = 0x35,
+ 	MCU_UNI_EVENT_BF = 0x33,
+ 	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
++	MCU_UNI_EVENT_RRO = 0x57,
+ };
+ 
+ #define MCU_UNI_CMD_EVENT			BIT(1)
+diff --git a/mt7996/init.c b/mt7996/init.c
+index d70dcf9f..93262297 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -759,6 +759,9 @@ static int mt7996_rro_init(struct mt7996_dev *dev)
+ 	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
+ 		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
+ 
++	INIT_DELAYED_WORK(&dev->rro.rro_del_work, mt7996_rro_delete_sessions);
++	INIT_LIST_HEAD(&dev->rro.rro_poll_list);
++
+ 	/* rro ind cmd queue init */
+ 	return mt7996_dma_rro_init(dev);
+ }
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 4fbbc077..3a89013c 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -1647,6 +1647,139 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 	}
+ }
+ 
++static struct mt7996_rro_addr *
++mt7996_rro_get_addr_elem(struct mt7996_dev *dev, u16 seid, u16 sn)
++{
++	struct mt7996_rro_cfg *rro = &dev->rro;
++	u32 idx;
++	void *addr;
++
++	if (seid == rro->particular_se_id) {
++		addr = rro->particular_session_va;
++		idx = sn % rro->win_sz;
++	} else {
++		addr = rro->addr_elem_alloc_va[seid / MT7996_RRO_SESSION_PER_CR];
++		idx = (seid % MT7996_RRO_SESSION_PER_CR) * rro->win_sz
++			+ (sn % rro->win_sz);
++	}
++	return addr + idx * sizeof(struct mt7996_rro_addr);
++}
++
++static bool mt7996_rro_reset_sessions(struct mt7996_dev *dev,
++				  u16 wcid, u8 partial_id)
++{
++	u32 sid = ((wcid & 0x7F) << 3) + partial_id;
++	u32 value[2];
++	struct mt7996_rro_ba_session *s;
++	struct  mt7996_rro_addr *elem;
++	int i;
++
++	mt76_wr(dev, MT_RRO_DBG_RD_CTRL, MT_RRO_DBG_RD_EXEC |
++		sid >> 1 | 0x200);
++
++	if (sid & 0x1) {
++		value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(2));
++		value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(2));
++	} else {
++		value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(0));
++		value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(1));
++	}
++
++	s = (struct mt7996_rro_ba_session *)&value[0];
++	if (!s->cn && s->ack_sn == s->last_in_sn) {
++		for (i = 0; i < MT7996_RRO_WIN_SIZE_MAX; i++) {
++			elem = mt7996_rro_get_addr_elem(dev, sid, i);
++			elem->signature = 0xff;
++		}
++		return true;
++	}
++
++	return false;
++}
++
++void  mt7996_rro_delete_sessions(struct work_struct *work)
++{
++	struct mt7996_dev *dev;
++	struct mt7996_rro_ba_session_elem *e;
++	int elem_nums;
++	LIST_HEAD(rro_poll_list);
++
++	dev = (struct mt7996_dev *)container_of(work, struct mt7996_dev,
++					       rro.rro_del_work.work);
++	elem_nums = dev->rro.elem_nums;
++
++	spin_lock_bh(&dev->rro.rro_stbl_lock);
++	list_splice_init(&dev->rro.rro_poll_list, &rro_poll_list);
++	spin_unlock_bh(&dev->rro.rro_stbl_lock);
++
++	do {
++		spin_lock_bh(&dev->rro.rro_stbl_lock);
++		if (list_empty(&rro_poll_list)) {
++			spin_unlock_bh(&dev->rro.rro_stbl_lock);
++			break;
++		}
++
++		e = list_first_entry(&rro_poll_list,
++				     struct mt7996_rro_ba_session_elem,
++				     poll_list);
++		if (!e) {
++			spin_unlock_bh(&dev->rro.rro_stbl_lock);
++			break;
++		}
++		list_del_init(&e->poll_list);
++		spin_unlock_bh(&dev->rro.rro_stbl_lock);
++
++		if (mt7996_rro_reset_sessions(dev, e->wlan_idx,
++					      e->partial_id)) {
++			mt7996_mcu_reset_rro_sessions(dev, e->wlan_idx,
++						      e->tid, e->partial_id);
++			kfree(e);
++			dev->rro.elem_nums--;
++		} else {
++			spin_lock_bh(&dev->rro.rro_stbl_lock);
++			list_add_tail(&e->poll_list, &dev->rro.rro_poll_list);
++			spin_unlock_bh(&dev->rro.rro_stbl_lock);
++		}
++		elem_nums--;
++	} while (elem_nums);
++
++	if (list_empty(&rro_poll_list))
++		ieee80211_queue_delayed_work(mt76_hw(dev),
++					     &dev->rro.rro_del_work,
++					     MT7996_WATCHDOG_TIME);
++}
++
++int mt7996_rro_add_delete_elem(struct mt7996_dev *dev,
++			       struct mt7996_sta *msta, u8 tidno)
++{
++	struct mt76_rx_tid *tid = NULL;
++	struct mt76_wcid *wcid = &msta->wcid;
++	struct mt7996_rro_ba_session_elem *e;
++	u16 idx = msta->wcid.idx;
++
++	tid = rcu_dereference(wcid->aggr[tidno]);
++	if (!tid)
++		return 0;
++
++	e = kzalloc(sizeof(*e), GFP_ATOMIC);
++	if (!e)
++		return -ENOMEM;
++
++	e->wlan_idx = idx;
++	e->tid = tidno;
++	e->partial_id = tid->partial_id;
++
++	spin_lock_bh(&dev->rro.rro_stbl_lock);
++	list_add_tail(&e->poll_list, &dev->rro.rro_poll_list);
++	spin_unlock_bh(&dev->rro.rro_stbl_lock);
++	dev->rro.elem_nums++;
++
++	ieee80211_queue_delayed_work(mt76_hw(dev),
++				     &dev->rro.rro_del_work,
++				     MT7996_WATCHDOG_TIME);
++	return 0;
++}
++
+ void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+@@ -1971,6 +2104,9 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
+ 	if (phy3)
+ 		ieee80211_stop_queues(phy3->mt76->hw);
+ 
++	if (dev->rro_support)
++		cancel_delayed_work_sync(&dev->rro.rro_del_work);
++
+ 	cancel_delayed_work_sync(&dev->mphy.mac_work);
+ 	if (phy2)
+ 		cancel_delayed_work_sync(&phy2->mt76->mac_work);
+@@ -2062,6 +2198,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	set_bit(MT76_RESET, &dev->mphy.state);
+ 	set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ 	wake_up(&dev->mt76.mcu.wait);
++
++	if (dev->rro_support)
++		cancel_delayed_work_sync(&dev->rro.rro_del_work);
++
+ 	cancel_delayed_work_sync(&dev->mphy.mac_work);
+ 	if (phy2) {
+ 		set_bit(MT76_RESET, &phy2->mt76->state);
+diff --git a/mt7996/main.c b/mt7996/main.c
+index cebac4ab..4cb72220 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -119,6 +119,9 @@ static void mt7996_stop(struct ieee80211_hw *hw)
+ 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ 
++	if (dev->rro_support)
++		cancel_delayed_work_sync(&dev->rro.rro_del_work);
++
+ 	cancel_delayed_work_sync(&phy->mt76->mac_work);
+ 	cancel_delayed_work_sync(&dev->scs_work);
+ 
+@@ -797,6 +800,10 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 		ret = mt7996_mcu_add_rx_ba(dev, params, true);
+ 		break;
+ 	case IEEE80211_AMPDU_RX_STOP:
++		if (dev->rro_support)  {
++			ret = mt7996_rro_add_delete_elem(dev, msta,
++							 params->tid);
++		}
+ 		mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
+ 		ret = mt7996_mcu_add_rx_ba(dev, params, false);
+ 		break;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 0c01e90b..094f3656 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -476,6 +476,41 @@ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	phy->throttle_state = n->duty_percent;
+ }
+ 
++static void mt7996_mcu_rx_rro(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_mcu_rro_event *event;
++
++	if (!dev->rro_support)
++		return;
++
++	event = (struct mt7996_mcu_rro_event*)skb->data;
++
++	switch (event->tag) {
++	case UNI_RRO_BA_SESSION_STATUS: {
++		struct mt7996_mcu_rro_ba *rro = (struct mt7996_mcu_rro_ba *)skb->data;
++		u16 idx = rro->wlan_id;
++		struct mt76_rx_tid *tid;
++		struct mt76_wcid *wcid;
++
++		wcid = rcu_dereference(dev->mt76.wcid[idx]);
++		if (!wcid || !wcid->sta)
++			return;
++
++		tid = rcu_dereference(wcid->aggr[rro->tid]);
++		if (!tid)
++			return;
++		tid->partial_id = rro->partial_id;
++
++		break;
++	}
++	default:
++		dev_info(dev->mt76.dev, "%s: unknown rro event tag %d\n",
++			 __func__, event->tag);
++		break;
++	}
++
++}
++
+ static void
+ mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -528,6 +563,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		mt7996_tm_rf_test_event(dev, skb);
+ 		break;
+ #endif
++	case MCU_UNI_EVENT_RRO:
++		mt7996_mcu_rx_rro(dev, skb);
++		break;
+ 	default:
+ 		break;
+ 	}
+@@ -4533,6 +4571,32 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val)
+ 				 sizeof(req), true);
+ }
+ 
++int mt7996_mcu_reset_rro_sessions(struct mt7996_dev *dev,
++					 u16 wcid, u8 tid, u8 pid)
++{
++	struct {
++		/* fixed field */
++		u8 __rsv[4];
++
++		__le16 tag;
++		__le16 len;
++		u16 wcid;
++		u8 tid;
++		u8 partial_id;
++		u8 pad[4];
++	} __packed req = {
++		.tag = cpu_to_le16(UNI_RRO_DEL_BA_SESSION),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.wcid = wcid,
++		.tid = tid,
++		.partial_id = pid,
++	};
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(RRO),
++				 &req, sizeof(req), true);
++}
++
++
+ int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
+ {
+ 	struct mt7996_dev *dev = phy->dev;
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index ec074bc6..10e3799f 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -191,6 +191,38 @@ struct mt7996_mcu_thermal_notify {
+ 	u8 __rsv2[4];
+ } __packed;
+ 
++struct mt7996_mcu_rro_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 __rsv1[4];
++
++	__le16 tag;
++	__le16 len;
++} __packed;
++
++struct mt7996_mcu_rro_ba {
++	struct mt7996_mcu_rro_event event;
++
++	u16 wlan_id;
++	u8 tid;
++	u8 partial_id;
++	__le32 status;
++}__packed;
++
++enum  {
++	UNI_RRO_BA_SESSION_STATUS = 0,
++	UNI_RRO_BA_SESSION_TBL	= 1,
++	UNI_RRO_BA_SESSION_MAX_NUM
++};
++
++struct mt7996_mcu_rro_del_ba {
++	struct mt7996_mcu_rro_event event;
++
++	u8  wlan_idx;
++	u8  tid;
++	u8 __rsv2[2];
++};
++
+ enum mt7996_chan_mib_offs {
+ 	UNI_MIB_OBSS_AIRTIME = 26,
+ 	UNI_MIB_NON_WIFI_TIME = 27,
+@@ -718,6 +750,8 @@ enum {
+ 	UNI_RRO_GET_BA_SESSION_TABLE,
+ 	UNI_RRO_SET_BYPASS_MODE,
+ 	UNI_RRO_SET_TXFREE_PATH,
++	UNI_RRO_DEL_BA_SESSION,
++	UNI_RRO_SET_FLUSH_TIMEOUT
+ };
+ 
+ enum{
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index b239c44c..88d42c3a 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -310,6 +310,28 @@ struct mt7996_rro_addr {
+ 	u32 signature	: 8;
+ };
+ 
++struct mt7996_rro_ba_session {
++	u32 ack_sn         :12;
++	u32 win_sz         :3;
++	u32 bn             :1;
++	u32 last_in_sn     :12;
++	u32 bc             :1;
++	u32 bd             :1;
++	u32 sat            :1;
++	u32 cn             :1;
++	u32 within_cnt     :12;
++	u32 to_sel         :3;
++	u32 rsv            :1;
++	u32 last_in_rxtime :12;
++};
++
++struct mt7996_rro_ba_session_elem {
++	struct list_head poll_list;
++	u16 wlan_idx;
++	u8 tid;
++	u8 partial_id;
++};
++
+ struct mt7996_rro_cfg {
+ 	u32 ind_signature;
+ 	void *ba_bitmap_cache_va[MT7996_RRO_BA_BITMAP_CR_CNT];
+@@ -324,6 +346,11 @@ struct mt7996_rro_cfg {
+ 	spinlock_t lock;
+ 	struct list_head pg_addr_cache;
+ 	struct list_head pg_hash_head[MT7996_RRO_MSDU_PG_HASH_SIZE];
++
++	struct delayed_work rro_del_work;
++	spinlock_t rro_stbl_lock;
++	struct list_head rro_poll_list;
++	u16 elem_nums;
+ };
+ 
+ struct mt7996_phy {
+@@ -670,6 +697,8 @@ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
+ int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set);
+ int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
+ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val);
++int mt7996_mcu_reset_rro_sessions(struct mt7996_dev *dev,
++				     u16 wcid, u8 tid, u8 pid);
+ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
+ int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
+ int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
+@@ -768,6 +797,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ 			  struct mt76_tx_info *tx_info);
+ void mt7996_tx_token_put(struct mt7996_dev *dev);
+ int mt7996_dma_rro_init(struct mt7996_dev *dev);
++void  mt7996_rro_delete_sessions(struct work_struct *work);
++int mt7996_rro_add_delete_elem(struct mt7996_dev *dev,
++			       struct mt7996_sta *msta, u8 tid);
+ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ 			 struct sk_buff *skb, u32 *info);
+ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+diff --git a/mt7996/regs.h b/mt7996/regs.h
+index 6624685e..f97c87c9 100644
+--- a/mt7996/regs.h
++++ b/mt7996/regs.h
+@@ -72,6 +72,11 @@ enum base_rev {
+ #define MT_RRO_ACK_SN_CTRL_SN_MASK		GENMASK(27, 16)
+ #define MT_RRO_ACK_SN_CTRL_SESSION_MASK		GENMASK(11, 0)
+ 
++#define MT_RRO_DBG_RD_CTRL			MT_RRO_TOP(0xe0)
++#define MT_RRO_DBG_RD_ADDR			GENMASK(15, 0)
++#define MT_RRO_DBG_RD_EXEC			BIT(31)
++
++#define MT_RRO_DBG_RDAT_DW(_n)			MT_RRO_TOP(0xf0 + _n * 0x4)
+ 
+ #define MT_MCU_INT_EVENT			0x2108
+ #define MT_MCU_INT_EVENT_DMA_STOPPED		BIT(0)
+-- 
+2.39.2
+
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/2008-wifi-mt76-add-SER-support-for-wed3.0.patch b/recipes-wifi/linux-mt76/files/patches-3.x/2008-wifi-mt76-add-SER-support-for-wed3.0.patch
new file mode 100644
index 0000000..0d2134b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/2008-wifi-mt76-add-SER-support-for-wed3.0.patch
@@ -0,0 +1,303 @@
+From 5df084a32eac68dd66a3b833cf5f718118850b08 Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Tue, 23 May 2023 12:06:29 +0800
+Subject: [PATCH 2008/2008] wifi: mt76: add SER support for wed3.0
+
+Change-Id: I2711b9dc336fca9a1ae32a8fbf27810a7e27b1e3
+---
+ dma.c         |  7 +++++--
+ mt7996/dma.c  | 48 +++++++++++++++++++++++++++++++++++++++++++++---
+ mt7996/mac.c  | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ mt7996/mmio.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 145 insertions(+), 6 deletions(-)
+
+diff --git a/dma.c b/dma.c
+index e5b4d898..e31f6390 100644
+--- a/dma.c
++++ b/dma.c
+@@ -770,8 +770,9 @@ int mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
+ 				q->head = q->ndesc - 1;
+ 				q->queued = q->ndesc - 1;
+ 			}
++			q->flags = flags;
+ 		} else {
+-			ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs, 0);
++			ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs, reset);
+ 			if (!ret)
+ 				q->wed_regs = wed->rx_ring[ring].reg_base;
+ 		}
+@@ -902,7 +903,9 @@ done:
+ 
+ 	/* reset WED rx queues */
+ 	mt76_dma_wed_setup(dev, q, true);
+-	if (q->flags != MT_WED_Q_TXFREE) {
++	if (q->flags != MT_WED_Q_TXFREE &&
++	    !((q->flags & MT_QFLAG_RRO) &&
++	    mtk_wed_device_active(&dev->mmio.wed))) {
+ 		mt76_dma_sync_idx(dev, q);
+ 		mt76_dma_rx_fill(dev, q);
+ 	}
+diff --git a/mt7996/dma.c b/mt7996/dma.c
+index c5c7f160..471ae81c 100644
+--- a/mt7996/dma.c
++++ b/mt7996/dma.c
+@@ -495,6 +495,12 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	if (mt7996_band_valid(dev, MT_BAND2)) {
+ 		/* rx data queue for band2 */
+ 		rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs;
++		if (mtk_wed_device_active(wed)) {
++			rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2);
++			if (mtk_wed_get_rx_capa(wed))
++				dev->mt76.q_rx[MT_RXQ_BAND2].flags = MT_WED_Q_RX(1);
++		}
++
+ 		ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
+ 				       MT_RXQ_ID(MT_RXQ_BAND2),
+ 				       MT7996_RX_RING_SIZE,
+@@ -582,11 +588,35 @@ int mt7996_dma_init(struct mt7996_dev *dev)
+ 	return 0;
+ }
+ 
++static void mt7996_dma_wed_reset(struct mt7996_dev *dev)
++{
++	struct mt76_dev *mdev = &dev->mt76;
++
++	if (!test_bit(MT76_STATE_WED_RESET, &dev->mphy.state))
++		return;
++
++	complete(&mdev->mmio.wed_reset);
++
++	if (!wait_for_completion_timeout(&dev->mt76.mmio.wed_reset_complete,
++					 3 * HZ))
++		dev_err(dev->mt76.dev, "wed reset complete timeout\n");
++}
++
++static void
++mt7996_dma_reset_tx_queue(struct mt7996_dev *dev, struct mt76_queue *q)
++{
++	mt76_queue_reset(dev, q, false);
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++		mt76_dma_wed_setup(&dev->mt76, q, true);
++}
++
+ 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);
++	struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
++	struct mtk_wed_device *wed_ext = &dev->mt76.mmio.wed_ext;
+ 	int i;
+ 
+ 	mt76_clear(dev, MT_WFDMA0_GLO_CFG,
+@@ -620,21 +650,33 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
+ 	if (force)
+ 		mt7996_wfsys_reset(dev);
+ 
++	if (dev->hif2 && mtk_wed_device_active(wed_ext))
++		mtk_wed_device_dma_reset(wed_ext);
++
++	if (mtk_wed_device_active(wed))
++		mtk_wed_device_dma_reset(wed);
++
+ 	mt7996_dma_disable(dev, force);
++	mt7996_dma_wed_reset(dev);
+ 
+ 	/* reset hw queues */
+ 	for (i = 0; i < __MT_TXQ_MAX; i++) {
+-		mt76_queue_reset(dev, dev->mphy.q_tx[i], false);
++		mt7996_dma_reset_tx_queue(dev, dev->mphy.q_tx[i]);
+ 		if (phy2)
+-			mt76_queue_reset(dev, phy2->q_tx[i], false);
++			mt7996_dma_reset_tx_queue(dev, phy2->q_tx[i]);
+ 		if (phy3)
+-			mt76_queue_reset(dev, phy3->q_tx[i], false);
++			mt7996_dma_reset_tx_queue(dev, phy3->q_tx[i]);
+ 	}
+ 
+ 	for (i = 0; i < __MT_MCUQ_MAX; i++)
+ 		mt76_queue_reset(dev, dev->mt76.q_mcu[i], false);
+ 
+ 	mt76_for_each_q_rx(&dev->mt76, i) {
++		if (mtk_wed_device_active(wed) &&
++		    ((dev->mt76.q_rx[i].flags & MT_QFLAG_RRO) ||
++		    dev->mt76.q_rx[i].flags == MT_WED_Q_TXFREE))
++			continue;
++
+ 		mt76_queue_reset(dev, &dev->mt76.q_rx[i], false);
+ 	}
+ 
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index 3a89013c..d1082e89 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2002,6 +2002,10 @@ mt7996_mac_restart(struct mt7996_dev *dev)
+ 	/* disable all tx/rx napi */
+ 	mt76_worker_disable(&dev->mt76.tx_worker);
+ 	mt76_for_each_q_rx(mdev, i) {
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++		    (mdev->q_rx[i].flags & MT_QFLAG_RRO))
++			continue;
++
+ 		if (mdev->q_rx[i].ndesc)
+ 			napi_disable(&dev->mt76.napi[i]);
+ 	}
+@@ -2015,6 +2019,10 @@ mt7996_mac_restart(struct mt7996_dev *dev)
+ 
+ 	local_bh_disable();
+ 	mt76_for_each_q_rx(mdev, i) {
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++		    (mdev->q_rx[i].flags & MT_QFLAG_RRO))
++			continue;
++
+ 		if (mdev->q_rx[i].ndesc) {
+ 			napi_enable(&dev->mt76.napi[i]);
+ 			napi_schedule(&dev->mt76.napi[i]);
+@@ -2189,6 +2197,13 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 
+ 	dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.",
+ 		 wiphy_name(dev->mt76.hw->wiphy));
++
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed_ext))
++		mtk_wed_device_stop(&dev->mt76.mmio.wed_ext);
++
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed))
++		mtk_wed_device_stop(&dev->mt76.mmio.wed);
++
+ 	ieee80211_stop_queues(mt76_hw(dev));
+ 	if (phy2)
+ 		ieee80211_stop_queues(phy2->mt76->hw);
+@@ -2212,8 +2227,13 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 		cancel_delayed_work_sync(&phy3->mt76->mac_work);
+ 	}
+ 	mt76_worker_disable(&dev->mt76.tx_worker);
+-	mt76_for_each_q_rx(&dev->mt76, i)
++	mt76_for_each_q_rx(&dev->mt76, i) {
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++		    (dev->mt76.q_rx[i].flags & MT_QFLAG_RRO))
++			continue;
++
+ 		napi_disable(&dev->mt76.napi[i]);
++	}
+ 	napi_disable(&dev->mt76.tx_napi);
+ 
+ 	mutex_lock(&dev->mt76.mutex);
+@@ -2236,6 +2256,29 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 	/* enable dma tx/rx and interrupt */
+ 	__mt7996_dma_enable(dev, false, false);
+ 
++
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
++		u32 wed_irq_mask = dev->mt76.mmio.irqmask |
++				   MT_INT_RRO_RX_DONE |
++				   MT_INT_TX_DONE_BAND2;
++
++		if (mtk_wed_get_rx_capa(&dev->mt76.mmio.wed))
++			wed_irq_mask &= ~MT_INT_RX_DONE_RRO_IND;
++
++		mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
++
++		mtk_wed_device_start_hwrro(&dev->mt76.mmio.wed, wed_irq_mask, true);
++		mt7996_irq_enable(dev, wed_irq_mask);
++		mt7996_irq_disable(dev, 0);
++	}
++
++	if (mtk_wed_device_active(&dev->mt76.mmio.wed_ext)) {
++		mt76_wr(dev, MT_INT1_MASK_CSR,
++			dev->mt76.mmio.irqmask | MT_INT_TX_DONE_BAND2);
++		mtk_wed_device_start(&dev->mt76.mmio.wed_ext,
++			dev->mt76.mmio.irqmask | MT_INT_TX_DONE_BAND2);
++	}
++
+ 	clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+ 	clear_bit(MT76_RESET, &dev->mphy.state);
+ 	if (phy2)
+@@ -2245,6 +2288,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
+ 
+ 	local_bh_disable();
+ 	mt76_for_each_q_rx(&dev->mt76, i) {
++		if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
++		    ((dev->mt76.q_rx[i].flags & MT_QFLAG_RRO)))
++			continue;
++
+ 		napi_enable(&dev->mt76.napi[i]);
+ 		napi_schedule(&dev->mt76.napi[i]);
+ 	}
+diff --git a/mt7996/mmio.c b/mt7996/mmio.c
+index 9960dca7..fe34bb7d 100644
+--- a/mt7996/mmio.c
++++ b/mt7996/mmio.c
+@@ -6,9 +6,11 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
++#include <linux/rtnetlink.h>
+ 
+ #include "mt7996.h"
+ #include "mac.h"
++#include "mcu.h"
+ #include "../trace.h"
+ #include "../dma.h"
+ 
+@@ -297,6 +299,43 @@ unmap:
+ 	mt7996_mmio_wed_release_rx_buf(wed);
+ 	return -ENOMEM;
+ }
++
++static int mt7996_mmio_wed_reset(struct mtk_wed_device *wed)
++{
++	struct mt76_dev *mdev = container_of(wed, struct mt76_dev, mmio.wed);
++	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
++	struct mt76_phy *mphy = &dev->mphy;
++	int ret;
++
++	ASSERT_RTNL();
++
++	if (test_and_set_bit(MT76_STATE_WED_RESET, &mphy->state))
++		return -EBUSY;
++
++	ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_L1,
++				 mphy->band_idx);
++	if (ret)
++		goto out;
++
++	rtnl_unlock();
++	if (!wait_for_completion_timeout(&mdev->mmio.wed_reset, 20 * HZ)) {
++		dev_err(mdev->dev, "wed reset timeout\n");
++		ret = -ETIMEDOUT;
++	}
++	rtnl_lock();
++out:
++	clear_bit(MT76_STATE_WED_RESET, &mphy->state);
++
++	return ret;
++}
++
++static void mt7996_mmio_wed_reset_complete(struct mtk_wed_device *wed)
++{
++	struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
++
++	complete(&dev->mmio.wed_reset_complete);
++}
++
+ #endif
+ 
+ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+@@ -421,6 +460,14 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
+ 	wed->wlan.init_rx_buf = mt7996_mmio_wed_init_rx_buf;
+ 	wed->wlan.release_rx_buf = mt7996_mmio_wed_release_rx_buf;
+ 	wed->wlan.update_wo_rx_stats = NULL;
++	if (hif2) {
++		wed->wlan.reset = NULL;
++		wed->wlan.reset_complete = NULL;
++	} else {
++		wed->wlan.reset = mt7996_mmio_wed_reset;
++		wed->wlan.reset_complete = mt7996_mmio_wed_reset_complete;
++	}
++
+ 
+ 	if (mtk_wed_device_attach(wed))
+ 		return 0;
+-- 
+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 12953cf..3114ebf 100644
--- a/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/patches.inc
@@ -1,28 +1,66 @@
 #patch patches (come from openwrt/lede/target/linux/mediatek)
 SRC_URI_append = " \
     file://0001-wifi-mt76-mt7996-add-eht-rx-rate-support.patch \
-    file://0002-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch \
-    file://0003-wifi-mt76-mt7996-move-radio-enable-command-to-mt7996.patch \
-    file://0004-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch \
-    file://0005-wifi-mt76-mt7996-add-muru-support.patch \
-    file://0006-wifi-mt76-mt7996-set-txd-v1.patch \
-    file://0007-wifi-mt76-mt7996-add-thermal-protection-support.patch \
-    file://0008-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch \
-    file://0009-wifi-mt76-mt7996-add-dsp-firmware-download.patch \
-    file://0010-wifi-mt76-mt7996-fix-icv-error-when-enable-AP-and-ST.patch \
-    file://0011-wifi-mt76-mt7996-set-wcid-in-txp.patch \
-    file://0012-wifi-mt76-mt7996-init-he-and-eht-cap-for-AP_VLAN.patch \
-    file://0013-wifi-mt76-mt7996-fix-beamform-mcu-cmd-configuration.patch \
-    file://0014-wifi-mt76-mt7996-Fix-using-the-wrong-phy-for-backgro.patch \
-    file://0015-wifi-mt76-mt7996-support-more-options-in-.set_bitrat.patch \
-    file://0016-wifi-mt76-mt7996-fill-txwi-by-SW-temporarily.patch \
-    file://0017-wifi-mt76-mt7996-update-wmm-queue-mapping.patch \
-    file://0018-wifi-mt76-mt7996-enable-IDS-debug-log.patch \
-    file://0019-mt76-testmode-add-atenl-support-in-mt7996.patch \
-    file://0020-mt76-testmode-add-basic-testmode-support.patch \
-    file://0021-mt76-testmode-add-chainmask-hacking-for-eagle-band-2.patch \
-    file://0022-mt76-revert-page-pool-changes.patch \
-    file://0999-mt76-mt7996-for-build-pass.patch \
-    file://1000-mt76-mt7996-add-debug-tool.patch \
-    file://1001-mt76-mt7996-add-txpower-support.patch \
+    file://0002-wifi-mt76-mt7996-move-radio-ctrl-commands-to-proper-.patch \
+    file://0003-wifi-mt76-connac-add-support-for-dsp-firmware-downlo.patch \
+    file://0004-wifi-mt76-mt7996-fix-bss-wlan_idx-when-sending-bss_i.patch \
+    file://0005-wifi-mt76-mt7996-init-he-and-eht-cap-for-AP_VLAN.patch \
+    file://0006-wifi-mt76-mt7996-enable-VHT-extended-NSS-BW-feature.patch \
+    file://0007-wifi-mt76-connac-add-support-to-set-ifs-time-by-mcu-.patch \
+    file://0008-wifi-mt76-mt7996-use-correct-phy-for-background-rada.patch \
+    file://0009-wifi-mt76-mt7996-fix-WA-event-ring-size.patch \
+    file://0010-wifi-mt76-mt7996-add-muru-support.patch \
+    file://0011-wifi-mt76-mt7996-increase-tx-token-size.patch \
+    file://0014-wifi-mt76-mt7996-set-wcid-in-txp.patch \
+    file://0015-wifi-mt76-mt7996-reduce-repeated-bss_info-and-sta_re.patch \
+    file://0016-wifi-mt76-connac-set-correct-muar_idx-for-connac3-ch.patch \
+    file://0017-wifi-mt76-mt7996-add-thermal-protection-support.patch \
+    file://0018-wifi-mt76-mt7996-add-thermal-sensor-device-support.patch \
+    file://0019-wifi-mt76-mt7996-fix-beamform-mcu-cmd-configuration.patch \
+    file://0020-wifi-mt76-mt7996-support-more-options-in-.set_bitrat.patch \
+    file://0021-wifi-mt76-mt7996-update-wmm-queue-mapping.patch \
+    file://0022-wifi-mt76-mt7996-enable-IDS-debug-log.patch \
+    file://0023-wifi-mt76-testmode-add-atenl-support-in-mt7996.patch \
+    file://0024-wifi-mt76-testmode-add-basic-testmode-support.patch \
+    file://0026-wifi-mt76-mt7996-add-led-feature-support.patch \
+    file://0027-wifi-mt76-mt7996-fix-twt-mcu-command.patch \
+    file://0028-wifi-mt76-mt7996-add-11v-mbss-support-for-mt76.patch \
+    file://0029-wifi-mt76-mt7996-Update-beacon-size-limitation-for-1.patch \
+    file://0030-wifi-mt76-mt7996-add-support-for-auxiliary-path.patch \
+    file://0031-wifi-mt76-mt7996-fix-memory-leak.patch \
+    file://0032-wifi-mt76-mt7996-add-eht-mode-tx-stats.patch \
+    file://0033-wifi-mt76-mt7996-disable-wfdma-tx-rx-during-SER.patch \
+    file://0034-wifi-mt76-mt7996-add-firmware-WA-s-coredump.patch \
+    file://0035-wifi-mt76-mt7996-make-band-capability-init-flexible.patch \
+    file://0036-wifi-mt76-mt7996-enable-SCS-feature-for-mt7996-drive.patch \
+    file://0037-wifi-mt76-mt7996-add-beacon-duplicate-tx-mode-suppor.patch \
+    file://0038-wifi-mt76-mt7996-fix-DFS-CAC-tx-emission-issue-after.patch \
+    file://0039-wifi-mt76-mt7996-fix-bss-rate-tlv-to-sync-firmware-c.patch \
+    file://0040-wifi-mt76-mt7996-fix-beamformee-ss-subfield-in-EHT-P.patch \
+    file://0999-wifi-mt76-mt7996-for-build-pass.patch \
+    file://1000-wifi-mt76-mt7996-add-debug-tool.patch \
+    file://1001-wifi-mt76-mt7996-add-txpower-support.patch \
+    file://1002-wifi-mt76-mt7996-add-mu-vendor-command-support.patch \
+    file://1003-wifi-mt76-mt7996-Add-air-monitor-support.patch \
+    file://1004-wifi-mt76-mt7996-add-driver-support-for-wpa3-ocv-and.patch \
+    file://1005-wifi-mt76-mt7996-add-U-NII-4-support.patch \
+    file://1006-wifi-mt76-testmode-add-testmode-pre-calibration-supp.patch \
+    file://1007-wifi-mt76-mt7996-add-binfile-mode-support.patch \
+    file://1008-wifi-mt76-mt7996-add-normal-mode-pre-calibration-sup.patch \
+    file://1009-wifi-mt76-mt7996-Beacon-protection-feature-added.patch \
+    file://1010-wifi-mt76-testmode-add-testmode-ZWDFS-verification-s.patch \
+    file://1011-wifi-mt76-mt7996-add-single-sku.patch \
+    file://1012-wifi-mt76-mt7996-add-vendor-cmd-to-get-available-col.patch \
+    file://1013-wifi-mt76-mt7996-get-tx_retries-and-tx_fails-from-tx.patch \
+    file://1014-wifi-mt76-mt7996-add-debugfs-for-fw-coredump.patch \
+    file://1015-wifi-mt76-mt7996-add-support-for-runtime-set-in-band.patch \
+    file://2000-wifi-mt76-rework-wed-rx-flow.patch \
+    file://2001-wifi-mt76-revert-page_poll-for-kernel-5.4.patch \
+    file://2002-wifi-mt76-wed-change-wed-token-init-size-to-adapt-we.patch \
+    file://2003-wifi-mt76-mt7996-wed-add-wed3.0-tx-support.patch \
+    file://2004-wifi-mt76-mt7996-wed-add-wed3.0-rx-support.patch \
+    file://2005-wifi-mt76-mt7996-wed-add-mt7996_net_setup_tc-to-supp.patch \
+    file://2006-wifi-mt76-add-random-early-drop-support.patch \
+    file://2007-wifi-mt76-mt7996-reset-addr_elem-when-delete-ba.patch \
+    file://2008-wifi-mt76-add-SER-support-for-wed3.0.patch \
     "
diff --git a/recipes-wifi/linux-mt76/files/patches/1010-wifi-mt76-testmode-additional-supports.patch b/recipes-wifi/linux-mt76/files/patches/1010-wifi-mt76-testmode-additional-supports.patch
index cbdece6..7902ebc 100644
--- a/recipes-wifi/linux-mt76/files/patches/1010-wifi-mt76-testmode-additional-supports.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1010-wifi-mt76-testmode-additional-supports.patch
@@ -1,32 +1,32 @@
-From 29deaa7cb606cf334238cb36d89891fa80bef0fc Mon Sep 17 00:00:00 2001
+From c311d022536c9c6be72f8e4a134f9dbaed13cd6d Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Thu, 21 Apr 2022 15:43:19 +0800
-Subject: [PATCH 1010/1033] wifi: mt76: testmode: additional supports
+Subject: [PATCH 1010/1014] wifi: mt76: testmode: additional supports
 
 Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
  dma.c             |    3 +-
  mac80211.c        |   12 +
- mt76.h            |  108 ++++-
+ mt76.h            |  108 +++-
  mt76_connac_mcu.c |    4 +
  mt76_connac_mcu.h |    2 +
  mt7915/eeprom.c   |    2 +-
  mt7915/init.c     |    2 +-
  mt7915/mac.c      |   39 +-
  mt7915/main.c     |    2 +-
- mt7915/mcu.c      |   19 +-
+ mt7915/mcu.c      |   22 +-
  mt7915/mcu.h      |   29 +-
  mt7915/mmio.c     |    2 +
  mt7915/mt7915.h   |   16 +-
  mt7915/regs.h     |    3 +
- mt7915/testmode.c | 1184 ++++++++++++++++++++++++++++++++++++++++++---
+ mt7915/testmode.c | 1221 ++++++++++++++++++++++++++++++++++++++++++---
  mt7915/testmode.h |  278 +++++++++++
- testmode.c        |  280 +++++++++--
+ testmode.c        |  282 +++++++++--
  testmode.h        |   75 +++
  tools/fields.c    |   84 +++-
  tx.c              |    3 +-
- 20 files changed, 1991 insertions(+), 156 deletions(-)
+ 20 files changed, 2023 insertions(+), 166 deletions(-)
 
 diff --git a/dma.c b/dma.c
 index c9d2671..fc92e39 100644
@@ -398,7 +398,7 @@
  		mvif->mt76.wmm_idx += 2;
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 5a68bb7..e4ab3e0 100644
+index 5a68bb7..5cea513 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -387,6 +387,11 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
@@ -421,7 +421,17 @@
  	    !rxd->seq)
  		mt7915_mcu_rx_unsolicited_event(dev, skb);
  	else
-@@ -2781,21 +2787,21 @@ static int mt7915_mcu_set_eeprom_flash(struct mt7915_dev *dev)
+@@ -2713,7 +2719,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
+ 	}
+ #endif
+ 
+-	if (mt76_connac_spe_idx(phy->mt76->antenna_mask))
++	if (mt76_connac_spe_idx(phy->mt76->antenna_mask) &&
++	    !mt76_testmode_enabled(phy->mt76))
+ 		req.tx_path_num = fls(phy->mt76->antenna_mask);
+ 
+ 	if (dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
+@@ -2781,21 +2788,21 @@ static int mt7915_mcu_set_eeprom_flash(struct mt7915_dev *dev)
  	return 0;
  }
  
@@ -446,7 +456,7 @@
  {
  	struct mt7915_mcu_eeprom_info req = {
  		.addr = cpu_to_le32(round_down(offset,
-@@ -2804,7 +2810,7 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
+@@ -2804,7 +2811,7 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
  	struct mt7915_mcu_eeprom_info *res;
  	struct sk_buff *skb;
  	int ret;
@@ -455,7 +465,7 @@
  
  	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
  					MCU_EXT_QUERY(EFUSE_ACCESS),
-@@ -2813,8 +2819,11 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
+@@ -2813,8 +2820,11 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset)
  		return ret;
  
  	res = (struct mt7915_mcu_eeprom_info *)skb->data;
@@ -595,7 +605,7 @@
  static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev)
  {
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index e7bc181..0339d4b 100644
+index e7bc181..b6f36f5 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -62,6 +62,7 @@ enum offs_rev {
@@ -611,12 +621,12 @@
  #define MT_WF_AGG(_band, ofs)		(MT_WF_AGG_BASE(_band) + (ofs))
  
 +#define MT_AGG_AALCR0(_band, _n)	MT_WF_AGG(_band, (__OFFS(AGG_AALCR0) +	\
-+			                                  (_n) * 4))
++							  (_n) * 4))
  #define MT_AGG_AWSCR0(_band, _n)	MT_WF_AGG(_band, (__OFFS(AGG_AWSCR0) +	\
  							  (_n) * 4))
  #define MT_AGG_PCR0(_band, _n)		MT_WF_AGG(_band, (__OFFS(AGG_PCR0) +	\
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 4693919..c44f13f 100644
+index 4693919..62ef4db 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -9,6 +9,9 @@
@@ -639,7 +649,7 @@
  };
  
  struct reg_band {
-@@ -33,6 +39,38 @@ struct reg_band {
+@@ -33,6 +39,57 @@ struct reg_band {
  #define TM_REG_MAX_ID	20
  static struct reg_band reg_backup_list[TM_REG_MAX_ID];
  
@@ -664,6 +674,25 @@
 +	return width_to_bw[width];
 +}
 +
++static int
++mt7915_tm_check_antenna(struct mt7915_phy *phy)
++{
++	struct mt76_testmode_data *td = &phy->mt76->test;
++	struct mt7915_dev *dev = phy->dev;
++	u8 band_idx = phy->mt76->band_idx;
++	u32 chainmask = phy->mt76->chainmask;
++
++	chainmask = chainmask >> (dev->chainshift * band_idx);
++	if (td->tx_antenna_mask & ~chainmask) {
++		dev_err(dev->mt76.dev,
++			"tx antenna mask %d exceeds hardware limitation (chainmask %d)\n",
++			td->tx_antenna_mask, chainmask);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
 +static void
 +mt7915_tm_update_channel(struct mt7915_phy *phy)
 +{
@@ -678,7 +707,7 @@
  
  static int
  mt7915_tm_set_tx_power(struct mt7915_phy *phy)
-@@ -119,18 +157,28 @@ mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en)
+@@ -119,18 +176,28 @@ mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en)
  }
  
  static int
@@ -711,7 +740,7 @@
  }
  
  static int
-@@ -141,7 +189,7 @@ mt7915_tm_set_phy_count(struct mt7915_phy *phy, u8 control)
+@@ -141,7 +208,7 @@ mt7915_tm_set_phy_count(struct mt7915_phy *phy, u8 control)
  		.testmode_en = 1,
  		.param_idx = MCU_ATE_SET_PHY_COUNT,
  		.param.cfg.enable = control,
@@ -720,7 +749,7 @@
  	};
  
  	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
-@@ -182,12 +230,738 @@ mt7915_tm_set_tam_arb(struct mt7915_phy *phy, bool enable, bool mu)
+@@ -182,12 +249,738 @@ mt7915_tm_set_tam_arb(struct mt7915_phy *phy, bool enable, bool mu)
  	return mt7915_mcu_set_muru_ctrl(dev, MURU_SET_ARB_OP_MODE, op_mode);
  }
  
@@ -1461,7 +1490,7 @@
  	struct edca *e = &req.edca[0];
  
  	e->queue = qid + mvif->mt76.wmm_idx * MT76_CONNAC_MAX_WMM_SETS;
-@@ -263,7 +1037,8 @@ done:
+@@ -263,7 +1056,8 @@ done:
  
  	return mt7915_tm_set_wmm_qid(phy,
  				     mt76_connac_lmac_mapping(IEEE80211_AC_BE),
@@ -1471,7 +1500,7 @@
  }
  
  static int
-@@ -339,7 +1114,7 @@ mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time)
+@@ -339,7 +1133,7 @@ mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time)
  	bitrate = cfg80211_calculate_bitrate(&rate);
  	tx_len = bitrate * tx_time / 10 / 8;
  
@@ -1480,7 +1509,7 @@
  	if (ret)
  		return ret;
  
-@@ -458,64 +1233,227 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
+@@ -458,64 +1252,227 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
  
  	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
  
@@ -1580,7 +1609,7 @@
 +	comm->spe_idx = phy->test.spe_idx;
 +
 +	dl->bw = mt7915_tm_chan_bw(chandef->width);
-+	dl->gi = td->tx_rate_sgi;;
++	dl->gi = td->tx_rate_sgi;
 +	dl->ltf = td->tx_ltf;
 +	dl->tx_mode = MT_PHY_TYPE_HE_MU;
 +
@@ -1739,7 +1768,7 @@
  
  	mt7915_tm_set_trx(phy, TM_MAC_TX, en);
  }
-@@ -544,10 +1482,6 @@ mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
+@@ -544,10 +1501,6 @@ mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
  		return ret;
  
  	rs_band = (struct mt7915_tm_rx_stat_band *)skb->data;
@@ -1750,7 +1779,7 @@
  
  	if (!clear) {
  		enum mt76_rxq_id q = req.band ? MT_RXQ_BAND1 : MT_RXQ_MAIN;
-@@ -562,13 +1496,61 @@ mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
+@@ -562,13 +1515,61 @@ mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
  	return 0;
  }
  
@@ -1813,7 +1842,7 @@
  
  		/* read-clear */
  		mt7915_tm_get_rx_stats(phy, true);
-@@ -576,9 +1558,12 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
+@@ -576,9 +1577,12 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
  		/* clear fw count */
  		mt7915_tm_set_phy_count(phy, 0);
  		mt7915_tm_set_phy_count(phy, 1);
@@ -1828,7 +1857,7 @@
  }
  
  static int
-@@ -617,34 +1602,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
+@@ -617,34 +1621,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
  	tx_cont->tx_ant = td->tx_antenna_mask;
  	tx_cont->band = band;
  
@@ -1864,7 +1893,7 @@
  
  	if (!en) {
  		req.op.rf.param.func_data = cpu_to_le32(band);
-@@ -728,6 +1686,12 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
+@@ -728,6 +1705,12 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
  		mt7915_tm_set_freq_offset(phy, en, en ? td->freq_offset : 0);
  	if (changed & BIT(TM_CHANGED_TXPOWER))
  		mt7915_tm_set_tx_power(phy);
@@ -1877,7 +1906,7 @@
  }
  
  static int
-@@ -737,6 +1701,11 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+@@ -737,6 +1720,11 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
  	struct mt7915_phy *phy = mphy->priv;
  	enum mt76_testmode_state prev_state = td->state;
  
@@ -1889,7 +1918,53 @@
  	mphy->test.state = state;
  
  	if (prev_state == MT76_TM_STATE_TX_FRAMES ||
-@@ -807,6 +1776,7 @@ static int
+@@ -757,7 +1745,7 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+ 	    (state == MT76_TM_STATE_OFF &&
+ 	     prev_state == MT76_TM_STATE_IDLE)) {
+ 		u32 changed = 0;
+-		int i;
++		int i, ret;
+ 
+ 		for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
+ 			u16 cur = tm_change_map[i];
+@@ -766,6 +1754,10 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+ 				changed |= BIT(i);
+ 		}
+ 
++		ret = mt7915_tm_check_antenna(phy);
++		if (ret)
++			return ret;
++
+ 		mt7915_tm_update_params(phy, changed);
+ 	}
+ 
+@@ -778,10 +1770,8 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
+ {
+ 	struct mt76_testmode_data *td = &mphy->test;
+ 	struct mt7915_phy *phy = mphy->priv;
+-	struct mt7915_dev *dev = phy->dev;
+-	u32 chainmask = mphy->chainmask, changed = 0;
+-	bool ext_phy = phy != &dev->phy;
+-	int i;
++	u32 changed = 0;
++	int i, ret;
+ 
+ 	BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
+ 
+@@ -789,9 +1779,9 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
+ 	    td->state == MT76_TM_STATE_OFF)
+ 		return 0;
+ 
+-	chainmask = ext_phy ? chainmask >> dev->chainshift : chainmask;
+-	if (td->tx_antenna_mask > chainmask)
+-		return -EINVAL;
++	ret = mt7915_tm_check_antenna(phy);
++	if (ret)
++		return ret;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
+ 		if (tb[tm_change_map[i]])
+@@ -807,6 +1797,7 @@ static int
  mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
  {
  	struct mt7915_phy *phy = mphy->priv;
@@ -1897,7 +1972,7 @@
  	void *rx, *rssi;
  	int i;
  
-@@ -852,11 +1822,75 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
+@@ -852,11 +1843,75 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
  
  	nla_nest_end(msg, rx);
  
@@ -1974,7 +2049,7 @@
 +	.set_eeprom = mt7915_tm_set_eeprom,
  };
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index a1c54c8..01b08e9 100644
+index a1c54c8..eb0e043 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -4,6 +4,8 @@
@@ -2121,12 +2196,12 @@
 +/* Common Config */
 +/* #define MURU_COMM_PPDU_FMT		BIT(0) */
 +/* #define MURU_COMM_SCH_TYPE		BIT(1) */
-+/* #define MURU_COMM_BAND			BIT(2) */
-+/* #define MURU_COMM_WMM			BIT(3) */
++/* #define MURU_COMM_BAND		BIT(2) */
++/* #define MURU_COMM_WMM		BIT(3) */
 +/* #define MURU_COMM_SPE_IDX		BIT(4) */
 +/* #define MURU_COMM_PROC_TYPE		BIT(5) */
 +/* #define MURU_COMM_SET		(MURU_COMM_PPDU_FMT | MURU_COMM_BAND | \ */
-+/* 				 MURU_COMM_WMM | MURU_COMM_SPE_IDX) */
++/*					 MURU_COMM_WMM | MURU_COMM_SPE_IDX) */
 +/* DL Config */
 +#define MURU_DL_BW			BIT(0)
 +#define MURU_DL_GI			BIT(1)
@@ -2289,7 +2364,7 @@
 +
  #endif
 diff --git a/testmode.c b/testmode.c
-index 1d0d5d3..fd3b9b2 100644
+index 1d0d5d3..97f65fd 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -27,28 +27,16 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
@@ -2340,8 +2415,8 @@
  		td->tx_queued++;
 +
 +		if (td->tx_rate_mode != MT76_TM_TX_MODE_HE_MU)
-+		    if (td->tx_queued - td->tx_done >= limit)
-+			    break;
++			if (td->tx_queued - td->tx_done >= limit)
++				break;
  	}
  
  	dev->queue_ops->kick(dev, q);
@@ -2368,7 +2443,7 @@
 +		q = phy->q_tx[qid];
 +		spin_lock_bh(&q->lock);
 +		mt76_testmode_queue_tx(phy, &phy->dev->global_wcid,
-+				td->tx_skb, q, qid, tx_queued_limit);
++				       td->tx_skb, q, qid, tx_queued_limit);
 +		spin_unlock_bh(&q->lock);
 +
 +		return;
@@ -2387,7 +2462,7 @@
 +
 +	mt76_testmode_queue_tx(phy, td->cur_entry, ed->tx_skb, q, qid, tx_queued_limit);
 +
-+	if (td->tx_pending % td->tx_count == 0 || is_mu)
++	if ((td->tx_count != UINT_MAX && td->tx_pending % td->tx_count == 0) || is_mu)
 +		td->cur_entry = list_next_entry(td->cur_entry, list);
  
  	spin_unlock_bh(&q->lock);
@@ -2686,7 +2761,7 @@
 +
 +		if (!tb[MT76_TM_ATTR_TXBF_PARAM] ||
 +		    mt76_tm_get_u8(tb[MT76_TM_ATTR_TXBF_ACT], &td->txbf_act,
-+		    0, MT76_TM_TXBF_ACT_MAX))
++				   0, MT76_TM_TXBF_ACT_MAX))
 +			goto out;
 +
 +		memset(td->txbf_param, 0, sizeof(td->txbf_param));
@@ -2743,10 +2818,14 @@
  	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
  	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
  	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
-@@ -643,6 +824,15 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
- 	     nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
- 		goto out;
- 
+@@ -640,7 +821,16 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) &&
+ 	     nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) ||
+ 	    (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) &&
+-	     nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
++	     nla_put_u32(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
++		goto out;
++
 +	if (nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, ed->tx_mpdu_len) ||
 +	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, ed->tx_rate_nss) ||
 +	    nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, ed->tx_rate_idx) ||
@@ -2754,13 +2833,11 @@
 +	    nla_put_u8(msg, MT76_TM_ATTR_AID, ed->aid) ||
 +	    nla_put_u8(msg, MT76_TM_ATTR_RU_ALLOC, ed->ru_alloc) ||
 +	    nla_put_u8(msg, MT76_TM_ATTR_RU_IDX, ed->ru_idx))
-+		goto out;
-+
+ 		goto out;
+ 
  	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
- 		a = nla_nest_start(msg, MT76_TM_ATTR_TX_POWER);
- 		if (!a)
 diff --git a/testmode.h b/testmode.h
-index 8961326..57949f2 100644
+index 8961326..8c55fa0 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -6,6 +6,8 @@
@@ -2778,10 +2855,10 @@
   * @MT76_TM_ATTR_MAC_ADDRS: array of nested MAC addresses (nested)
 + *
 + * @MT76_TM_ATTR_EEPROM_ACTION: eeprom setting actions
-+ * 	(u8, see &enum mt76_testmode_eeprom_action)
++ *	(u8, see &enum mt76_testmode_eeprom_action)
 + * @MT76_TM_ATTR_EEPROM_OFFSET: offset of eeprom data block for writing (u32)
 + * @MT76_TM_ATTR_EEPROM_VAL: values for writing into a 16-byte data block
-+ * 	(nested, u8 attrs)
++ *	(nested, u8 attrs)
 + *
 + * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
 + *
@@ -2814,7 +2891,7 @@
 + * enum mt76_testmode_eeprom_action - eeprom setting actions
 + *
 + * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
-+ * 	eeprom data block
++ *	eeprom data block
 + * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
 + * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
 + */
@@ -2832,7 +2909,7 @@
 + * enum mt76_testmode_cfg - packet tx phy mode
 + *
 + * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
-+ * 	eeprom data block
++ *	eeprom data block
 + * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
 + * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
 + */
diff --git a/recipes-wifi/linux-mt76/files/patches/1011-wifi-mt76-testmode-add-pre-cal-support.patch b/recipes-wifi/linux-mt76/files/patches/1011-wifi-mt76-testmode-add-pre-cal-support.patch
index ac7b121..82e8e7d 100644
--- a/recipes-wifi/linux-mt76/files/patches/1011-wifi-mt76-testmode-add-pre-cal-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1011-wifi-mt76-testmode-add-pre-cal-support.patch
@@ -1,10 +1,9 @@
-From 0b10a07db9b3c9dd93861d71da449c521e228b3e Mon Sep 17 00:00:00 2001
+From b933c76718db1c21ecaaf0cddfa4d598617153ea Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Wed, 31 Aug 2022 20:06:52 +0800
-Subject: [PATCH 1011/1033] wifi: mt76: testmode: add pre-cal support
+Subject: [PATCH 1011/1014] wifi: mt76: testmode: add pre-cal support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Change-Id: Ibfbbc3443de994eeb4daa5e364b0a90f5d7d3bcd
 ---
  eeprom.c          |   6 +-
  mt76.h            |   1 +
@@ -114,7 +113,7 @@
  
  #endif
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index e4ab3e0..abb54eb 100644
+index 5cea513..2a515e3 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -391,6 +391,9 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
@@ -127,7 +126,7 @@
  #endif
  	default:
  		break;
-@@ -2887,7 +2890,7 @@ int mt7915_mcu_apply_group_cal(struct mt7915_dev *dev)
+@@ -2888,7 +2891,7 @@ int mt7915_mcu_apply_group_cal(struct mt7915_dev *dev)
  	u8 idx = 0, *cal = dev->cal, *eep = dev->mt76.eeprom.data;
  	u32 total = MT_EE_CAL_GROUP_SIZE;
  
@@ -136,7 +135,7 @@
  		return 0;
  
  	/*
-@@ -2967,11 +2970,29 @@ int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy)
+@@ -2968,11 +2971,29 @@ int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy)
  {
  	struct mt7915_dev *dev = phy->dev;
  	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
@@ -192,7 +191,7 @@
  static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev)
  {
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index c44f13f..146c4d3 100644
+index 62ef4db..b2d26ff 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -5,6 +5,7 @@
@@ -203,7 +202,7 @@
  
  enum {
  	TM_CHANGED_TXPOWER,
-@@ -1580,18 +1581,16 @@ mt7915_tm_rf_switch_mode(struct mt7915_dev *dev, u32 oper)
+@@ -1599,18 +1600,16 @@ mt7915_tm_rf_switch_mode(struct mt7915_dev *dev, u32 oper)
  static int
  mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
  {
@@ -224,7 +223,7 @@
  		.icap_len = 120,
  		.op.rf.func_idx = cpu_to_le32(func_idx),
  	};
-@@ -1676,6 +1675,316 @@ out:
+@@ -1695,6 +1694,316 @@ out:
  				 sizeof(req), true);
  }
  
@@ -541,7 +540,7 @@
  static void
  mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
  {
-@@ -1720,6 +2029,10 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+@@ -1739,6 +2048,10 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
  	else if (prev_state == MT76_TM_STATE_OFF ||
  		 state == MT76_TM_STATE_OFF)
  		mt7915_tm_init(phy, !(state == MT76_TM_STATE_OFF));
@@ -552,7 +551,7 @@
  
  	if ((state == MT76_TM_STATE_IDLE &&
  	     prev_state == MT76_TM_STATE_OFF) ||
-@@ -1888,9 +2201,113 @@ mt7915_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
+@@ -1909,9 +2222,113 @@ mt7915_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
  	return ret;
  }
  
@@ -667,7 +666,7 @@
 +	.dump_precal = mt7915_tm_dump_precal,
  };
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index 01b08e9..d500987 100644
+index eb0e043..7569826 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -81,6 +81,11 @@ struct tm_tx_cont {
@@ -735,7 +734,7 @@
  	TAM_ARB_OP_MODE_NORMAL = 1,
  	TAM_ARB_OP_MODE_TEST,
 diff --git a/testmode.c b/testmode.c
-index fd3b9b2..b5a919a 100644
+index 97f65fd..21362bd 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -766,6 +766,18 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
@@ -768,7 +767,7 @@
  
  	if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
 diff --git a/testmode.h b/testmode.h
-index 57949f2..34936e5 100644
+index 8c55fa0..109a556 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -19,6 +19,7 @@
diff --git a/recipes-wifi/linux-mt76/files/patches/1012-wifi-mt76-testmode-add-iBF-command-mode-support.patch b/recipes-wifi/linux-mt76/files/patches/1012-wifi-mt76-testmode-add-iBF-command-mode-support.patch
index 5a4d921..fb2113a 100644
--- a/recipes-wifi/linux-mt76/files/patches/1012-wifi-mt76-testmode-add-iBF-command-mode-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1012-wifi-mt76-testmode-add-iBF-command-mode-support.patch
@@ -1,10 +1,9 @@
-From 5537367425c0e52ce5da53612e4aaa1bc756c39f Mon Sep 17 00:00:00 2001
+From e44d59d15e90c11b1466b12ac9401922c11504f6 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Mon, 12 Sep 2022 18:16:54 +0800
-Subject: [PATCH 1012/1033] wifi: mt76: testmode: add iBF command mode support
+Subject: [PATCH 1012/1014] wifi: mt76: testmode: add iBF command mode support
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Change-Id: I7eea1d6412563f889e5774e787e58ce9eba001bd
 ---
  mt7915/testmode.c | 21 ++++++++++++++-------
  testmode.c        | 41 +++++++++++++++++++++++++++++++++++++++++
@@ -13,10 +12,10 @@
  4 files changed, 85 insertions(+), 7 deletions(-)
 
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 146c4d3..6a18cdb 100644
+index b2d26ff..40a3a06 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
-@@ -701,6 +701,7 @@ mt7915_tm_txbf_profile_update(struct mt7915_phy *phy, u16 *val, bool ebf)
+@@ -720,6 +720,7 @@ mt7915_tm_txbf_profile_update(struct mt7915_phy *phy, u16 *val, bool ebf)
  	struct ieee80211_vif *vif = phy->monitor_vif;
  	struct mt7915_tm_pfmu_tag *tag = phy->dev->test.txbf_pfmu_tag;
  	u8 pfmu_idx = val[0], nc = val[2], nr;
@@ -24,7 +23,7 @@
  	int ret;
  
  	if (td->tx_antenna_mask == 3)
-@@ -748,7 +749,7 @@ mt7915_tm_txbf_profile_update(struct mt7915_phy *phy, u16 *val, bool ebf)
+@@ -767,7 +768,7 @@ mt7915_tm_txbf_profile_update(struct mt7915_phy *phy, u16 *val, bool ebf)
  	if (ret)
  		return ret;
  
@@ -33,7 +32,7 @@
  		return mt7915_tm_txbf_apply_tx(phy, 1, false, true, true);
  
  	return 0;
-@@ -775,7 +776,7 @@ mt7915_tm_txbf_phase_cal(struct mt7915_phy *phy, u16 *val)
+@@ -794,7 +795,7 @@ mt7915_tm_txbf_phase_cal(struct mt7915_phy *phy, u16 *val)
  		.group_l_m_n = val[1],
  		.sx2 = val[2],
  		.cal_type = val[3],
@@ -42,7 +41,7 @@
  	};
  	struct mt7915_tm_txbf_phase *phase =
  		(struct mt7915_tm_txbf_phase *)dev->test.txbf_phase_cal;
-@@ -814,6 +815,8 @@ int mt7915_tm_txbf_status_read(struct mt7915_dev *dev, struct sk_buff *skb)
+@@ -833,6 +834,8 @@ int mt7915_tm_txbf_status_read(struct mt7915_dev *dev, struct sk_buff *skb)
  			phase = &phase[cal->group];
  			memcpy(&phase->phase, cal->buf + 16, sizeof(phase->phase));
  			phase->status = cal->status;
@@ -51,7 +50,7 @@
  			break;
  		case IBF_PHASE_CAL_VERIFY:
  		case IBF_PHASE_CAL_VERIFY_INSTRUMENT:
-@@ -865,7 +868,6 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
+@@ -884,7 +887,6 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
  	pfmu_data->phi11 = cpu_to_le16(phi11);
  	pfmu_data->phi21 = cpu_to_le16(phi21);
  	pfmu_data->phi31 = cpu_to_le16(phi31);
@@ -59,7 +58,7 @@
  	if (subc_id == 63) {
  		struct mt7915_dev *dev = phy->dev;
  		struct {
-@@ -923,8 +925,8 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
+@@ -942,8 +944,8 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
  	struct mt76_testmode_data *td = &phy->mt76->test;
  	u16 *val = td->txbf_param;
  
@@ -70,7 +69,7 @@
  
  	switch (td->txbf_act) {
  	case MT76_TM_TXBF_ACT_INIT:
-@@ -942,10 +944,17 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
+@@ -961,10 +963,17 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
  		return mt7915_tm_txbf_profile_update(phy, val, true);
  	case MT76_TM_TXBF_ACT_PHASE_CAL:
  		return mt7915_tm_txbf_phase_cal(phy, val);
@@ -88,7 +87,7 @@
  	default:
  		break;
  	};
-@@ -1072,7 +1081,6 @@ mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time)
+@@ -1091,7 +1100,6 @@ mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time)
  		rate.legacy = sband->bitrates[rate.mcs].bitrate;
  		break;
  	case MT76_TM_TX_MODE_HT:
@@ -96,7 +95,7 @@
  		flags |= RATE_INFO_FLAGS_MCS;
  
  		if (td->tx_rate_sgi)
-@@ -1437,7 +1445,6 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
+@@ -1456,7 +1464,6 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
  			if (duty_cycle < 100)
  				tx_time = duty_cycle * ipg / (100 - duty_cycle);
  		}
@@ -105,7 +104,7 @@
  		mt7915_tm_set_tx_len(phy, tx_time);
  
 diff --git a/testmode.c b/testmode.c
-index b5a919a..f1982ac 100644
+index 21362bd..39cacb2 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -533,6 +533,42 @@ out:
@@ -152,7 +151,7 @@
  		      void *data, int len)
  {
 @@ -671,6 +707,11 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- 		    0, MT76_TM_TXBF_ACT_MAX))
+ 				   0, MT76_TM_TXBF_ACT_MAX))
  			goto out;
  
 +		if (td->txbf_act == MT76_TM_TXBF_ACT_PROF_UPDATE_ALL_CMD) {
@@ -164,7 +163,7 @@
  		nla_for_each_nested(cur, tb[MT76_TM_ATTR_TXBF_PARAM], rem) {
  			if (nla_len(cur) != 2 ||
 diff --git a/testmode.h b/testmode.h
-index 34936e5..bbfb313 100644
+index 109a556..d2675dd 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -281,8 +281,10 @@ enum mt76_testmode_txbf_act {
diff --git a/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-testmode-add-ZWDFS-test-mode-support.patch b/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-testmode-add-ZWDFS-test-mode-support.patch
index 164b46a..f920fd6 100644
--- a/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-testmode-add-ZWDFS-test-mode-support.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-testmode-add-ZWDFS-test-mode-support.patch
@@ -1,22 +1,21 @@
-From 32e4319f0e34cb0a1ebb2dbb45c6561a96196a52 Mon Sep 17 00:00:00 2001
+From ee654a359005c8f5e48cc34a42dc76a6550d7e7f Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 27 Oct 2022 17:42:07 +0800
-Subject: [PATCH 1013/1033] wifi: mt76: testmode: add ZWDFS test mode support
+Subject: [PATCH 1013/1014] wifi: mt76: testmode: add ZWDFS test mode support
 
-Change-Id: I14d104b7158a35acf6b0595357d07fb87f5a9d94
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
  mt76.h            |   9 ++
  mt76_connac_mcu.h |   2 +
- mt7915/mcu.c      |  66 +++++++++++++
- mt7915/mcu.h      |  46 +++++++++
+ mt7915/mcu.c      |  66 +++++++++++
+ mt7915/mcu.h      |  46 ++++++++
  mt7915/mt7915.h   |   4 +
  mt7915/regs.h     |   2 +
- mt7915/testmode.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++
- testmode.c        |  25 ++++-
- testmode.h        |  45 +++++++++
- tools/fields.c    |  22 +++++
- 10 files changed, 452 insertions(+), 1 deletion(-)
+ mt7915/testmode.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++
+ testmode.c        |  25 +++-
+ testmode.h        |  45 ++++++++
+ tools/fields.c    |  22 ++++
+ 10 files changed, 508 insertions(+), 1 deletion(-)
 
 diff --git a/mt76.h b/mt76.h
 index c632852..f4412a2 100644
@@ -59,7 +58,7 @@
  
  enum {
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index abb54eb..2432e57 100644
+index 2a515e3..cb70af5 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -2619,6 +2619,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy,
@@ -70,7 +69,7 @@
  		req.band_idx = phy->mt76->band_idx;
  		req.scan_mode = 2;
  		break;
-@@ -4601,3 +4602,68 @@ int mt7915_mcu_set_amsdu_algo(struct mt7915_dev *dev, u16 wcid, u8 enable)
+@@ -4602,3 +4603,68 @@ int mt7915_mcu_set_amsdu_algo(struct mt7915_dev *dev, u16 wcid, u8 enable)
  	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MEC_CTRL), &req, sizeof(req), true);
  }
  #endif
@@ -219,7 +218,7 @@
  int mt7915_mtk_init_debugfs(struct mt7915_phy *phy, struct dentry *dir);
  int mt7915_dbg_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3, bool wait_resp);
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 0339d4b..0798b08 100644
+index b6f36f5..a7e8598 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -1205,6 +1205,8 @@ enum offs_rev {
@@ -232,7 +231,7 @@
  #define MT_WF_PHY_BASE			0x83080000
  #define MT_WF_PHY(ofs)			(MT_WF_PHY_BASE + (ofs))
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 6a18cdb..09b0e98 100644
+index 40a3a06..b98aaaa 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -13,6 +13,12 @@ enum {
@@ -261,12 +260,53 @@
  };
  
  struct reg_band {
-@@ -962,6 +974,216 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
+@@ -981,6 +993,272 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
  	return 0;
  }
  
++static u8
++mt7915_tm_get_center_chan(struct mt7915_phy *phy, struct cfg80211_chan_def *chandef,
++			  int width_mhz)
++{
++	struct mt76_phy *mphy = phy->mt76;
++	const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
++	u32 bitmap, i, offset, size = 32;
++	u16 first_control = 0, control_chan = chandef->chan->hw_value;
++	static const u32 width_to_bitmap[] = {
++		[NL80211_CHAN_WIDTH_20_NOHT] = 0x0,
++		[NL80211_CHAN_WIDTH_20] = 0x0,
++		[NL80211_CHAN_WIDTH_40] = 0x55554055,
++		[NL80211_CHAN_WIDTH_80] = 0x44444011,
++		[NL80211_CHAN_WIDTH_80P80] = 0x0,
++		[NL80211_CHAN_WIDTH_160] = 0x04004001,
++	};
++
++	bitmap = width_to_bitmap[chandef->width];
++	if (!bitmap)
++		return control_chan;
++
++	offset = width_mhz / 10 - 2;
++	for (i = 0; i < size; i++) {
++		if (!((1 << i) & bitmap))
++			continue;
++
++		if (control_chan >= chan[i].hw_value)
++			first_control = chan[i].hw_value;
++		else
++			break;
++	}
++
++	if (chandef->width == NL80211_CHAN_WIDTH_40 &&
++	    control_chan >= chan[size].hw_value)
++		return chan[size].hw_value + offset;
++	else if (first_control == 0)
++		return control_chan;
++
++	return first_control + offset;
++}
++
 +static int
-+mt7915_tm_set_offchan(struct mt7915_phy *phy)
++mt7915_tm_set_offchan(struct mt7915_phy *phy, bool no_center)
 +{
 +	struct mt76_phy *mphy = phy->mt76;
 +	struct mt7915_dev *dev = phy->dev;
@@ -293,10 +333,24 @@
 +
 +	chandef.width = td->offchan_bw;
 +	width_mhz = bw_to_mhz[chandef.width];
-+	chandef.center_freq1 = freq;
 +	chan = ieee80211_get_channel(hw->wiphy, freq);
++	if (!chan) {
++		ret = -EINVAL;
++		dev_info(dev->mt76.dev, "Failed to set offchan (invalid control channel)!\n");
++		goto out;
++	}
 +	chandef.chan = chan;
 +
++	if (no_center)
++		td->offchan_center_ch = mt7915_tm_get_center_chan(phy, &chandef, width_mhz);
++	chandef.center_freq1 = ieee80211_channel_to_frequency(td->offchan_center_ch,
++							      NL80211_BAND_5GHZ);
++	if (!cfg80211_chandef_valid(&chandef)) {
++		ret = -EINVAL;
++		dev_info(dev->mt76.dev, "Failed to set offchan, chandef is invalid!\n");
++		goto out;
++	}
++
 +	memset(&dev->rdd2_chandef, 0, sizeof(struct cfg80211_chan_def));
 +
 +	ret = mt7915_mcu_rdd_background_enable(phy, &chandef);
@@ -311,6 +365,7 @@
 +
 +out:
 +	td->offchan_ch = 0;
++	td->offchan_center_ch = 0;
 +	td->offchan_bw = 0;
 +
 +	return ret;
@@ -478,7 +533,7 @@
  static int
  mt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min,
  		      u16 cw_max, u16 txop, u8 tx_cmd)
-@@ -1249,6 +1471,8 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
+@@ -1268,6 +1546,8 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
  		phy->mt76->test.tx_mpdu_len = 0;
  		phy->test.bf_en = 0;
  		mt7915_tm_set_entry(phy);
@@ -487,13 +542,13 @@
  	}
  }
  
-@@ -2008,6 +2232,14 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
+@@ -2027,6 +2307,14 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
  		mt7915_tm_set_cfg(phy);
  	if (changed & BIT(TM_CHANGED_TXBF_ACT))
  		mt7915_tm_set_txbf(phy);
 +	if ((changed & BIT(TM_CHANGED_OFF_CHAN_CH)) &&
 +	    (changed & BIT(TM_CHANGED_OFF_CHAN_BW)))
-+		mt7915_tm_set_offchan(phy);
++		mt7915_tm_set_offchan(phy, !(changed & BIT(TM_CHANGED_OFF_CHAN_CENTER_CH)));
 +	if ((changed & BIT(TM_CHANGED_IPI_THRESHOLD)) &&
 +	    (changed & BIT(TM_CHANGED_IPI_PERIOD)))
 +		mt7915_tm_set_ipi(phy);
@@ -503,7 +558,7 @@
  
  static int
 diff --git a/testmode.c b/testmode.c
-index f1982ac..503d714 100644
+index 39cacb2..293ad98 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -24,6 +24,13 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
@@ -567,11 +622,11 @@
  	     nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
  	    (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
 diff --git a/testmode.h b/testmode.h
-index bbfb313..e03fa6d 100644
+index d2675dd..97e7596 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -63,6 +63,20 @@
-  * 	(nested, u8 attrs)
+  *	(nested, u8 attrs)
   *
   * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
 + * @MT76_TM_ATTR_TXBF_ACT: txbf setting actions (u8)
diff --git a/recipes-wifi/linux-mt76/files/patches/1014-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch b/recipes-wifi/linux-mt76/files/patches/1014-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
index 3ae285a..1163a02 100644
--- a/recipes-wifi/linux-mt76/files/patches/1014-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1014-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
@@ -1,4 +1,4 @@
-From 4340e9c467389c104e62d7cd060a5494d83f24bf Mon Sep 17 00:00:00 2001
+From 44b3c5fed9a9c63e7da34004d265669f241a2886 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 15 Dec 2022 19:45:18 +0800
 Subject: [PATCH] wifi: mt76: testmode: add iBF/eBF cal and cert commands with
@@ -836,7 +836,7 @@
 +}
 +#endif
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 0d90251e..a0d5324b 100644
+index a7e8598..5d91b80 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -61,6 +61,7 @@ enum offs_rev {
@@ -858,7 +858,7 @@
  #define MT_WF_RMAC_BASE(_band)		((_band) ? 0x820f5000 : 0x820e5000)
  #define MT_WF_RMAC(_band, ofs)		(MT_WF_RMAC_BASE(_band) + (ofs))
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 09b0e98e..b99bed54 100644
+index b98aaaa..5665da7 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -53,6 +53,8 @@ struct reg_band {
@@ -1491,7 +1491,7 @@
  
 -	pr_info("ibf cal process: act = %u, val = %u, %u, %u, %u, %u, %u\n",
 -		td->txbf_act, val[0], val[1], val[2], val[3], val[4], val[5]);
-+	dev_info(phy->dev->mt76.dev, "ibf cal process: act = %u, val = %u, %u, %u, %u, %u, %u\n",
++	dev_info(phy->dev->mt76.dev, "ibf cal process: act = %u, val = %u, %u, %u, %u, %u, %u, %u\n",
 +		 td->txbf_act, val[0], val[1], val[2], val[3], val[4], val[5], val[6]);
  
  	switch (td->txbf_act) {
@@ -1701,7 +1701,7 @@
  	rateval =  mode << 6 | rate_idx;
  	tx_cont->rateval = cpu_to_le16(rateval);
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index d500987d..19823694 100644
+index 7569826..5aba13c 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -311,137 +311,7 @@ struct mt7915_tm_muru {
@@ -1845,7 +1845,7 @@
  
  #endif
 diff --git a/testmode.c b/testmode.c
-index 503d714b..91638083 100644
+index 293ad98..f16c6ea 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -194,6 +194,7 @@ mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len,
@@ -1857,7 +1857,7 @@
  	memcpy(hdr->addr2, addr[1], ETH_ALEN);
  	memcpy(hdr->addr3, addr[2], ETH_ALEN);
 diff --git a/testmode.h b/testmode.h
-index e03fa6df..0e96173b 100644
+index 97e7596..e13920e 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -299,7 +299,10 @@ enum mt76_testmode_cfg {
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
index a95a6bc..1c7710b 100755
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_dsp.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
index 5b5e33b..7b151c9 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_rom_patch.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
index 854c01c..b0433e8 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wa.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
index 4a60cc8..7188830 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm.bin
Binary files differ
diff --git a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
index 11bbd95..9ddee02 100644
--- a/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
+++ b/recipes-wifi/linux-mt76/files/src/firmware/mt7996/mt7996_wm_tm.bin
Binary files differ
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0018-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0018-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
index c53b55a..bf91686 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0018-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0018-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
@@ -1,19 +1,18 @@
-From 3237a993233da052219018eec10ca82d79225fdb Mon Sep 17 00:00:00 2001
+From 705e1a59381e7bbd92043ad4338834aad504f232 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Mon, 20 Feb 2023 16:58:20 +0800
-Subject: [PATCH 18/28] hostapd: mtk: Fix auto ht issue when switching to DFS
- channel
+Subject: [PATCH] hostapd: mtk: Fix auto ht issue when switching to DFS channel
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
- hostapd/ctrl_iface.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
+ hostapd/ctrl_iface.c | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 07de0ad..be86f6c 100644
+index 07de0ad..3c38df5 100644
 --- a/hostapd/ctrl_iface.c
 +++ b/hostapd/ctrl_iface.c
-@@ -2773,6 +2773,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+@@ -2773,6 +2773,13 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
  		break;
  	}
  
@@ -21,12 +20,13 @@
 +		settings.freq_params.ht_enabled = iface->conf->ieee80211n;
 +		settings.freq_params.vht_enabled = iface->conf->ieee80211ac;
 +		settings.freq_params.he_enabled = iface->conf->ieee80211ax;
++		settings.freq_params.eht_enabled = iface->conf->ieee80211be;
 +	}
 +
  	if (settings.freq_params.center_freq1)
  		dfs_range += hostapd_is_dfs_overlap(
  			iface, bandwidth, settings.freq_params.center_freq1);
-@@ -2810,12 +2816,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+@@ -2810,12 +2817,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
  		return 0;
  	}
  
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch
new file mode 100644
index 0000000..1c5cacc
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch
@@ -0,0 +1,53 @@
+From 43c8934074a4f6fd1e98143b3bd011e71fe69fdb Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Fri Jun 9 09:03:05 2023 +0800
+Subject: hostapd: mtk: Add HE capabilities check
+
+Add HE capabilities check.
+Since "HE capabilities" check has been removed by driver,
+add the support for "HE capabilities" check in hostapd.
+
+---
+ src/ap/hw_features.c         | 26 ++++++++++++++++++++++++++
+ 1 files changed, 26 insertions(+)
+
+diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
+index 9967494..309f2d5 100644
+--- a/src/ap/hw_features.c
++++ b/src/ap/hw_features.c
+@@ -680,6 +680,32 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+ #ifdef CONFIG_IEEE80211AX
+ static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+ {
++	struct hostapd_hw_modes *mode = iface->current_mode;
++	struct he_capabilities *he_cap = &mode->he_capab[IEEE80211_MODE_AP];
++	struct hostapd_config *conf = iface->conf;
++
++#define HE_CAP_CHECK(hw_cap, field, phy_idx, cfg_cap)					\
++	do {									\
++		if (cfg_cap && !(hw_cap[phy_idx] & field)) {	\
++			wpa_printf(MSG_ERROR, "Driver does not support configured" \
++				     " HE capability [%s]", #field);		\
++			return 0;						\
++		}								\
++	} while (0)
++
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_LDPC_CODING_IN_PAYLOAD,
++		     HE_PHYCAP_LDPC_CODING_IN_PAYLOAD_IDX,
++		     conf->he_phy_capab.he_ldpc);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMER_CAPAB,
++		     HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX,
++		     conf->he_phy_capab.he_su_beamformer);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_SU_BEAMFORMEE_CAPAB,
++		     HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX,
++		     conf->he_phy_capab.he_su_beamformee);
++	HE_CAP_CHECK(he_cap->phy_cap, HE_PHYCAP_MU_BEAMFORMER_CAPAB,
++		     HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX,
++		     conf->he_phy_capab.he_mu_beamformer);
++
+ 	return 1;
+ }
+ #endif /* CONFIG_IEEE80211AX */
+-- 
+2.39.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0104-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0104-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch
new file mode 100644
index 0000000..01a134e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0104-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch
@@ -0,0 +1,44 @@
+From 0aaec4ebc72e40da76a279d817763f4655f45d21 Mon Sep 17 00:00:00 2001
+From: mtk23510 <rudra.shahi@mediatek.com>
+Date: Fri, 26 May 2023 14:52:35 +0800
+Subject: [PATCH] hostapd: mtk: Add support for gtk rekeying in hostapd cli
+
+Signed-off-by: mtk23510 <rudra.shahi@mediatek.com>
+---
+ hostapd/hostapd_cli.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
+index 02f8546..d529bbc 100644
+--- a/hostapd/hostapd_cli.c
++++ b/hostapd/hostapd_cli.c
+@@ -1256,6 +1256,15 @@ static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
+ }
+ 
+ 
++#ifdef CONFIG_TESTING_OPTIONS
++static int hostapd_cli_cmd_rekey_gtk(struct wpa_ctrl *ctrl, int argc,
++				      char *argv[])
++{
++	return wpa_ctrl_command(ctrl, "REKEY_GTK");
++}
++#endif
++
++
+ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+ {
+ 	char cmd[256];
+@@ -1761,6 +1770,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ 	  "= disable hostapd on current interface" },
+ 	{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
+ 	  "= update Beacon frame contents\n"},
++#ifdef CONFIG_TESTING_OPTIONS
++	{ "rekey_gtk", hostapd_cli_cmd_rekey_gtk, NULL,
++	  "= rekey gtk\n"},
++#endif
+ 	{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
+ 	  "= drop all ERP keys"},
+ 	{ "log_level", hostapd_cli_cmd_log_level, NULL,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
index 38ca4d8..a0c06b1 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
@@ -88,6 +88,8 @@
     file://mtk-0028-hostapd-mtk-Add-muru-user-number-debug-command.patch \
     file://mtk-0029-hostapd-mtk-Fix-CCA-issue.patch \
     file://mtk-0030-hostapd-mtk-Fix-unexpected-AP-beacon-state-transitio.patch \
+    file://mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch \
     file://mtk-0100-hostapd-mtk-update-eht-operation-element.patch \
     file://mtk-0103-hostapd-mtk-Add-BW320-channel-switch-command.patch \
+    file://mtk-0104-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch \
     "
diff --git a/recipes-wifi/wpa-supplicant/files/patches/mtk-0019-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch b/recipes-wifi/wpa-supplicant/files/patches/mtk-0019-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
index f89da87..d5f0dac 100644
--- a/recipes-wifi/wpa-supplicant/files/patches/mtk-0019-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches/mtk-0019-hostapd-mtk-Fix-auto-ht-issue-when-switching-to-DFS-.patch
@@ -1,19 +1,18 @@
-From 0aa1200534c41279f5f05e1919040a86f003ca0a Mon Sep 17 00:00:00 2001
+From a71a78bc51b74d331aeb3f900c03480d058d5233 Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Mon, 20 Feb 2023 16:58:20 +0800
-Subject: [PATCH 19/29] hostapd: mtk: Fix auto ht issue when switching to DFS
- channel
+Subject: [PATCH] hostapd: mtk: Fix auto ht issue when switching to DFS channel
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
- hostapd/ctrl_iface.c | 13 +++++++------
- 1 file changed, 7 insertions(+), 6 deletions(-)
+ hostapd/ctrl_iface.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 61c9e80..06cbea1 100644
+index 61c9e80..c33b7a4 100644
 --- a/hostapd/ctrl_iface.c
 +++ b/hostapd/ctrl_iface.c
-@@ -2698,6 +2698,13 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+@@ -2698,6 +2698,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
  		break;
  	}
  
@@ -21,13 +20,12 @@
 +		settings.freq_params.ht_enabled = iface->conf->ieee80211n;
 +		settings.freq_params.vht_enabled = iface->conf->ieee80211ac;
 +		settings.freq_params.he_enabled = iface->conf->ieee80211ax;
-+		settings.freq_params.eht_enabled = iface->conf->ieee80211be;
 +	}
 +
  	if (settings.freq_params.center_freq1)
  		dfs_range += hostapd_is_dfs_overlap(
  			iface, bandwidth, settings.freq_params.center_freq1);
-@@ -2735,12 +2742,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+@@ -2735,12 +2741,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
  		return 0;
  	}