[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, ¶ms.chandef, NL80211_DFS_AVAILABLE);
++ }
++
+ if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.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;
}