[Refactor and sync kernel/wifi from Openwrt]

[Description]
Refactor and sync kernel/wifi from Openwrt

[Release-log]
N/A

diff --git a/recipes-connectivity/hostapd/files/patches/914-Add-mtk_vendor.h.patch b/recipes-connectivity/hostapd/files/patches/914-Add-mtk_vendor.h.patch
new file mode 100644
index 0000000..eb77284
--- /dev/null
+++ b/recipes-connectivity/hostapd/files/patches/914-Add-mtk_vendor.h.patch
@@ -0,0 +1,214 @@
+From 21ba11c81b8208280f112b0518735427edecedd8 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 15:04:57 +0800
+Subject: [PATCH 1/1] Add mtk_vendor.h
+
+---
+ src/common/mtk_vendor.h | 195 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 195 insertions(+)
+ create mode 100644 src/common/mtk_vendor.h
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+new file mode 100644
+index 0000000..528387f
+--- /dev/null
++++ b/src/common/mtk_vendor.h
+@@ -0,0 +1,195 @@
++// SPDX-License-Identifier: ISC
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++#ifndef MTK_VENDOR_H
++#define MTK_VENDOR_H
++
++#define OUI_MTK    0x000ce7
++
++enum mtk_nl80211_vendor_subcmds {
++	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
++	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
++	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++};
++
++enum mtk_vendor_attr_edcca_ctrl {
++	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++	EDCCA_CTRL_SET_EN = 0,
++	EDCCA_CTRL_SET_THERS,
++	EDCCA_CTRL_GET_EN,
++	EDCCA_CTRL_GET_THERS,
++	EDCCA_CTRL_NUM,
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++};
++
++enum mtk_vendor_attr_csi_ctrl {
++	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++	MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++	MTK_VENDOR_ATTR_CSI_DATA_VER,
++	MTK_VENDOR_ATTR_CSI_DATA_TS,
++	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++	MTK_VENDOR_ATTR_CSI_DATA_SNR,
++	MTK_VENDOR_ATTR_CSI_DATA_BW,
++	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++	MTK_VENDOR_ATTR_CSI_DATA_TA,
++	MTK_VENDOR_ATTR_CSI_DATA_I,
++	MTK_VENDOR_ATTR_CSI_DATA_Q,
++	MTK_VENDOR_ATTR_CSI_DATA_INFO,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_MODE,
++	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++	MTK_VENDOR_ATTR_CSI_DATA_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 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
++};
++
++enum mtk_vendor_attr_wireless_ctrl {
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
++};
++
++enum mtk_vendor_attr_rfeature_ctrl {
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++};
++
++#define CSI_MAX_COUNT 256
++#define ETH_ALEN 6
++
++struct csi_data {
++	s16 data_i[CSI_MAX_COUNT];
++	s16 data_q[CSI_MAX_COUNT];
++	s8 rssi;
++	u8 snr;
++	u32 ts;
++	u8 data_bw;
++	u8 pri_ch_idx;
++	u8 ta[ETH_ALEN];
++	u32 info;
++	u8 rx_mode;
++	u32 h_idx;
++	u16 tx_idx;
++	u16 rx_idx;
++};
++
++struct amnt_data {
++	u8 idx;
++	u8 addr[ETH_ALEN];
++	s8 rssi[4];
++	u32 last_seen;
++};
++#endif /* MTK_VENDOR_H */
+-- 
+2.18.0
+
diff --git a/recipes-connectivity/hostapd/files/patches/915-Support-new-hostapd-configuration-edcca_enable-and-e.patch b/recipes-connectivity/hostapd/files/patches/915-Support-new-hostapd-configuration-edcca_enable-and-e.patch
new file mode 100644
index 0000000..36092dd
--- /dev/null
+++ b/recipes-connectivity/hostapd/files/patches/915-Support-new-hostapd-configuration-edcca_enable-and-e.patch
@@ -0,0 +1,249 @@
+From 801aa2e893945b44d2a10b73fcc2cff269e02497 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 16:31:34 +0800
+Subject: [PATCH 915/917] Support new hostapd configuration, edcca_enable and
+ edcca_compensation
+
+---
+ hostapd/config_file.c             | 20 +++++++++++++++
+ src/ap/ap_config.c                |  3 +++
+ src/ap/ap_config.h                | 10 ++++++++
+ src/ap/ap_drv_ops.c               |  9 +++++++
+ src/ap/ap_drv_ops.h               |  2 +-
+ src/ap/hostapd.c                  |  3 +++
+ src/drivers/driver.h              |  2 ++
+ src/drivers/driver_nl80211.c      | 42 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  7 ++++++
+ 10 files changed, 98 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index e57c78b..19a2fd5 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4741,6 +4741,26 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "eht_mu_beamformer") == 0) {
+ 		conf->eht_phy_capab.mu_beamformer = atoi(pos);
+ #endif /* CONFIG_IEEE80211BE */
++	} else if (os_strcmp(buf, "edcca_enable") == 0) {
++		int mode = atoi(pos);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
++				  " allowed value 0 (Force Disable) or 1(Auto) ",
++				   line, mode);
++			return 1;
++		}
++		conf->edcca_enable = (u8) mode;
++	} else if (os_strcmp(buf, "edcca_compensation") == 0) {
++		int val = atoi(pos);
++		if (val < EDCCA_MIN_COMPENSATION ||
++			val > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
++				   " value %d; allowed value %d ~ %d.",
++				   line, val, EDCCA_MIN_COMPENSATION,
++				   EDCCA_MAX_COMPENSATION);
++			return 1;
++		}
++		conf->edcca_compensation = (s8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index f248281..427f16e 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -294,6 +294,9 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+ #endif /* CONFIG_AIRTIME_POLICY */
+ 
++	conf->edcca_enable = EDCCA_MODE_AUTO;
++	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++
+ 	return conf;
+ }
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 5dab8be..9bbe7eb 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1150,8 +1150,18 @@ struct hostapd_config {
+ #define CH_SWITCH_EHT_ENABLED BIT(0)
+ #define CH_SWITCH_EHT_DISABLED BIT(1)
+ 	unsigned int ch_switch_eht_config;
++	u8 edcca_enable;
++	s8 edcca_compensation;
+ };
+ 
++enum edcca_mode {
++	EDCCA_MODE_FORCE_DISABLE = 0,
++	EDCCA_MODE_AUTO = 1,
++};
++
++#define EDCCA_DEFAULT_COMPENSATION -6
++#define EDCCA_MIN_COMPENSATION -126
++#define EDCCA_MAX_COMPENSATION 126
+ 
+ static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf)
+ {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index f7ad3ed..b8b98e4 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1012,3 +1012,12 @@ int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable)
+ 		return 0;
+ 	return hapd->driver->dpp_listen(hapd->drv_priv, enable);
+ }
++
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
++		return 0;
++	return hapd->driver->configure_edcca_threshold(hapd->drv_priv,
++				hapd->iconf->edcca_enable,
++				hapd->iconf->edcca_compensation);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index b4fb766..f8fef19 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -138,7 +138,7 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd);
+ int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+ 			     u16 reason_code, const u8 *ie, size_t ielen);
+ int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
+-
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index e61ac39..c1edaab 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2295,6 +2295,9 @@ dfs_offload:
+ 	}
+ #endif /* CONFIG_MESH */
+ 
++	if (hostapd_drv_configure_edcca_threshold(hapd) < 0)
++		goto fail;
++
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+ 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 836c16b..fc96fef 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -4673,6 +4673,8 @@ struct wpa_driver_ops {
+ 			      const u8 *match, size_t match_len,
+ 			      bool multicast);
+ #endif /* CONFIG_TESTING_OPTIONS */
++	int (*configure_edcca_threshold)(void *priv, const u8 edcca_enable,
++				  const s8 edcca_compensation);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index cd4a853..4351e74 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -35,6 +35,7 @@
+ #include "radiotap_iter.h"
+ #include "rfkill.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ #ifndef NETLINK_CAP_ACK
+@@ -12350,6 +12351,45 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
+ 
+ #endif /* CONFIG_TESTING_OPTIONS */
+ 
++static int nl80211_configure_edcca_threshold(void *priv,
++						const u8 edcca_enable,
++						const s8 edcca_compensation)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	/* Prepare nl80211 cmd */
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Driver does not support setting EDCCA threshold");
++		return -1;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++		edcca_compensation)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to configure EDCCA. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+@@ -12496,4 +12536,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.register_frame = testing_nl80211_register_frame,
+ 	.radio_disable = testing_nl80211_radio_disable,
+ #endif /* CONFIG_TESTING_OPTIONS */
++/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
++	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 80d4564..b677907 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -180,6 +180,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int unsol_bcast_probe_resp:1;
+ 	unsigned int qca_do_acs:1;
+ 	unsigned int brcm_do_acs:1;
++	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 
+ 	u64 vendor_scan_cookie;
+ 	u64 remain_on_chan_cookie;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index a0803ba..6c743bf 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -18,6 +18,7 @@
+ #include "common/qca-vendor-attr.h"
+ #include "common/brcm_vendor.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+@@ -1044,6 +1045,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 					break;
+ 				}
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++			} else if (vinfo->vendor_id == OUI_MTK) {
++				switch (vinfo->subcmd) {
++				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL :
++					drv->mtk_edcca_vendor_cmd_avail = 1;
++					break;
++				}
+ 			}
+ 
+ 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+-- 
+2.18.0
+
diff --git a/recipes-connectivity/hostapd/files/patches/916-Add-hostapd-command-handler-for-SET_EDCCA-GET_EDCCA-.patch b/recipes-connectivity/hostapd/files/patches/916-Add-hostapd-command-handler-for-SET_EDCCA-GET_EDCCA-.patch
new file mode 100644
index 0000000..7f94fa3
--- /dev/null
+++ b/recipes-connectivity/hostapd/files/patches/916-Add-hostapd-command-handler-for-SET_EDCCA-GET_EDCCA-.patch
@@ -0,0 +1,137 @@
+From a00f78d06067b5c139c043c9acf359810e963108 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 24 Jun 2022 22:32:40 +0800
+Subject: [PATCH 916/917] Add hostapd command handler for SET_EDCCA, GET_EDCCA
+ and APPLY_EDCCA
+
+---
+ hostapd/ctrl_iface.c | 99 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 99 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index b997ed8..5e9af7f 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -598,6 +598,19 @@ static const char * pbc_status_str(enum pbc_status status)
+ }
+ 
+ 
++static const char * edcca_mode_str(enum edcca_mode status)
++{
++	switch (status) {
++		case EDCCA_MODE_FORCE_DISABLE:
++			return "Force Disable";
++		case EDCCA_MODE_AUTO:
++			return "Auto";
++		default:
++			return "Unknown";
++	}
++}
++
++
+ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+ 					     char *buf, size_t buflen)
+ {
+@@ -3307,6 +3320,85 @@ static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd,
+ #endif /* ANDROID */
+ 
+ 
++static int
++hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *config, *value;
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "enable") == 0) {
++		int mode = atoi(value);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
++			return -1;
++		}
++		hapd->iconf->edcca_enable = (u8) mode;
++	} else if (os_strcmp(config, "compensation") == 0) {
++		int compensation = atoi(value);
++		if (compensation < EDCCA_MIN_COMPENSATION ||
++			compensation > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
++			return -1;
++		}
++		hapd->iconf->edcca_compensation = (s8) compensation;
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for SET_EDCCA", config);
++		return -1;
++	}
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++static int
++hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	int ret;
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	ret = os_snprintf(pos, end - pos, "EDCCA Mode: %s\n",
++			  edcca_mode_str(hapd->iconf->edcca_enable));
++
++	if (os_snprintf_error(end - pos, ret))
++		return pos - buf;
++	pos += ret;
++
++	ret = os_snprintf(pos, end - pos, "EDCCA compensation %d\n",
++			  hapd->iconf->edcca_compensation);
++
++	if (os_snprintf_error(end - pos, ret))
++		return pos - buf;
++	pos += ret;
++
++	return pos - buf;
++}
++
++
++static int
++hostapd_ctrl_iface_apply_edcca(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	if(hostapd_drv_configure_edcca_threshold(hapd) == 0) {
++		return os_snprintf(buf, buflen, "OK\n");
++	} else {
++		return -1;
++	}
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -3840,6 +3932,13 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
+ 							  reply_size);
+ #endif /* ANDROID */
++	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
++							  reply_size);
++	} else if (os_strncmp(buf, "GET_EDCCA", 9) == 0) {
++		reply_len = hostapd_ctrl_iface_get_edcca(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "APPLY_EDCCA", 11) == 0) {
++		reply_len = hostapd_ctrl_iface_apply_edcca(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+-- 
+2.18.0
+
diff --git a/recipes-connectivity/hostapd/files/patches/992-openssl-include-rsa.patch b/recipes-connectivity/hostapd/files/patches/992-openssl-include-rsa.patch
new file mode 100644
index 0000000..581ae9f
--- /dev/null
+++ b/recipes-connectivity/hostapd/files/patches/992-openssl-include-rsa.patch
@@ -0,0 +1,32 @@
+From f374d52079111a4340acb6df835f45ac6b5f3f60 Mon Sep 17 00:00:00 2001
+From: Andre Heider <a.heider@gmail.com>
+Date: Wed, 22 Jun 2022 14:13:55 +0200
+Subject: OpenSSL: Include rsa.h for all OpenSSL versions
+
+This fixes the build with OpenSSL 1.1.1:
+../src/crypto/crypto_openssl.c: In function 'crypto_rsa_oaep_sha256_decrypt':
+../src/crypto/crypto_openssl.c:4404:49: error: 'RSA_PKCS1_OAEP_PADDING' undeclared (first use in this function)
+
+Signed-off-by: Andre Heider <a.heider@gmail.com>
+---
+ src/crypto/crypto_openssl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/src/crypto/crypto_openssl.c
++++ b/src/crypto/crypto_openssl.c
+@@ -16,6 +16,7 @@
+ #include <openssl/dh.h>
+ #include <openssl/hmac.h>
+ #include <openssl/rand.h>
++#include <openssl/rsa.h>
+ #include <openssl/pem.h>
+ #ifdef CONFIG_ECC
+ #include <openssl/ec.h>
+@@ -25,7 +26,6 @@
+ #include <openssl/provider.h>
+ #include <openssl/core_names.h>
+ #include <openssl/param_build.h>
+-#include <openssl/rsa.h>
+ #include <openssl/encoder.h>
+ #include <openssl/decoder.h>
+ #else /* OpenSSL version >= 3.0 */
diff --git a/recipes-connectivity/hostapd/files/patches/patches.inc b/recipes-connectivity/hostapd/files/patches/patches.inc
index 1eb26eb..1cb4185 100644
--- a/recipes-connectivity/hostapd/files/patches/patches.inc
+++ b/recipes-connectivity/hostapd/files/patches/patches.inc
@@ -62,6 +62,10 @@
     file://910-master-Add-hostapd_neighbor_set_pref_by_non_pref_chan.patch \
     file://911-master-print-sae-groups-by-hostapd-ctrl.patch \
     file://913-master-add-support-for-runtime-set-in-band-discover.patch \
+    file://914-Add-mtk_vendor.h.patch \
+    file://915-Support-new-hostapd-configuration-edcca_enable-and-e.patch \
+    file://916-Add-hostapd-command-handler-for-SET_EDCCA-GET_EDCCA-.patch \
     file://990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch \
     file://991-fix-compile.patch \
+    file://992-openssl-include-rsa.patch \
     "
diff --git a/recipes-connectivity/wpa-supplicant/files/patches/914-Add-mtk_vendor.h.patch b/recipes-connectivity/wpa-supplicant/files/patches/914-Add-mtk_vendor.h.patch
new file mode 100644
index 0000000..eb77284
--- /dev/null
+++ b/recipes-connectivity/wpa-supplicant/files/patches/914-Add-mtk_vendor.h.patch
@@ -0,0 +1,214 @@
+From 21ba11c81b8208280f112b0518735427edecedd8 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 15:04:57 +0800
+Subject: [PATCH 1/1] Add mtk_vendor.h
+
+---
+ src/common/mtk_vendor.h | 195 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 195 insertions(+)
+ create mode 100644 src/common/mtk_vendor.h
+
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+new file mode 100644
+index 0000000..528387f
+--- /dev/null
++++ b/src/common/mtk_vendor.h
+@@ -0,0 +1,195 @@
++// SPDX-License-Identifier: ISC
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++#ifndef MTK_VENDOR_H
++#define MTK_VENDOR_H
++
++#define OUI_MTK    0x000ce7
++
++enum mtk_nl80211_vendor_subcmds {
++	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
++	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
++	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
++	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
++};
++
++enum mtk_vendor_attr_edcca_ctrl {
++	MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++	MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++	EDCCA_CTRL_SET_EN = 0,
++	EDCCA_CTRL_SET_THERS,
++	EDCCA_CTRL_GET_EN,
++	EDCCA_CTRL_GET_THERS,
++	EDCCA_CTRL_NUM,
++};
++
++static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++};
++
++enum mtk_vendor_attr_csi_ctrl {
++	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++	MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++	MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++	MTK_VENDOR_ATTR_CSI_DATA_VER,
++	MTK_VENDOR_ATTR_CSI_DATA_TS,
++	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++	MTK_VENDOR_ATTR_CSI_DATA_SNR,
++	MTK_VENDOR_ATTR_CSI_DATA_BW,
++	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++	MTK_VENDOR_ATTR_CSI_DATA_TA,
++	MTK_VENDOR_ATTR_CSI_DATA_I,
++	MTK_VENDOR_ATTR_CSI_DATA_Q,
++	MTK_VENDOR_ATTR_CSI_DATA_INFO,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_MODE,
++	MTK_VENDOR_ATTR_CSI_DATA_H_IDX,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++	MTK_VENDOR_ATTR_CSI_DATA_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 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
++};
++
++enum mtk_vendor_attr_wireless_ctrl {
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMPDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
++};
++
++enum mtk_vendor_attr_rfeature_ctrl {
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++};
++
++#define CSI_MAX_COUNT 256
++#define ETH_ALEN 6
++
++struct csi_data {
++	s16 data_i[CSI_MAX_COUNT];
++	s16 data_q[CSI_MAX_COUNT];
++	s8 rssi;
++	u8 snr;
++	u32 ts;
++	u8 data_bw;
++	u8 pri_ch_idx;
++	u8 ta[ETH_ALEN];
++	u32 info;
++	u8 rx_mode;
++	u32 h_idx;
++	u16 tx_idx;
++	u16 rx_idx;
++};
++
++struct amnt_data {
++	u8 idx;
++	u8 addr[ETH_ALEN];
++	s8 rssi[4];
++	u32 last_seen;
++};
++#endif /* MTK_VENDOR_H */
+-- 
+2.18.0
+
diff --git a/recipes-connectivity/wpa-supplicant/files/patches/915-Support-new-hostapd-configuration-edcca_enable-and-e.patch b/recipes-connectivity/wpa-supplicant/files/patches/915-Support-new-hostapd-configuration-edcca_enable-and-e.patch
new file mode 100644
index 0000000..36092dd
--- /dev/null
+++ b/recipes-connectivity/wpa-supplicant/files/patches/915-Support-new-hostapd-configuration-edcca_enable-and-e.patch
@@ -0,0 +1,249 @@
+From 801aa2e893945b44d2a10b73fcc2cff269e02497 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 16:31:34 +0800
+Subject: [PATCH 915/917] Support new hostapd configuration, edcca_enable and
+ edcca_compensation
+
+---
+ hostapd/config_file.c             | 20 +++++++++++++++
+ src/ap/ap_config.c                |  3 +++
+ src/ap/ap_config.h                | 10 ++++++++
+ src/ap/ap_drv_ops.c               |  9 +++++++
+ src/ap/ap_drv_ops.h               |  2 +-
+ src/ap/hostapd.c                  |  3 +++
+ src/drivers/driver.h              |  2 ++
+ src/drivers/driver_nl80211.c      | 42 +++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |  1 +
+ src/drivers/driver_nl80211_capa.c |  7 ++++++
+ 10 files changed, 98 insertions(+), 1 deletion(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index e57c78b..19a2fd5 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4741,6 +4741,26 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "eht_mu_beamformer") == 0) {
+ 		conf->eht_phy_capab.mu_beamformer = atoi(pos);
+ #endif /* CONFIG_IEEE80211BE */
++	} else if (os_strcmp(buf, "edcca_enable") == 0) {
++		int mode = atoi(pos);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
++				  " allowed value 0 (Force Disable) or 1(Auto) ",
++				   line, mode);
++			return 1;
++		}
++		conf->edcca_enable = (u8) mode;
++	} else if (os_strcmp(buf, "edcca_compensation") == 0) {
++		int val = atoi(pos);
++		if (val < EDCCA_MIN_COMPENSATION ||
++			val > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
++				   " value %d; allowed value %d ~ %d.",
++				   line, val, EDCCA_MIN_COMPENSATION,
++				   EDCCA_MAX_COMPENSATION);
++			return 1;
++		}
++		conf->edcca_compensation = (s8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index f248281..427f16e 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -294,6 +294,9 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+ #endif /* CONFIG_AIRTIME_POLICY */
+ 
++	conf->edcca_enable = EDCCA_MODE_AUTO;
++	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++
+ 	return conf;
+ }
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 5dab8be..9bbe7eb 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1150,8 +1150,18 @@ struct hostapd_config {
+ #define CH_SWITCH_EHT_ENABLED BIT(0)
+ #define CH_SWITCH_EHT_DISABLED BIT(1)
+ 	unsigned int ch_switch_eht_config;
++	u8 edcca_enable;
++	s8 edcca_compensation;
+ };
+ 
++enum edcca_mode {
++	EDCCA_MODE_FORCE_DISABLE = 0,
++	EDCCA_MODE_AUTO = 1,
++};
++
++#define EDCCA_DEFAULT_COMPENSATION -6
++#define EDCCA_MIN_COMPENSATION -126
++#define EDCCA_MAX_COMPENSATION 126
+ 
+ static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf)
+ {
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index f7ad3ed..b8b98e4 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1012,3 +1012,12 @@ int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable)
+ 		return 0;
+ 	return hapd->driver->dpp_listen(hapd->drv_priv, enable);
+ }
++
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
++		return 0;
++	return hapd->driver->configure_edcca_threshold(hapd->drv_priv,
++				hapd->iconf->edcca_enable,
++				hapd->iconf->edcca_compensation);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index b4fb766..f8fef19 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -138,7 +138,7 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd);
+ int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+ 			     u16 reason_code, const u8 *ie, size_t ielen);
+ int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
+-
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index e61ac39..c1edaab 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2295,6 +2295,9 @@ dfs_offload:
+ 	}
+ #endif /* CONFIG_MESH */
+ 
++	if (hostapd_drv_configure_edcca_threshold(hapd) < 0)
++		goto fail;
++
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+ 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 836c16b..fc96fef 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -4673,6 +4673,8 @@ struct wpa_driver_ops {
+ 			      const u8 *match, size_t match_len,
+ 			      bool multicast);
+ #endif /* CONFIG_TESTING_OPTIONS */
++	int (*configure_edcca_threshold)(void *priv, const u8 edcca_enable,
++				  const s8 edcca_compensation);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index cd4a853..4351e74 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -35,6 +35,7 @@
+ #include "radiotap_iter.h"
+ #include "rfkill.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ #ifndef NETLINK_CAP_ACK
+@@ -12350,6 +12351,45 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
+ 
+ #endif /* CONFIG_TESTING_OPTIONS */
+ 
++static int nl80211_configure_edcca_threshold(void *priv,
++						const u8 edcca_enable,
++						const s8 edcca_compensation)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	/* Prepare nl80211 cmd */
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_ERROR,
++			   "nl80211: Driver does not support setting EDCCA threshold");
++		return -1;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++		edcca_compensation)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to configure EDCCA. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+@@ -12496,4 +12536,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.register_frame = testing_nl80211_register_frame,
+ 	.radio_disable = testing_nl80211_radio_disable,
+ #endif /* CONFIG_TESTING_OPTIONS */
++/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
++	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 80d4564..b677907 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -180,6 +180,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int unsol_bcast_probe_resp:1;
+ 	unsigned int qca_do_acs:1;
+ 	unsigned int brcm_do_acs:1;
++	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 
+ 	u64 vendor_scan_cookie;
+ 	u64 remain_on_chan_cookie;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index a0803ba..6c743bf 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -18,6 +18,7 @@
+ #include "common/qca-vendor-attr.h"
+ #include "common/brcm_vendor.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+@@ -1044,6 +1045,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 					break;
+ 				}
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++			} else if (vinfo->vendor_id == OUI_MTK) {
++				switch (vinfo->subcmd) {
++				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL :
++					drv->mtk_edcca_vendor_cmd_avail = 1;
++					break;
++				}
+ 			}
+ 
+ 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+-- 
+2.18.0
+
diff --git a/recipes-connectivity/wpa-supplicant/files/patches/916-Add-hostapd-command-handler-for-SET_EDCCA-GET_EDCCA-.patch b/recipes-connectivity/wpa-supplicant/files/patches/916-Add-hostapd-command-handler-for-SET_EDCCA-GET_EDCCA-.patch
new file mode 100644
index 0000000..7f94fa3
--- /dev/null
+++ b/recipes-connectivity/wpa-supplicant/files/patches/916-Add-hostapd-command-handler-for-SET_EDCCA-GET_EDCCA-.patch
@@ -0,0 +1,137 @@
+From a00f78d06067b5c139c043c9acf359810e963108 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 24 Jun 2022 22:32:40 +0800
+Subject: [PATCH 916/917] Add hostapd command handler for SET_EDCCA, GET_EDCCA
+ and APPLY_EDCCA
+
+---
+ hostapd/ctrl_iface.c | 99 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 99 insertions(+)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index b997ed8..5e9af7f 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -598,6 +598,19 @@ static const char * pbc_status_str(enum pbc_status status)
+ }
+ 
+ 
++static const char * edcca_mode_str(enum edcca_mode status)
++{
++	switch (status) {
++		case EDCCA_MODE_FORCE_DISABLE:
++			return "Force Disable";
++		case EDCCA_MODE_AUTO:
++			return "Auto";
++		default:
++			return "Unknown";
++	}
++}
++
++
+ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+ 					     char *buf, size_t buflen)
+ {
+@@ -3307,6 +3320,85 @@ static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd,
+ #endif /* ANDROID */
+ 
+ 
++static int
++hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *config, *value;
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "enable") == 0) {
++		int mode = atoi(value);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
++			return -1;
++		}
++		hapd->iconf->edcca_enable = (u8) mode;
++	} else if (os_strcmp(config, "compensation") == 0) {
++		int compensation = atoi(value);
++		if (compensation < EDCCA_MIN_COMPENSATION ||
++			compensation > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
++			return -1;
++		}
++		hapd->iconf->edcca_compensation = (s8) compensation;
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for SET_EDCCA", config);
++		return -1;
++	}
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++static int
++hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	int ret;
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++
++	ret = os_snprintf(pos, end - pos, "EDCCA Mode: %s\n",
++			  edcca_mode_str(hapd->iconf->edcca_enable));
++
++	if (os_snprintf_error(end - pos, ret))
++		return pos - buf;
++	pos += ret;
++
++	ret = os_snprintf(pos, end - pos, "EDCCA compensation %d\n",
++			  hapd->iconf->edcca_compensation);
++
++	if (os_snprintf_error(end - pos, ret))
++		return pos - buf;
++	pos += ret;
++
++	return pos - buf;
++}
++
++
++static int
++hostapd_ctrl_iface_apply_edcca(struct hostapd_data *hapd, char *buf,
++					 size_t buflen)
++{
++	if(hostapd_drv_configure_edcca_threshold(hapd) == 0) {
++		return os_snprintf(buf, buflen, "OK\n");
++	} else {
++		return -1;
++	}
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -3840,6 +3932,13 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
+ 							  reply_size);
+ #endif /* ANDROID */
++	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
++							  reply_size);
++	} else if (os_strncmp(buf, "GET_EDCCA", 9) == 0) {
++		reply_len = hostapd_ctrl_iface_get_edcca(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "APPLY_EDCCA", 11) == 0) {
++		reply_len = hostapd_ctrl_iface_apply_edcca(hapd, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+-- 
+2.18.0
+
diff --git a/recipes-connectivity/wpa-supplicant/files/patches/992-openssl-include-rsa.patch b/recipes-connectivity/wpa-supplicant/files/patches/992-openssl-include-rsa.patch
new file mode 100644
index 0000000..581ae9f
--- /dev/null
+++ b/recipes-connectivity/wpa-supplicant/files/patches/992-openssl-include-rsa.patch
@@ -0,0 +1,32 @@
+From f374d52079111a4340acb6df835f45ac6b5f3f60 Mon Sep 17 00:00:00 2001
+From: Andre Heider <a.heider@gmail.com>
+Date: Wed, 22 Jun 2022 14:13:55 +0200
+Subject: OpenSSL: Include rsa.h for all OpenSSL versions
+
+This fixes the build with OpenSSL 1.1.1:
+../src/crypto/crypto_openssl.c: In function 'crypto_rsa_oaep_sha256_decrypt':
+../src/crypto/crypto_openssl.c:4404:49: error: 'RSA_PKCS1_OAEP_PADDING' undeclared (first use in this function)
+
+Signed-off-by: Andre Heider <a.heider@gmail.com>
+---
+ src/crypto/crypto_openssl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/src/crypto/crypto_openssl.c
++++ b/src/crypto/crypto_openssl.c
+@@ -16,6 +16,7 @@
+ #include <openssl/dh.h>
+ #include <openssl/hmac.h>
+ #include <openssl/rand.h>
++#include <openssl/rsa.h>
+ #include <openssl/pem.h>
+ #ifdef CONFIG_ECC
+ #include <openssl/ec.h>
+@@ -25,7 +26,6 @@
+ #include <openssl/provider.h>
+ #include <openssl/core_names.h>
+ #include <openssl/param_build.h>
+-#include <openssl/rsa.h>
+ #include <openssl/encoder.h>
+ #include <openssl/decoder.h>
+ #else /* OpenSSL version >= 3.0 */
diff --git a/recipes-connectivity/wpa-supplicant/files/patches/patches.inc b/recipes-connectivity/wpa-supplicant/files/patches/patches.inc
index 1eb26eb..1cb4185 100644
--- a/recipes-connectivity/wpa-supplicant/files/patches/patches.inc
+++ b/recipes-connectivity/wpa-supplicant/files/patches/patches.inc
@@ -62,6 +62,10 @@
     file://910-master-Add-hostapd_neighbor_set_pref_by_non_pref_chan.patch \
     file://911-master-print-sae-groups-by-hostapd-ctrl.patch \
     file://913-master-add-support-for-runtime-set-in-band-discover.patch \
+    file://914-Add-mtk_vendor.h.patch \
+    file://915-Support-new-hostapd-configuration-edcca_enable-and-e.patch \
+    file://916-Add-hostapd-command-handler-for-SET_EDCCA-GET_EDCCA-.patch \
     file://990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch \
     file://991-fix-compile.patch \
+    file://992-openssl-include-rsa.patch \
     "
diff --git a/recipes-kernel/linux-mt76/files/patches/0011-mt76-testmode-use-random-payload-for-tx-packets.patch b/recipes-kernel/linux-mt76/files/patches/0011-mt76-testmode-use-random-payload-for-tx-packets.patch
new file mode 100644
index 0000000..dc04e36
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/0011-mt76-testmode-use-random-payload-for-tx-packets.patch
@@ -0,0 +1,56 @@
+From 5b712b2ec82bb9e88346b379b5c6645b1fa7d7fe Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Wed, 6 Jul 2022 21:52:45 +0800
+Subject: [PATCH] mt76: testmode: use random payload for tx packets
+
+Compared to fixed payload packets, random payload packets have better
+measured EVM under the same txpower. Our tests show EVM becomes at least
+3 dB better in test cases with high rate and long tx length, which also
+aligns the testing result of proprietary driver.
+
+Suggested-by: Jm Chen <jm.chen@mediatek.com>
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ drivers/net/wireless/mediatek/mt76/testmode.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/testmode.c b/testmode.c
+index 4a24f6c9..31439b39 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -1,5 +1,7 @@
+ // SPDX-License-Identifier: ISC
+ /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/random.h>
+ #include "mt76.h"
+ 
+ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
+@@ -124,12 +126,14 @@ int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len)
+ 	if (!head)
+ 		return -ENOMEM;
+ 
+-	hdr = __skb_put_zero(head, head_len);
++	hdr = __skb_put_zero(head, sizeof(*hdr));
+ 	hdr->frame_control = cpu_to_le16(fc);
+ 	memcpy(hdr->addr1, td->addr[0], ETH_ALEN);
+ 	memcpy(hdr->addr2, td->addr[1], ETH_ALEN);
+ 	memcpy(hdr->addr3, td->addr[2], ETH_ALEN);
+ 	skb_set_queue_mapping(head, IEEE80211_AC_BE);
++	get_random_bytes(__skb_put(head, head_len - sizeof(*hdr)),
++			 head_len - sizeof(*hdr));
+ 
+ 	info = IEEE80211_SKB_CB(head);
+ 	info->flags = IEEE80211_TX_CTL_INJECTED |
+@@ -157,7 +161,7 @@ int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len)
+ 			return -ENOMEM;
+ 		}
+ 
+-		__skb_put_zero(frag, frag_len);
++		get_random_bytes(__skb_put(frag, frag_len), frag_len);
+ 		head->len += frag->len;
+ 		head->data_len += frag->len;
+ 
+-- 
+2.25.1
+
diff --git a/recipes-kernel/linux-mt76/files/patches/1117-mt76-mt7915-add-vendor-subcmd-EDCCA-ctrl.patch b/recipes-kernel/linux-mt76/files/patches/1117-mt76-mt7915-add-vendor-subcmd-EDCCA-ctrl.patch
new file mode 100644
index 0000000..c25f9b2
--- /dev/null
+++ b/recipes-kernel/linux-mt76/files/patches/1117-mt76-mt7915-add-vendor-subcmd-EDCCA-ctrl.patch
@@ -0,0 +1,245 @@
+From c1e72950b8f7df7c36c64e27613637f88e3c1ba3 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Fri, 24 Jun 2022 11:15:45 +0800
+Subject: [PATCH 1117/1117] mt76: mt7915: add vendor subcmd EDCCA ctrl
+
+Change-Id: I92dabf8be9c5a7ecec78f35325bc5645af8d15ab
+---
+ mt76_connac_mcu.h |  1 +
+ mt7915/main.c     |  3 +++
+ mt7915/mcu.c      | 38 ++++++++++++++++++++++++++++
+ mt7915/mcu.h      | 12 +++++++++
+ mt7915/mt7915.h   |  2 ++
+ mt7915/vendor.c   | 63 +++++++++++++++++++++++++++++++++++++++++++++++
+ mt7915/vendor.h   | 19 ++++++++++++++
+ 7 files changed, 138 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index a0e6fa6e..1747e06d 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1147,6 +1147,7 @@ enum {
+ 	MCU_EXT_CMD_SMESH_CTRL = 0xae,
+ 	MCU_EXT_CMD_RX_STAT_USER_CTRL = 0xb3,
+ 	MCU_EXT_CMD_CERT_CFG = 0xb7,
++	MCU_EXT_CMD_EDCCA = 0xba,
+ 	MCU_EXT_CMD_CSI_CTRL = 0xc2,
+ };
+ 
+diff --git a/mt7915/main.c b/mt7915/main.c
+index 6c97ce78..1dc41ab6 100644
+--- a/mt7915/main.c
++++ b/mt7915/main.c
+@@ -456,6 +456,9 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
+ 			mutex_unlock(&dev->mt76.mutex);
+ 		}
+ #endif
++		ret = mt7915_mcu_set_edcca(phy, EDCCA_CTRL_SET_EN, NULL, 0);
++		if (ret)
++			return ret;
+ 		ieee80211_stop_queues(hw);
+ 		ret = mt7915_set_channel(phy);
+ 		if (ret)
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index 46eef36a..205ecbab 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -4217,3 +4217,41 @@ int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set)
+ 
+ 	return 0;
+ }
++
++int mt7915_mcu_set_edcca(struct mt7915_phy *phy, int mode, u8 *value,
++			 s8 compensation)
++{
++	static const u8 ch_band[] = {
++		[NL80211_BAND_2GHZ] = 0,
++		[NL80211_BAND_5GHZ] = 1,
++		[NL80211_BAND_6GHZ] = 2,
++	};
++	struct mt7915_dev *dev = phy->dev;
++	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
++	struct {
++		u8 band_idx;
++		u8 cmd_idx;
++		u8 setting[3];
++		bool record_in_fw;
++		u8 region;
++		s8 thres_compensation;
++	} __packed req = {
++		.band_idx = phy->band_idx,
++		.cmd_idx = mode,
++		.record_in_fw = false,
++		.region = dev->mt76.region,
++		.thres_compensation = compensation,
++	};
++
++	if (ch_band[chandef->chan->band] != 2)
++		return 0;
++
++	if (mode == EDCCA_CTRL_SET_EN) {
++		if (!value)
++			req.setting[0] = EDCCA_MODE_AUTO;
++		else
++			req.setting[0] = value[0];
++	}
++
++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EDCCA), &req, sizeof(req), true);
++}
+diff --git a/mt7915/mcu.h b/mt7915/mcu.h
+index 873a8055..72c2cfc6 100644
+--- a/mt7915/mcu.h
++++ b/mt7915/mcu.h
+@@ -785,4 +785,16 @@ enum {
+ };
+ #endif
+ 
++enum {
++   EDCCA_CTRL_SET_EN = 0,
++   EDCCA_CTRL_SET_THERS,
++   EDCCA_CTRL_GET_EN,
++   EDCCA_CTRL_GET_THERS,
++   EDCCA_CTRL_NUM,
++};
++
++enum {
++   EDCCA_MODE_FORCE_DISABLE,
++   EDCCA_MODE_AUTO,
++};
+ #endif
+diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
+index 0b7f73b3..5c58a29c 100644
+--- a/mt7915/mt7915.h
++++ b/mt7915/mt7915.h
+@@ -718,6 +718,8 @@ void mt7915_vendor_amnt_fill_rx(struct mt7915_phy *phy, struct sk_buff *skb);
+ int mt7915_vendor_amnt_sta_remove(struct mt7915_phy *phy,
+ 				  struct ieee80211_sta *sta);
+ #endif
++int mt7915_mcu_set_edcca(struct mt7915_phy *phy, int mode, u8 *value,
++			 s8 compensation);
+ 
+ #ifdef MTK_DEBUG
+ int mt7915_mtk_init_debugfs(struct mt7915_phy *phy, struct dentry *dir);
+diff --git a/mt7915/vendor.c b/mt7915/vendor.c
+index 77d71e48..5a28a554 100644
+--- a/mt7915/vendor.c
++++ b/mt7915/vendor.c
+@@ -62,6 +62,17 @@ phy_capa_dump_policy[NUM_MTK_VENDOR_ATTRS_PHY_CAPA_DUMP] = {
+ 	[MTK_VENDOR_ATTR_PHY_CAPA_DUMP_MAX_SUPPORTED_STA] = { .type = NLA_U16 },
+ };
+ 
++static const struct nla_policy
++edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
++       [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
++       [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
++       [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++       [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++       [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++       [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_S8 },
++};
++
++
+ struct csi_null_tone {
+ 	u8 start;
+ 	u8 end;
+@@ -1015,6 +1026,47 @@ mt7915_vendor_phy_capa_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ 	return len;
+ }
+ 
++static int mt7915_vendor_edcca_ctrl(struct wiphy *wiphy,
++				  struct wireless_dev *wdev,
++				  const void *data,
++				  int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7915_phy *phy = mt7915_hw_phy(hw);
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
++	int err;
++	u8 edcca_mode;
++	s8 edcca_compensation;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
++			edcca_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
++		return -EINVAL;
++
++	edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
++	if (edcca_mode == EDCCA_CTRL_SET_EN) {
++		u8 edcca_value[3] = {0};
++		if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] ||
++			!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE]) {
++			return -EINVAL;
++		}
++		edcca_value[0] =
++			nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
++		edcca_compensation =
++			nla_get_s8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE]);
++
++		err = mt7915_mcu_set_edcca(phy, edcca_mode, edcca_value,
++					 edcca_compensation);
++		if (err)
++			return err;
++	}
++	return 0;
++}
++
++
+ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+ 	{
+ 		.info = {
+@@ -1083,6 +1135,17 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
+ 		.dumpit = mt7915_vendor_phy_capa_ctrl_dump,
+ 		.policy = phy_capa_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_PHY_CAPA_CTRL_MAX,
++	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7915_vendor_edcca_ctrl,
++		.policy = edcca_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
+ 	}
+ };
+ 
+diff --git a/mt7915/vendor.h b/mt7915/vendor.h
+index 719b851f..83c41bc1 100644
+--- a/mt7915/vendor.h
++++ b/mt7915/vendor.h
+@@ -10,8 +10,27 @@ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
+ 	MTK_NL80211_VENDOR_SUBCMD_HEMU_CTRL = 0xc5,
+ 	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL = 0xc6,
++	MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ };
+ 
++
++enum mtk_vendor_attr_edcca_ctrl {
++        MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
++
++        MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
++        MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
++        MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
++        MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
++        MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
++        MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++
++        /* keep last */
++        NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
++        MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
++                NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
++};
++
++
+ enum mtk_capi_control_changed {
+ 	CAPI_RFEATURE_CHANGED		= BIT(16),
+ 	CAPI_WIRELESS_CHANGED		= BIT(17),
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux-mt76/files/patches/3002-mt76-add-wed-rx-support.patch b/recipes-kernel/linux-mt76/files/patches/3002-mt76-add-wed-rx-support.patch
index 5c5a05d..7040ca6 100755
--- a/recipes-kernel/linux-mt76/files/patches/3002-mt76-add-wed-rx-support.patch
+++ b/recipes-kernel/linux-mt76/files/patches/3002-mt76-add-wed-rx-support.patch
@@ -5,10 +5,10 @@
 
 Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
 ---
- dma.c             | 219 +++++++++++++++++++++++++++++++++--------
+ dma.c             | 248 +++++++++++++++++++++++++++++++++--------
  dma.h             |  10 ++
  mac80211.c        |   8 +-
- mt76.h            |  24 ++++-
+ mt76.h            |  24 +++-
  mt7603/dma.c      |   2 +-
  mt7603/mt7603.h   |   2 +-
  mt7615/mac.c      |   2 +-
@@ -17,7 +17,7 @@
  mt76x02.h         |   2 +-
  mt76x02_txrx.c    |   2 +-
  mt7915/dma.c      |  10 ++
- mt7915/mac.c      |  89 ++++++++++++++++-
+ mt7915/mac.c      | 101 ++++++++++++++++-
  mt7915/mcu.c      |   3 +
  mt7915/mmio.c     |  26 ++++-
  mt7915/mt7915.h   |   7 +-
@@ -25,13 +25,11 @@
  mt7921/mac.c      |   2 +-
  mt7921/mt7921.h   |   4 +-
  mt7921/pci_mac.c  |   4 +-
- tx.c              |  34 +++++++
- 21 files changed, 410 insertions(+), 65 deletions(-)
- mode change 100755 => 100644 mt7915/mac.c
- mode change 100755 => 100644 mt7915/mmio.c
+ tx.c              |  34 ++++++
+ 21 files changed, 448 insertions(+), 68 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index 03ee910..094aede 100644
+index 03ee910..e46dbed 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -98,6 +98,63 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
@@ -98,7 +96,29 @@
  static void
  mt76_free_pending_txwi(struct mt76_dev *dev)
  {
-@@ -141,12 +198,15 @@ mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -112,6 +169,21 @@ mt76_free_pending_txwi(struct mt76_dev *dev)
+ 	local_bh_enable();
+ }
+ 
++static void
++mt76_free_pending_rxwi(struct mt76_dev *dev)
++{
++	struct mt76_txwi_cache *r;
++
++	local_bh_disable();
++	while ((r = __mt76_get_rxwi(dev)) != NULL) {
++		if (r->buf)
++			skb_free_frag(r->buf);
++
++		kfree(r);
++	}
++	local_bh_enable();
++}
++
+ static void
+ mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
+ {
+@@ -141,12 +213,15 @@ mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
  static int
  mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
  		 struct mt76_queue_buf *buf, int nbufs, u32 info,
@@ -115,7 +135,7 @@
  
  	if (txwi) {
  		q->entry[q->head].txwi = DMA_DUMMY_DATA;
-@@ -162,28 +222,42 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -162,28 +237,42 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
  		desc = &q->desc[idx];
  		entry = &q->entry[idx];
  
@@ -178,7 +198,7 @@
  		WRITE_ONCE(desc->buf0, cpu_to_le32(buf0));
  		WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
  		WRITE_ONCE(desc->info, cpu_to_le32(info));
-@@ -272,33 +346,63 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
+@@ -272,33 +361,63 @@ 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,
@@ -248,7 +268,7 @@
  {
  	int idx = q->tail;
  
-@@ -314,7 +418,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
+@@ -314,7 +433,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
  	q->tail = (q->tail + 1) % q->ndesc;
  	q->queued--;
  
@@ -257,7 +277,7 @@
  }
  
  static int
-@@ -336,7 +440,7 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -336,7 +455,7 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q,
  	buf.len = skb->len;
  
  	spin_lock_bh(&q->lock);
@@ -266,7 +286,7 @@
  	mt76_dma_kick_queue(dev, q);
  	spin_unlock_bh(&q->lock);
  
-@@ -413,7 +517,7 @@ mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -413,7 +532,7 @@ mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
  		goto unmap;
  
  	return mt76_dma_add_buf(dev, q, tx_info.buf, tx_info.nbuf,
@@ -275,15 +295,16 @@
  
  unmap:
  	for (n--; n > 0; n--)
-@@ -448,6 +552,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -448,6 +567,8 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
  	int frames = 0;
  	int len = SKB_WITH_OVERHEAD(q->buf_size);
  	int offset = q->buf_offset;
 +	struct mtk_wed_device *wed = &dev->mmio.wed;
++	struct page_frag_cache *rx_page;
  
  	if (!q->ndesc)
  		return 0;
-@@ -456,10 +561,27 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -456,10 +577,29 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
  
  	while (q->queued < q->ndesc - 1) {
  		struct mt76_queue_buf qbuf;
@@ -291,7 +312,9 @@
 +		bool skip_alloc = false;
 +		struct mt76_txwi_cache *r = NULL;
 +
++		rx_page = &q->rx_page;
 +		if (mtk_wed_device_active(wed) && type == MT76_WED_Q_RX) {
++			rx_page = &wed->rx_page;
 +			r = mt76_get_rxwi(dev);
 +			if (!r)
 +				return -ENOMEM;
@@ -307,14 +330,14 @@
 -		if (!buf)
 -			break;
 +		if (!skip_alloc) {
-+			buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
++			buf = page_frag_alloc(rx_page, q->buf_size, GFP_ATOMIC);
 +			if (!buf)
 +				break;
 +		}
  
  		addr = dma_map_single(dev->dma_dev, buf, len, DMA_FROM_DEVICE);
  		if (unlikely(dma_mapping_error(dev->dma_dev, addr))) {
-@@ -470,7 +592,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -470,7 +610,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
  		qbuf.addr = addr + offset;
  		qbuf.len = len - offset;
  		qbuf.skip_unmap = false;
@@ -323,7 +346,7 @@
  		frames++;
  	}
  
-@@ -516,6 +638,11 @@ mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -516,6 +656,11 @@ mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q)
  		if (!ret)
  			q->wed_regs = wed->txfree_ring.reg_base;
  		break;
@@ -335,7 +358,7 @@
  	default:
  		ret = -EINVAL;
  	}
-@@ -531,7 +658,8 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -531,7 +676,8 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
  		     int idx, int n_desc, int bufsize,
  		     u32 ring_base)
  {
@@ -345,7 +368,7 @@
  
  	spin_lock_init(&q->lock);
  	spin_lock_init(&q->cleanup_lock);
-@@ -541,6 +669,11 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+@@ -541,6 +687,11 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
  	q->buf_size = bufsize;
  	q->hw_idx = idx;
  
@@ -357,7 +380,7 @@
  	size = q->ndesc * sizeof(struct mt76_desc);
  	q->desc = dmam_alloc_coherent(dev->dma_dev, size, &q->desc_dma, GFP_KERNEL);
  	if (!q->desc)
-@@ -573,7 +706,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+@@ -573,7 +724,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
  
  	spin_lock_bh(&q->lock);
  	do {
@@ -366,7 +389,7 @@
  		if (!buf)
  			break;
  
-@@ -614,7 +747,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
+@@ -614,7 +765,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
  
  static void
  mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
@@ -375,7 +398,7 @@
  {
  	struct sk_buff *skb = q->rx_head;
  	struct skb_shared_info *shinfo = skb_shinfo(skb);
-@@ -634,7 +767,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
+@@ -634,7 +785,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
  
  	q->rx_head = NULL;
  	if (nr_frags < ARRAY_SIZE(shinfo->frags))
@@ -384,7 +407,7 @@
  	else
  		dev_kfree_skb(skb);
  }
-@@ -655,6 +788,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -655,6 +806,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
  	}
  
  	while (done < budget) {
@@ -392,7 +415,7 @@
  		u32 info;
  
  		if (check_ddone) {
-@@ -665,10 +799,13 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -665,10 +817,13 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
  				break;
  		}
  
@@ -407,7 +430,7 @@
  		if (q->rx_head)
  			data_len = q->buf_size;
  		else
-@@ -681,7 +818,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -681,7 +836,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
  		}
  
  		if (q->rx_head) {
@@ -416,7 +439,7 @@
  			continue;
  		}
  
-@@ -708,7 +845,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
+@@ -708,7 +863,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
  			continue;
  		}
  
@@ -425,6 +448,36 @@
  		continue;
  
  free_frag:
+@@ -785,8 +940,8 @@ EXPORT_SYMBOL_GPL(mt76_dma_attach);
+ 
+ void mt76_dma_cleanup(struct mt76_dev *dev)
+ {
+-	int i;
+-
++	int i, type;
++
+ 	mt76_worker_disable(&dev->tx_worker);
+ 	netif_napi_del(&dev->tx_napi);
+ 
+@@ -801,12 +956,17 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
+ 
+ 	mt76_for_each_q_rx(dev, i) {
+ 		netif_napi_del(&dev->napi[i]);
+-		mt76_dma_rx_cleanup(dev, &dev->q_rx[i]);
++		type = FIELD_GET(MT_QFLAG_WED_TYPE, dev->q_rx[i].flags);
++		if (type != MT76_WED_Q_RX)
++			mt76_dma_rx_cleanup(dev, &dev->q_rx[i]);
+ 	}
+ 
+ 	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_rxwi(dev);
+ }
+ EXPORT_SYMBOL_GPL(mt76_dma_cleanup);
 diff --git a/dma.h b/dma.h
 index fdf786f..90370d1 100644
 --- a/dma.h
@@ -628,7 +681,7 @@
  int mt7615_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
  		       struct ieee80211_sta *sta);
 diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index cd35068..f90a08f 100644
+index cd35068..2454846 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -1190,6 +1190,7 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
@@ -737,9 +790,7 @@
  				       MT_RXQ_ID(MT_RXQ_EXT),
  				       MT7915_RX_RING_SIZE,
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-old mode 100755
-new mode 100644
-index bc8da4d..79b7d01
+index bc8da4d..dd87a40 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -217,7 +217,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
@@ -751,23 +802,32 @@
  {
  	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
  	struct mt76_phy *mphy = &dev->mt76.phy;
+@@ -234,7 +234,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
+ 	bool unicast, insert_ccmp_hdr = false;
+ 	u8 remove_pad, amsdu_info;
+ 	u8 mode = 0, qos_ctl = 0;
+-	struct mt7915_sta *msta;
++	struct mt7915_sta *msta = NULL;
+ 	bool hdr_trans;
+ 	u16 hdr_gap;
+ 	u16 seq_ctrl = 0;
 @@ -494,6 +494,27 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
  #endif
  	} else {
  		status->flag |= RX_FLAG_8023;
-+		if (msta || msta->vif) {
++		if (msta && msta->vif) {
 +			struct mtk_wed_device *wed;
 +			int type;
 +
 +			wed = &dev->mt76.mmio.wed;
 +			type = FIELD_GET(MT_QFLAG_WED_TYPE, dev->mt76.q_rx[q].flags);
 +			if ((mtk_wed_device_active(wed) && type == MT76_WED_Q_RX) &&
-+			    (info & MT_DMA_INFO_PPE_VLD)){
++			    (info & MT_DMA_INFO_PPE_VLD)) {
 +				struct ieee80211_vif *vif;
 +				u32 hash, reason;
 +
 +				vif = container_of((void *)msta->vif, struct ieee80211_vif,
-+					   drv_priv);
++						   drv_priv);
 +
 +				skb->dev = ieee80211_vif_to_netdev(vif);
 +				reason = FIELD_GET(MT_DMA_PPE_CPU_REASON, info);
@@ -779,7 +839,7 @@
  	}
  
  	if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
-@@ -840,6 +861,68 @@ u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
+@@ -840,6 +861,80 @@ u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id)
  	return MT_TXD_TXP_BUF_SIZE;
  }
  
@@ -826,6 +886,7 @@
 +{
 +	struct mt76_txwi_cache *rxwi;
 +	struct mt7915_dev *dev;
++	struct page *page;
 +	int token;
 +
 +	dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
@@ -835,6 +896,9 @@
 +		if(!rxwi)
 +			continue;
 +
++		if(!rxwi->buf)
++			continue;
++
 +		dma_unmap_single(dev->mt76.dma_dev, rxwi->dma_addr,
 +			 wed->wlan.rx_pkt_size, DMA_FROM_DEVICE);
 +		skb_free_frag(rxwi->buf);
@@ -842,13 +906,21 @@
 +
 +		mt76_put_rxwi(&dev->mt76, rxwi);
 +	}
++
++	if (wed->rx_page.va)
++		return;
++
++	page = virt_to_page(wed->rx_page.va);
++	__page_frag_cache_drain(page, wed->rx_page.pagecnt_bias);
++	memset(&wed->rx_page, 0, sizeof(wed->rx_page));
++
 +	return;
 +}
 +
  static void
  mt7915_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
  {
-@@ -1120,7 +1203,7 @@ bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len)
+@@ -1120,7 +1215,7 @@ bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len)
  }
  
  void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
@@ -857,7 +929,7 @@
  {
  	struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
  	__le32 *rxd = (__le32 *)skb->data;
-@@ -1154,7 +1237,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+@@ -1154,7 +1249,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
  		dev_kfree_skb(skb);
  		break;
  	case PKT_TYPE_NORMAL:
@@ -867,7 +939,7 @@
  			return;
  		}
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 1468c3c..4f64df4 100644
+index 1468c3c..5eace9e 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -1704,6 +1704,7 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
@@ -888,9 +960,7 @@
  				     MCU_EXT_CMD(STA_REC_UPDATE), true);
  }
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-old mode 100755
-new mode 100644
-index b4a3120..08ff556
+index b4a3120..08ff556 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -28,6 +28,9 @@ static const u32 mt7915_reg[] = {
diff --git a/recipes-kernel/linux-mt76/files/patches/patches.inc b/recipes-kernel/linux-mt76/files/patches/patches.inc
index da26649..5297bc1 100644
--- a/recipes-kernel/linux-mt76/files/patches/patches.inc
+++ b/recipes-kernel/linux-mt76/files/patches/patches.inc
@@ -7,6 +7,7 @@
     file://0005-mt76-mt7915-drop-undefined-action-frame.patch \
     file://0008-mt76-common-RF-CR-idx-require-8-bits.patch \
     file://0010-mt76-mt7915-4addr-null-frame-using-fixed-rate-to-suc.patch \
+    file://0011-mt76-testmode-use-random-payload-for-tx-packets.patch \
     file://100-Revert-of-net-pass-the-dst-buffer-to-of_get_mac_addr.patch \
     file://1001-mt76-mt7915-add-mtk-internal-debug-tools-for-mt76.patch \
     file://1002-mt76-mt7915-csi-implement-csi-support.patch \
@@ -22,6 +23,7 @@
     file://1114-mt76-airtime-fairness-feature-off-in-mac80211.patch \
     file://1115-mt76-mt7915-add-mt7986-and-mt7916-pre-calibration.patch \
     file://1116-mt76-mt7915-add-vendor-dump-phy-capa.patch \
+    file://1117-mt76-mt7915-add-vendor-subcmd-EDCCA-ctrl.patch \
     file://3001-mt76-add-wed-tx-support.patch \
     file://3002-mt76-add-wed-rx-support.patch \
     file://3003-mt76-add-fill-receive-path-to-report-wed-idx.patch \
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/201-extra_optimization.patch b/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/201-extra_optimization.patch
deleted file mode 100644
index c606487..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/201-extra_optimization.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Subject: Upgrade to Linux 2.6.19
-
-- Includes large parts of the patch from #1021 by dpalffy
-- Includes RB532 NAND driver changes by n0-1
-
-[john@phrozen.org: feix will add this to his upstream queue]
-
-lede-commit: bff468813f78f81e36ebb2a3f4354de7365e640f
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
- Makefile | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/Makefile
-+++ b/Makefile
-@@ -719,11 +719,11 @@ KBUILD_CFLAGS	+= $(call cc-disable-warni
- KBUILD_CFLAGS	+= $(call cc-disable-warning, address-of-packed-member)
- 
- ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
--KBUILD_CFLAGS += -O2
-+KBUILD_CFLAGS += -O2 $(EXTRA_OPTIMIZATION)
- else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
--KBUILD_CFLAGS += -O3
-+KBUILD_CFLAGS += -O3 $(EXTRA_OPTIMIZATION)
- else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
--KBUILD_CFLAGS += -Os
-+KBUILD_CFLAGS += -Os -fno-reorder-blocks -fno-tree-ch $(EXTRA_OPTIMIZATION)
- endif
- 
- # Tell gcc to never replace conditional load with a non-conditional one
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/pending-5.4.inc b/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/pending-5.4.inc
index 1b5f456..8317acf 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/pending-5.4.inc
+++ b/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/pending-5.4.inc
@@ -12,7 +12,6 @@
     file://180-net-phy-at803x-add-support-for-AT8032.patch \
     file://190-rtc-rs5c372-support_alarms_up_to_1_week.patch \
     file://191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch \
-    file://201-extra_optimization.patch \
     file://203-kallsyms_uncompressed.patch \
     file://205-backtrace_module_info.patch \
     file://240-remove-unsane-filenames-from-deps_initramfs-list.patch \
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts
index 745606f..396f483 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts
@@ -179,6 +179,12 @@
 		#address-cells = <1>;
 		#size-cells = <1>;
 		compatible = "spi-nand";
+		spi-cal-enable;
+		spi-cal-mode = "read-data";
+		spi-cal-datalen = <7>;
+		spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>;
+		spi-cal-addrlen = <5>;
+		spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>;
 		reg = <0>;
 		spi-max-frequency = <52000000>;
 		spi-tx-buswidth = <4>;
@@ -190,6 +196,18 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&spic_pins>;
 	status = "disabled";
+
+	slb9670: slb9670@0 {
+		compatible = "infineon,slb9670";
+		reg = <0>; /* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-cal-enable;
+		spi-cal-mode = "read-data";
+		spi-cal-datalen = <2>;
+		spi-cal-data = /bits/ 8 <0x00 0x1b>;
+		spi-max-frequency = <20000000>;
+	};
 };
 
 &pio {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts
index abd7fd3..47bb702 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts
@@ -123,6 +123,13 @@
 		#address-cells = <1>;
 		#size-cells = <1>;
 		compatible = "jedec,spi-nor";
+		spi-cal-enable;
+		spi-cal-mode = "read-data";
+		spi-cal-datalen = <7>;
+		spi-cal-data = /bits/ 8 <
+			0x53 0x46 0x5F 0x42 0x4F 0x4F 0x54>; /* SF_BOOT */
+		spi-cal-addrlen = <1>;
+		spi-cal-addr = /bits/ 32 <0x0>;
 		reg = <0>;
 		spi-max-frequency = <52000000>;
 		spi-tx-buswidth = <4>;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
index 7ba9a01..38d2b53 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
@@ -631,38 +631,32 @@
 
 int tx_ring_read(struct seq_file *seq, void *v)
 {
+	struct mtk_eth *eth = g_eth;
 	struct mtk_tx_ring *ring = &g_eth->tx_ring;
-	struct mtk_tx_dma *tx_ring;
+	struct mtk_tx_dma_v2 *tx_ring;
 	int i = 0;
 
-	tx_ring =
-	    kmalloc(sizeof(struct mtk_tx_dma) * MTK_DMA_SIZE, GFP_KERNEL);
-	if (!tx_ring) {
-		seq_puts(seq, " allocate temp tx_ring fail.\n");
-		return 0;
-	}
-
-	for (i = 0; i < MTK_DMA_SIZE; i++)
-		tx_ring[i] = ring->dma[i];
-
 	seq_printf(seq, "free count = %d\n", (int)atomic_read(&ring->free_count));
 	seq_printf(seq, "cpu next free: %d\n", (int)(ring->next_free - ring->dma));
 	seq_printf(seq, "cpu last free: %d\n", (int)(ring->last_free - ring->dma));
 	for (i = 0; i < MTK_DMA_SIZE; i++) {
-		dma_addr_t tmp = ring->phys + i * sizeof(*tx_ring);
+		dma_addr_t tmp = ring->phys + i * eth->soc->txrx.txd_size;
+
+		tx_ring = ring->dma + i * eth->soc->txrx.txd_size;
 
 		seq_printf(seq, "%d (%pad): %08x %08x %08x %08x", i, &tmp,
-			   *(int *)&tx_ring[i].txd1, *(int *)&tx_ring[i].txd2,
-			   *(int *)&tx_ring[i].txd3, *(int *)&tx_ring[i].txd4);
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-		seq_printf(seq, " %08x %08x %08x %08x",
-			   *(int *)&tx_ring[i].txd5, *(int *)&tx_ring[i].txd6,
-			   *(int *)&tx_ring[i].txd7, *(int *)&tx_ring[i].txd8);
-#endif
+			   tx_ring->txd1, tx_ring->txd2,
+			   tx_ring->txd3, tx_ring->txd4);
+
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+			seq_printf(seq, " %08x %08x %08x %08x",
+				   tx_ring->txd5, tx_ring->txd6,
+				   tx_ring->txd7, tx_ring->txd8);
+		}
+
 		seq_printf(seq, "\n");
 	}
 
-	kfree(tx_ring);
 	return 0;
 }
 
@@ -682,34 +676,27 @@
 int hwtx_ring_read(struct seq_file *seq, void *v)
 {
 	struct mtk_eth *eth = g_eth;
-	struct mtk_tx_dma *hwtx_ring;
+	struct mtk_tx_dma_v2 *hwtx_ring;
 	int i = 0;
 
-	hwtx_ring =
-	    kmalloc(sizeof(struct mtk_tx_dma) * MTK_DMA_SIZE, GFP_KERNEL);
-	if (!hwtx_ring) {
-		seq_puts(seq, " allocate temp hwtx_ring fail.\n");
-		return 0;
-	}
-
-	for (i = 0; i < MTK_DMA_SIZE; i++)
-		hwtx_ring[i] = eth->scratch_ring[i];
-
 	for (i = 0; i < MTK_DMA_SIZE; i++) {
-		dma_addr_t addr = eth->phy_scratch_ring + i * sizeof(*hwtx_ring);
+		dma_addr_t addr = eth->phy_scratch_ring + i * eth->soc->txrx.txd_size;
+
+		hwtx_ring = eth->scratch_ring + i * eth->soc->txrx.txd_size;
 
 		seq_printf(seq, "%d (%pad): %08x %08x %08x %08x", i, &addr,
-			   *(int *)&hwtx_ring[i].txd1, *(int *)&hwtx_ring[i].txd2,
-			   *(int *)&hwtx_ring[i].txd3, *(int *)&hwtx_ring[i].txd4);
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-		seq_printf(seq, " %08x %08x %08x %08x",
-			   *(int *)&hwtx_ring[i].txd5, *(int *)&hwtx_ring[i].txd6,
-			   *(int *)&hwtx_ring[i].txd7, *(int *)&hwtx_ring[i].txd8);
-#endif
+			   hwtx_ring->txd1, hwtx_ring->txd2,
+			   hwtx_ring->txd3, hwtx_ring->txd4);
+
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+			seq_printf(seq, " %08x %08x %08x %08x",
+				   hwtx_ring->txd5, hwtx_ring->txd6,
+				   hwtx_ring->txd7, hwtx_ring->txd8);
+		}
+
 		seq_printf(seq, "\n");
 	}
 
-	kfree(hwtx_ring);
 	return 0;
 }
 
@@ -728,36 +715,29 @@
 
 int rx_ring_read(struct seq_file *seq, void *v)
 {
+	struct mtk_eth *eth = g_eth;
 	struct mtk_rx_ring *ring = &g_eth->rx_ring[0];
-	struct mtk_rx_dma *rx_ring;
-
+	struct mtk_rx_dma_v2 *rx_ring;
 	int i = 0;
 
-	rx_ring =
-	    kmalloc(sizeof(struct mtk_rx_dma) * MTK_DMA_SIZE, GFP_KERNEL);
-	if (!rx_ring) {
-		seq_puts(seq, " allocate temp rx_ring fail.\n");
-		return 0;
-	}
-
-	for (i = 0; i < MTK_DMA_SIZE; i++)
-		rx_ring[i] = ring->dma[i];
-
 	seq_printf(seq, "next to read: %d\n",
 		   NEXT_DESP_IDX(ring->calc_idx, MTK_DMA_SIZE));
 	for (i = 0; i < MTK_DMA_SIZE; i++) {
+		rx_ring = ring->dma + i * eth->soc->txrx.rxd_size;
+
 		seq_printf(seq, "%d: %08x %08x %08x %08x", i,
-			   *(int *)&rx_ring[i].rxd1, *(int *)&rx_ring[i].rxd2,
-			   *(int *)&rx_ring[i].rxd3, *(int *)&rx_ring[i].rxd4);
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-		seq_printf(seq, " %08x %08x %08x %08x",
-			   *(int *)&rx_ring[i].rxd5, *(int *)&rx_ring[i].rxd6,
-			   *(int *)&rx_ring[i].rxd7, *(int *)&rx_ring[i].rxd8);
-#endif
+			   rx_ring->rxd1, rx_ring->rxd2,
+			   rx_ring->rxd3, rx_ring->rxd4);
+
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+			seq_printf(seq, " %08x %08x %08x %08x",
+				   rx_ring->rxd5, rx_ring->rxd6,
+				   rx_ring->rxd7, rx_ring->rxd8);
+		}
+
 		seq_printf(seq, "\n");
 	}
 
-	kfree(rx_ring);
 	return 0;
 }
 
@@ -902,17 +882,18 @@
 	.release = single_release
 };
 
-void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma *rxd)
+void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd)
 {
+	struct mtk_eth *eth = g_eth;
 	u32 idx, agg_cnt, agg_size;
 
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-	idx = ring_no - 4;
-	agg_cnt = RX_DMA_GET_AGG_CNT_V2(rxd->rxd6);
-#else
-	idx = ring_no - 1;
-	agg_cnt = RX_DMA_GET_AGG_CNT(rxd->rxd2);
-#endif
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+		idx = ring_no - 4;
+		agg_cnt = RX_DMA_GET_AGG_CNT_V2(rxd->rxd6);
+	} else {
+		idx = ring_no - 1;
+		agg_cnt = RX_DMA_GET_AGG_CNT(rxd->rxd2);
+	}
 
 	agg_size = RX_DMA_GET_PLEN0(rxd->rxd2);
 
@@ -922,17 +903,18 @@
 	hw_lro_tot_agg_cnt[idx] += agg_cnt;
 }
 
-void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma *rxd)
+void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd)
 {
+	struct mtk_eth *eth = g_eth;
 	u32 idx, flush_reason;
 
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-	idx = ring_no - 4;
-	flush_reason = RX_DMA_GET_FLUSH_RSN_V2(rxd->rxd6);
-#else
-	idx = ring_no - 1;
-	flush_reason = RX_DMA_GET_REV(rxd->rxd2);
-#endif
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+		idx = ring_no - 4;
+		flush_reason = RX_DMA_GET_FLUSH_RSN_V2(rxd->rxd6);
+	} else {
+		idx = ring_no - 1;
+		flush_reason = RX_DMA_GET_REV(rxd->rxd2);
+	}
 
 	if ((flush_reason & 0x7) == MTK_HW_LRO_AGG_FLUSH)
 		hw_lro_agg_flush_cnt[idx]++;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
index 2113c1f..52bd729 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
@@ -279,7 +279,7 @@
 int mtketh_debugfs_init(struct mtk_eth *eth);
 void mtketh_debugfs_exit(struct mtk_eth *eth);
 int mtk_do_priv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma *rxd);
-void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma *rxd);
+void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd);
+void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd);
 
 #endif /* MTK_ETH_DBG_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index e6f6cce..9489d1d 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -823,8 +823,8 @@
 	return buf_size;
 }
 
-static inline bool mtk_rx_get_desc(struct mtk_rx_dma *rxd,
-				   struct mtk_rx_dma *dma_rxd)
+static bool mtk_rx_get_desc(struct mtk_eth *eth, struct mtk_rx_dma_v2 *rxd,
+			    struct mtk_rx_dma_v2 *dma_rxd)
 {
 	rxd->rxd2 = READ_ONCE(dma_rxd->rxd2);
 	if (!(rxd->rxd2 & RX_DMA_DONE))
@@ -833,16 +833,19 @@
 	rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
 	rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
 	rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-	rxd->rxd5 = READ_ONCE(dma_rxd->rxd5);
-	rxd->rxd6 = READ_ONCE(dma_rxd->rxd6);
-#endif
+
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+		rxd->rxd5 = READ_ONCE(dma_rxd->rxd5);
+		rxd->rxd6 = READ_ONCE(dma_rxd->rxd6);
+	}
+
 	return true;
 }
 
 /* the qdma core needs scratch memory to be setup */
 static int mtk_init_fq_dma(struct mtk_eth *eth)
 {
+	const struct mtk_soc_data *soc = eth->soc;
 	dma_addr_t phy_ring_tail;
 	int cnt = MTK_DMA_SIZE;
 	dma_addr_t dma_addr;
@@ -850,9 +853,9 @@
 
 	if (!eth->soc->has_sram) {
 		eth->scratch_ring = dma_alloc_coherent(eth->dev,
-					       cnt * sizeof(struct mtk_tx_dma),
+					       cnt * soc->txrx.txd_size,
 					       &eth->phy_scratch_ring,
-					       GFP_ATOMIC);
+					       GFP_KERNEL);
 	} else {
 		eth->scratch_ring = eth->base + MTK_ETH_SRAM_OFFSET;
 	}
@@ -860,8 +863,7 @@
 	if (unlikely(!eth->scratch_ring))
                         return -ENOMEM;
 
-	eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE,
-				    GFP_KERNEL);
+	eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
 	if (unlikely(!eth->scratch_head))
 		return -ENOMEM;
 
@@ -871,26 +873,26 @@
 	if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
 		return -ENOMEM;
 
-	phy_ring_tail = eth->phy_scratch_ring +
-			(sizeof(struct mtk_tx_dma) * (cnt - 1));
+	phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1);
 
 	for (i = 0; i < cnt; i++) {
-		eth->scratch_ring[i].txd1 =
-				(dma_addr + (i * MTK_QDMA_PAGE_SIZE));
+		struct mtk_tx_dma_v2 *txd;
+
+		txd = eth->scratch_ring + i * soc->txrx.txd_size;
+		txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
 		if (i < cnt - 1)
-			eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring +
-				((i + 1) * sizeof(struct mtk_tx_dma)));
-		eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE);
+			txd->txd2 = eth->phy_scratch_ring +
+				(i + 1) * soc->txrx.txd_size;
 
-		eth->scratch_ring[i].txd4 = 0;
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-		if (eth->soc->has_sram && ((sizeof(struct mtk_tx_dma)) > 16)) {
-			eth->scratch_ring[i].txd5 = 0;
-			eth->scratch_ring[i].txd6 = 0;
-			eth->scratch_ring[i].txd7 = 0;
-			eth->scratch_ring[i].txd8 = 0;
+		txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
+		txd->txd4 = 0;
+
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+			txd->txd5 = 0;
+			txd->txd6 = 0;
+			txd->txd7 = 0;
+			txd->txd8 = 0;
 		}
-#endif
 	}
 
 	mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD);
@@ -903,28 +905,26 @@
 
 static inline void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc)
 {
-	void *ret = ring->dma;
-
-	return ret + (desc - ring->phys);
+	return ring->dma + (desc - ring->phys);
 }
 
 static inline struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
-						    struct mtk_tx_dma *txd)
+						    void *txd, u32 txd_size)
 {
-	int idx = txd - ring->dma;
+	int idx = (txd - ring->dma) / txd_size;
 
 	return &ring->buf[idx];
 }
 
 static struct mtk_tx_dma *qdma_to_pdma(struct mtk_tx_ring *ring,
-				       struct mtk_tx_dma *dma)
+				       void *dma)
 {
 	return ring->dma_pdma - ring->dma + dma;
 }
 
-static int txd_to_idx(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma)
+static int txd_to_idx(struct mtk_tx_ring *ring, void *dma, u32 txd_size)
 {
-	return ((void *)dma - (void *)ring->dma) / sizeof(*dma);
+	return (dma - ring->dma) / txd_size;
 }
 
 static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf,
@@ -992,20 +992,129 @@
 	}
 }
 
+static void mtk_tx_set_dma_desc_v1(struct sk_buff *skb, struct net_device *dev, void *txd,
+				struct mtk_tx_dma_desc_info *info)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+	struct mtk_tx_dma *desc = txd;
+	u32 data;
+
+	WRITE_ONCE(desc->txd1, info->addr);
+
+	data = TX_DMA_SWC | QID_LOW_BITS(info->qid) | TX_DMA_PLEN0(info->size);
+	if (info->last)
+		data |= TX_DMA_LS0;
+	WRITE_ONCE(desc->txd3, data);
+
+	data = (mac->id + 1) << TX_DMA_FPORT_SHIFT; /* forward port */
+	data |= QID_HIGH_BITS(info->qid);
+	if (info->first) {
+		if (info->gso)
+			data |= TX_DMA_TSO;
+		/* tx checksum offload */
+		if (info->csum)
+			data |= TX_DMA_CHKSUM;
+		/* vlan header offload */
+		if (info->vlan)
+			data |= TX_DMA_INS_VLAN | info->vlan_tci;
+	}
+
+#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE)
+	if (HNAT_SKB_CB2(skb)->magic == 0x78681415) {
+		data &= ~(0x7 << TX_DMA_FPORT_SHIFT);
+		data |= 0x4 << TX_DMA_FPORT_SHIFT;
+	}
+
+	trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
+		     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
+#endif
+	WRITE_ONCE(desc->txd4, data);
+}
+
+static void mtk_tx_set_dma_desc_v2(struct sk_buff *skb, struct net_device *dev, void *txd,
+				struct mtk_tx_dma_desc_info *info)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+	struct mtk_tx_dma_v2 *desc = txd;
+	u32 data = 0;
+	u16 qid;
+
+	if(!info->qid && mac->id)
+		qid = MTK_QDMA_GMAC2_QID;
+
+	WRITE_ONCE(desc->txd1, info->addr);
+
+	data = TX_DMA_PLEN0(info->size);
+	if (info->last)
+		data |= TX_DMA_LS0;
+	WRITE_ONCE(desc->txd3, data);
+
+	data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */
+	data |= TX_DMA_SWC_V2 | QID_BITS_V2(qid);
+#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE)
+	if (HNAT_SKB_CB2(skb)->magic == 0x78681415) {
+		data &= ~(0xf << TX_DMA_FPORT_SHIFT_V2);
+		data |= 0x4 << TX_DMA_FPORT_SHIFT_V2;
+	}
+
+	trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
+		     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
+#endif
+	WRITE_ONCE(desc->txd4, data);
+
+	data = 0;
+	if (info->first) {
+		if (info->gso)
+			data |= TX_DMA_TSO_V2;
+		/* tx checksum offload */
+		if (info->csum)
+			data |= TX_DMA_CHKSUM_V2;
+	}
+	WRITE_ONCE(desc->txd5, data);
+
+	data = 0;
+	if (info->first && info->vlan)
+		data |= TX_DMA_INS_VLAN_V2 | info->vlan_tci;
+	WRITE_ONCE(desc->txd6, data);
+
+	WRITE_ONCE(desc->txd7, 0);
+	WRITE_ONCE(desc->txd8, 0);
+}
+
+static void mtk_tx_set_dma_desc(struct sk_buff *skb, struct net_device *dev, void *txd,
+				struct mtk_tx_dma_desc_info *info)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+		mtk_tx_set_dma_desc_v2(skb, dev, txd, info);
+	else
+		mtk_tx_set_dma_desc_v1(skb, dev, txd, info);
+}
+
 static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
 		      int tx_num, struct mtk_tx_ring *ring, bool gso)
 {
+	struct mtk_tx_dma_desc_info txd_info = {
+		.size = skb_headlen(skb),
+		.qid = skb->mark & MTK_QDMA_TX_MASK,
+		.gso = gso,
+		.csum = skb->ip_summed == CHECKSUM_PARTIAL,
+		.vlan = skb_vlan_tag_present(skb),
+		.vlan_tci = skb_vlan_tag_get(skb),
+		.first = true,
+		.last = !skb_is_nonlinear(skb),
+	};
 	struct mtk_mac *mac = netdev_priv(dev);
 	struct mtk_eth *eth = mac->hw;
+	const struct mtk_soc_data *soc = eth->soc;
 	struct mtk_tx_dma *itxd, *txd;
 	struct mtk_tx_dma *itxd_pdma, *txd_pdma;
 	struct mtk_tx_buf *itx_buf, *tx_buf;
-	dma_addr_t mapped_addr;
-	unsigned int nr_frags;
 	int i, n_desc = 1;
-	u32 txd4 = 0, txd5 = 0, txd6 = 0;
-	u32 fport;
-	u32 qid = 0;
 	int k = 0;
 
 	itxd = ring->next_free;
@@ -1013,93 +1122,35 @@
 	if (itxd == ring->last_free)
 		return -ENOMEM;
 
-	itx_buf = mtk_desc_to_tx_buf(ring, itxd);
+	itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
 	memset(itx_buf, 0, sizeof(*itx_buf));
 
-	mapped_addr = dma_map_single(eth->dev, skb->data,
-				     skb_headlen(skb), DMA_TO_DEVICE);
-	if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
+	txd_info.addr = dma_map_single(eth->dev, skb->data, txd_info.size,
+				       DMA_TO_DEVICE);
+	if (unlikely(dma_mapping_error(eth->dev, txd_info.addr)))
 		return -ENOMEM;
 
+	mtk_tx_set_dma_desc(skb, dev, itxd, &txd_info);
+
-	WRITE_ONCE(itxd->txd1, mapped_addr);
 	itx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
 	itx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 :
 			  MTK_TX_FLAGS_FPORT1;
-	setup_tx_buf(eth, itx_buf, itxd_pdma, mapped_addr, skb_headlen(skb),
+	setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size,
 		     k++);
 
-	nr_frags = skb_shinfo(skb)->nr_frags;
-
-        qid = skb->mark & (MTK_QDMA_TX_MASK);
-
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-	if(!qid && mac->id)
-		qid = MTK_QDMA_GMAC2_QID;
-#endif
-
-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-		/* set the forward port */
-		fport = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2;
-		txd4 |= fport;
-
-		if (gso)
-			txd5 |= TX_DMA_TSO_V2;
-
-		/* TX Checksum offload */
-		if (skb->ip_summed == CHECKSUM_PARTIAL)
-			txd5 |= TX_DMA_CHKSUM_V2;
-
-		/* VLAN header offload */
-		if (skb_vlan_tag_present(skb))
-			txd6 |= TX_DMA_INS_VLAN_V2 | skb_vlan_tag_get(skb);
-
-		txd4 = txd4 | TX_DMA_SWC_V2;
-	} else {
-		/* set the forward port */
-		fport = (mac->id + 1) << TX_DMA_FPORT_SHIFT;
-		txd4 |= fport;
-
-                if (gso)
-                        txd4 |= TX_DMA_TSO;
-
-                /* TX Checksum offload */
-                if (skb->ip_summed == CHECKSUM_PARTIAL)
-                        txd4 |= TX_DMA_CHKSUM;
-
-		/* VLAN header offload */
-		if (skb_vlan_tag_present(skb))
-			txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb);
-	}
 	/* TX SG offload */
 	txd = itxd;
 	txd_pdma = qdma_to_pdma(ring, txd);
 
-#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE)
-	if (HNAT_SKB_CB2(skb)->magic == 0x78681415) {
-		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-			txd4 &= ~(0xf << TX_DMA_FPORT_SHIFT_V2);
-			txd4 |= 0x4 << TX_DMA_FPORT_SHIFT_V2;
-		} else {
-			txd4 &= ~(0x7 << TX_DMA_FPORT_SHIFT);
-			txd4 |= 0x4 << TX_DMA_FPORT_SHIFT;
-		}
-	}
-
-	trace_printk("[%s] nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
-		     __func__, nr_frags, HNAT_SKB_CB2(skb)->magic, txd4);
-#endif
-
-	for (i = 0; i < nr_frags; i++) {
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
 		unsigned int offset = 0;
 		int frag_size = skb_frag_size(frag);
 
 		while (frag_size) {
-			bool last_frag = false;
-			unsigned int frag_map_size;
 			bool new_desc = true;
 
-			if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) ||
+			if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) ||
 			    (i & 0x1)) {
 				txd = mtk_qdma_phys_to_virt(ring, txd->txd2);
 				txd_pdma = qdma_to_pdma(ring, txd);
@@ -1111,35 +1162,20 @@
 				new_desc = false;
 			}
 
+			memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
+			txd_info.size = min(frag_size, MTK_TX_DMA_BUF_LEN);
+			txd_info.qid = skb->mark & MTK_QDMA_TX_MASK;
+			txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 &&
+					!(frag_size - txd_info.size);
+			txd_info.addr = skb_frag_dma_map(eth->dev, frag,
+							 offset, txd_info.size,
+							 DMA_TO_DEVICE);
+			if (unlikely(dma_mapping_error(eth->dev, txd_info.addr)))
+ 				goto err_dma;
 
-			frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN);
-			mapped_addr = skb_frag_dma_map(eth->dev, frag, offset,
-						       frag_map_size,
-						       DMA_TO_DEVICE);
-			if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
-				goto err_dma;
+			mtk_tx_set_dma_desc(skb, dev, txd, &txd_info);
 
-			if (i == nr_frags - 1 &&
-			    (frag_size - frag_map_size) == 0)
-				last_frag = true;
-
-			WRITE_ONCE(txd->txd1, mapped_addr);
-
-			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-				WRITE_ONCE(txd->txd3, (TX_DMA_PLEN0(frag_map_size) |
-					   last_frag * TX_DMA_LS0));
-				WRITE_ONCE(txd->txd4, fport | TX_DMA_SWC_V2 |
-						      QID_BITS_V2(qid));
-			} else {
-				WRITE_ONCE(txd->txd3,
-					   (TX_DMA_SWC | QID_LOW_BITS(qid) |
-					    TX_DMA_PLEN0(frag_map_size) |
-					    last_frag * TX_DMA_LS0));
-				WRITE_ONCE(txd->txd4,
-					   fport | QID_HIGH_BITS(qid));
-			}
-
-			tx_buf = mtk_desc_to_tx_buf(ring, txd);
+			tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size);
 			if (new_desc)
 				memset(tx_buf, 0, sizeof(*tx_buf));
 			tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC;
@@ -1147,36 +1183,18 @@
 			tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 :
 					 MTK_TX_FLAGS_FPORT1;
 
-			setup_tx_buf(eth, tx_buf, txd_pdma, mapped_addr,
-				     frag_map_size, k++);
+			setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr,
+				     txd_info.size, k++);
 
-			frag_size -= frag_map_size;
-			offset += frag_map_size;
+			frag_size -= txd_info.size;
+			offset += txd_info.size;
 		}
 	}
 
 	/* store skb to cleanup */
 	itx_buf->skb = skb;
 
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-	WRITE_ONCE(itxd->txd5, txd5);
-	WRITE_ONCE(itxd->txd6, txd6);
-	WRITE_ONCE(itxd->txd7, 0);
-	WRITE_ONCE(itxd->txd8, 0);
-#endif
-
-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-		WRITE_ONCE(itxd->txd4, txd4 | QID_BITS_V2(qid));
-		WRITE_ONCE(itxd->txd3, (TX_DMA_PLEN0(skb_headlen(skb)) |
-				(!nr_frags * TX_DMA_LS0)));
-	} else {
-		WRITE_ONCE(itxd->txd4, txd4 | QID_HIGH_BITS(qid));
-		WRITE_ONCE(itxd->txd3,
-			   TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
-			   (!nr_frags * TX_DMA_LS0) | QID_LOW_BITS(qid));
-	}
-
-	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+	if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
 		if (k & 0x1)
 			txd_pdma->txd2 |= TX_DMA_LS0;
 		else
@@ -1194,12 +1212,12 @@
 	 */
 	wmb();
 
-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+	if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
 		if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) ||
 		    !netdev_xmit_more())
 			mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR);
 	} else {
-		int next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd),
+		int next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->txrx.txd_size),
 					     ring->dma_size);
 		mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0);
 	}
@@ -1208,13 +1226,13 @@
 
 err_dma:
 	do {
-		tx_buf = mtk_desc_to_tx_buf(ring, itxd);
+		tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
 
 		/* unmap dma */
 		mtk_tx_unmap(eth, tx_buf, false);
 
 		itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
-		if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+		if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA))
 			itxd_pdma->txd2 = TX_DMA_DESP2_DEF;
 
 		itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
@@ -1334,12 +1352,15 @@
 	int idx;
 
 	for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) {
+		struct mtk_rx_dma *rxd;
+
 		if (!IS_NORMAL_RING(i) && !IS_HW_LRO_RING(i))
 			continue;
 
 		ring = &eth->rx_ring[i];
 		idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
-		if (ring->dma[idx].rxd2 & RX_DMA_DONE) {
+		rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
+		if (rxd->rxd2 & RX_DMA_DONE) {
 			ring->calc_idx_update = true;
 			return ring;
 		}
@@ -1373,7 +1394,7 @@
 	int idx;
 	struct sk_buff *skb;
 	u8 *data, *new_data;
-	struct mtk_rx_dma *rxd, trxd;
+	struct mtk_rx_dma_v2 *rxd, trxd;
 	int done = 0;
 
 	if (unlikely(!ring))
@@ -1383,7 +1404,7 @@
 		struct net_device *netdev;
 		unsigned int pktlen;
 		dma_addr_t dma_addr;
-		int mac;
+		int mac = 0;
 
 		if (eth->hwlro)
 			ring = mtk_get_rx_ring(eth);
@@ -1392,21 +1413,19 @@
 			goto rx_done;
 
 		idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
-		rxd = &ring->dma[idx];
+		rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
 		data = ring->data[idx];
 
-		if (!mtk_rx_get_desc(&trxd, rxd))
+		if (!mtk_rx_get_desc(eth, &trxd, rxd))
 			break;
 
 		/* find out which mac the packet come from. values start at 1 */
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
 			mac = 0;
 		} else {
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
 			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-				mac = RX_DMA_GET_SPORT(trxd.rxd5) - 1;
+				mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1;
 			else
-#endif
 				mac = (trxd.rxd4 & RX_DMA_SPECIAL_TAG) ?
 				      0 : RX_DMA_GET_SPORT(trxd.rxd4) - 1;
 		}
@@ -1486,11 +1505,9 @@
 		}
 
 #if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE)
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
 			*(u32 *)(skb->head) = trxd.rxd5;
 		else
-#endif
 			*(u32 *)(skb->head) = trxd.rxd4;
 
 		skb_hnat_alg(skb) = 0;
@@ -1546,6 +1563,7 @@
 static void mtk_poll_tx_qdma(struct mtk_eth *eth, int budget,
 			    unsigned int *done, unsigned int *bytes)
 {
+	const struct mtk_soc_data *soc = eth->soc;
 	struct mtk_tx_ring *ring = &eth->tx_ring;
 	struct mtk_tx_dma *desc;
 	struct sk_buff *skb;
@@ -1566,7 +1584,7 @@
 
 		desc = mtk_qdma_phys_to_virt(ring, desc->txd2);
 
-		tx_buf = mtk_desc_to_tx_buf(ring, desc);
+		tx_buf = mtk_desc_to_tx_buf(ring, desc, soc->txrx.txd_size);
 		if (tx_buf->flags & MTK_TX_FLAGS_FPORT1)
 			mac = 1;
 
@@ -1617,7 +1635,7 @@
 
 		mtk_tx_unmap(eth, tx_buf, true);
 
-		desc = &ring->dma[cpu];
+		desc = ring->dma + cpu * eth->soc->txrx.txd_size;
 		ring->last_free = desc;
 		atomic_inc(&ring->free_count);
 
@@ -1738,8 +1756,10 @@
 
 static int mtk_tx_alloc(struct mtk_eth *eth)
 {
+	const struct mtk_soc_data *soc = eth->soc;
 	struct mtk_tx_ring *ring = &eth->tx_ring;
-	int i, sz = sizeof(*ring->dma);
+	int i, sz = soc->txrx.txd_size;
+	struct mtk_tx_dma_v2 *txd, *pdma_txd;
 
 	ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf),
 			       GFP_KERNEL);
@@ -1748,9 +1768,9 @@
 
 	if (!eth->soc->has_sram)
 		ring->dma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
-					       &ring->phys, GFP_ATOMIC);
+					       &ring->phys, GFP_KERNEL);
 	else {
-		ring->dma =  eth->scratch_ring + MTK_DMA_SIZE;
+		ring->dma =  eth->scratch_ring + MTK_DMA_SIZE * sz;
 		ring->phys = eth->phy_scratch_ring + MTK_DMA_SIZE * sz;
 	}
 
@@ -1761,17 +1781,17 @@
 		int next = (i + 1) % MTK_DMA_SIZE;
 		u32 next_ptr = ring->phys + next * sz;
 
-		ring->dma[i].txd2 = next_ptr;
-		ring->dma[i].txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
-		ring->dma[i].txd4 = 0;
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-                if (eth->soc->has_sram && ( sz > 16)) {
-                        ring->dma[i].txd5 = 0;
-                        ring->dma[i].txd6 = 0;
-                        ring->dma[i].txd7 = 0;
-                        ring->dma[i].txd8 = 0;
-                }
-#endif
+		txd = ring->dma + i * sz;
+		txd->txd2 = next_ptr;
+		txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
+		txd->txd4 = 0;
+
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+			txd->txd5 = 0;
+			txd->txd6 = 0;
+			txd->txd7 = 0;
+			txd->txd8 = 0;
+		}
 	}
 
 	/* On MT7688 (PDMA only) this driver uses the ring->dma structs
@@ -1780,21 +1800,22 @@
 	 */
 	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
 		ring->dma_pdma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
-						    &ring->phys_pdma,
-						    GFP_ATOMIC);
+						    &ring->phys_pdma, GFP_KERNEL);
 		if (!ring->dma_pdma)
 			goto no_tx_mem;
 
 		for (i = 0; i < MTK_DMA_SIZE; i++) {
-			ring->dma_pdma[i].txd2 = TX_DMA_DESP2_DEF;
-			ring->dma_pdma[i].txd4 = 0;
+			pdma_txd = ring->dma_pdma + i *sz;
+
+			pdma_txd->txd2 = TX_DMA_DESP2_DEF;
+			pdma_txd->txd4 = 0;
 		}
 	}
 
 	ring->dma_size = MTK_DMA_SIZE;
 	atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
-	ring->next_free = &ring->dma[0];
-	ring->last_free = &ring->dma[MTK_DMA_SIZE - 1];
+	ring->next_free = ring->dma;
+	ring->last_free = (void *)txd;
 	ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz));
 	ring->thresh = MAX_SKB_FRAGS;
 
@@ -1827,6 +1848,7 @@
 
 static void mtk_tx_clean(struct mtk_eth *eth)
 {
+	const struct mtk_soc_data *soc = eth->soc;
 	struct mtk_tx_ring *ring = &eth->tx_ring;
 	int i;
 
@@ -1839,17 +1861,15 @@
 
 	if (!eth->soc->has_sram && ring->dma) {
 		dma_free_coherent(eth->dev,
-				  MTK_DMA_SIZE * sizeof(*ring->dma),
-				  ring->dma,
-				  ring->phys);
+				  MTK_DMA_SIZE * soc->txrx.txd_size,
+				  ring->dma, ring->phys);
 		ring->dma = NULL;
 	}
 
 	if (ring->dma_pdma) {
 		dma_free_coherent(eth->dev,
-				  MTK_DMA_SIZE * sizeof(*ring->dma_pdma),
-				  ring->dma_pdma,
-				  ring->phys_pdma);
+				  MTK_DMA_SIZE * soc->txrx.txd_size,
+				  ring->dma_pdma, ring->phys_pdma);
 		ring->dma_pdma = NULL;
 	}
 }
@@ -1892,43 +1912,46 @@
 	if ((!eth->soc->has_sram) || (eth->soc->has_sram
 				&& (rx_flag != MTK_RX_FLAGS_NORMAL)))
 		ring->dma = dma_alloc_coherent(eth->dev,
-					       rx_dma_size * sizeof(*ring->dma),
-					       &ring->phys, GFP_ATOMIC);
+					       rx_dma_size * eth->soc->txrx.rxd_size,
+					       &ring->phys, GFP_KERNEL);
 	else {
 		struct mtk_tx_ring *tx_ring = &eth->tx_ring;
-		ring->dma = (struct mtk_rx_dma *)(tx_ring->dma +
-			     MTK_DMA_SIZE * (ring_no + 1));
+		ring->dma = tx_ring->dma + MTK_DMA_SIZE *
+			    eth->soc->txrx.rxd_size * (ring_no + 1);
 		ring->phys = tx_ring->phys + MTK_DMA_SIZE *
-			     sizeof(*tx_ring->dma) * (ring_no + 1);
+			     eth->soc->txrx.rxd_size * (ring_no + 1);
 	}
 
 	if (!ring->dma)
 		return -ENOMEM;
 
 	for (i = 0; i < rx_dma_size; i++) {
+		struct mtk_rx_dma_v2 *rxd;
+
 		dma_addr_t dma_addr = dma_map_single(eth->dev,
 				ring->data[i] + NET_SKB_PAD + eth->ip_align,
 				ring->buf_size,
 				DMA_FROM_DEVICE);
 		if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
 			return -ENOMEM;
-		ring->dma[i].rxd1 = (unsigned int)dma_addr;
+
+		rxd = ring->dma + i * eth->soc->txrx.rxd_size;
+		rxd->rxd1 = (unsigned int)dma_addr;
 
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
-			ring->dma[i].rxd2 = RX_DMA_LSO;
+			rxd->rxd2 = RX_DMA_LSO;
 		else
-			ring->dma[i].rxd2 = RX_DMA_PLEN0(ring->buf_size);
+			rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size);
 
-		ring->dma[i].rxd3 = 0;
-		ring->dma[i].rxd4 = 0;
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-		if (eth->soc->has_sram && ((sizeof(struct mtk_rx_dma)) > 16)) {
-			ring->dma[i].rxd5 = 0;
-			ring->dma[i].rxd6 = 0;
-			ring->dma[i].rxd7 = 0;
-			ring->dma[i].rxd8 = 0;
+		rxd->rxd3 = 0;
+		rxd->rxd4 = 0;
+
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+			rxd->rxd5 = 0;
+			rxd->rxd6 = 0;
+			rxd->rxd7 = 0;
+			rxd->rxd8 = 0;
 		}
-#endif
 	}
 	ring->dma_size = rx_dma_size;
 	ring->calc_idx_update = false;
@@ -1963,12 +1986,17 @@
 
 	if (ring->data && ring->dma) {
 		for (i = 0; i < ring->dma_size; i++) {
+			struct mtk_rx_dma *rxd;
+
 			if (!ring->data[i])
 				continue;
-			if (!ring->dma[i].rxd1)
+
+			rxd = ring->dma + i * eth->soc->txrx.rxd_size;
+			if (!rxd->rxd1)
 				continue;
+
 			dma_unmap_single(eth->dev,
-					 ring->dma[i].rxd1,
+					 rxd->rxd1,
 					 ring->buf_size,
 					 DMA_FROM_DEVICE);
 			skb_free_frag(ring->data[i]);
@@ -1982,7 +2010,7 @@
 
 	if (ring->dma) {
 		dma_free_coherent(eth->dev,
-				  ring->dma_size * sizeof(*ring->dma),
+				  ring->dma_size * eth->soc->txrx.rxd_size,
 				  ring->dma,
 				  ring->phys);
 		ring->dma = NULL;
@@ -2455,6 +2483,7 @@
 
 static void mtk_dma_free(struct mtk_eth *eth)
 {
+	const struct mtk_soc_data *soc = eth->soc;
 	int i;
 
 	for (i = 0; i < MTK_MAC_COUNT; i++)
@@ -2462,9 +2491,8 @@
 			netdev_reset_queue(eth->netdev[i]);
 	if ( !eth->soc->has_sram && eth->scratch_ring) {
 		dma_free_coherent(eth->dev,
-				  MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
-				  eth->scratch_ring,
-				  eth->phy_scratch_ring);
+				  MTK_DMA_SIZE * soc->txrx.txd_size,
+				  eth->scratch_ring, eth->phy_scratch_ring);
 		eth->scratch_ring = NULL;
 		eth->phy_scratch_ring = 0;
 	}
@@ -3873,6 +3901,12 @@
 	.required_clks = MT7623_CLKS_BITMAP,
 	.required_pctl = true,
 	.has_sram = false,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
 };
 
 static const struct mtk_soc_data mt7621_data = {
@@ -3881,6 +3915,12 @@
 	.required_clks = MT7621_CLKS_BITMAP,
 	.required_pctl = false,
 	.has_sram = false,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
 };
 
 static const struct mtk_soc_data mt7622_data = {
@@ -3890,6 +3930,12 @@
 	.required_clks = MT7622_CLKS_BITMAP,
 	.required_pctl = false,
 	.has_sram = false,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
 };
 
 static const struct mtk_soc_data mt7623_data = {
@@ -3898,6 +3944,12 @@
 	.required_clks = MT7623_CLKS_BITMAP,
 	.required_pctl = true,
 	.has_sram = false,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
 };
 
 static const struct mtk_soc_data mt7629_data = {
@@ -3907,6 +3959,12 @@
 	.required_clks = MT7629_CLKS_BITMAP,
 	.required_pctl = false,
 	.has_sram = false,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
 };
 
 static const struct mtk_soc_data mt7986_data = {
@@ -3916,6 +3974,12 @@
 	.required_clks = MT7986_CLKS_BITMAP,
 	.required_pctl = false,
 	.has_sram = true,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma_v2),
+		.rxd_size = sizeof(struct mtk_rx_dma_v2),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
+	},
 };
 
 static const struct mtk_soc_data mt7981_data = {
@@ -3925,6 +3989,12 @@
 	.required_clks = MT7981_CLKS_BITMAP,
 	.required_pctl = false,
 	.has_sram = true,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma_v2),
+		.rxd_size = sizeof(struct mtk_rx_dma_v2),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
+	},
 };
 
 static const struct mtk_soc_data rt5350_data = {
@@ -3933,6 +4003,12 @@
 	.required_clks = MT7628_CLKS_BITMAP,
 	.required_pctl = false,
 	.has_sram = false,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
 };
 
 const struct of_device_id of_mtk_match[] = {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 88f9280..7fa0db8 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -494,25 +494,15 @@
 #define TX_DMA_FPORT_MASK_V2       0xf
 #define TX_DMA_SWC_V2              BIT(30)
 
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-#define MTK_TX_DMA_BUF_LEN      0xffff
-#define MTK_TX_DMA_BUF_SHIFT    8
-#else
 #define MTK_TX_DMA_BUF_LEN      0x3fff
+#define MTK_TX_DMA_BUF_LEN_V2   0xffff
 #define MTK_TX_DMA_BUF_SHIFT    16
-#endif
+#define MTK_TX_DMA_BUF_SHIFT_V2 8
 
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
-#define MTK_RX_DMA_BUF_LEN      0xffff
-#define MTK_RX_DMA_BUF_SHIFT    8
-#define RX_DMA_SPORT_SHIFT      26
-#define RX_DMA_SPORT_MASK       0xf
-#else
-#define MTK_RX_DMA_BUF_LEN      0x3fff
-#define MTK_RX_DMA_BUF_SHIFT    16
 #define RX_DMA_SPORT_SHIFT      19
+#define RX_DMA_SPORT_SHIFT_V2   26
 #define RX_DMA_SPORT_MASK       0x7
-#endif
+#define RX_DMA_SPORT_MASK_V2    0xf
 
 /* QDMA descriptor txd4 */
 #define TX_DMA_CHKSUM		(0x7 << 29)
@@ -524,10 +514,9 @@
 /* QDMA descriptor txd3 */
 #define TX_DMA_OWNER_CPU	BIT(31)
 #define TX_DMA_LS0		BIT(30)
-#define TX_DMA_PLEN0(_x)	(((_x) & MTK_TX_DMA_BUF_LEN) << MTK_TX_DMA_BUF_SHIFT)
-#define TX_DMA_PLEN1(_x)	((_x) & MTK_TX_DMA_BUF_LEN)
+#define TX_DMA_PLEN0(_x)	(((_x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
+#define TX_DMA_PLEN1(_x)	((_x) & eth->soc->txrx.dma_max_len)
 #define TX_DMA_SWC		BIT(14)
-#define TX_DMA_SDL(_x)		(TX_DMA_PLEN0(_x))
 
 /* PDMA on MT7628 */
 #define TX_DMA_DONE		BIT(31)
@@ -537,8 +526,8 @@
 /* QDMA descriptor rxd2 */
 #define RX_DMA_DONE		BIT(31)
 #define RX_DMA_LSO		BIT(30)
-#define RX_DMA_PLEN0(_x)	(((_x) & MTK_RX_DMA_BUF_LEN) << MTK_RX_DMA_BUF_SHIFT)
-#define RX_DMA_GET_PLEN0(_x)	(((_x) >> MTK_RX_DMA_BUF_SHIFT) & MTK_RX_DMA_BUF_LEN)
+#define RX_DMA_PLEN0(_x)	(((_x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
+#define RX_DMA_GET_PLEN0(_x)	(((_x) >> eth->soc->txrx.dma_len_offset) & eth->soc->txrx.dma_max_len)
 #define RX_DMA_GET_AGG_CNT(_x)	(((_x) >> 2) & 0xff)
 #define RX_DMA_GET_REV(_x)	(((_x) >> 10) & 0x1f)
 #define RX_DMA_VTAG		BIT(15)
@@ -554,6 +543,7 @@
 #define RX_DMA_SPECIAL_TAG	BIT(22)		/* switch header in packet */
 
 #define RX_DMA_GET_SPORT(_x) 	(((_x) >> RX_DMA_SPORT_SHIFT) & RX_DMA_SPORT_MASK)
+#define RX_DMA_GET_SPORT_V2(_x) (((_x) >> RX_DMA_SPORT_SHIFT_V2) & RX_DMA_SPORT_MASK_V2)
 
 /* PDMA V2 descriptor rxd3 */
 #define RX_DMA_VTAG_V2          BIT(0)
@@ -774,12 +764,17 @@
 	unsigned int rxd2;
 	unsigned int rxd3;
 	unsigned int rxd4;
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+} __packed __aligned(4);
+
+struct mtk_rx_dma_v2 {
+	unsigned int rxd1;
+	unsigned int rxd2;
+	unsigned int rxd3;
+	unsigned int rxd4;
 	unsigned int rxd5;
 	unsigned int rxd6;
 	unsigned int rxd7;
 	unsigned int rxd8;
-#endif
 } __packed __aligned(4);
 
 struct mtk_tx_dma {
@@ -787,12 +782,17 @@
 	unsigned int txd2;
 	unsigned int txd3;
 	unsigned int txd4;
-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+} __packed __aligned(4);
+
+struct mtk_tx_dma_v2 {
+	unsigned int txd1;
+	unsigned int txd2;
+	unsigned int txd3;
+	unsigned int txd4;
 	unsigned int txd5;
 	unsigned int txd6;
 	unsigned int txd7;
 	unsigned int txd8;
-#endif
 } __packed __aligned(4);
 
 struct mtk_eth;
@@ -951,16 +951,16 @@
  *			are present
  */
 struct mtk_tx_ring {
-	struct mtk_tx_dma *dma;
+	void *dma;
 	struct mtk_tx_buf *buf;
 	dma_addr_t phys;
-	struct mtk_tx_dma *next_free;
-	struct mtk_tx_dma *last_free;
+	void *next_free;
+	void *last_free;
 	u32 last_free_ptr;
 	u16 thresh;
 	atomic_t free_count;
 	int dma_size;
-	struct mtk_tx_dma *dma_pdma;	/* For MT7628/88 PDMA handling */
+	void *dma_pdma;	/* For MT7628/88 PDMA handling */
 	dma_addr_t phys_pdma;
 	int cpu_idx;
 };
@@ -982,7 +982,7 @@
  * @ring_no:		The index of ring
  */
 struct mtk_rx_ring {
-	struct mtk_rx_dma *dma;
+	void *dma;
 	u8 **data;
 	dma_addr_t phys;
 	u16 frag_size;
@@ -1143,6 +1143,18 @@
 			MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \
 			MTK_NETSYS_V2)
 
+struct mtk_tx_dma_desc_info {
+	dma_addr_t	addr;
+	u32		size;
+	u16		vlan_tci;
+	u16		qid;
+	u8		gso:1;
+	u8		csum:1;
+	u8		vlan:1;
+	u8		first:1;
+	u8		last:1;
+};
+
 /* struct mtk_eth_data -	This is the structure holding all differences
  *				among various plaforms
  * @ana_rgc3:                   The offset for register ANA_RGC3 related to
@@ -1153,6 +1165,10 @@
  *				the target SoC
  * @required_pctl		A bool value to show whether the SoC requires
  *				the extra setup for those pins used by GMAC.
+ * @txd_size			Tx DMA descriptor size.
+ * @rxd_size			Rx DMA descriptor size.
+ * @dma_max_len			Max DMA tx/rx buffer length.
+ * @dma_len_offset		Tx/Rx DMA length field offset.
  */
 struct mtk_soc_data {
 	u32             ana_rgc3;
@@ -1161,6 +1177,12 @@
 	bool		required_pctl;
 	netdev_features_t hw_features;
 	bool		has_sram;
+	struct {
+		u32	txd_size;
+		u32	rxd_size;
+		u32	dma_max_len;
+		u32	dma_len_offset;
+	} txrx;
 };
 
 /* currently no SoC has more than 2 macs */
@@ -1269,7 +1291,7 @@
 	struct mtk_rx_ring		rx_ring_qdma;
 	struct napi_struct		tx_napi;
 	struct mtk_napi			rx_napi[MTK_RX_NAPI_NUM];
-	struct mtk_tx_dma		*scratch_ring;
+	void				*scratch_ring;
 	struct mtk_reset_event		reset_event;
 	dma_addr_t			phy_scratch_ring;
 	void				*scratch_head;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9990-mt7622-backport-nf-hw-offload-framework-and-ups.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9990-mt7622-backport-nf-hw-offload-framework-and-ups.patch
index 6b40b56..e2cb03b 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9990-mt7622-backport-nf-hw-offload-framework-and-ups.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9990-mt7622-backport-nf-hw-offload-framework-and-ups.patch
@@ -130,35 +130,59 @@
  	for (i = 0; i < MTK_MAX_DEVS; i++) {
  		if (!eth->netdev[i])
  			continue;
-@@ -3781,6 +3801,7 @@ static const struct mtk_soc_data mt2701_data = {
+@@ -3781,12 +3801,13 @@ static const struct mtk_soc_data mt2701_data = {
  	.required_clks = MT7623_CLKS_BITMAP,
  	.required_pctl = true,
  	.has_sram = false,
 +	.offload_version = 2,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
  };
  
  static const struct mtk_soc_data mt7621_data = {
-@@ -3789,6 +3810,7 @@ static const struct mtk_soc_data mt7621_data = {
+@@ -3789,12 +3810,13 @@ static const struct mtk_soc_data mt7621_data = {
  	.required_clks = MT7621_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = false,
 +	.offload_version = 2,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
  };
  
  static const struct mtk_soc_data mt7622_data = {
-@@ -3798,6 +3820,7 @@ static const struct mtk_soc_data mt7622_data = {
+@@ -3798,12 +3820,13 @@ static const struct mtk_soc_data mt7622_data = {
  	.required_clks = MT7622_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = false,
 +	.offload_version = 2,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
  };
  
  static const struct mtk_soc_data mt7623_data = {
-@@ -3806,6 +3829,7 @@ static const struct mtk_soc_data mt7623_data = {
+@@ -3806,12 +3829,13 @@ static const struct mtk_soc_data mt7623_data = {
  	.required_clks = MT7623_CLKS_BITMAP,
  	.required_pctl = true,
  	.has_sram = false,
 +	.offload_version = 2,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma),
+		.rxd_size = sizeof(struct mtk_rx_dma),
+		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+	},
  };
  
  static const struct mtk_soc_data mt7629_data = {
@@ -6114,7 +6138,7 @@
 index 000000000..ae1eb2656
 --- /dev/null
 +++ b/net/netfilter/xt_FLOWOFFLOAD.c
-@@ -0,0 +1,719 @@
+@@ -0,0 +1,728 @@
 +/*
 + * Copyright (C) 2018-2021 Felix Fietkau <nbd@nbd.name>
 + *
@@ -6585,17 +6609,24 @@
 +				       devs[!dir]->ifindex,
 +				       devs);
 +	if (ret)
-+		return ret;
++		goto err_route_dir1;
 +
 +	if (route->tuple[dir].xmit_type	== FLOW_OFFLOAD_XMIT_NEIGH &&
 +	    route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
-+		if (nf_dev_forward_path(route, ct, dir, devs))
-+			return -1;
-+		if (nf_dev_forward_path(route, ct, !dir, devs))
-+			return -1;
++		if (nf_dev_forward_path(route, ct, dir, devs) ||
++		    nf_dev_forward_path(route, ct, !dir, devs)) {
++			ret = -1;
++			goto err_route_dir2;
++		}
 +	}
 +
 +	return 0;
++
++err_route_dir2:
++	dst_release(route->tuple[!dir].dst);
++err_route_dir1:
++	dst_release(route->tuple[dir].dst);
++	return ret;
 +}
 +
 +static unsigned int
@@ -6680,6 +6711,7 @@
 +	xt_flowoffload_check_device(table, devs[0]);
 +	xt_flowoffload_check_device(table, devs[1]);
 +
++	dst_release(route.tuple[dir].dst);
 +	dst_release(route.tuple[!dir].dst);
 +
 +	return XT_CONTINUE;
@@ -6687,6 +6719,7 @@
 +err_flow_add:
 +	flow_offload_free(flow);
 +err_flow_alloc:
++	dst_release(route.tuple[dir].dst);
 +	dst_release(route.tuple[!dir].dst);
 +err_flow_route:
 +	clear_bit(IPS_OFFLOAD_BIT, &ct->status);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9993-add-wed.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9993-add-wed.patch
index 42a9077..66c497d 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9993-add-wed.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9993-add-wed.patch
@@ -168,7 +168,7 @@
  	if (!eth->soc->has_sram) {
 -		eth->scratch_ring = dma_alloc_coherent(eth->dev,
 +		eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
- 					       cnt * sizeof(struct mtk_tx_dma),
+ 					       cnt * soc->txrx.txd_size,
  					       &eth->phy_scratch_ring,
  					       GFP_ATOMIC);
 @@ -866,10 +869,10 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
@@ -219,33 +219,33 @@
  	itx_buf = mtk_desc_to_tx_buf(ring, itxd);
  	memset(itx_buf, 0, sizeof(*itx_buf));
  
--	mapped_addr = dma_map_single(eth->dev, skb->data,
-+	mapped_addr = dma_map_single(eth->dma_dev, skb->data,
- 				     skb_headlen(skb), DMA_TO_DEVICE);
--	if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
-+	if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
+-	txd_info.addr = dma_map_single(eth->dev, skb->data, txd_info.size,
++	txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size,
+ 				       DMA_TO_DEVICE);
+-	if (unlikely(dma_mapping_error(eth->dev, txd_info.addr)))
++	if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr)))
  		return -ENOMEM;
  
  	WRITE_ONCE(itxd->txd1, mapped_addr);
 @@ -1114,10 +1117,10 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
- 
+ 			txd_info.qid = skb->mark & MTK_QDMA_TX_MASK;
+ 			txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 &&
+ 					!(frag_size - txd_info.size);
+-			txd_info.addr = skb_frag_dma_map(eth->dev, frag,
++			txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag,
+ 							 offset, txd_info.size,
+ 							 DMA_TO_DEVICE);
+-			if (unlikely(dma_mapping_error(eth->dev, txd_info.addr)))
++			if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr)))
+  				goto err_dma;
  
- 			frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN);
--			mapped_addr = skb_frag_dma_map(eth->dev, frag, offset,
-+			mapped_addr = skb_frag_dma_map(eth->dma_dev, frag, offset,
- 						       frag_map_size,
- 						       DMA_TO_DEVICE);
--			if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
-+			if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
- 				goto err_dma;
- 
- 			if (i == nr_frags - 1 &&
+ 			mtk_tx_set_dma_desc(skb, dev, txd, &txd_info);
 @@ -1384,6 +1387,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
  		struct net_device *netdev;
  		unsigned int pktlen;
  		dma_addr_t dma_addr;
 +		u32 hash, reason;
- 		int mac;
+ 		int mac = 0;
  
  		if (eth->hwlro)
 @@ -1427,18 +1431,18 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
@@ -294,49 +294,48 @@
  	if (!eth->soc->has_sram)
 -		ring->dma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
 +		ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
- 					       &ring->phys, GFP_ATOMIC);
+ 					       &ring->phys, GFP_KERNEL);
  	else {
  		ring->dma =  eth->scratch_ring + MTK_DMA_SIZE;
-@@ -1780,7 +1795,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
+@@ -1780,6 +1795,6 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
  	 * descriptors in ring->dma_pdma.
  	 */
  	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
 -		ring->dma_pdma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
 +		ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
- 						    &ring->phys_pdma,
- 						    GFP_ATOMIC);
+ 						    &ring->phys_pdma, GFP_KERNEL);
  		if (!ring->dma_pdma)
-@@ -1839,7 +1854,7 @@ static void mtk_tx_clean(struct mtk_eth *eth)
+@@ -1839,6 +1854,6 @@ static void mtk_tx_clean(struct mtk_eth *eth)
  	}
  
  	if (!eth->soc->has_sram && ring->dma) {
 -		dma_free_coherent(eth->dev,
 +		dma_free_coherent(eth->dma_dev,
- 				  MTK_DMA_SIZE * sizeof(*ring->dma),
- 				  ring->dma,
- 				  ring->phys);
-@@ -1847,7 +1862,7 @@ static void mtk_tx_clean(struct mtk_eth *eth)
+ 				  MTK_DMA_SIZE * soc->txrx.txd_size,
+ 				  ring->dma, ring->phys);
+@@ -1847,6 +1862,6 @@ static void mtk_tx_clean(struct mtk_eth *eth)
  	}
  
  	if (ring->dma_pdma) {
 -		dma_free_coherent(eth->dev,
 +		dma_free_coherent(eth->dma_dev,
- 				  MTK_DMA_SIZE * sizeof(*ring->dma_pdma),
- 				  ring->dma_pdma,
- 				  ring->phys_pdma);
+ 				  MTK_DMA_SIZE * soc->txrx.txd_size,
+ 				  ring->dma_pdma, ring->phys_pdma);
 @@ -1892,7 +1907,7 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
  
  	if ((!eth->soc->has_sram) || (eth->soc->has_sram
  				&& (rx_flag != MTK_RX_FLAGS_NORMAL)))
 -		ring->dma = dma_alloc_coherent(eth->dev,
 +		ring->dma = dma_alloc_coherent(eth->dma_dev,
- 					       rx_dma_size * sizeof(*ring->dma),
- 					       &ring->phys, GFP_ATOMIC);
+ 					       rx_dma_size * eth->soc->txrx.rxd_size,
+ 					       &ring->phys, GFP_KERNEL);
  	else {
-@@ -1907,11 +1922,11 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
+@@ -1907,13 +1922,13 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
  		return -ENOMEM;
  
  	for (i = 0; i < rx_dma_size; i++) {
+ 		struct mtk_rx_dma_v2 *rxd;
+
 -		dma_addr_t dma_addr = dma_map_single(eth->dev,
 +		dma_addr_t dma_addr = dma_map_single(eth->dma_dev,
  				ring->data[i] + NET_SKB_PAD + eth->ip_align,
@@ -345,15 +344,16 @@
 -		if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
 +		if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
  			return -ENOMEM;
- 		ring->dma[i].rxd1 = (unsigned int)dma_addr;
- 
-@@ -1968,7 +1983,7 @@ static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, int in_s
+
+ 		rxd = ring->dma + i * eth->soc->txrx.rxd_size;
+@@ -1968,8 +1983,8 @@ static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, int in_s
+ 			rxd = ring->dma + i * eth->soc->txrx.rxd_size;
+ 			if (!rxd->rxd1)
  				continue;
- 			if (!ring->dma[i].rxd1)
- 				continue;
+
 -			dma_unmap_single(eth->dev,
 +			dma_unmap_single(eth->dma_dev,
- 					 ring->dma[i].rxd1,
+ 					 rxd->rxd1,
  					 ring->buf_size,
  					 DMA_FROM_DEVICE);
 @@ -1982,7 +1997,7 @@ static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, int in_s
@@ -362,18 +362,17 @@
  	if (ring->dma) {
 -		dma_free_coherent(eth->dev,
 +		dma_free_coherent(eth->dma_dev,
- 				  ring->dma_size * sizeof(*ring->dma),
+ 				  ring->dma_size * eth->soc->txrx.rxd_size,
  				  ring->dma,
  				  ring->phys);
-@@ -2462,7 +2477,7 @@ static void mtk_dma_free(struct mtk_eth *eth)
+@@ -2462,6 +2477,6 @@ static void mtk_dma_free(struct mtk_eth *eth)
  		if (eth->netdev[i])
  			netdev_reset_queue(eth->netdev[i]);
  	if ( !eth->soc->has_sram && eth->scratch_ring) {
 -		dma_free_coherent(eth->dev,
 +		dma_free_coherent(eth->dma_dev,
- 				  MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
- 				  eth->scratch_ring,
- 				  eth->phy_scratch_ring);
+ 				  MTK_DMA_SIZE * soc->txrx.txd_size,
+ 				  eth->scratch_ring, eth->phy_scratch_ring);
 @@ -2661,7 +2676,7 @@ static int mtk_open(struct net_device *dev)
  		if (err)
  			return err;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9994-ethernet-update-ppe-from-mt7622-to-mt7986.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9994-ethernet-update-ppe-from-mt7622-to-mt7986.patch
index c2564ba..6a4766d 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9994-ethernet-update-ppe-from-mt7622-to-mt7986.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9994-ethernet-update-ppe-from-mt7622-to-mt7986.patch
@@ -46,11 +46,17 @@
  
  		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
  			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-@@ -3926,6 +3937,7 @@ static const struct mtk_soc_data mt7986_data = {
+@@ -3926,12 +3937,13 @@ static const struct mtk_soc_data mt7986_data = {
  	.required_clks = MT7986_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = true,
 +	.offload_version = 2,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma_v2),
+ 		.rxd_size = sizeof(struct mtk_rx_dma_v2),
+ 		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+ 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
+ 	},
  };
  
  static const struct mtk_soc_data mt7981_data = {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9996-add-wed-tx-support-for-mt7986.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9996-add-wed-tx-support-for-mt7986.patch
index 1ba2c2b..16a2b27 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9996-add-wed-tx-support-for-mt7986.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9996-add-wed-tx-support-for-mt7986.patch
@@ -1252,19 +1252,15 @@
  
  	struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES];
  	struct mtk_wed_ring txfree_ring;
-@@ -43,8 +57,19 @@ struct mtk_wed_device {
+@@ -43,8 +57,17 @@ struct mtk_wed_device {
  	/* filled by driver: */
  	struct {
  		struct pci_dev *pci_dev;
--
--		u32 wpdma_phys;
 +		void __iomem *base;
 +		u32 bus_type;
-+
-+		union {
-+			u32 wpdma_phys;
-+			u32 wpdma_int;
-+		};
+ 
+ 		u32 wpdma_phys;
++		u32 wpdma_int;
 +		u32 wpdma_mask;
 +		u32 wpdma_tx;
 +		u32 wpdma_txfree;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9997-add-wed-rx-support-for-mt7896.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9997-add-wed-rx-support-for-mt7896.patch
index bc87d67..3df0ab7 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9997-add-wed-rx-support-for-mt7896.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9997-add-wed-rx-support-for-mt7896.patch
@@ -8,16 +8,16 @@
  arch/arm64/boot/dts/mediatek/mt7986a.dtsi     |  42 +-
  arch/arm64/boot/dts/mediatek/mt7986b.dtsi     |  42 +-
  drivers/net/ethernet/mediatek/Makefile        |   2 +-
- drivers/net/ethernet/mediatek/mtk_wed.c       | 544 +++++++++++++++--
- drivers/net/ethernet/mediatek/mtk_wed.h       |  50 ++
- drivers/net/ethernet/mediatek/mtk_wed_ccif.c  | 121 ++++
+ drivers/net/ethernet/mediatek/mtk_wed.c       | 613 ++++++++++++++++--
+ drivers/net/ethernet/mediatek/mtk_wed.h       |  51 ++
+ drivers/net/ethernet/mediatek/mtk_wed_ccif.c  | 133 ++++
  drivers/net/ethernet/mediatek/mtk_wed_ccif.h  |  45 ++
  .../net/ethernet/mediatek/mtk_wed_debugfs.c   |  90 +++
- drivers/net/ethernet/mediatek/mtk_wed_mcu.c   | 561 ++++++++++++++++++
+ drivers/net/ethernet/mediatek/mtk_wed_mcu.c   | 561 ++++++++++++++++
  drivers/net/ethernet/mediatek/mtk_wed_mcu.h   | 125 ++++
- drivers/net/ethernet/mediatek/mtk_wed_regs.h  | 145 ++++-
- drivers/net/ethernet/mediatek/mtk_wed_wo.c    | 548 +++++++++++++++++
- drivers/net/ethernet/mediatek/mtk_wed_wo.h    | 334 +++++++++++
+ drivers/net/ethernet/mediatek/mtk_wed_regs.h  | 147 ++++-
+ drivers/net/ethernet/mediatek/mtk_wed_wo.c    | 588 +++++++++++++++++
+ drivers/net/ethernet/mediatek/mtk_wed_wo.h    | 336 ++++++++++
  include/linux/soc/mediatek/mtk_wed.h          |  63 +-
  14 files changed, 2643 insertions(+), 69 deletions(-)
  mode change 100644 => 100755 drivers/net/ethernet/mediatek/mtk_wed.c
@@ -171,7 +171,7 @@
  		resets = <&ethsysrst 0>;
  		reset-names = "wocpu_rst";
 diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
-index 3528f1b3c..0c724a55c 100644
+index 3528f1b..0c724a5 100644
 --- a/drivers/net/ethernet/mediatek/Makefile
 +++ b/drivers/net/ethernet/mediatek/Makefile
 @@ -10,5 +10,5 @@ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o
@@ -182,9 +182,7 @@
 +obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o mtk_wed_wo.o mtk_wed_mcu.o mtk_wed_ccif.o
  obj-$(CONFIG_NET_MEDIATEK_HNAT)			+= mtk_hnat/
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
-old mode 100644
-new mode 100755
-index 48b0353bb..c4aab12b0
+index 48b0353..4d47b3a 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 @@ -13,11 +13,19 @@
@@ -208,7 +206,7 @@
  static struct mtk_wed_hw *hw_list[2];
  static DEFINE_MUTEX(hw_lock);
  
-@@ -51,6 +59,12 @@ wdma_set(struct mtk_wed_device *dev, u32 reg, u32 mask)
+@@ -51,6 +59,56 @@ wdma_set(struct mtk_wed_device *dev, u32 reg, u32 mask)
  	wdma_m32(dev, reg, 0, mask);
  }
  
@@ -218,10 +216,54 @@
 +	wdma_m32(dev, reg, mask, 0);
 +}
 +
++static u32
++mtk_wdma_read_reset(struct mtk_wed_device *dev)
++{
++	return wdma_r32(dev, MTK_WDMA_GLO_CFG);
++}
++
++static void
++mtk_wdma_rx_reset(struct mtk_wed_device *dev)
++{
++	u32 status;
++	u32 mask = MTK_WDMA_GLO_CFG_RX_DMA_BUSY;
++	int i;
++
++	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_DMA_EN);
++	if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
++			       !(status & mask), 0, 1000))
++		WARN_ON_ONCE(1);
++
++	for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++)
++		if (!dev->rx_wdma[i].desc) {
++			wdma_w32(dev, MTK_WDMA_RING_RX(i) +
++				 MTK_WED_RING_OFS_CPU_IDX, 0);
++	}
++}
++
++static void
++mtk_wdma_tx_reset(struct mtk_wed_device *dev)
++{
++	u32 status;
++	u32 mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY;
++	int i;
++
++	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
++	if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
++			       !(status & mask), 0, 1000))
++		WARN_ON_ONCE(1);
++
++	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
++		if (!dev->tx_wdma[i].desc) {
++			wdma_w32(dev, MTK_WDMA_RING_TX(i) +
++				 MTK_WED_RING_OFS_CPU_IDX, 0);
++	}
++}
++
  static u32
  mtk_wed_read_reset(struct mtk_wed_device *dev)
  {
-@@ -68,6 +82,48 @@ mtk_wed_reset(struct mtk_wed_device *dev, u32 mask)
+@@ -68,6 +126,52 @@ mtk_wed_reset(struct mtk_wed_device *dev, u32 mask)
  		WARN_ON_ONCE(1);
  }
  
@@ -235,6 +277,10 @@
 +	u32 value;
 +	unsigned long timeout = jiffies + WOCPU_TIMEOUT;
 +
++	mtk_wdma_rx_reset(dev);
++
++	mtk_wed_reset(dev, MTK_WED_RESET_WED);
++
 +	mtk_wed_mcu_send_msg(wo, MODULE_ID_WO, WO_CMD_CHANGE_STATE,
 +			     &state, sizeof(state), false);
 +
@@ -270,7 +316,30 @@
  static struct mtk_wed_hw *
  mtk_wed_assign(struct mtk_wed_device *dev)
  {
-@@ -205,6 +261,42 @@ free_pagelist:
+@@ -178,7 +282,7 @@ mtk_wed_free_buffer(struct mtk_wed_device *dev)
+ {
+ 	struct mtk_wdma_desc *desc = dev->buf_ring.desc;
+ 	void **page_list = dev->buf_ring.pages;
+-	int page_idx;
++	int ring_size, page_idx;
+ 	int i;
+ 
+ 	if (!page_list)
+@@ -187,6 +291,13 @@ mtk_wed_free_buffer(struct mtk_wed_device *dev)
+ 	if (!desc)
+ 		goto free_pagelist;
+ 
++	if (dev->ver == MTK_WED_V1) {
++		ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
++	} else {
++		ring_size = MTK_WED_VLD_GROUP_SIZE * MTK_WED_PER_GROUP_PKT +
++			    MTK_WED_WDMA_RING_SIZE * 2;
++	}
++
+ 	for (i = 0, page_idx = 0; i < dev->buf_ring.size; i += MTK_WED_BUF_PER_PAGE) {
+ 		void *page = page_list[page_idx++];
+ 
+@@ -205,6 +316,42 @@ free_pagelist:
  	kfree(page_list);
  }
  
@@ -299,21 +368,21 @@
 +mtk_wed_free_rx_bm(struct mtk_wed_device *dev)
 +{
 +	struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc;
-+	int ring_size =dev->rx_buf_ring.size;
++	int ring_size = dev->rx_buf_ring.size;
 +
 +	if (!desc)
 +		return;
 +
 +	dev->wlan.release_rx_buf(dev);
 +
-+	dma_free_coherent(dev->hw->dev, ring_size * sizeof(*desc),
-+			  desc, dev->buf_ring.desc_phys);
++	/* dma_free_coherent(dev->hw->dev, ring_size * sizeof(*desc),
++			  desc, dev->buf_ring.desc_phys); */
 +}
 +
  static void
  mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, int scale)
  {
-@@ -226,13 +318,22 @@ mtk_wed_free_tx_rings(struct mtk_wed_device *dev)
+@@ -226,13 +373,22 @@ mtk_wed_free_tx_rings(struct mtk_wed_device *dev)
  		mtk_wed_free_ring(dev, &dev->tx_wdma[i], dev->ver);
  }
  
@@ -337,7 +406,7 @@
  	/* wed control cr set */
  	wed_set(dev, MTK_WED_CTRL,
  		MTK_WED_CTRL_WDMA_INT_AGENT_EN |
-@@ -251,7 +352,7 @@ mtk_wed_set_int(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -251,7 +407,7 @@ mtk_wed_set_int(struct mtk_wed_device *dev, u32 irq_mask)
  		wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
  			MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
  	} else {
@@ -346,7 +415,7 @@
  		wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
  			MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
  			MTK_WED_WPDMA_INT_CTRL_TX0_DONE_CLR |
-@@ -262,22 +363,30 @@ mtk_wed_set_int(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -262,22 +418,30 @@ mtk_wed_set_int(struct mtk_wed_device *dev, u32 irq_mask)
  			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG,
  				   dev->wlan.tx_tbit[1]));
  
@@ -381,7 +450,7 @@
  	}
  
  	wdma_w32(dev, MTK_WDMA_INT_MASK, wdma_mask);
-@@ -312,6 +421,39 @@ mtk_wed_set_512_support(struct mtk_wed_device *dev, bool en)
+@@ -312,6 +476,39 @@ mtk_wed_set_512_support(struct mtk_wed_device *dev, bool en)
  	}
  }
  
@@ -421,7 +490,7 @@
  static void
  mtk_wed_dma_enable(struct mtk_wed_device *dev)
  {
-@@ -336,9 +478,14 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev)
+@@ -336,9 +533,14 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev)
  		wdma_set(dev, MTK_WDMA_GLO_CFG,
  			 MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
  	} else {
@@ -436,7 +505,7 @@
  		wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
  			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
  			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
-@@ -346,6 +493,15 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev)
+@@ -346,6 +548,15 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev)
  		wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
  			MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
  			MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
@@ -452,7 +521,7 @@
  	}
  }
  
-@@ -363,19 +519,23 @@ mtk_wed_dma_disable(struct mtk_wed_device *dev)
+@@ -363,19 +574,23 @@ mtk_wed_dma_disable(struct mtk_wed_device *dev)
  		MTK_WED_GLO_CFG_TX_DMA_EN |
  		MTK_WED_GLO_CFG_RX_DMA_EN);
  
@@ -480,7 +549,20 @@
  	}
  }
  
+@@ -384,8 +599,11 @@ mtk_wed_stop(struct mtk_wed_device *dev)
+ {
+ 	mtk_wed_dma_disable(dev);
+ 
-@@ -395,6 +555,11 @@ mtk_wed_stop(struct mtk_wed_device *dev)
+-	if (dev->ver > MTK_WED_V1)
++	if (dev->ver > MTK_WED_V1) {
+ 		mtk_wed_set_512_support(dev, false);
++		wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
++		wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0);
++	}
+ 
+ 	mtk_wed_set_ext_int(dev, false);
+ 
+@@ -395,6 +613,11 @@ mtk_wed_stop(struct mtk_wed_device *dev)
  		MTK_WED_CTRL_WED_TX_BM_EN |
  		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
  
@@ -492,25 +574,27 @@
  	wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0);
  	wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0);
  	wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
-@@ -416,9 +581,17 @@ mtk_wed_detach(struct mtk_wed_device *dev)
- 	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
+@@ -417,8 +640,19 @@ mtk_wed_detach(struct mtk_wed_device *dev)
  
  	mtk_wed_reset(dev, MTK_WED_RESET_WED);
-+	if (dev->ver > MTK_WED_V1)
-+		mtk_wed_wo_reset(dev);
-+
+ 
 +	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
 +	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
 +	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
- 
++
  	mtk_wed_free_buffer(dev);
  	mtk_wed_free_tx_rings(dev);
-+	if (dev->ver > MTK_WED_V1)
++	if (dev->ver > MTK_WED_V1) {
++		mtk_wed_wo_reset(dev);
 +		mtk_wed_free_rx_rings(dev);
++		mtk_wed_wo_exit(hw);
++	}
++
++	mtk_wdma_tx_reset(dev);
  
  	if (dev->wlan.bus_type == MTK_BUS_TYPE_PCIE) {
  		wlan_node = dev->wlan.pci_dev->dev.of_node;
-@@ -477,7 +650,6 @@ mtk_wed_bus_init(struct mtk_wed_device *dev)
+@@ -477,7 +711,6 @@ mtk_wed_bus_init(struct mtk_wed_device *dev)
  		value = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
  		value = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
  
@@ -518,7 +602,7 @@
  		wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
  		wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER);
  
-@@ -501,6 +673,9 @@ mtk_wed_set_wpdma(struct mtk_wed_device *dev)
+@@ -501,6 +734,9 @@ mtk_wed_set_wpdma(struct mtk_wed_device *dev)
  		wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK,  dev->wlan.wpdma_mask);
  		wed_w32(dev, MTK_WED_WPDMA_CFG_TX,  dev->wlan.wpdma_tx);
  		wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE,  dev->wlan.wpdma_txfree);
@@ -528,12 +612,12 @@
  	} else {
  		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys);
  	}
-@@ -549,24 +722,92 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev)
+@@ -549,24 +785,92 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev)
  			FIELD_PREP(MTK_WED_WDMA_OFST1_RX_CTRL,
  				   MTK_WDMA_RING_RX(0)));
  	}
 +}
-+
+ 
 +static void
 +mtk_wed_rx_bm_hw_init(struct mtk_wed_device *dev)
 +{
@@ -541,7 +625,7 @@
 +		FIELD_PREP(MTK_WED_RX_BM_RX_DMAD_SDL0,  dev->wlan.rx_pkt_size));
 +
 +	wed_w32(dev, MTK_WED_RX_BM_BASE, dev->rx_buf_ring.desc_phys);
- 
++
 +	wed_w32(dev, MTK_WED_RX_BM_INIT_PTR, MTK_WED_RX_BM_INIT_SW_TAIL |
 +		FIELD_PREP(MTK_WED_RX_BM_SW_TAIL, dev->wlan.rx_pkt));
 +
@@ -629,13 +713,13 @@
  		rev_size = size;
  		thr = 0;
  	}
-@@ -609,13 +852,48 @@ mtk_wed_hw_init(struct mtk_wed_device *dev)
+@@ -609,13 +913,48 @@ mtk_wed_hw_init(struct mtk_wed_device *dev)
  }
  
  static void
 -mtk_wed_ring_reset(struct mtk_wdma_desc *desc, int size, int scale)
 +mtk_wed_rx_hw_init(struct mtk_wed_device *dev)
- {
++{
 +	wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
 +		MTK_WED_WPDMA_RX_D_RST_CRX_IDX0 |
 +		MTK_WED_WPDMA_RX_D_RST_CRX_IDX1 |
@@ -664,7 +748,7 @@
 +
 +static void
 +mtk_wed_ring_reset(struct mtk_wdma_desc *desc, int size, int scale, bool tx)
-+{
+ {
 +	__le32 ctrl;
  	int i;
  
@@ -680,7 +764,7 @@
  		desc->buf1 = 0;
  		desc->info = 0;
  		desc += scale;
-@@ -674,7 +952,7 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+@@ -674,7 +1013,7 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
  		if (!desc)
  			continue;
  
@@ -689,7 +773,7 @@
  	}
  
  	if (mtk_wed_poll_busy(dev))
-@@ -729,9 +1007,24 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+@@ -729,9 +1068,24 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
  
  }
  
@@ -715,7 +799,7 @@
  {
  	ring->desc = dma_alloc_coherent(dev->hw->dev,
  					size * sizeof(*ring->desc) * scale,
-@@ -740,17 +1033,18 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
+@@ -740,17 +1094,18 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
  		return -ENOMEM;
  
  	ring->size = size;
@@ -737,7 +821,7 @@
  		return -ENOMEM;
  
  	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
-@@ -767,22 +1061,143 @@ mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size)
+@@ -767,22 +1122,143 @@ mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size)
  	return 0;
  }
  
@@ -887,7 +971,7 @@
  	mtk_wed_set_ext_int(dev, true);
  
  	if (dev->ver == MTK_WED_V1) {
-@@ -797,6 +1212,19 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -797,6 +1273,19 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
  		val |= BIT(0);
  		regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
  	} else {
@@ -907,7 +991,7 @@
  		mtk_wed_set_512_support(dev, true);
  	}
  
-@@ -841,9 +1269,17 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -841,9 +1330,17 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  			    wed_r32(dev, MTK_WED_REV_ID));
  
  	ret = mtk_wed_buffer_alloc(dev);
@@ -928,7 +1012,7 @@
  	}
  
  	mtk_wed_hw_init_early(dev);
-@@ -851,7 +1287,12 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -851,7 +1348,12 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  	if (dev->ver == MTK_WED_V1)
  		regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
  				   BIT(hw->index), 0);
@@ -941,7 +1025,7 @@
  out:
  	mutex_unlock(&hw_lock);
  
-@@ -877,10 +1318,10 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
+@@ -877,10 +1379,10 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
  
  	BUG_ON(idx > ARRAY_SIZE(dev->tx_ring));
  
@@ -954,7 +1038,7 @@
  		return -ENOMEM;
  
  	ring->reg_base = MTK_WED_RING_TX(idx);
-@@ -927,6 +1368,35 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
+@@ -927,6 +1429,35 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
  	return 0;
  }
  
@@ -990,7 +1074,7 @@
  static u32
  mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
  {
-@@ -1014,6 +1484,8 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1014,6 +1545,8 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  		.attach = mtk_wed_attach,
  		.tx_ring_setup = mtk_wed_tx_ring_setup,
  		.txfree_ring_setup = mtk_wed_txfree_ring_setup,
@@ -999,7 +1083,7 @@
  		.start = mtk_wed_start,
  		.stop = mtk_wed_stop,
  		.reset_dma = mtk_wed_reset_dma,
-@@ -1022,6 +1494,7 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1022,6 +1555,7 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  		.irq_get = mtk_wed_irq_get,
  		.irq_set_mask = mtk_wed_irq_set_mask,
  		.detach = mtk_wed_detach,
@@ -1008,7 +1092,7 @@
  	struct device_node *eth_np = eth->dev->of_node;
  	struct platform_device *pdev;
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed.h b/drivers/net/ethernet/mediatek/mtk_wed.h
-index 9b17b7405..ec79b0d42 100644
+index 9b17b74..8ef5253 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed.h
 +++ b/drivers/net/ethernet/mediatek/mtk_wed.h
 @@ -13,6 +13,7 @@
@@ -1093,11 +1177,12 @@
  void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  		    void __iomem *wdma, u32 wdma_phy, int index);
  void mtk_wed_exit(void);
-@@ -146,4 +185,15 @@ static inline void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw)
+@@ -146,4 +185,16 @@ static inline void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw)
  }
  #endif
  
 +int wed_wo_hardware_init(struct mtk_wed_wo *wo, irq_handler_t isr);
++void wed_wo_hardware_exit(struct mtk_wed_wo *wo);
 +int wed_wo_mcu_init(struct mtk_wed_wo *wo);
 +int mtk_wed_exception_init(struct mtk_wed_wo *wo);
 +void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo, struct sk_buff *skb);
@@ -1111,10 +1196,10 @@
  #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_ccif.c b/drivers/net/ethernet/mediatek/mtk_wed_ccif.c
 new file mode 100644
-index 000000000..732ffc8cf
+index 0000000..22ef337
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_ccif.c
-@@ -0,0 +1,121 @@
+@@ -0,0 +1,133 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +
 +#include <linux/soc/mediatek/mtk_wed.h>
@@ -1228,17 +1313,29 @@
 +	return 0;
 +
 +free_irq:
-+	devm_free_irq(wo->hw->dev, wo->ccif.irq, wo);
++	free_irq(wo->ccif.irq, wo);
 +
 +	return ret;
 +}
 +
-+static void wed_wo_hardware_exit(struct mtk_wed_wo *wo)
++void wed_wo_hardware_exit(struct mtk_wed_wo *wo)
 +{
++	wo->drv_ops->set_isr(wo, 0);
++
++	disable_irq(wo->ccif.irq);
++	free_irq(wo->ccif.irq, wo);
++
++	tasklet_disable(&wo->irq_tasklet);
++	netif_napi_del(&wo->napi);
++
++	mtk_wed_wo_q_tx_clean(wo, &wo->q_tx, true);
++	mtk_wed_wo_q_rx_clean(wo, &wo->q_rx);
++	mtk_wed_wo_q_free(wo, &wo->q_tx);
++	mtk_wed_wo_q_free(wo, &wo->q_rx);
 +}
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_ccif.h b/drivers/net/ethernet/mediatek/mtk_wed_ccif.h
 new file mode 100644
-index 000000000..68ade449c
+index 0000000..68ade44
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_ccif.h
 @@ -0,0 +1,45 @@
@@ -1288,7 +1385,7 @@
 +
 +#endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-index f420f187e..fea7ae2fc 100644
+index f420f18..fea7ae2 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 @@ -2,6 +2,7 @@
@@ -1430,7 +1527,7 @@
  }
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 new file mode 100644
-index 000000000..bd1ab9500
+index 0000000..bd1ab95
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 @@ -0,0 +1,561 @@
@@ -1997,7 +2094,7 @@
 +
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.h b/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
 new file mode 100644
-index 000000000..6a5ac7672
+index 0000000..6a5ac76
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
 @@ -0,0 +1,125 @@
@@ -2127,7 +2224,7 @@
 +
 +#endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-index 69f136ed4..e911b5315 100644
+index e107de7..64a2483 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 @@ -4,6 +4,8 @@
@@ -2147,7 +2244,7 @@
  
  struct mtk_wdma_desc {
  	__le32 buf0;
-@@ -37,6 +40,8 @@ struct mtk_wdma_desc {
+@@ -41,6 +44,8 @@ struct mtk_wdma_desc {
  #define MTK_WED_RESET_WED_TX_DMA			BIT(12)
  #define MTK_WED_RESET_WDMA_RX_DRV			BIT(17)
  #define MTK_WED_RESET_WDMA_INT_AGENT			BIT(19)
@@ -2156,7 +2253,7 @@
  #define MTK_WED_RESET_WED				BIT(31)
  
  #define MTK_WED_CTRL					0x00c
-@@ -48,8 +53,12 @@ struct mtk_wdma_desc {
+@@ -52,8 +57,12 @@ struct mtk_wdma_desc {
  #define MTK_WED_CTRL_WED_TX_BM_BUSY			BIT(9)
  #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN		BIT(10)
  #define MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY		BIT(11)
@@ -2171,7 +2268,7 @@
  #define MTK_WED_CTRL_FINAL_DIDX_READ			BIT(24)
  #define MTK_WED_CTRL_ETH_DMAD_FMT			BIT(25)
  #define MTK_WED_CTRL_MIB_READ_CLEAR			BIT(28)
-@@ -64,8 +73,8 @@ struct mtk_wdma_desc {
+@@ -68,8 +77,8 @@ struct mtk_wdma_desc {
  #define MTK_WED_EXT_INT_STATUS_TX_TKID_LO_TH		BIT(10)
  #define MTK_WED_EXT_INT_STATUS_TX_TKID_HI_TH		BIT(11)
  #endif
@@ -2182,7 +2279,7 @@
  #define MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR	BIT(16)
  #define MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR	BIT(17)
  #define MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT		BIT(18)
-@@ -82,8 +91,8 @@ struct mtk_wdma_desc {
+@@ -86,8 +95,8 @@ struct mtk_wdma_desc {
  #define MTK_WED_EXT_INT_STATUS_ERROR_MASK		(MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \
  							 MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \
  							 MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID | \
@@ -2193,7 +2290,7 @@
  							 MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR | \
  							 MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR | \
  							 MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | \
-@@ -92,6 +101,8 @@ struct mtk_wdma_desc {
+@@ -96,6 +105,8 @@ struct mtk_wdma_desc {
  							 MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR)
  
  #define MTK_WED_EXT_INT_MASK				0x028
@@ -2202,7 +2299,7 @@
  
  #define MTK_WED_STATUS					0x060
  #define MTK_WED_STATUS_TX				GENMASK(15, 8)
-@@ -179,6 +190,9 @@ struct mtk_wdma_desc {
+@@ -183,6 +194,9 @@ struct mtk_wdma_desc {
  
  #define MTK_WED_RING_RX(_n)				(0x400 + (_n) * 0x10)
  
@@ -2212,7 +2309,7 @@
  #define MTK_WED_WPDMA_INT_TRIGGER			0x504
  #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE		BIT(1)
  #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE		GENMASK(5, 4)
-@@ -235,13 +249,19 @@ struct mtk_wdma_desc {
+@@ -239,13 +253,19 @@ struct mtk_wdma_desc {
  
  #define MTK_WED_WPDMA_INT_CTRL_TX			0x530
  #define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN 		BIT(0)
@@ -2233,7 +2330,7 @@
  
  #define MTK_WED_WPDMA_INT_CTRL_TX_FREE			0x538
  #define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN		BIT(0)
-@@ -266,13 +286,43 @@ struct mtk_wdma_desc {
+@@ -270,13 +290,43 @@ struct mtk_wdma_desc {
  #define MTK_WED_WPDMA_TX_MIB(_n)			(0x5a0 + (_n) * 4)
  #define MTK_WED_WPDMA_TX_COHERENT_MIB(_n)		(0x5d0 + (_n) * 4)
  
@@ -2277,7 +2374,7 @@
  #define MTK_WED_WDMA_GLO_CFG_RX_DRV_EN			BIT(2)
  #define MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY		BIT(3)
  #define MTK_WED_WDMA_GLO_CFG_BT_SIZE			GENMASK(5, 4)
-@@ -316,6 +366,20 @@ struct mtk_wdma_desc {
+@@ -320,6 +370,20 @@ struct mtk_wdma_desc {
  #define MTK_WED_WDMA_RX_RECYCLE_MIB(_n)			(0xae8 + (_n) * 4)
  #define MTK_WED_WDMA_RX_PROCESSED_MIB(_n)		(0xaf0 + (_n) * 4)
  
@@ -2298,7 +2395,17 @@
  #define MTK_WED_RING_OFS_BASE				0x00
  #define MTK_WED_RING_OFS_COUNT				0x04
  #define MTK_WED_RING_OFS_CPU_IDX			0x08
-@@ -355,4 +419,71 @@ struct mtk_wdma_desc {
+@@ -330,7 +394,9 @@ struct mtk_wdma_desc {
+ 
+ #define MTK_WDMA_GLO_CFG				0x204
+ #define MTK_WDMA_GLO_CFG_TX_DMA_EN			BIT(0)
++#define MTK_WDMA_GLO_CFG_TX_DMA_BUSY			BIT(1)
+ #define MTK_WDMA_GLO_CFG_RX_DMA_EN			BIT(2)
++#define MTK_WDMA_GLO_CFG_RX_DMA_BUSY			BIT(3)
+ #define MTK_WDMA_GLO_CFG_RX_INFO3_PRERES		BIT(26)
+ #define MTK_WDMA_GLO_CFG_RX_INFO2_PRERES		BIT(27)
+ #define MTK_WDMA_GLO_CFG_RX_INFO1_PRERES		BIT(28)
+@@ -359,4 +425,71 @@ struct mtk_wdma_desc {
  /* DMA channel mapping */
  #define HIFSYS_DMA_AG_MAP				0x008
  
@@ -2372,10 +2479,10 @@
  #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.c b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
 new file mode 100644
-index 000000000..10618fc1a
+index 0000000..e101f17
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-@@ -0,0 +1,548 @@
+@@ -0,0 +1,588 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +
 +#include <linux/kernel.h>
@@ -2426,8 +2533,7 @@
 +	woccif_w32(wo, q->regs->ring_size, q->ndesc);
 +
 +	/* wo fw start from 1 */
-+	q->head = woccif_r32(wo, q->regs->dma_idx) + 1;
-+	q->tail = q->head;
++	q->tail = q->head = 1;
 +}
 +
 +static void
@@ -2550,6 +2656,23 @@
 +}
 +
 +static void
++woif_q_free(struct mtk_wed_wo *dev, struct wed_wo_queue *q)
++{
++	int size;
++
++	if (!q)
++		return;
++
++	if (!q->desc)
++		return;
++
++	woccif_w32(dev, q->regs->cpu_idx, 0);
++
++	size = q->ndesc * sizeof(struct wed_wo_desc);
++	dma_free_coherent(dev->hw->dev, size, q->desc, q->desc_dma);
++}
++
++static void
 +woif_q_tx_clean(struct mtk_wed_wo *wo, struct wed_wo_queue *q, bool flush)
 +{
 +	int last;
@@ -2594,11 +2717,6 @@
 +	}
 +}
 +
-+static void
-+woif_q_rx_clean(struct mtk_wed_wo *wo, struct wed_wo_queue *q)
-+{
-+}
-+
 +static void *
 +woif_q_deq(struct mtk_wed_wo *wo, struct wed_wo_queue *q, bool flush,
 +		 int *len, u32 *info, bool *more)
@@ -2641,6 +2759,35 @@
 +	return buf;
 +}
 +
++static void
++woif_q_rx_clean(struct mtk_wed_wo *wo, struct wed_wo_queue *q)
++{
++	struct page *page;
++	void *buf;
++	bool more;
++
++	if (!q->ndesc)
++		return;
++
++	spin_lock_bh(&q->lock);
++	do {
++		buf = woif_q_deq(wo, q, true, NULL, NULL, &more);
++		if (!buf)
++			break;
++
++		skb_free_frag(buf);
++	} while (1);
++	spin_unlock_bh(&q->lock);
++
++	if (!q->rx_page.va)
++		return;
++
++	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));
++
++}
++
 +static int
 +woif_q_init(struct mtk_wed_wo *dev,
 +	       int (*poll)(struct napi_struct *napi, int budget))
@@ -2740,6 +2887,7 @@
 +static const struct wed_wo_queue_ops wo_queue_ops = {
 +	.init = woif_q_init,
 +	.alloc = woif_q_alloc,
++	.free = woif_q_free,
 +	.reset = woif_q_reset,
 +	.tx_skb = woif_q_tx_skb,
 +	.tx_clean = woif_q_tx_clean,
@@ -2910,26 +3058,25 @@
 +
 +void mtk_wed_wo_exit(struct mtk_wed_hw *hw)
 +{
-+/*
-+#ifdef CONFIG_WED_HW_RRO_SUPPORT
-+		woif_bus_exit(woif);
-+		wo_exception_exit(woif);
-+#endif
-+*/
 +	struct mtk_wed_wo *wo = hw->wed_wo;
 +
++	wed_wo_hardware_exit(wo);
++
 +	if (wo->exp.log) {
 +		dma_unmap_single(wo->hw->dev, wo->exp.phys, wo->exp.log_size, DMA_FROM_DEVICE);
 +		kfree(wo->exp.log);
 +	}
 +
++	wo->hw = NULL;
++	memset(wo, 0, sizeof(*wo));
++	kfree(wo);
 +}
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.h b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
 new file mode 100644
-index 000000000..00b39e779
+index 0000000..d962e3a
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -0,0 +1,334 @@
+@@ -0,0 +1,336 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +/* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> */
 +
@@ -3159,7 +3306,7 @@
 +	int (*alloc)(struct mtk_wed_wo *wo, struct wed_wo_queue *q,
 +		     int idx, int n_desc, int bufsize,
 +		     struct wed_wo_queue_regs *regs);
-+
++	void (*free)(struct mtk_wed_wo *wo, struct wed_wo_queue *q);
 +	void (*reset)(struct mtk_wed_wo *wo, struct wed_wo_queue *q);
 +
 +	int (*tx_skb)(struct mtk_wed_wo *wo, struct wed_wo_queue *q,
@@ -3199,7 +3346,8 @@
 +
 +#define mtk_wed_wo_q_init(wo, ...)	(wo)->queue_ops->init((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_alloc(wo, ...)	(wo)->queue_ops->alloc((wo), __VA_ARGS__)
-+#define mtk_wed_wo_q_reset(wo, ...)	(wo)->queue_ops->init((wo), __VA_ARGS__)
++#define mtk_wed_wo_q_free(wo, ...)	(wo)->queue_ops->free((wo), __VA_ARGS__)
++#define mtk_wed_wo_q_reset(wo, ...)	(wo)->queue_ops->reset((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_tx_skb(wo, ...)	(wo)->queue_ops->tx_skb((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_tx_skb1(wo, ...)	(wo)->queue_ops->tx_skb1((wo), __VA_ARGS__)
 +#define mtk_wed_wo_q_tx_clean(wo, ...)	(wo)->queue_ops->tx_clean((wo), __VA_ARGS__)
@@ -3262,6 +3410,7 @@
 +}
 +
 +int mtk_wed_wo_init(struct mtk_wed_hw *hw);
++void mtk_wed_wo_exit(struct mtk_wed_hw *hw);
 +#endif
 +
 diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9998-ethernet-update-ppe-backward-compatible-two-way-hash.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9998-ethernet-update-ppe-backward-compatible-two-way-hash.patch
index d655632..244788d 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9998-ethernet-update-ppe-backward-compatible-two-way-hash.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9998-ethernet-update-ppe-backward-compatible-two-way-hash.patch
@@ -12,52 +12,88 @@
  			if (!eth->ppe[i]) {
  				err = -ENOMEM;
  				goto err_free_dev;
-@@ -3927,6 +3928,7 @@ static const struct mtk_soc_data mt2701_data = {
+@@ -3927,12 +3928,13 @@ static const struct mtk_soc_data mt2701_data = {
  	.required_clks = MT7623_CLKS_BITMAP,
  	.required_pctl = true,
  	.has_sram = false,
 +	.hash_way = 2,
  	.offload_version = 2,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma),
+ 		.rxd_size = sizeof(struct mtk_rx_dma),
+ 		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+ 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+ 	},
  };
  
-@@ -3936,6 +3938,7 @@ static const struct mtk_soc_data mt7621_data = {
+@@ -3936,12 +3938,13 @@ static const struct mtk_soc_data mt7621_data = {
  	.required_clks = MT7621_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = false,
 +	.hash_way = 2,
  	.offload_version = 2,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma),
+ 		.rxd_size = sizeof(struct mtk_rx_dma),
+ 		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+ 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+ 	},
  };
  
-@@ -3946,6 +3949,7 @@ static const struct mtk_soc_data mt7622_data = {
+@@ -3946,12 +3949,13 @@ static const struct mtk_soc_data mt7622_data = {
  	.required_clks = MT7622_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = false,
 +	.hash_way = 2,
  	.offload_version = 2,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma),
+ 		.rxd_size = sizeof(struct mtk_rx_dma),
+ 		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+ 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+ 	},
  };
  
-@@ -3955,6 +3959,7 @@ static const struct mtk_soc_data mt7623_data = {
+@@ -3955,12 +3959,13 @@ static const struct mtk_soc_data mt7623_data = {
  	.required_clks = MT7623_CLKS_BITMAP,
  	.required_pctl = true,
  	.has_sram = false,
 +	.hash_way = 2,
  	.offload_version = 2,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma),
+ 		.rxd_size = sizeof(struct mtk_rx_dma),
+ 		.dma_max_len = MTK_TX_DMA_BUF_LEN,
+ 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
+ 	},
  };
  
-@@ -3974,6 +3979,7 @@ static const struct mtk_soc_data mt7986_data = {
+@@ -3974,12 +3979,13 @@ static const struct mtk_soc_data mt7986_data = {
  	.required_clks = MT7986_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = true,
 +	.hash_way = 4,
  	.offload_version = 2,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma_v2),
+ 		.rxd_size = sizeof(struct mtk_rx_dma_v2),
+ 		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+ 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
+ 	},
  };
  
-@@ -3984,6 +3990,8 @@ static const struct mtk_soc_data mt7981_data = {
+@@ -3984,12 +3990,14 @@ static const struct mtk_soc_data mt7981_data = {
  	.required_clks = MT7981_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = true,
 +	.hash_way = 4,
 +	.offload_version = 2,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma_v2),
+ 		.rxd_size = sizeof(struct mtk_rx_dma_v2),
+ 		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+ 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
+ 	},
  };
  
  static const struct mtk_soc_data rt5350_data = {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9013-drivers-spi-mt65xx-Move-chip_config-to-driver-priv.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9013-drivers-spi-mt65xx-Move-chip_config-to-driver-priv.patch
new file mode 100644
index 0000000..d64f376
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9013-drivers-spi-mt65xx-Move-chip_config-to-driver-priv.patch
@@ -0,0 +1,142 @@
+From 45ec6dfcc5f48127d5bd440fb615bbf48f3fc9c1 Mon Sep 17 00:00:00 2001
+From: "SkyLake.Huang" <skylake.huang@mediatek.com>
+Date: Thu, 23 Jun 2022 18:29:51 +0800
+Subject: [PATCH] drivers: spi-mt65xx: Move chip_config to driver's private
+ data
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ drivers/spi/spi-mt65xx.c                 | 31 +++++++++++-------------
+ include/linux/platform_data/spi-mt65xx.h | 17 -------------
+ 2 files changed, 14 insertions(+), 34 deletions(-)
+ delete mode 100644 include/linux/platform_data/spi-mt65xx.h
+
+diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
+index c19e2d4d7..0afd00891 100644
+--- a/drivers/spi/spi-mt65xx.c
++++ b/drivers/spi/spi-mt65xx.c
+@@ -14,7 +14,6 @@
+ #include <linux/of.h>
+ #include <linux/of_gpio.h>
+ #include <linux/platform_device.h>
+-#include <linux/platform_data/spi-mt65xx.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi-mem.h>
+@@ -123,6 +122,11 @@ struct mtk_spi_compatible {
+ 	bool need_ahb_clk;
+ };
+ 
++struct mtk_spi_config {
++	u32 sample_sel;
++	u32 get_tick_dly;
++};
++
+ struct mtk_spi {
+ 	void __iomem *base;
+ 	u32 state;
+@@ -135,6 +139,7 @@ struct mtk_spi {
+ 	struct scatterlist *tx_sgl, *rx_sgl;
+ 	u32 tx_sgl_len, rx_sgl_len;
+ 	const struct mtk_spi_compatible *dev_comp;
++	struct mtk_spi_config dev_config;
+ 
+ 	struct completion spimem_done;
+ 	bool use_spimem;
+@@ -189,15 +194,6 @@ static const struct mtk_spi_compatible mt8183_compat = {
+ 	.enhance_timing = true,
+ };
+ 
+-/*
+- * A piece of default chip info unless the platform
+- * supplies it.
+- */
+-static const struct mtk_chip_config mtk_default_chip_info = {
+-	.sample_sel = 0,
+-	.get_tick_dly = 2,
+-};
+-
+ static const struct of_device_id mtk_spi_of_match[] = {
+ 	{ .compatible = "mediatek,ipm-spi-single",
+ 		.data = (void *)&ipm_compat_single,
+@@ -255,7 +251,6 @@ static int mtk_spi_hw_init(struct spi_master *master,
+ {
+ 	u16 cpha, cpol;
+ 	u32 reg_val;
+-	struct mtk_chip_config *chip_config = spi->controller_data;
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
+ 
+ 	cpha = spi->mode & SPI_CPHA ? 1 : 0;
+@@ -270,13 +265,13 @@ static int mtk_spi_hw_init(struct spi_master *master,
+ 
+ 			reg_val = readl(mdata->base + SPI_CMD_REG);
+ 			reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK;
+-			reg_val |= chip_config->get_tick_dly
++			reg_val |= mdata->dev_config.get_tick_dly
+ 				   << SPI_CMD_IPM_GET_TICKDLY_OFFSET;
+ 			writel(reg_val, mdata->base + SPI_CMD_REG);
+ 		} else {
+ 			reg_val = readl(mdata->base + SPI_CFG1_REG);
+ 			reg_val &= ~SPI_CFG1_GET_TICKDLY_MASK;
+-			reg_val |= chip_config->get_tick_dly
++			reg_val |= mdata->dev_config.get_tick_dly
+ 				   << SPI_CFG1_GET_TICKDLY_OFFSET;
+ 			writel(reg_val, mdata->base + SPI_CFG1_REG);
+ 		}
+@@ -326,7 +321,7 @@ static int mtk_spi_hw_init(struct spi_master *master,
+ 		else
+ 			reg_val &= ~SPI_CMD_CS_POL;
+ 
+-		if (chip_config->sample_sel)
++		if (mdata->dev_config.sample_sel)
+ 			reg_val |= SPI_CMD_SAMPLE_SEL;
+ 		else
+ 			reg_val &= ~SPI_CMD_SAMPLE_SEL;
+@@ -623,9 +618,6 @@ static int mtk_spi_setup(struct spi_device *spi)
+ {
+ 	struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
+ 
+-	if (!spi->controller_data)
+-		spi->controller_data = (void *)&mtk_default_chip_info;
+-
+ 	if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio))
+ 		gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+ 
+@@ -1025,6 +1017,11 @@ static int mtk_spi_probe(struct platform_device *pdev)
+ 	}
+ 
+ 	mdata = spi_master_get_devdata(master);
++
++	/* Set device configs to default first. Calibrate it later. */
++	mdata->dev_config.sample_sel = 0;
++	mdata->dev_config.get_tick_dly = 2;
++
+ 	mdata->dev_comp = of_id->data;
+ 
+ 	if (mdata->dev_comp->enhance_timing)
+diff --git a/include/linux/platform_data/spi-mt65xx.h b/include/linux/platform_data/spi-mt65xx.h
+deleted file mode 100644
+index fae9bc15c..000000000
+--- a/include/linux/platform_data/spi-mt65xx.h
++++ /dev/null
+@@ -1,17 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-only */
+-/*
+- *  MTK SPI bus driver definitions
+- *
+- * Copyright (c) 2015 MediaTek Inc.
+- * Author: Leilk Liu <leilk.liu@mediatek.com>
+- */
+-
+-#ifndef ____LINUX_PLATFORM_DATA_SPI_MTK_H
+-#define ____LINUX_PLATFORM_DATA_SPI_MTK_H
+-
+-/* Board specific platform_data */
+-struct mtk_chip_config {
+-	u32 sample_sel;
+-	u32 get_tick_dly;
+-};
+-#endif
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9014-drivers-spi-Add-support-for-dynamic-calibration.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9014-drivers-spi-Add-support-for-dynamic-calibration.patch
new file mode 100644
index 0000000..688f33f
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9014-drivers-spi-Add-support-for-dynamic-calibration.patch
@@ -0,0 +1,243 @@
+From a84c53fce4ccd67c147dcbb2dcf4fdeceab05981 Mon Sep 17 00:00:00 2001
+From: "SkyLake.Huang" <skylake.huang@mediatek.com>
+Date: Thu, 23 Jun 2022 18:35:52 +0800
+Subject: [PATCH] drivers: spi: Add support for dynamic calibration
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ drivers/spi/spi.c       | 137 ++++++++++++++++++++++++++++++++++++++++
+ include/linux/spi/spi.h |  42 ++++++++++++
+ 2 files changed, 179 insertions(+)
+
+diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
+index e562735a3..d548036b1 100644
+--- a/drivers/spi/spi.c
++++ b/drivers/spi/spi.c
+@@ -1109,6 +1109,70 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
+ 	return 0;
+ }
+ 
++int spi_do_calibration(struct spi_controller *ctlr, struct spi_device *spi,
++	int (*cal_read)(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen), void *drv_priv)
++{
++	int datalen = ctlr->cal_rule->datalen;
++	int addrlen = ctlr->cal_rule->addrlen;
++	u8 *buf;
++	int ret;
++	int i;
++	struct list_head *cal_head, *listptr;
++	struct spi_cal_target *target;
++
++	/* Calculate calibration result */
++	int hit_val, total_hit, origin;
++	bool hit;
++
++	/* Make sure we can start calibration */
++	if(!ctlr->cal_target || !ctlr->cal_rule || !ctlr->append_caldata)
++		return 0;
++
++	buf = kzalloc(datalen * sizeof(u8), GFP_KERNEL);
++	if(!buf)
++		return -ENOMEM;
++
++	ret = ctlr->append_caldata(ctlr);
++	if (ret)
++		goto cal_end;
++
++	cal_head = ctlr->cal_target;
++	list_for_each(listptr, cal_head) {
++		target = list_entry(listptr, struct spi_cal_target, list);
++
++		hit = false;
++		hit_val = 0;
++		total_hit = 0;
++		origin = *target->cal_item;
++
++		for(i=target->cal_min; i<=target->cal_max; i+=target->step) {
++			*target->cal_item = i;
++			ret = (*cal_read)(drv_priv, ctlr->cal_rule->addr, addrlen, buf, datalen);
++			if(ret)
++				break;
++			dev_dbg(&spi->dev, "controller cal item value: 0x%x\n", i);
++			if(memcmp(ctlr->cal_rule->match_data, buf, datalen * sizeof(u8)) == 0) {
++				hit = true;
++				hit_val += i;
++				total_hit++;
++				dev_dbg(&spi->dev, "golden data matches data read!\n");
++			}
++		}
++		if(hit) {
++			*target->cal_item = DIV_ROUND_CLOSEST(hit_val, total_hit);
++			dev_info(&spi->dev, "calibration result: 0x%x", *target->cal_item);
++		} else {
++			*target->cal_item = origin;
++			dev_warn(&spi->dev, "calibration failed, fallback to default: 0x%x", origin);
++		}
++	}
++
++cal_end:
++	kfree(buf);
++	return ret? ret: 0;
++}
++EXPORT_SYMBOL_GPL(spi_do_calibration);
++
+ static void _spi_transfer_delay_ns(u32 ns)
+ {
+ 	if (!ns)
+@@ -1720,6 +1784,75 @@ void spi_flush_queue(struct spi_controller *ctlr)
+ /*-------------------------------------------------------------------------*/
+ 
+ #if defined(CONFIG_OF)
++static inline void alloc_cal_data(struct list_head **cal_target,
++	struct spi_cal_rule **cal_rule, bool enable)
++{
++	if(enable) {
++		*cal_target = kmalloc(sizeof(struct list_head), GFP_KERNEL);
++		INIT_LIST_HEAD(*cal_target);
++		*cal_rule = kmalloc(sizeof(struct spi_cal_rule), GFP_KERNEL);
++	} else {
++		kfree(*cal_target);
++		kfree(*cal_rule);
++	}
++}
++
++static int of_spi_parse_cal_dt(struct spi_controller *ctlr, struct spi_device *spi,
++			   struct device_node *nc)
++{
++	u32 value;
++	int rc;
++	const char *cal_mode;
++
++	rc = of_property_read_bool(nc, "spi-cal-enable");
++	if (rc)
++		alloc_cal_data(&ctlr->cal_target, &ctlr->cal_rule, true);
++	else
++		return 0;
++
++	rc = of_property_read_string(nc, "spi-cal-mode", &cal_mode);
++	if(!rc) {
++		if(strcmp("read-data", cal_mode) == 0){
++			ctlr->cal_rule->mode = SPI_CAL_READ_DATA;
++		} else if(strcmp("read-pp", cal_mode) == 0) {
++			ctlr->cal_rule->mode = SPI_CAL_READ_PP;
++			return 0;
++		} else if(strcmp("read-sfdp", cal_mode) == 0){
++			ctlr->cal_rule->mode = SPI_CAL_READ_SFDP;
++			return 0;
++		}
++	} else
++		goto err;
++
++	ctlr->cal_rule->datalen = 0;
++	rc = of_property_read_u32(nc, "spi-cal-datalen", &value);
++	if(!rc && value > 0) {
++		ctlr->cal_rule->datalen = value;
++
++		ctlr->cal_rule->match_data = kzalloc(value * sizeof(u8), GFP_KERNEL);
++		rc = of_property_read_u8_array(nc, "spi-cal-data",
++				ctlr->cal_rule->match_data, value);
++		if(rc)
++			kfree(ctlr->cal_rule->match_data);
++	}
++
++	rc = of_property_read_u32(nc, "spi-cal-addrlen", &value);
++	if(!rc && value > 0) {
++		ctlr->cal_rule->addrlen = value;
++
++		ctlr->cal_rule->addr = kzalloc(value * sizeof(u32), GFP_KERNEL);
++		rc = of_property_read_u32_array(nc, "spi-cal-addr",
++				ctlr->cal_rule->addr, value);
++		if(rc)
++			kfree(ctlr->cal_rule->addr);
++	}
++	return 0;
++
++err:
++	alloc_cal_data(&ctlr->cal_target, &ctlr->cal_rule, false);
++	return 0;
++}
++
+ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
+ 			   struct device_node *nc)
+ {
+@@ -1841,6 +1974,10 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
+ 	if (rc)
+ 		goto err_out;
+ 
++	rc = of_spi_parse_cal_dt(ctlr, spi, nc);
++	if (rc)
++		goto err_out;
++
+ 	/* Store a pointer to the node in the device structure */
+ 	of_node_get(nc);
+ 	spi->dev.of_node = nc;
+diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
+index 7067f85ce..5330cd9b0 100644
+--- a/include/linux/spi/spi.h
++++ b/include/linux/spi/spi.h
+@@ -264,6 +264,40 @@ struct spi_driver {
+ 	struct device_driver	driver;
+ };
+ 
++enum {
++	SPI_CAL_READ_DATA = 0,
++	SPI_CAL_READ_PP = 1, /* only for SPI-NAND */
++	SPI_CAL_READ_SFDP = 2, /* only for SPI-NOR */
++};
++
++struct nand_addr {
++	unsigned int lun;
++	unsigned int plane;
++	unsigned int eraseblock;
++	unsigned int page;
++	unsigned int dataoffs;
++};
++
++/**
++ * Read calibration rule from device dts node.
++ * Once calibration result matches the rule, we regard is as success.
++ */
++struct spi_cal_rule {
++	int datalen;
++	u8 *match_data;
++	int addrlen;
++	u32 *addr;
++	int mode;
++};
++
++struct spi_cal_target {
++	u32 *cal_item;
++	int cal_min; /* min of cal_item */
++	int cal_max; /* max of cal_item */
++	int step; /* Increase/decrease cal_item */
++	struct list_head list;
++};
++
+ static inline struct spi_driver *to_spi_driver(struct device_driver *drv)
+ {
+ 	return drv ? container_of(drv, struct spi_driver, driver) : NULL;
+@@ -606,6 +640,11 @@ struct spi_controller {
+ 	void			*dummy_rx;
+ 	void			*dummy_tx;
+ 
++	/* For calibration */
++	int (*append_caldata)(struct spi_controller *ctlr);
++	struct list_head *cal_target;
++	struct spi_cal_rule *cal_rule;
++
+ 	int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs);
+ };
+ 
+@@ -1369,6 +1408,9 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
+ 	{ return 0; }
+ #endif
+ 
++extern int spi_do_calibration(struct spi_controller *ctlr,
++	struct spi_device *spi, int (*cal_read)(void *, u32 *, int, u8 *, int), void *drv_priv);
++
+ /* If you're hotplugging an adapter with devices (parport, usb, etc)
+  * use spi_new_device() to describe each device.  You can also call
+  * spi_unregister_device() to start making that device vanish, but
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9015-drivers-spi-mem-Add-spi-calibration-hook.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9015-drivers-spi-mem-Add-spi-calibration-hook.patch
new file mode 100644
index 0000000..dcea7de
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9015-drivers-spi-mem-Add-spi-calibration-hook.patch
@@ -0,0 +1,48 @@
+From a4f235c3a3c4d25aa6a4417ce64831ca0b38c324 Mon Sep 17 00:00:00 2001
+From: "SkyLake.Huang" <skylake.huang@mediatek.com>
+Date: Thu, 23 Jun 2022 18:37:55 +0800
+Subject: [PATCH] drivers: spi-mem: Add spi calibration hook
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ drivers/spi/spi-mem.c       | 8 ++++++++
+ include/linux/spi/spi-mem.h | 4 ++++
+ 2 files changed, 12 insertions(+)
+
+diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
+index 33115bcfc..b5ab26e9c 100644
+--- a/drivers/spi/spi-mem.c
++++ b/drivers/spi/spi-mem.c
+@@ -383,6 +383,14 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+ }
+ EXPORT_SYMBOL_GPL(spi_mem_exec_op);
+ 
++int spi_mem_do_calibration(struct spi_mem *mem,
++	int (*cal_read)(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen),
++	void *priv)
++{
++	return spi_do_calibration(mem->spi->controller, mem->spi, cal_read, priv);
++}
++EXPORT_SYMBOL_GPL(spi_mem_do_calibration);
++
+ /**
+  * spi_mem_get_name() - Return the SPI mem device name to be used by the
+  *			upper layer if necessary
+diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
+index af9ff2f0f..e2cde8305 100644
+--- a/include/linux/spi/spi-mem.h
++++ b/include/linux/spi/spi-mem.h
+@@ -332,6 +332,10 @@ bool spi_mem_supports_op(struct spi_mem *mem,
+ int spi_mem_exec_op(struct spi_mem *mem,
+ 		    const struct spi_mem_op *op);
+ 
++int spi_mem_do_calibration(struct spi_mem *mem,
++			int (*cal_read)(void *, u32 *, int, u8 *, int),
++			void *priv);
++
+ const char *spi_mem_get_name(struct spi_mem *mem);
+ 
+ struct spi_mem_dirmap_desc *
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9016-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9016-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch
new file mode 100644
index 0000000..2c51aa9
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9016-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch
@@ -0,0 +1,48 @@
+From ac9ed3898b80a81ce220a682749767ef189094a8 Mon Sep 17 00:00:00 2001
+From: "SkyLake.Huang" <skylake.huang@mediatek.com>
+Date: Thu, 23 Jun 2022 18:39:03 +0800
+Subject: [PATCH] drivers: spi-mt65xx: Add controller's calibration paramter
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ drivers/spi/spi-mt65xx.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
+index 0afd00891..1b272d15c 100644
+--- a/drivers/spi/spi-mt65xx.c
++++ b/drivers/spi/spi-mt65xx.c
+@@ -727,6 +727,21 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
+ 	return IRQ_HANDLED;
+ }
+ 
++static int mtk_spi_append_caldata(struct spi_controller *ctlr)
++{
++	struct spi_cal_target *cal_target = kmalloc(sizeof(*cal_target), GFP_KERNEL);
++	struct mtk_spi *mdata = spi_master_get_devdata(ctlr);
++
++	cal_target->cal_item = &mdata->dev_config.get_tick_dly;
++	cal_target->cal_min = 0;
++	cal_target->cal_max = 7;
++	cal_target->step = 1;
++
++	list_add(&cal_target->list, ctlr->cal_target);
++
++	return 0;
++}
++
+ static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem,
+                                       struct spi_mem_op *op)
+ {
+@@ -1009,6 +1024,8 @@ static int mtk_spi_probe(struct platform_device *pdev)
+ 	master->can_dma = mtk_spi_can_dma;
+ 	master->setup = mtk_spi_setup;
+ 
++	master->append_caldata = mtk_spi_append_caldata;
++
+ 	of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node);
+ 	if (!of_id) {
+ 		dev_err(&pdev->dev, "failed to probe of_node\n");
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch
new file mode 100644
index 0000000..374531b
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch
@@ -0,0 +1,89 @@
+From 6bd88d34cb5a5cb1d7c544c9f5b430105b000308 Mon Sep 17 00:00:00 2001
+From: "SkyLake.Huang" <skylake.huang@mediatek.com>
+Date: Thu, 23 Jun 2022 18:39:56 +0800
+Subject: [PATCH] drivers: mtd: spinand: Add calibration support for spinand
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ drivers/mtd/nand/spi/core.c | 58 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 58 insertions(+)
+
+diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
+index 9f5f95ff7..b346c7a8a 100644
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -789,6 +789,60 @@ static int spinand_manufacturer_match(struct spinand_device *spinand,
+ 	return -ENOTSUPP;
+ }
+ 
++int spinand_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen) {
++	int ret;
++	u8 status;
++	struct spinand_device *spinand = (struct spinand_device *)priv;
++	struct device *dev = &spinand->spimem->spi->dev;
++
++	typedef struct nand_pos my_pos;
++	my_pos pos;
++	typedef struct nand_page_io_req my_req;
++	my_req req;
++
++	if(addrlen != sizeof(struct nand_addr)/sizeof(unsigned int)) {
++		dev_err(dev, "Must provide correct addr(length) for spinand calibration\n");
++		return -EINVAL;
++	}
++
++	ret = spinand_reset_op(spinand);
++	if (ret)
++		return ret;
++
++	/* We should store our golden data in first target because
++	 * we can't switch target at this moment.
++	 */
++	pos = (my_pos){
++		.target = 0,
++		.lun = *addr,
++		.plane = *(addr+1),
++		.eraseblock = *(addr+2),
++		.page = *(addr+3),
++	};
++
++	req = (my_req){
++		.pos = pos,
++		.dataoffs = *(addr+4),
++		.datalen = readlen,
++		.databuf.in = buf,
++		.mode = MTD_OPS_AUTO_OOB,
++	};
++
++	ret = spinand_load_page_op(spinand, &req);
++	if (ret)
++		return ret;
++
++	ret = spinand_wait(spinand, &status);
++	if (ret < 0)
++		return ret;
++
++	struct spi_mem_op op = SPINAND_PAGE_READ_FROM_CACHE_OP(
++		false, 0, 1, buf, readlen);
++	ret = spi_mem_exec_op(spinand->spimem, &op);
++
++	return 0;
++}
++
+ static int spinand_id_detect(struct spinand_device *spinand)
+ {
+ 	u8 *id = spinand->id.data;
+@@ -1004,6 +1058,10 @@ static int spinand_init(struct spinand_device *spinand)
+ 	if (!spinand->scratchbuf)
+ 		return -ENOMEM;
+ 
++	ret = spi_mem_do_calibration(spinand->spimem, spinand_cal_read, spinand);
++	if (ret)
++		dev_err(dev, "Failed to calibrate SPI-NAND (err = %d)\n", ret);
++
+ 	ret = spinand_detect(spinand);
+ 	if (ret)
+ 		goto err_free_bufs;
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch
new file mode 100644
index 0000000..023f52e
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch
@@ -0,0 +1,48 @@
+From b242e30661dac5c1c127999600029cd5b3f6b458 Mon Sep 17 00:00:00 2001
+From: "SkyLake.Huang" <skylake.huang@mediatek.com>
+Date: Thu, 23 Jun 2022 18:40:59 +0800
+Subject: [PATCH] drivers: mtd: spi-nor: Add calibration support for spi-nor
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
+index 945833cbb..38a8948b3 100644
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -4893,6 +4893,20 @@ static void spi_nor_debugfs_init(struct spi_nor *nor,
+ 					 info->id_len, info->id);
+ }
+ 
++static int spi_nor_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen)
++{
++	int ret;
++	struct spi_nor *nor = (struct spi_nor *)priv;
++
++	nor->reg_proto = SNOR_PROTO_1_1_1;
++	nor->read_proto = SNOR_PROTO_1_1_1;
++	nor->read_opcode = SPINOR_OP_READ;
++	nor->addr_width = 3;
++	nor->read_dummy = 0;
++
++	return spi_nor_read_raw(nor, *addr, readlen, buf);
++}
++
+ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
+ 						       const char *name)
+ {
+@@ -4967,6 +4981,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
+ 	if (!nor->bouncebuf)
+ 		return -ENOMEM;
+ 
++	if(nor->spimem)
++		spi_mem_do_calibration(nor->spimem, spi_nor_cal_read, nor);
++
+ 	info = spi_nor_get_flash_info(nor, name);
+ 	if (IS_ERR(info))
+ 		return PTR_ERR(info);
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch
new file mode 100644
index 0000000..8b39b12
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch
@@ -0,0 +1,97 @@
+From 6110010f7b88392a3094f2aaec91ee54151cde2a Mon Sep 17 00:00:00 2001
+From: "SkyLake.Huang" <skylake.huang@mediatek.com>
+Date: Thu, 23 Jun 2022 18:43:02 +0800
+Subject: [PATCH] drivers: char: tpm: Add calibration example for SPI TPM
+ module
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ drivers/char/tpm/tpm_tis_core.c | 19 +++++++++++++++++++
+ drivers/char/tpm/tpm_tis_core.h |  2 ++
+ drivers/char/tpm/tpm_tis_spi.c  |  7 +++++++
+ 3 files changed, 28 insertions(+)
+
+diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
+index 70f785994..b9898a56d 100644
+--- a/drivers/char/tpm/tpm_tis_core.c
++++ b/drivers/char/tpm/tpm_tis_core.c
+@@ -817,6 +817,21 @@ static const struct tpm_class_ops tpm_tis = {
+ 	.clk_enable = tpm_tis_clkrun_enable,
+ };
+ 
++int tpm_tis_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen)
++{
++	int rc;
++	u32 vendor;
++
++	rc = tpm_tis_read32((struct tpm_tis_data *)priv, TPM_DID_VID(0), &vendor);
++	if (rc < 0)
++		return -EIO;
++
++	buf[0] = (vendor >> 24) & 0xff;
++	buf[1] = (vendor >> 16) & 0xff;
++
++	return 0;
++}
++
+ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
+ 		      const struct tpm_tis_phy_ops *phy_ops,
+ 		      acpi_handle acpi_dev_handle)
+@@ -864,6 +879,10 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
+ 	if (chip->ops->clk_enable != NULL)
+ 		chip->ops->clk_enable(chip, true);
+ 
++	rc = priv->phy_ops->do_calibration(priv, dev);
++	if (rc)
++		goto out_err;
++
+ 	if (wait_startup(chip, 0) != 0) {
+ 		rc = -ENODEV;
+ 		goto out_err;
+diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
+index 7337819f5..7bb0bc8b6 100644
+--- a/drivers/char/tpm/tpm_tis_core.h
++++ b/drivers/char/tpm/tpm_tis_core.h
+@@ -106,6 +106,7 @@ struct tpm_tis_phy_ops {
+ 	int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result);
+ 	int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result);
+ 	int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src);
++	int (*do_calibration)(struct tpm_tis_data *data, struct device *dev);
+ };
+ 
+ static inline int tpm_tis_read_bytes(struct tpm_tis_data *data, u32 addr,
+@@ -158,6 +159,7 @@ static inline bool is_bsw(void)
+ }
+ 
+ void tpm_tis_remove(struct tpm_chip *chip);
++int tpm_tis_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen);
+ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
+ 		      const struct tpm_tis_phy_ops *phy_ops,
+ 		      acpi_handle acpi_dev_handle);
+diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c
+index 19513e622..3be2d53a5 100644
+--- a/drivers/char/tpm/tpm_tis_spi.c
++++ b/drivers/char/tpm/tpm_tis_spi.c
+@@ -184,12 +184,19 @@ static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
+ 	return rc;
+ }
+ 
++int tpm_tis_spi_do_calibration(struct tpm_tis_data *priv, struct device *dev) {
++	struct spi_device *spi = container_of(dev,
++                                         struct spi_device, dev);
++	return spi_do_calibration(spi->master, spi, tpm_tis_cal_read, priv);
++}
++
+ static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
+ 	.read_bytes = tpm_tis_spi_read_bytes,
+ 	.write_bytes = tpm_tis_spi_write_bytes,
+ 	.read16 = tpm_tis_spi_read16,
+ 	.read32 = tpm_tis_spi_read32,
+ 	.write32 = tpm_tis_spi_write32,
++	.do_calibration = tpm_tis_spi_do_calibration,
+ };
+ 
+ static int tpm_tis_spi_probe(struct spi_device *dev)
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
index cc42f21..ae8ba9b 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
@@ -103,4 +103,11 @@
     file://9009-Add-spi-runtime-PM-support.patch \
     file://9010-iwconfig-wireless-rate-fix.patch;apply=no \
     file://9011-Modify-tick_delay-for-spi-work-safety.patch \
+    file://9013-drivers-spi-mt65xx-Move-chip_config-to-driver-priv.patch \
+    file://9014-drivers-spi-Add-support-for-dynamic-calibration.patch \
+    file://9015-drivers-spi-mem-Add-spi-calibration-hook.patch \
+    file://9016-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch \
+    file://9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch \
+    file://9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch \
+    file://9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch \
     "