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

[Description]
11f0341c [kernel][pinctrl][Backport spinlock for mtk_rmw]
eb40a7b4 [openwrt][mt7988][tops][Fix TCP not binding over L2TP/IPSec]
90df6779 [kernel][common][eth][Add a Ethernet passive Mux support]
979b8fbc [openwrt][mt7988][crypto][Add default enable upstream lookaside driver]
ced9d91c [openwrt][mt7988][tops][fix configuration typo]
648ba5b4 [MAC80211][hnat][Fix LAN/WAN software path low t-Put issue]
3190b596 [MAC80211][mt7986][hnat][Add hw configuration for PPE]
f807e962 [openwrt][mt7988][tops][update tops-tool default behavior]
83f9d770 [openwrt][mt7988][tops][split release and develop package]
f60f869c [openwrt][mt7988][crypto][prevent hnat bind UDP flow]
cadd001c [openwrt][mt7988][tops][prevent hnat bind UDP flow]
3174c47a [kernel][mt7988][eth][Add a link down recovery handler for the USXGMII]
405dbcd5 [openwrt][mt7988][tops: init skb_hnat_is_decrypt(skb)]
e8133606 [kernel][common][eth][Update Airoha EN8811H 2.5G PHY driver to v1.2.2]
6a9ef281 [openwrt][mt7988][tops: update firmware to support L2TP over IPsec]
f4508a0d [kernel][common][eth][Update gangload timeout mechanism for the Aquantia PHY driver]
5c8145fc [[openwrt][mt7988][config][GDM/GMAC FSM poll update]]
a4724176 [Kernel][hnat][Fix tport_id, tops_entry and cdrt_id in entry_detail]
f9ec3668 [kernel][common][eth][Update RX ring debug command]
f17a714b [openwrt][mt7988][tops][support L2TP/IPSec]
198a6450 [openwrt][mt7988][pce][support get CDRT index API]
6ac1fd75 [openwrt][mt7988][tops][tnl-offload: support with crypto-eip]
eccf181f [kernel][common][app][Fix coverity issue for the switch utility]
9a41e836 [kernel][misc][IPv4/IPv6 multicast]
6d5ef6d9 [openwrt][mt7988][tops][add cpu utilization fetching]
adbb1c6b [openwrt][mt7988][crypto-eip: update CLS API usage]
2c2bad81 [openwrt][mt7988][tops][separate CLS_ENTRY and TOPS_ENTRY dependency]
1c33e019 [openwrt][mt7988][pce][separate CLS entry definition from TOPS_ENTRY]
0c10c4bd [kernel][mt7988][emmc][platform.sh: Align upgrade script with mt7986's]
1dfa59c4 [MAC80211][hnat][Update 2.5G LAN/WAN low t-Put issue for the PPPQ]
4df6d59c [[openwrt][mt7986/81/88][config][lro ring id operate error]]
b7adb872 [openwrt][mt7988][eth][Fix coverity issue for Ethernet driver]
e172205a [openwrt][mt7988][crypto][EIP197 Alpha Release]
e6740a1e [openwrt][mt7988][arm64: dts: mt7988: add device tree node for eip197 inline mode]
3ec85b5c [openwrt][mt7988][crypto][EIP197 DDK Porting]
1af86bb6 [openwrt][mt7988][tops][fix coverity issue]
969c5b8b [MAC80211][wed][fix wed init fail issue]
97e18807 [MAC80211][hnat][Fix 2.5G LAN/WAN low t-Put issue for the PPPQ]
dce0053c [[openwrt][mt7986/81][config][allocate qdma dmad in dram instead of sram]]
5b358add [MAC80211][wed][repatch because apply patch error]
6d263a9d [openwrt][mt7988][tops][Fix conflict: empty line]
f46ad9a7 [Kernel][hnat][Fix ipv4 in ipv6 packets can't be bind in MAPE scene]
6e24e5fc [kernel][common][eth][Remove phylink set line rate to maximum]
1e4cffaf [openwrt][mt7988][tops][Add rate limit on QDMA queue]
c7f321d3 [MAC80211][wed][Change WARN_RATELIMIT to pr_err for wed ext interrupt]

[Release-log]

Change-Id: I0e6dcb33e6d94e5ea0e3887a0c14a54aa554e8a3
diff --git a/recipes-devtools/smp/files/smp-mt76.sh b/recipes-devtools/smp/files/smp-mt76.sh
index 83d7799..709e228 100644
--- a/recipes-devtools/smp/files/smp-mt76.sh
+++ b/recipes-devtools/smp/files/smp-mt76.sh
@@ -214,6 +214,15 @@
 		#	wifi3_irq=247
 		#fi
 	fi
+
+	for vif in $NET_IF_LIST;
+	do
+		if [[ "$vif" == "lan"* ]] ||  \
+		[[ "$vif" == "wlan"* ]] || [[ "$vif" == "phy"* ]]; then
+			LAN_IF_LIST="$LAN_IF_LIST $vif"
+		fi
+	done;
+	dbg2 "$LAN_IF_LIST = $LAN_IF_LIST"
 	# Please update the CPU binding in each cases.
 	# CPU#_AFFINITY="add binding irq number here"
 	# CPU#_RPS="add binding interface name here"
@@ -234,8 +243,8 @@
 		CPU2_AFFINITY="$wifi2_irq $wifi3_irq"
 		CPU3_AFFINITY="$wifi1_irq"
 
-		CPU0_RPS="$RPS_IF_LIST"
-		CPU1_RPS="$RPS_IF_LIST"
+		CPU0_RPS="$LAN_IF_LIST"
+		CPU1_RPS="$LAN_IF_LIST"
 		CPU2_RPS="$RPS_IF_LIST"
 		CPU3_RPS="$RPS_IF_LIST"
 	fi
diff --git a/recipes-devtools/switch/files/src/switch_ioctl.c b/recipes-devtools/switch/files/src/switch_ioctl.c
index f6b97d4..366079e 100644
--- a/recipes-devtools/switch/files/src/switch_ioctl.c
+++ b/recipes-devtools/switch/files/src/switch_ioctl.c
@@ -235,18 +235,24 @@
 	mii.reg_num = 13;
 	mii.val_in = dev;
 	ret = ioctl(sk, method, &ifr);
+	if (ret < 0)
+		printf("ioctl failed with error %d\n", ret);
 
 	method = RAETH_MII_WRITE;
 	mii.phy_id = port_num;
 	mii.reg_num = 14;
 	mii.val_in = reg;
 	ret = ioctl(sk, method, &ifr);
+	if (ret < 0)
+		printf("ioctl failed with error %d\n", ret);
 
 	method = RAETH_MII_WRITE;
 	mii.phy_id = port_num;
 	mii.reg_num = 13;
 	mii.val_in = (0x6000 | dev);
 	ret = ioctl(sk, method, &ifr);
+	if (ret < 0)
+		printf("ioctl failed with error %d\n", ret);
 
 	usleep(1000);
 
@@ -254,6 +260,8 @@
 	mii.phy_id = port_num;
 	mii.reg_num = 14;
 	ret = ioctl(sk, method, &ifr);
+	if (ret < 0)
+		printf("ioctl failed with error %d\n", ret);
 
 	close(sk);
 	*value = mii.val_out;
@@ -283,18 +291,24 @@
 	mii.reg_num = 13;
 	mii.val_in = dev;
 	ret = ioctl(sk, method, &ifr);
+	if (ret < 0)
+		printf("ioctl failed with error %d\n", ret);
 
 	method = RAETH_MII_WRITE;
 	mii.phy_id = port_num;
 	mii.reg_num = 14;
 	mii.val_in = reg;
 	ret = ioctl(sk, method, &ifr);
+	if (ret < 0)
+		printf("ioctl failed with error %d\n", ret);
 
 	method = RAETH_MII_WRITE;
 	mii.phy_id = port_num;
 	mii.reg_num = 13;
 	mii.val_in = (0x6000 | dev);
 	ret = ioctl(sk, method, &ifr);
+	if (ret < 0)
+		printf("ioctl failed with error %d\n", ret);
 
 	usleep(1000);
 
@@ -303,6 +317,8 @@
 	mii.reg_num = 14;
 	mii.val_in = value;
 	ret = ioctl(sk, method, &ifr);
+	if (ret < 0)
+		printf("ioctl failed with error %d\n", ret);
 
 	close(sk);
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
index 92b32c5..561450e 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
@@ -587,7 +587,8 @@
 	};
 
 	crypto: crypto@15600000 {
-		compatible = "inside-secure,safexcel-eip197b";
+		compatible = "inside-secure,safexcel-eip197b",
+			     "security-ip-197-srv";
 		reg = <0 0x15600000 0 0x180000>;
 		interrupts = <GIC_SPI 214 IRQ_TYPE_LEVEL_HIGH>,
 			     <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>,
@@ -595,6 +596,7 @@
 			     <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>;
 		interrupt-names = "ring0", "ring1", "ring2", "ring3";
 		status = "okay";
+		eth = <&eth>;
 	};
 
 	afe: audio-controller@11210000 {
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 a1db345..e50e1ac 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
@@ -921,26 +921,30 @@
 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_ring *ring;
 	struct mtk_rx_dma_v2 *rx_ring;
-	int i = 0;
+	int i = 0, j = 0;
 
-	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;
+	for (j = 0; j < MTK_RX_NAPI_NUM; j++) {
+		ring = &g_eth->rx_ring[j];
 
-		seq_printf(seq, "%d: %08x %08x %08x %08x", i,
-			   rx_ring->rxd1, rx_ring->rxd2,
-			   rx_ring->rxd3, rx_ring->rxd4);
+		seq_printf(seq, "[Ring%d] next to read: %d\n", j,
+			   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;
 
-		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) {
-			seq_printf(seq, " %08x %08x %08x %08x",
-				   rx_ring->rxd5, rx_ring->rxd6,
-				   rx_ring->rxd7, rx_ring->rxd8);
-		}
+			seq_printf(seq, "%d: %08x %08x %08x %08x", i,
+				   rx_ring->rxd1, rx_ring->rxd2,
+				   rx_ring->rxd3, rx_ring->rxd4);
 
-		seq_printf(seq, "\n");
+			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) {
+				seq_printf(seq, " %08x %08x %08x %08x",
+					   rx_ring->rxd5, rx_ring->rxd6,
+					   rx_ring->rxd7, rx_ring->rxd8);
+			}
+
+			seq_puts(seq, "\n");
+		}
 	}
 
 	return 0;
@@ -1782,7 +1786,7 @@
 		    ((reg_op1 >> MTK_LRO_RING_AGE_TIME_L_OFFSET) & 0x3ff);
 		seq_printf(seq,
 			   "Ring[%d]: MAX_AGG_CNT=%d, AGG_TIME=%d, AGE_TIME=%d, Threshold=%d\n",
-			   (MTK_HAS_CAPS(g_eth->soc->caps, MTK_NETSYS_RX_V2)) ?
+			   !(MTK_HAS_CAPS(g_eth->soc->caps, MTK_NETSYS_RX_V2)) ?
 			   i : i+3,
 			   agg_cnt, agg_time, age_time, reg_op4);
 	}
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 29630a8..b0cfc66 100644
--- 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
@@ -878,18 +878,26 @@
 static int mtk_gdm_fsm_get(struct mtk_mac *mac, u32 gdm)
 {
 	u32 fsm = mtk_r32(mac->hw, gdm);
-	u32 ret = 0;
+	u32 ret = 0, val = 0;
 
-	if (mac->type == MTK_GDM_TYPE)
+	switch (mac->type) {
+	case MTK_GDM_TYPE:
 		ret = fsm == 0;
-	else if (mac->type == MTK_XGDM_TYPE) {
-		if (mac->id == MTK_GMAC1_ID) {
-			if (((fsm & 0x7ffffff) == 0) &&
-				(mtk_r32(mac->hw, MTK_MAC_FSM(mac->id)) == 0x1010000))
-				ret = 1;
+		break;
+	case MTK_XGDM_TYPE:
+		ret = fsm == 0x10000000;
+		break;
+	default:
+		break;
+	}
+
+	if ((mac->type == MTK_XGDM_TYPE) && (mac->id != MTK_GMAC1_ID)) {
+		val = mtk_r32(mac->hw, MTK_MAC_FSM(mac->id));
+		if ((val == 0x02010100) || (val == 0x01010100)) {
+			ret = (mac->interface == PHY_INTERFACE_MODE_XGMII) ?
+				((fsm & 0x0fffffff) == 0) : ((fsm & 0x00ffffff) == 0);
 		} else
-			ret = ((mac->interface == PHY_INTERFACE_MODE_XGMII) ?
-				((fsm & 0xfffffff) == 0) : ((fsm & 0x0ffffff) == 0));
+			ret = 0;
 	}
 
 	return ret;
@@ -913,7 +921,7 @@
 		pr_info("%s mac id invalid", __func__);
 		break;
 	}
-	msleep(500);
+
 	while (i < 3) {
 		if (mtk_gdm_fsm_get(mac, gdm))
 			break;
@@ -927,7 +935,7 @@
 
 static void mtk_pse_port_link_set(struct mtk_mac *mac, bool up)
 {
-	u32 fe_glo_cfg, val;
+	u32 fe_glo_cfg, val = 0;
 
 	fe_glo_cfg = mtk_r32(mac->hw, MTK_FE_GLO_CFG(mac->id));
 	switch (mac->id) {
@@ -956,6 +964,8 @@
 {
 	struct mtk_mac *mac = container_of(config, struct mtk_mac,
 					   phylink_config);
+	struct mtk_eth *eth = mac->hw;
+	unsigned int id;
 	u32 mcr, sts;
 
 	mtk_pse_port_link_set(mac, false);
@@ -964,8 +974,9 @@
 		mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK);
 		mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
 	} else if (mac->type == MTK_XGDM_TYPE && mac->id != MTK_GMAC1_ID) {
-		mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
+		struct mtk_usxgmii_pcs *mpcs;
 
+		mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
 		mcr &= 0xfffffff0;
 		mcr |= XMAC_MCR_TRX_DISABLE;
 		mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
@@ -973,6 +984,10 @@
 		sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
 		sts &= ~MTK_XGMAC_FORCE_LINK(mac->id);
 		mtk_w32(mac->hw, sts, MTK_XGMAC_STS(mac->id));
+
+		id = mtk_mac2xgmii_id(eth, mac->id);
+		mpcs = &eth->usxgmii->pcs[id];
+		cancel_delayed_work_sync(&mpcs->link_poll);
 	}
 }
 
@@ -3345,7 +3360,7 @@
 	if (eth->hwlro) {
 		mtk_hwlro_rx_uninit(eth);
 
-		i = (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) ? 1 : 4;
+		i = (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) ? 4 : 1;
 		for (; i < MTK_MAX_RX_RING_NUM; i++)
 			mtk_rx_clean(eth, &eth->rx_ring[i], 0);
 	}
@@ -4560,6 +4575,151 @@
 #endif
 };
 
+static void mux_poll(struct work_struct *work)
+{
+	struct mtk_mux *mux = container_of(work, struct mtk_mux, poll.work);
+	struct mtk_mac *mac = mux->mac;
+	struct mtk_eth *eth = mac->hw;
+	struct net_device *dev = eth->netdev[mac->id];
+	unsigned int channel;
+
+	if (IS_ERR(mux->gpio[0]) || IS_ERR(mux->gpio[1]))
+		goto exit;
+
+	channel = gpiod_get_value_cansleep(mux->gpio[0]);
+	if (mux->channel == channel || !netif_running(dev))
+		goto exit;
+
+	rtnl_lock();
+
+	mtk_stop(dev);
+
+	if (channel == 0 || channel == 1) {
+		mac->of_node = mux->data[channel]->of_node;
+		mac->phylink = mux->data[channel]->phylink;
+	};
+
+	dev_info(eth->dev, "ethernet mux: switch to channel%d\n", channel);
+
+	gpiod_set_value_cansleep(mux->gpio[1], channel);
+
+	mtk_open(dev);
+
+	rtnl_unlock();
+
+	mux->channel = channel;
+
+exit:
+	mod_delayed_work(system_wq, &mux->poll, msecs_to_jiffies(100));
+}
+
+static int mtk_add_mux_channel(struct mtk_mux *mux, struct device_node *np)
+{
+	const __be32 *_id = of_get_property(np, "reg", NULL);
+	struct mtk_mac *mac = mux->mac;
+	struct mtk_eth *eth = mac->hw;
+	struct mtk_mux_data *data;
+	struct phylink *phylink;
+	int phy_mode, id;
+
+	if (!_id) {
+		dev_err(eth->dev, "missing mux channel id\n");
+		return -EINVAL;
+	}
+
+	id = be32_to_cpup(_id);
+	if (id < 0 || id > 1) {
+		dev_err(eth->dev, "%d is not a valid mux channel id\n", id);
+		return -EINVAL;
+	}
+
+	data = kmalloc(sizeof(*data), GFP_KERNEL);
+	if (unlikely(!data)) {
+		dev_err(eth->dev, "failed to create mux data structure\n");
+		return -ENOMEM;
+	}
+
+	mux->data[id] = data;
+
+	/* phylink create */
+	phy_mode = of_get_phy_mode(np);
+	if (phy_mode < 0) {
+		dev_err(eth->dev, "incorrect phy-mode\n");
+		return -EINVAL;
+	}
+
+	phylink = phylink_create(&mux->mac->phylink_config,
+				 of_fwnode_handle(np),
+				 phy_mode, &mtk_phylink_ops);
+	if (IS_ERR(phylink)) {
+		dev_err(eth->dev, "failed to create phylink structure\n");
+		return PTR_ERR(phylink);
+	}
+
+	data->of_node = np;
+	data->phylink = phylink;
+
+	return 0;
+}
+
+static int mtk_add_mux(struct mtk_eth *eth, struct device_node *np)
+{
+	const __be32 *_id = of_get_property(np, "reg", NULL);
+	struct device_node *child;
+	struct mtk_mux *mux;
+	unsigned int id;
+	int err;
+
+	if (!_id) {
+		dev_err(eth->dev, "missing attach mac id\n");
+		return -EINVAL;
+	}
+
+	id = be32_to_cpup(_id);
+	if (id < 0 || id >= MTK_MAX_DEVS) {
+		dev_err(eth->dev, "%d is not a valid attach mac id\n", id);
+		return -EINVAL;
+	}
+
+	mux = kmalloc(sizeof(struct mtk_mux), GFP_KERNEL);
+	if (unlikely(!mux)) {
+		dev_err(eth->dev, "failed to create mux structure\n");
+		return -ENOMEM;
+	}
+
+	eth->mux[id] = mux;
+
+	mux->mac = eth->mac[id];
+	mux->channel = 0;
+
+	mux->gpio[0] = fwnode_get_named_gpiod(of_fwnode_handle(np),
+					      "mod-def0-gpios", 0,
+					      GPIOD_IN, "?");
+	if (IS_ERR(mux->gpio[0]))
+		dev_err(eth->dev, "failed to requset gpio for mod-def0-gpios\n");
+
+	mux->gpio[1] = fwnode_get_named_gpiod(of_fwnode_handle(np),
+					      "chan-sel-gpios", 0,
+					      GPIOD_OUT_LOW, "?");
+	if (IS_ERR(mux->gpio[1]))
+		dev_err(eth->dev, "failed to requset gpio for chan-sel-gpios\n");
+
+	for_each_child_of_node(np, child) {
+		err = mtk_add_mux_channel(mux, child);
+		if (err) {
+			dev_err(eth->dev, "failed to add mtk_mux\n");
+			of_node_put(child);
+			return -ECHILD;
+		}
+		of_node_put(child);
+	}
+
+	INIT_DELAYED_WORK(&mux->poll, mux_poll);
+	mod_delayed_work(system_wq, &mux->poll, msecs_to_jiffies(3000));
+
+	return 0;
+}
+
 static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
 {
 	const __be32 *_id = of_get_property(np, "reg", NULL);
@@ -4757,7 +4917,7 @@
 
 static int mtk_probe(struct platform_device *pdev)
 {
-	struct device_node *mac_np;
+	struct device_node *mac_np, *mux_np;
 	struct mtk_eth *eth;
 	int err, i;
 
@@ -4928,6 +5088,26 @@
 		}
 	}
 
+	mux_np = of_get_child_by_name(eth->dev->of_node, "mux-bus");
+	if (mux_np) {
+		struct device_node *child;
+
+		for_each_available_child_of_node(mux_np, child) {
+			if (!of_device_is_compatible(child,
+						     "mediatek,eth-mux"))
+				continue;
+
+			if (!of_device_is_available(child))
+				continue;
+
+			err = mtk_add_mux(eth, child);
+			if (err)
+				dev_err(&pdev->dev, "failed to add mux\n");
+
+			of_node_put(mux_np);
+		};
+	}
+
 	err = mtk_napi_init(eth);
 	if (err)
 		goto err_free_dev;
@@ -5158,7 +5338,7 @@
 	.hw_features = MTK_HW_FEATURES,
 	.required_clks = MT7986_CLKS_BITMAP,
 	.required_pctl = false,
-	.has_sram = true,
+	.has_sram = false,
 	.rss_num = 0,
 	.txrx = {
 		.txd_size = sizeof(struct mtk_tx_dma_v2),
@@ -5176,7 +5356,7 @@
 	.hw_features = MTK_HW_FEATURES,
 	.required_clks = MT7981_CLKS_BITMAP,
 	.required_pctl = false,
-	.has_sram = true,
+	.has_sram = false,
 	.rss_num = 0,
 	.txrx = {
 		.txd_size = sizeof(struct mtk_tx_dma_v2),
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
old mode 100755
new mode 100644
index f87635c..88df3b2
--- 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
@@ -945,6 +945,11 @@
 #define USXGMII_LPA_LINK	BIT(15)
 #define USXGMII_LPA_LATCH	BIT(31)
 
+/* Register to read PCS Link status */
+#define RG_PCS_RX_STATUS0	0x904
+#define RG_PCS_RX_STATUS_UPDATE	BIT(16)
+#define RG_PCS_RX_LINK_STATUS	BIT(2)
+
 /* Register to control USXGMII XFI PLL digital */
 #define XFI_PLL_DIG_GLB8	0x08
 #define RG_XFI_PLL_EN		BIT(31)
@@ -1035,6 +1040,7 @@
 
 struct mtk_eth;
 struct mtk_mac;
+struct mtk_mux;
 
 /* struct mtk_hw_stats - the structure that holds the traffic statistics.
  * @stats_lock:		make sure that stats operations are atomic
@@ -1715,7 +1721,9 @@
 	struct mtk_eth		*eth;
 	struct regmap		*regmap;
 	struct regmap		*regmap_pextp;
+	struct delayed_work	link_poll;
 	phy_interface_t		interface;
+	unsigned int		mode;
 	u8			id;
 	struct phylink_pcs	pcs;
 };
@@ -1801,6 +1809,7 @@
 	struct net_device		dummy_dev;
 	struct net_device		*netdev[MTK_MAX_DEVS];
 	struct mtk_mac			*mac[MTK_MAX_DEVS];
+	struct mtk_mux			*mux[MTK_MAX_DEVS];
 	int				irq_fe[MTK_FE_IRQ_NUM];
 	int				irq_pdma[MTK_PDMA_IRQ_NUM];
 	u8				hwver;
@@ -1865,6 +1874,25 @@
 	u32				tx_lpi_timer;
 };
 
+/* struct mtk_mux_data -	the structure that holds the private data about the
+ *			 Passive MUXs of the SoC
+ */
+struct mtk_mux_data {
+	struct device_node		*of_node;
+	struct phylink			*phylink;
+};
+
+/* struct mtk_mux -	the structure that holds the info about the Passive MUXs of the
+ *			SoC
+ */
+struct mtk_mux {
+	struct delayed_work		poll;
+	struct gpio_desc		*gpio[2];
+	struct mtk_mux_data		*data[2];
+	struct mtk_mac			*mac;
+	unsigned int			channel;
+};
+
 /* the struct describing the SoC. these are declared in the soc_xyz.c files */
 extern struct mtk_eth *g_eth;
 extern const struct of_device_id of_mtk_match[];
@@ -1895,6 +1923,7 @@
 int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r);
 int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r);
 int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range);
+void mtk_usxgmii_link_poll(struct work_struct *work);
 
 void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
 u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 96d446e..f648386 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -371,7 +371,11 @@
 	pr_info("==========<PPE_ID=%d, Flow Table Entry=%d (%p)>===============\n",
 		ppe_id, index, entry);
 	if (debug_level >= 2) {
-		print_cnt = 20;
+		if (hnat_priv->data->version == MTK_HNAT_V3)
+			print_cnt = 28;
+		else
+			print_cnt = 20;
+
 		for (i = 0; i < print_cnt; i++)
 			pr_info("%02d: %08X\n", i, *(p + i));
 	}
@@ -619,10 +623,23 @@
 			"TCP" :	entry->ipv6_5t_route.bfib1.udp == 1 ?
 			"UDP" :	"Unknown");
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
-		pr_info("tport_id = %d, tops_entry = %d, cdrt_id = %d\n",
-			entry->ipv6_5t_route.tport_id,
-			entry->ipv6_5t_route.tops_entry,
-			entry->ipv6_5t_route.cdrt_id);
+		if (IS_IPV6_HNAT(entry) || IS_IPV6_HNAPT(entry)) {
+			pr_info("tport_id = %d, tops_entry = %d, cdrt_id = %d\n",
+				entry->ipv6_hnapt.tport_id,
+				entry->ipv6_hnapt.tops_entry,
+				entry->ipv6_hnapt.cdrt_id);
+
+		} else if (IS_IPV4_MAPE(entry) || IS_IPV4_MAPT(entry)) {
+			pr_info("tport_id = %d, tops_entry = %d, cdrt_id = %d\n",
+				entry->ipv4_mape.tport_id,
+				entry->ipv4_mape.tops_entry,
+				entry->ipv4_mape.cdrt_id);
+		} else {
+			pr_info("tport_id = %d, tops_entry = %d, cdrt_id = %d\n",
+				entry->ipv6_5t_route.tport_id,
+				entry->ipv6_5t_route.tops_entry,
+				entry->ipv6_5t_route.cdrt_id);
+		}
 #endif
 		pr_info("=========================================\n\n");
 	}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index 31c03ca..d34ac9d 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
@@ -1246,6 +1246,10 @@
 	switch (ntohs(eth->h_proto)) {
 	case ETH_P_IP:
 		iph = ip_hdr(skb);
+		/* Do not bind if pkt is fragmented */
+		if (ip_is_fragment(iph))
+			return 0;
+
 		switch (iph->protocol) {
 		case IPPROTO_UDP:
 			udp = 1;
@@ -2521,10 +2525,6 @@
 	if (unlikely(!skb_hnat_is_hashed(skb)))
 		return 0;
 
-	/* Do not bind if pkt is fragmented */
-	if (ip_is_fragment(ip_hdr(skb)))
-		return 0;
-
 	if (out->netdev_ops->ndo_flow_offload_check) {
 		out->netdev_ops->ndo_flow_offload_check(&hw_path);
 		out = (IS_GMAC1_MODE) ? hw_path.virt_dev : hw_path.dev;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
index a181fc2..0cadc85 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
@@ -468,6 +468,25 @@
 	udelay(400);
 }
 
+int mtk_usxgmii_link_status(struct mtk_usxgmii_pcs *mpcs)
+{
+	unsigned int val;
+
+	/* Refresh USXGMII link status by toggling RG_PCS_RX_STATUS_UPDATE */
+	regmap_read(mpcs->regmap, RG_PCS_RX_STATUS0, &val);
+	val |= RG_PCS_RX_STATUS_UPDATE;
+	regmap_write(mpcs->regmap, RG_PCS_RX_STATUS0, val);
+
+	regmap_read(mpcs->regmap, RG_PCS_RX_STATUS0, &val);
+	val &= ~RG_PCS_RX_STATUS_UPDATE;
+	regmap_write(mpcs->regmap, RG_PCS_RX_STATUS0, val);
+
+	/* Read USXGMII link status */
+	regmap_read(mpcs->regmap, RG_PCS_RX_STATUS0, &val);
+
+	return FIELD_GET(RG_PCS_RX_LINK_STATUS, val);
+}
+
 void mtk_usxgmii_reset(struct mtk_eth *eth, int id)
 {
 	u32 val = 0;
@@ -580,6 +599,7 @@
 
 	if (mpcs->interface != interface) {
 		mpcs->interface = interface;
+		mpcs->mode = mode;
 		mode_changed = true;
 	}
 
@@ -725,15 +745,34 @@
 	regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val);
 }
 
+void mtk_usxgmii_link_poll(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct mtk_usxgmii_pcs *mpcs = container_of(dwork, struct mtk_usxgmii_pcs, link_poll);
+
+	if (!mtk_usxgmii_link_status(mpcs)) {
+		mtk_usxgmii_pcs_config(&mpcs->pcs, mpcs->mode,
+				       mpcs->interface, NULL, false);
+
+		queue_delayed_work(system_power_efficient_wq, &mpcs->link_poll,
+				   msecs_to_jiffies(1000));
+	}
+}
+
 static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
 				    phy_interface_t interface,
 				    int speed, int duplex)
 {
+	struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
+
 	/* Reconfiguring USXGMII to ensure the quality of the RX signal
 	 * after the line side link up.
 	 */
 	mtk_usxgmii_pcs_config(pcs, mode,
 			       interface, NULL, false);
+
+	queue_delayed_work(system_power_efficient_wq, &mpcs->link_poll,
+			   msecs_to_jiffies(1000));
 }
 
 static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = {
@@ -765,6 +804,8 @@
 		ss->pcs[i].pcs.poll = true;
 		ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
 
+		INIT_DELAYED_WORK(&ss->pcs[i].link_poll, mtk_usxgmii_link_poll);
+
 		of_node_put(np);
 	}
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h.h
new file mode 100644
index 0000000..6a0afaf
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*************************************************
+ * FILE NAME:  air_en8811h.h
+ * PURPOSE:
+ *      EN8811H PHY Driver for Linux
+ * NOTES:
+ *
+ *  Copyright (C) 2023 Airoha Technology Corp.
+ *************************************************/
+#ifndef __EN8811H_H
+#define __EN8811H_H
+
+#define EN8811H_MD32_DM             "EthMD32.dm.bin"
+#define EN8811H_MD32_DSP            "EthMD32.DSP.bin"
+
+#define EN8811H_PHY_ID1             0x03a2
+#define EN8811H_PHY_ID2             0xa411
+#define EN8811H_PHY_ID              ((EN8811H_PHY_ID1 << 16) | EN8811H_PHY_ID2)
+#define EN8811H_PHY_READY           0x02
+#define MAX_RETRY                   25
+
+#define EN8811H_TX_POL_NORMAL   0x1
+#define EN8811H_TX_POL_REVERSE  0x0
+
+#define EN8811H_RX_POL_NORMAL   (0x0 << 1)
+#define EN8811H_RX_POL_REVERSE  (0x1 << 1)
+
+
+/***************************************************************
+ * The following led_cfg example is for reference only.
+ * LED0 Link 2500/Blink 2500 TxRx   (GPIO5)    <-> BASE_T_LED0,
+ * LED1 Link 1000/Blink 1000 TxRx   (GPIO4)    <-> BASE_T_LED1,
+ * LED2 Link 100 /Blink 100  TxRx   (GPIO3)    <-> BASE_T_LED2,
+ ***************************************************************/
+/* User-defined.B */
+#define AIR_LED0_ON     (LED_ON_EVT_LINK_2500M)
+#define AIR_LED0_BLK    (LED_BLK_EVT_2500M_TX_ACT | LED_BLK_EVT_2500M_RX_ACT)
+#define AIR_LED1_ON     (LED_ON_EVT_LINK_1000M)
+#define AIR_LED1_BLK    (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT)
+#define AIR_LED2_ON     (LED_ON_EVT_LINK_100M)
+#define AIR_LED2_BLK    (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT)
+/* User-defined.E */
+
+/* CL45 MDIO control */
+#define MII_MMD_ACC_CTL_REG         0x0d
+#define MII_MMD_ADDR_DATA_REG       0x0e
+#define MMD_OP_MODE_DATA            BIT(14)
+
+#define EN8811H_DRIVER_VERSION      "v1.2.2"
+
+#define LED_ON_CTRL(i)              (0x024 + ((i)*2))
+#define LED_ON_EN                   (1 << 15)
+#define LED_ON_POL                  (1 << 14)
+#define LED_ON_EVT_MASK             (0x1ff)
+/* LED ON Event Option.B */
+#define LED_ON_EVT_LINK_2500M       (1 << 8)
+#define LED_ON_EVT_FORCE            (1 << 6)
+#define LED_ON_EVT_LINK_DOWN        (1 << 3)
+#define LED_ON_EVT_LINK_100M        (1 << 1)
+#define LED_ON_EVT_LINK_1000M       (1 << 0)
+/* LED ON Event Option.E */
+
+#define LED_BLK_CTRL(i)             (0x025 + ((i)*2))
+#define LED_BLK_EVT_MASK            (0xfff)
+/* LED Blinking Event Option.B*/
+#define LED_BLK_EVT_2500M_RX_ACT    (1 << 11)
+#define LED_BLK_EVT_2500M_TX_ACT    (1 << 10)
+#define LED_BLK_EVT_FORCE           (1 << 9)
+#define LED_BLK_EVT_100M_RX_ACT     (1 << 3)
+#define LED_BLK_EVT_100M_TX_ACT     (1 << 2)
+#define LED_BLK_EVT_1000M_RX_ACT    (1 << 1)
+#define LED_BLK_EVT_1000M_TX_ACT    (1 << 0)
+/* LED Blinking Event Option.E*/
+#define EN8811H_LED_COUNT           3
+
+#define LED_BCR                     (0x021)
+#define LED_BCR_EXT_CTRL            (1 << 15)
+#define LED_BCR_CLK_EN              (1 << 3)
+#define LED_BCR_TIME_TEST           (1 << 2)
+#define LED_BCR_MODE_MASK           (3)
+#define LED_BCR_MODE_DISABLE        (0)
+
+#define LED_ON_DUR                  (0x022)
+#define LED_ON_DUR_MASK             (0xffff)
+
+#define LED_BLK_DUR                 (0x023)
+#define LED_BLK_DUR_MASK            (0xffff)
+
+#define UNIT_LED_BLINK_DURATION     1024
+
+#define GET_BIT(val, bit) ((val & BIT(bit)) >> bit)
+
+#define INVALID_DATA                0xffff
+#define PBUS_INVALID_DATA           0xffffffff
+
+struct en8811h_priv {
+	struct dentry       *debugfs_root;
+	unsigned int        dm_crc32;
+	unsigned int        dsp_crc32;
+	char                buf[512];
+};
+
+struct air_base_t_led_cfg {
+	u16 en;
+	u16 gpio;
+	u16 pol;
+	u16 on_cfg;
+	u16 blk_cfg;
+};
+enum air_led_gpio {
+	AIR_LED2_GPIO3 = 3,
+	AIR_LED1_GPIO4,
+	AIR_LED0_GPIO5,
+	AIR_LED_LAST
+};
+
+enum air_base_t_led {
+	AIR_BASE_T_LED0,
+	AIR_BASE_T_LED1,
+	AIR_BASE_T_LED2,
+	AIR_BASE_T_LED3
+};
+
+enum air_led_blk_dur {
+	AIR_LED_BLK_DUR_32M,
+	AIR_LED_BLK_DUR_64M,
+	AIR_LED_BLK_DUR_128M,
+	AIR_LED_BLK_DUR_256M,
+	AIR_LED_BLK_DUR_512M,
+	AIR_LED_BLK_DUR_1024M,
+	AIR_LED_BLK_DUR_LAST
+};
+
+enum air_led_polarity {
+	AIR_ACTIVE_LOW,
+	AIR_ACTIVE_HIGH,
+};
+enum air_led_mode {
+	AIR_LED_MODE_DISABLE,
+	AIR_LED_MODE_USER_DEFINE,
+	AIR_LED_MODE_LAST
+};
+
+#endif /* End of __EN8811H_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.c
new file mode 100644
index 0000000..731a37d
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.c
@@ -0,0 +1,1227 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*************************************************
+ * FILE NAME:  air_en8811h_api.c
+ * PURPOSE:
+ *      EN8811H PHY Driver for Linux
+ * NOTES:
+ *
+ *  Copyright (C) 2023 Airoha Technology Corp.
+ *************************************************/
+
+/* INCLUDE FILE DECLARATIONS
+*/
+#include <linux/uaccess.h>
+#include <linux/trace_seq.h>
+#include <linux/seq_file.h>
+#include <linux/u64_stats_sync.h>
+#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
+#include <linux/ctype.h>
+#include <linux/of_mdio.h>
+#include <linux/of_address.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/debugfs.h>
+
+#include "air_en8811h.h"
+#include "air_en8811h_api.h"
+
+/*
+struct air_phy_debug {
+	struct dentry *root;
+};
+struct air_phy_debug air_debug;
+*/
+
+static const char * const tx_rx_string[32] = {
+	"Tx Reverse, Rx Normal",
+	"Tx Normal, Rx Normal",
+	"Tx Reverse, Rx Reverse",
+	"Tx Normal, Rx Reverse",
+};
+
+/* Airoha MII read function */
+static int __air_mii_cl22_read(struct mii_bus *ebus,
+					int addr, unsigned int phy_register)
+{
+	int read_data;
+#if (KERNEL_VERSION(4, 16, 0) < LINUX_VERSION_CODE)
+	read_data = __mdiobus_read(ebus, addr, phy_register);
+#else
+	read_data = ebus->read(ebus, addr, phy_register);
+#endif
+	return read_data;
+}
+/* Airoha MII write function */
+static int __air_mii_cl22_write(struct mii_bus *ebus, int addr,
+			unsigned int phy_register, unsigned int write_data)
+{
+	int ret = 0;
+#if (KERNEL_VERSION(4, 16, 0) < LINUX_VERSION_CODE)
+	ret = __mdiobus_write(ebus, addr, phy_register, write_data);
+#else
+	ret = ebus->write(ebus, addr, phy_register, write_data);
+#endif
+	return ret;
+}
+
+/* Airoha MII read function */
+int air_mii_cl22_read(struct mii_bus *ebus, int addr, unsigned int phy_register)
+{
+	int read_data;
+
+	mutex_lock(&ebus->mdio_lock);
+	read_data = __air_mii_cl22_read(ebus, addr, phy_register);
+	mutex_unlock(&ebus->mdio_lock);
+	return read_data;
+}
+/* Airoha MII write function */
+int air_mii_cl22_write(struct mii_bus *ebus, int addr,
+			unsigned int phy_register, unsigned int write_data)
+{
+	int ret = 0;
+
+	mutex_lock(&ebus->mdio_lock);
+	ret = __air_mii_cl22_write(ebus, addr, phy_register, write_data);
+	mutex_unlock(&ebus->mdio_lock);
+	return ret;
+}
+
+static int __air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
+{
+	int ret = 0;
+	int data;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ACC_CTL_REG, devad);
+	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ADDR_DATA_REG, reg);
+	ret |= __air_mii_cl22_write(mbus, addr,
+			MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
+	if (ret) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return INVALID_DATA;
+	}
+	data = __air_mii_cl22_read(mbus, addr, MII_MMD_ADDR_DATA_REG);
+	return data;
+}
+
+static int __air_mii_cl45_write(struct phy_device *phydev,
+					int devad, u16 reg, u16 write_data)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ACC_CTL_REG, devad);
+	ret |= __air_mii_cl22_write(mbus, addr, MII_MMD_ADDR_DATA_REG, reg);
+	ret |= __air_mii_cl22_write(mbus, addr,
+		MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
+	ret |= __air_mii_cl22_write(mbus, addr,
+			MII_MMD_ADDR_DATA_REG, write_data);
+	if (ret) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
+{
+	int data;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	mutex_lock(&mbus->mdio_lock);
+	data = __air_mii_cl45_read(phydev, devad, reg);
+	mutex_unlock(&mbus->mdio_lock);
+	if (data == INVALID_DATA) {
+		dev_err(dev, "__airoha_cl45_read fail\n");
+		return INVALID_DATA;
+	}
+	return data;
+}
+
+int air_mii_cl45_write(struct phy_device *phydev,
+				int devad, u16 reg, u16 write_data)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	mutex_lock(&mbus->mdio_lock);
+	ret |= __air_mii_cl45_write(phydev, devad, reg, write_data);
+	mutex_unlock(&mbus->mdio_lock);
+	if (ret) {
+		dev_err(dev, "__airoha_cl45_write, ret: %d\n", ret);
+		return ret;
+	}
+	return ret;
+}
+
+/* EN8811H PBUS read function */
+static unsigned int __air_pbus_reg_read(struct phy_device *phydev,
+		unsigned int pbus_address)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+	struct device *dev = phydev_dev(phydev);
+	unsigned int pbus_data_low, pbus_data_high;
+	unsigned int pbus_data;
+	int ret = 0;
+
+	ret |= __air_mii_cl22_write(mbus, (addr + 8),
+			0x1F, (pbus_address >> 6));
+	pbus_data_low = __air_mii_cl22_read(mbus, (addr + 8),
+				((pbus_address >> 2) & 0xf));
+	pbus_data_high = __air_mii_cl22_read(mbus, (addr + 8), 0x10);
+	pbus_data = (pbus_data_high << 16) + pbus_data_low;
+	if (ret) {
+		dev_err(dev, "%s: ret: %d\n", __func__, ret);
+		return ret;
+	}
+	return pbus_data;
+}
+
+unsigned int air_pbus_reg_read(struct phy_device *phydev,
+		unsigned int pbus_address)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	struct device *dev = phydev_dev(phydev);
+	int ret = 0;
+	unsigned int data;
+
+	mutex_lock(&mbus->mdio_lock);
+	data = __air_pbus_reg_read(phydev, pbus_address);
+	mutex_unlock(&mbus->mdio_lock);
+	if (ret) {
+		dev_err(dev, "%s: ret: %d\n", __func__, ret);
+		return ret;
+	}
+	return data;
+}
+
+/* EN8811H PBUS write function */
+static int __air_pbus_reg_write(struct phy_device *phydev,
+		unsigned int pbus_address, unsigned long pbus_data)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+	struct device *dev = phydev_dev(phydev);
+	int ret = 0;
+
+	ret |= __air_mii_cl22_write(mbus, (addr + 8),
+			0x1F, (pbus_address >> 6));
+	ret |= __air_mii_cl22_write(mbus, (addr + 8),
+			((pbus_address >> 2) & 0xf), (pbus_data & 0xFFFF));
+	ret |= __air_mii_cl22_write(mbus, (addr + 8),
+			0x10, (pbus_data >> 16));
+	if (ret) {
+		dev_err(dev, "%s: ret: %d\n", __func__, ret);
+		return ret;
+	}
+	return 0;
+}
+
+int air_pbus_reg_write(struct phy_device *phydev,
+		unsigned int pbus_address, unsigned int pbus_data)
+{
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	struct device *dev = phydev_dev(phydev);
+	int ret = 0;
+
+	mutex_lock(&mbus->mdio_lock);
+	ret |= __air_pbus_reg_write(phydev, pbus_address, pbus_data);
+	mutex_unlock(&mbus->mdio_lock);
+	if (ret) {
+		dev_err(dev, "%s: ret: %d\n", __func__, ret);
+		return ret;
+	}
+	return 0;
+}
+/* EN8811H BUCK write function */
+static int __air_buckpbus_reg_write(struct phy_device *phydev,
+			unsigned int pbus_address, unsigned int pbus_data)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	/* page 4 */
+	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 4);
+	ret |= __air_mii_cl22_write(mbus, addr, 0x10, 0);
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x11, ((pbus_address >> 16) & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x12, (pbus_address & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x13, ((pbus_data >> 16) & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr, 0x14, (pbus_data & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 0);
+	if (ret < 0) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+/* EN8811H BUCK read function */
+static unsigned int __air_buckpbus_reg_read(struct phy_device *phydev,
+			unsigned int pbus_address)
+{
+	unsigned int pbus_data = 0, pbus_data_low, pbus_data_high;
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	/* page 4 */
+	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 4);
+	ret |= __air_mii_cl22_write(mbus, addr, 0x10, 0);
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x15, ((pbus_address >> 16) & 0xffff));
+	ret |= __air_mii_cl22_write(mbus, addr,
+			0x16, (pbus_address & 0xffff));
+	if (ret) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return PBUS_INVALID_DATA;
+	}
+
+	pbus_data_high = __air_mii_cl22_read(mbus, addr, 0x17);
+	pbus_data_low = __air_mii_cl22_read(mbus, addr, 0x18);
+	pbus_data = (pbus_data_high << 16) + pbus_data_low;
+	ret |= __air_mii_cl22_write(mbus, addr, 0x1F, 0);
+	if (ret) {
+		dev_err(dev, "__air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	return pbus_data;
+}
+
+unsigned int air_buckpbus_reg_read(struct phy_device *phydev,
+			unsigned int pbus_address)
+{
+	unsigned int data;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	mutex_lock(&mbus->mdio_lock);
+	data = __air_buckpbus_reg_read(phydev, pbus_address);
+	mutex_unlock(&mbus->mdio_lock);
+	if (data == INVALID_DATA) {
+		dev_err(dev, "__air_buckpbus_reg_read fail\n");
+		return INVALID_DATA;
+	}
+	return data;
+}
+
+int air_buckpbus_reg_write(struct phy_device *phydev,
+		unsigned int pbus_address, unsigned int pbus_data)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+
+	mutex_lock(&mbus->mdio_lock);
+	ret |= __air_buckpbus_reg_write(phydev, pbus_address, pbus_data);
+	mutex_unlock(&mbus->mdio_lock);
+	if (ret) {
+		dev_err(dev, "__air_buckpbus_reg_write, ret: %d\n", ret);
+		return ret;
+	}
+	return ret;
+}
+
+static int air_resolve_an_speed(struct phy_device *phydev)
+{
+	int lpagb = 0, advgb = 0, common_adv_gb = 0;
+	int lpa = 0, adv = 0, common_adv = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	dev_dbg(dev, "AN mode!\n");
+	dev_dbg(dev, "SPEED 1000/100!\n");
+	lpagb = air_mii_cl22_read(mbus,
+				addr, MII_STAT1000);
+	if (lpagb < 0)
+		return lpagb;
+	advgb = air_mii_cl22_read(mbus,
+				addr, MII_CTRL1000);
+	if (adv < 0)
+		return adv;
+	common_adv_gb = (lpagb & (advgb << 2));
+
+	lpa = air_mii_cl22_read(mbus, addr, MII_LPA);
+	if (lpa < 0)
+		return lpa;
+	adv = air_mii_cl22_read(mbus,
+				addr, MII_ADVERTISE);
+	if (adv < 0)
+		return adv;
+	phydev->pause = GET_BIT(adv, 10);
+	phydev->asym_pause = GET_BIT(adv, 11);
+	common_adv = (lpa & adv);
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_HALF;
+	if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF)) {
+		phydev->speed = SPEED_1000;
+		if (common_adv_gb & LPA_1000FULL)
+			phydev->duplex = DUPLEX_FULL;
+	} else if (common_adv & (LPA_100FULL | LPA_100HALF)) {
+		phydev->speed = SPEED_100;
+		if (common_adv & LPA_100FULL)
+			phydev->duplex = DUPLEX_FULL;
+	} else {
+		if (common_adv & LPA_10FULL)
+			phydev->duplex = DUPLEX_FULL;
+	}
+	return 0;
+}
+
+int air_get_autonego(struct phy_device *phydev, int *an)
+{
+	int reg;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	reg = air_mii_cl22_read(mbus, addr, MII_BMCR);
+	if (reg < 0)
+		return -EINVAL;
+	if (reg & BMCR_ANENABLE)
+		*an = AUTONEG_ENABLE;
+	else
+		*an = AUTONEG_DISABLE;
+	return 0;
+}
+
+static int air_read_status(struct phy_device *phydev)
+{
+	int ret = 0, reg = 0, an = AUTONEG_DISABLE, bmcr = 0;
+	u32 pbus_value = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_UNKNOWN;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+	phydev->link = 0;
+	phydev->autoneg = AUTONEG_DISABLE;
+	reg = air_mii_cl22_read(mbus, addr, MII_BMSR);
+	if (reg < 0) {
+		dev_err(dev, "MII_BMSR reg %d!\n", reg);
+		return reg;
+	}
+	reg = air_mii_cl22_read(mbus, addr, MII_BMSR);
+	if (reg < 0) {
+		dev_err(dev, "MII_BMSR reg %d!\n", reg);
+		return reg;
+	}
+	if (reg & BMSR_LSTATUS) {
+		phydev->link = 1;
+		ret = air_get_autonego(phydev, &an);
+		if (ret < 0)
+			return ret;
+		phydev->autoneg = an;
+		pbus_value = air_buckpbus_reg_read(phydev, 0x109D4);
+		if (0x10 & pbus_value) {
+			phydev->speed = SPEED_2500;
+			phydev->duplex = DUPLEX_FULL;
+		} else {
+			ret = air_get_autonego(phydev, &an);
+			if (phydev->autoneg == AUTONEG_ENABLE) {
+				ret = air_resolve_an_speed(phydev);
+				if (ret < 0)
+					return ret;
+			} else {
+				dev_dbg(dev, "Force mode!\n");
+				bmcr = air_mii_cl22_read(mbus, addr, MII_BMCR);
+
+				if (bmcr < 0)
+					return bmcr;
+
+				if (bmcr & BMCR_FULLDPLX)
+					phydev->duplex = DUPLEX_FULL;
+				else
+					phydev->duplex = DUPLEX_HALF;
+
+				if (bmcr & BMCR_SPEED1000)
+					phydev->speed = SPEED_1000;
+				else if (bmcr & BMCR_SPEED100)
+					phydev->speed = SPEED_100;
+				else
+					phydev->speed = SPEED_UNKNOWN;
+			}
+		}
+	}
+
+	return ret;
+}
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+static void air_polarity_help(void)
+{
+	pr_notice("\nUsage:\n"
+			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
+			"echo [tx polarity] [rx polarity] > /[debugfs]/polarity\n"
+			"option: tx_normal, tx_reverse, rx_normal, rx_revers\n");
+}
+
+static int air_set_polarity(struct phy_device *phydev, int tx_rx)
+{
+	int ret = 0;
+	unsigned int pbus_data = 0;
+
+	pr_debug("\nPolarit %s\n", tx_rx_string[tx_rx]);
+	pbus_data = air_buckpbus_reg_read(phydev, 0xca0f8) & ~(BIT(0) | BIT(1));
+	pbus_data |= tx_rx;
+	ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_data);
+	if (ret < 0)
+		pr_notice("\n%s:air_buckpbus_reg_write fail\n", __func__);
+	pbus_data = air_buckpbus_reg_read(phydev, 0xca0f8);
+	pr_notice("\nPolarity %s confirm....(%02lx)\n",
+			tx_rx_string[tx_rx], pbus_data & (BIT(0) | BIT(1)));
+
+	return ret;
+}
+
+static int air_set_mode(struct phy_device *phydev, int dbg_mode)
+{
+	int ret = 0, val = 0;
+	unsigned int pbus_data = 0;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	switch (dbg_mode) {
+	case AIR_PORT_MODE_FORCE_100:
+		pr_notice("\nForce 100M\n");
+		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) | BIT(8);
+		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) & ~BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl45_read(phydev, 0x7, 0x20) & ~BIT(7);
+		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_FORCE_1000:
+		pr_notice("\nForce 1000M\n");
+		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) & ~BIT(8);
+		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl45_read(phydev, 0x7, 0x20) & ~BIT(7);
+		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_FORCE_2500:
+		pr_notice("\nForce 2500M\n");
+		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) & ~BIT(8);
+		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) & ~BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl45_read(phydev, 0x7, 0x20) | BIT(7);
+		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_AUTONEGO:
+		pr_notice("\nAutonego mode\n");
+		val = air_mii_cl22_read(mbus, addr, MII_ADVERTISE) | BIT(8);
+		ret = air_mii_cl22_write(mbus, addr, MII_ADVERTISE, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_CTRL1000) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_CTRL1000, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl45_read(phydev, 0x7, 0x20) | BIT(7);
+		ret = air_mii_cl45_write(phydev, 0x7, 0x20, val);
+		if (unlikely(ret < 0))
+			break;
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(9);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_POWER_DOWN:
+		pr_notice("\nPower Down\n");
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) | BIT(11);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_POWER_UP:
+		pr_notice("\nPower Up\n");
+		val = air_mii_cl22_read(mbus, addr, MII_BMCR) & ~BIT(11);
+		ret = air_mii_cl22_write(mbus, addr, MII_BMCR, val);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_FC_DIS:
+		pr_notice("\nFlow Control Disabled\n");
+		pbus_data = air_buckpbus_reg_read(phydev, 0xe0008);
+		pbus_data &= ~(BIT(6) | BIT(7));
+		ret = air_buckpbus_reg_write(phydev, 0xe0008, pbus_data);
+		if (unlikely(ret < 0))
+			break;
+		pbus_data = air_buckpbus_reg_read(phydev, 0xe000c);
+		pbus_data &= ~(BIT(0) | BIT(1));
+		ret = air_buckpbus_reg_write(phydev, 0xe000c, pbus_data);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	case AIR_PORT_MODE_FC_EN:
+		pr_notice("\nFlow Control Enabled\n");
+		pbus_data = air_buckpbus_reg_read(phydev, 0xe0008);
+		pbus_data |= (BIT(6) | BIT(7));
+		ret = air_buckpbus_reg_write(phydev, 0xe0008, pbus_data);
+		if (unlikely(ret < 0))
+			break;
+		pbus_data = air_buckpbus_reg_read(phydev, 0xe000c);
+		pbus_data |= (BIT(0) | BIT(1));
+		ret = air_buckpbus_reg_write(phydev, 0xe000c, pbus_data);
+		if (unlikely(ret < 0))
+			break;
+		break;
+	default:
+		pr_notice("\nWrong Port mode\n");
+		break;
+	}
+	return ret;
+}
+
+static int airphy_info_show(struct seq_file *seq, void *v)
+{
+	struct phy_device *phydev = seq->private;
+	struct en8811h_priv *priv = phydev->priv;
+	unsigned int val = 0;
+
+	seq_puts(seq, "<<AIR EN8811H Driver info>>\n");
+	seq_printf(seq, "| Driver Version       : %s\n",
+							EN8811H_DRIVER_VERSION);
+	val = air_buckpbus_reg_read(phydev, 0xcf914);
+	seq_printf(seq, "| Boot mode            : %s\n",
+		((val & BIT(24)) >> 24) ? "Flash" : "Download Code");
+	seq_printf(seq, "| EthMD32.dm.bin  CRC32: %08x\n",
+				priv->dm_crc32);
+	seq_printf(seq, "| EthMD32.DSP.bin CRC32: %08x\n",
+				priv->dsp_crc32);
+	val = air_buckpbus_reg_read(phydev, 0x3b3c);
+	seq_printf(seq, "| MD32 FW Version      : %08x\n", val);
+	val = air_mii_cl45_read(phydev, 0x1e, 0x8009);
+	seq_printf(seq, "| MD32 FW Status       : %08x\n",
+				air_mii_cl45_read(phydev, 0x1e, 0x8009));
+	val = (air_buckpbus_reg_read(phydev, 0xca0f8) & 0x3);
+	seq_printf(seq, "| Tx, Rx Polarity      : %s(%02d)\n",
+						tx_rx_string[val], val);
+
+	seq_puts(seq, "\n");
+
+	return 0;
+}
+
+static int airphy_info_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, airphy_info_show, inode->i_private);
+}
+
+static int airphy_fcm_counter_show(struct phy_device *phydev,
+				struct seq_file *seq)
+{
+	int ret = 0;
+	u32 pkt_cnt = 0;
+
+	seq_puts(seq, "|\t<<FCM Counter>>\n");
+	seq_puts(seq, "| Rx from Line side_S      :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0090);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Rx from Line side_T      :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0094);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Tx to System side_S      :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe009c);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Tx to System side_T      :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe00A0);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Rx from System side_S    :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0078);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Rx from System side_T    :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe007C);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Tx to Line side_S        :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0084);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| Tx to Line side_T        :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0088);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	ret = air_buckpbus_reg_write(phydev, 0xe0074, 0xf);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int airphy_ss_counter_show(struct phy_device *phydev,
+				struct seq_file *seq)
+{
+	int ret = 0;
+	u32 pkt_cnt = 0;
+
+	seq_puts(seq, "|\t<<SS Counter>>\n");
+	ret = air_buckpbus_reg_write(phydev, 0xC602C, 0x3);
+	if (ret < 0)
+		return ret;
+	seq_puts(seq, "| TX Start                 :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60B0);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| TX Terminal              :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60B4);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| RX Start                 :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60BC);
+	seq_printf(seq, "%010u |\n", pkt_cnt);
+	seq_puts(seq, "| RX Terminal              :");
+	pkt_cnt = air_buckpbus_reg_read(phydev, 0xC60C0);
+	seq_printf(seq, "%010u |\n\n", pkt_cnt);
+	ret = air_buckpbus_reg_write(phydev, 0xC602C, 0x4);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int airphy_counter_show(struct seq_file *seq, void *v)
+{
+	struct phy_device *phydev = seq->private;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int ret = 0, addr = phydev_addr(phydev);
+	u32 pkt_cnt = 0;
+
+	ret = air_read_status(phydev);
+	if (ret < 0)
+		return ret;
+	seq_puts(seq, "==========AIR PHY COUNTER==========\n");
+	ret = airphy_fcm_counter_show(phydev, seq);
+	if (ret < 0)
+		return ret;
+	if (phydev->link && phydev->speed == SPEED_2500) {
+		seq_puts(seq, "|\t<<LS Counter>>\n");
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x10);
+		if (ret < 0)
+			return ret;
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x0);
+		if (ret < 0)
+			return ret;
+		seq_puts(seq, "|\tBefore EF\n");
+		seq_puts(seq, "| TX to Line side_S        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x3071c);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX to Line side_T        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30720);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX from Line side_S      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x3072c);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX from Line side_T      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30730);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX_ENC                   :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30724);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX_DEC                   :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30728);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "|\tAfter EF\n");
+		seq_puts(seq, "| TX to Line side_S        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30734);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX to Line side_T        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30738);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX from Line side_S      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30764);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX from Line side_T      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x30768);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x13);
+		if (ret < 0)
+			return ret;
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x3);
+		if (ret < 0)
+			return ret;
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x10);
+		if (ret < 0)
+			return ret;
+		ret = air_buckpbus_reg_write(phydev, 0x30718, 0x0);
+		if (ret < 0)
+			return ret;
+		seq_puts(seq, "|\t<<MAC Counter>>\n");
+		seq_puts(seq, "| TX Error from System side:");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x131000);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX Error to System side  :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x132000);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX from System side      :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x131004);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX to System Side        :");
+		pkt_cnt = air_buckpbus_reg_read(phydev, 0x132004);
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+	}
+	if (phydev->link && ((phydev->speed != SPEED_2500))) {
+		seq_puts(seq, "|\t<<LS Counter>>\n");
+		ret = air_mii_cl22_write(mbus, addr, 0x1f, 1);
+		if (ret < 0)
+			return ret;
+		seq_puts(seq, "| RX from Line side        :");
+		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x12) & 0x7fff;
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| RX Error from Line side  :");
+		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x17) & 0xff;
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0);
+		if (ret < 0)
+			return ret;
+		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0x52B5);
+		if (ret < 0)
+			return ret;
+		ret = air_mii_cl22_write(mbus, addr, 0x10, 0xBF92);
+		if (ret < 0)
+			return ret;
+		seq_puts(seq, "| TX to Line side          :");
+		pkt_cnt = (air_mii_cl22_read(mbus, addr, 0x11) & 0x7ffe) >> 1;
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		seq_puts(seq, "| TX Error to Line side    :");
+		pkt_cnt = air_mii_cl22_read(mbus, addr, 0x12);
+		pkt_cnt &= 0x7f;
+		seq_printf(seq, "%010u |\n", pkt_cnt);
+		ret = air_mii_cl22_write(mbus, addr, 0x1f, 0);
+		if (ret < 0)
+			return ret;
+	}
+	if (phydev->link) {
+		ret = airphy_ss_counter_show(phydev, seq);
+		if (ret < 0)
+			return ret;
+	}
+	return ret;
+}
+
+static int airphy_counter_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, airphy_counter_show, inode->i_private);
+}
+
+static ssize_t airphy_polarity_write(struct file *file, const char __user *ptr,
+					size_t len, loff_t *off)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[32], param1[32], param2[32];
+	int count = len, ret = 0, tx_rx = 0;
+
+	memset(buf, 0, 32);
+	memset(param1, 0, 32);
+	memset(param2, 0, 32);
+
+	if (count > sizeof(buf) - 1)
+		return -EINVAL;
+	if (copy_from_user(buf, ptr, len))
+		return -EFAULT;
+	if (sscanf(buf, "%s %s", param1, param2) == -1)
+		return -EFAULT;
+
+	if (!strncmp("tx_normal", param1, strlen("tx_normal"))) {
+		if (!strncmp("rx_normal", param2, strlen("rx_normal")))
+			tx_rx = AIR_POL_TX_NOR_RX_NOR;
+		else if (!strncmp("rx_reverse", param2, strlen("rx_reverse")))
+			tx_rx = AIR_POL_TX_NOR_RX_REV;
+		else {
+			pr_notice("\nRx param is not correct.\n");
+			return -EINVAL;
+		}
+	} else if (!strncmp("tx_reverse", param1, strlen("tx_reverse"))) {
+		if (!strncmp("rx_normal", param2, strlen("rx_normal")))
+			tx_rx = AIR_POL_TX_REV_RX_NOR;
+		else if (!strncmp("rx_reverse", param2, strlen("rx_reverse")))
+			tx_rx = AIR_POL_TX_REV_RX_REV;
+		else {
+			pr_notice("\nRx param is not correct.\n");
+			return -EINVAL;
+		}
+	} else {
+		air_polarity_help();
+		return count;
+	}
+	pr_notice("\nSet Polarity %s\n", tx_rx_string[tx_rx]);
+	ret = air_set_polarity(phydev, tx_rx);
+	if (ret < 0)
+		return ret;
+	return count;
+}
+static void airphy_port_mode_help(void)
+{
+	pr_notice("\nUsage:\n"
+			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
+			"echo [mode] [para] > /[debugfs]/port_mode\n"
+			"echo re-an > /[debugfs]/port_mode\n"
+			"echo auto > /[debugfs]/port_mode\n"
+			"echo 2500 > /[debugfs]/port_mode\n"
+			"echo 1000 > /[debugfs]/port_mode\n"
+			"echo 100 > /[debugfs]/port_mode\n"
+			"echo power up/down >  /[debugfs]/port_mode\n");
+}
+
+static ssize_t airphy_port_mode(struct file *file, const char __user *ptr,
+					size_t len, loff_t *off)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[32], cmd[32], param[32];
+	int count = len, ret = 0;
+	int num = 0, val = 0;
+
+	memset(buf, 0, 32);
+	memset(cmd, 0, 32);
+	memset(param, 0, 32);
+
+	if (count > sizeof(buf) - 1)
+		return -EINVAL;
+	if (copy_from_user(buf, ptr, len))
+		return -EFAULT;
+
+	num = sscanf(buf, "%s %s", cmd, param);
+	if (num < 1 || num > 3)
+		return -EFAULT;
+
+	if (!strncmp("auto", cmd, strlen("auto")))
+		ret = air_set_mode(phydev, AIR_PORT_MODE_AUTONEGO);
+	else if (!strncmp("2500", cmd, strlen("2500")))
+		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_2500);
+	else if (!strncmp("1000", cmd, strlen("1000")))
+		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_1000);
+	else if (!strncmp("100", cmd, strlen("100")))
+		ret = air_set_mode(phydev, AIR_PORT_MODE_FORCE_100);
+	else if (!strncmp("re-an", cmd, strlen("re-an"))) {
+		val = phy_read(phydev, MII_BMCR) | BIT(9);
+		ret = phy_write(phydev, MII_BMCR, val);
+	} else if (!strncmp("power", cmd, strlen("power"))) {
+		if (!strncmp("down", param, strlen("down")))
+			ret = air_set_mode(phydev, AIR_PORT_MODE_POWER_DOWN);
+		else if (!strncmp("up", param, strlen("up")))
+			ret = air_set_mode(phydev, AIR_PORT_MODE_POWER_UP);
+	} else if (!strncmp("int_sw_release", cmd, strlen("int_sw_release"))) {
+		ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
+		pr_notice("\ninternal sw reset released\n");
+	} else if (!strncmp("help", cmd, strlen("help"))) {
+		airphy_port_mode_help();
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static void airphy_debugfs_buckpbus_help(void)
+{
+	pr_notice("\nUsage:\n"
+			"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
+			"Read:\n"
+			"echo r [buckpbus_register] > /[debugfs]/buckpbus_op\n"
+			"Write:\n"
+			"echo w [buckpbus_register] [value] > /[debugfs]/buckpbus_op\n");
+}
+
+
+static ssize_t airphy_debugfs_buckpbus(struct file *file,
+					const char __user *buffer, size_t count,
+					loff_t *data)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[64];
+	int ret = 0;
+	unsigned int reg, val;
+
+	memset(buf, 0, 64);
+	if (count > sizeof(buf) - 1)
+		return -EINVAL;
+	if (copy_from_user(buf, buffer, count))
+		return -EFAULT;
+
+	if (buf[0] == 'w') {
+		if (sscanf(buf, "w %x %x", &reg, &val) == -1)
+			return -EFAULT;
+
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
+			phydev_addr(phydev), reg, val);
+		ret = air_buckpbus_reg_write(phydev, reg, val);
+		if (ret < 0) {
+			pr_notice("\nbuckpbus_reg_write fail\n");
+			return -EIO;
+		}
+		val = air_buckpbus_reg_read(phydev, reg);
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
+			phydev_addr(phydev), reg, val);
+	} else if (buf[0] == 'r') {
+		if (sscanf(buf, "r %x", &reg) == -1)
+			return -EFAULT;
+
+		val = air_buckpbus_reg_read(phydev, reg);
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
+		phydev_addr(phydev), reg, val);
+	} else
+		airphy_debugfs_buckpbus_help();
+
+	return count;
+}
+
+static ssize_t airphy_debugfs_pbus(struct file *file,
+					const char __user *buffer, size_t count,
+					loff_t *data)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[64];
+	int ret = 0;
+	unsigned int reg, val;
+
+	memset(buf, 0, 64);
+	if (copy_from_user(buf, buffer, count))
+		return -EFAULT;
+
+	if (buf[0] == 'w') {
+		if (sscanf(buf, "w %x %x", &reg, &val) == -1)
+			return -EFAULT;
+
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
+			phydev_addr(phydev), reg, val);
+		ret = air_pbus_reg_write(phydev, reg, val);
+		if (ret < 0) {
+			pr_notice("\npbus_reg_write fail\n");
+			return -EIO;
+		}
+		val = air_pbus_reg_read(phydev, reg);
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
+			phydev_addr(phydev), reg, val);
+	} else if (buf[0] == 'r') {
+		if (sscanf(buf, "r %x", &reg) == -1)
+			return -EFAULT;
+
+		val = air_pbus_reg_read(phydev, reg);
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
+		phydev_addr(phydev), reg, val);
+	} else
+		airphy_debugfs_buckpbus_help();
+
+	return count;
+}
+
+static int airphy_link_status(struct seq_file *seq, void *v)
+{
+	int ret = 0;
+	struct phy_device *phydev = seq->private;
+
+	ret = air_read_status(phydev);
+	if (ret < 0)
+		return ret;
+
+	seq_printf(seq, "%s Information:\n", dev_name(phydev_dev(phydev)));
+	seq_printf(seq, "\tPHYAD: %02d\n", phydev_addr(phydev));
+	seq_printf(seq, "\tLink Status: %s\n", phydev->link ? "UP" : "DOWN");
+	if (phydev->link) {
+		seq_printf(seq, "\tAuto-Nego: %s\n",
+				phydev->autoneg ? "on" : "off");
+		seq_puts(seq, "\tSpeed: ");
+		if (phydev->speed == SPEED_UNKNOWN)
+			seq_printf(seq, "Unknown! (%i)\n", phydev->speed);
+		else
+			seq_printf(seq, "%uMb/s\n", phydev->speed);
+
+		seq_printf(seq, "\tDuplex: %s\n",
+				phydev->duplex ? "Full" : "Half");
+		seq_puts(seq, "\n");
+	}
+
+	return ret;
+}
+
+static int airphy_link_status_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, airphy_link_status, inode->i_private);
+}
+
+static int dbg_regs_show(struct seq_file *seq, void *v)
+{
+	struct phy_device *phydev = seq->private;
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+	seq_puts(seq, "\t<<DEBUG REG DUMP>>\n");
+	seq_printf(seq, "| RG_MII_BMSR           : 0x%08x |\n",
+		   air_mii_cl22_read(mbus, addr, MII_BMSR));
+	seq_printf(seq, "| RG_MII_REF_CLK        : 0x%08x |\n",
+		   air_mii_cl22_read(mbus, addr, 0x1d));
+	seq_printf(seq, "| RG_SYS_LINK_MODE      : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xe0004));
+	seq_printf(seq, "| RG_FCM_CTRL           : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xe000C));
+	seq_printf(seq, "| RG_SS_PAUSE_TIME      : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xe0020));
+	seq_printf(seq, "| RG_MIN_IPG_NUM        : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xe002C));
+	seq_printf(seq, "| RG_CTROL_0            : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xc0000));
+	seq_printf(seq, "| RG_LINK_STATUS        : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xc0b04));
+	seq_printf(seq, "| RG_LINK_PARTNER_AN    : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0xc0014));
+	seq_printf(seq, "| RG_FN_PWR_CTRL_STATUS : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0x1020c));
+	seq_printf(seq, "| RG_WHILE_LOOP_COUNT   : 0x%08x |\n",
+		   air_buckpbus_reg_read(phydev, 0x3A48));
+
+	return 0;
+}
+
+static int airphy_dbg_regs_show_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dbg_regs_show, inode->i_private);
+}
+
+static const struct file_operations airphy_info_fops = {
+	.owner = THIS_MODULE,
+	.open = airphy_info_open,
+	.read = seq_read,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_counter_fops = {
+	.owner = THIS_MODULE,
+	.open = airphy_counter_open,
+	.read = seq_read,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_debugfs_buckpbus_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = airphy_debugfs_buckpbus,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_debugfs_pbus_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = airphy_debugfs_pbus,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_port_mode_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = airphy_port_mode,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_polarity_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = airphy_polarity_write,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_link_status_fops = {
+	.owner = THIS_MODULE,
+	.open = airphy_link_status_open,
+	.read = seq_read,
+	.llseek = noop_llseek,
+};
+
+static const struct file_operations airphy_dbg_reg_show_fops = {
+	.owner = THIS_MODULE,
+	.open = airphy_dbg_regs_show_open,
+	.read = seq_read,
+	.llseek = noop_llseek,
+};
+
+int airphy_debugfs_init(struct phy_device *phydev)
+{
+	int ret = 0;
+	struct en8811h_priv *priv = phydev->priv;
+	struct dentry *dir = priv->debugfs_root;
+
+	dev_dbg(phydev_dev(phydev), "%s: start\n", __func__);
+	dir = debugfs_create_dir(dev_name(phydev_dev(phydev)), NULL);
+	if (!dir) {
+		dev_err(phydev_dev(phydev), "%s:err at %d\n",
+					 __func__, __LINE__);
+		ret = -ENOMEM;
+	}
+	debugfs_create_file(DEBUGFS_DRIVER_INFO, S_IFREG | 0444,
+					dir, phydev,
+					&airphy_info_fops);
+	debugfs_create_file(DEBUGFS_COUNTER, S_IFREG | 0444,
+					dir, phydev,
+					&airphy_counter_fops);
+	debugfs_create_file(DEBUGFS_BUCKPBUS_OP, S_IFREG | 0200,
+					dir, phydev,
+					&airphy_debugfs_buckpbus_fops);
+	debugfs_create_file(DEBUGFS_PBUS_OP, S_IFREG | 0200,
+					dir, phydev,
+					&airphy_debugfs_pbus_fops);
+	debugfs_create_file(DEBUGFS_PORT_MODE, S_IFREG | 0200,
+					dir, phydev,
+					&airphy_port_mode_fops);
+	debugfs_create_file(DEBUGFS_POLARITY, S_IFREG | 0200,
+					dir, phydev,
+					&airphy_polarity_fops);
+	debugfs_create_file(DEBUGFS_LINK_STATUS, S_IFREG | 0444,
+					dir, phydev,
+					&airphy_link_status_fops);
+	debugfs_create_file(DEBUGFS_DBG_REG_SHOW, S_IFREG | 0444,
+					dir, phydev,
+					&airphy_dbg_reg_show_fops);
+
+	priv->debugfs_root = dir;
+	return ret;
+}
+
+void air_debugfs_remove(struct phy_device *phydev)
+{
+	struct en8811h_priv *priv = phydev->priv;
+
+	debugfs_remove_recursive(priv->debugfs_root);
+	priv->debugfs_root = NULL;
+}
+#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.h
new file mode 100644
index 0000000..a1d97ce
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_api.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*************************************************
+ * FILE NAME:  air_en8811h_api.h
+ * PURPOSE:
+ *      EN8811H PHY Driver for Linux
+ * NOTES:
+ *
+ *  Copyright (C) 2023 Airoha Technology Corp.
+ *************************************************/
+#ifndef __EN8811H_API_H
+#define __EN8811H_API_H
+#include <linux/version.h>
+
+#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
+#define phydev_mdio_bus(_dev) (_dev->bus)
+#define phydev_addr(_dev) (_dev->addr)
+#define phydev_dev(_dev) (&_dev->dev)
+#else
+#define phydev_mdio_bus(_dev) (_dev->mdio.bus)
+#define phydev_addr(_dev) (_dev->mdio.addr)
+#define phydev_dev(_dev) (&_dev->mdio.dev)
+#endif
+
+#define BUFFER_LENGTH 512
+
+#define DEBUGFS_COUNTER		        "counter"
+#define DEBUGFS_DRIVER_INFO	        "drvinfo"
+#define DEBUGFS_PORT_MODE           "port_mode"
+#define DEBUGFS_BUCKPBUS_OP         "buckpbus_op"
+#define DEBUGFS_PBUS_OP             "pbus_op"
+#define DEBUGFS_POLARITY            "polarity"
+#define DEBUGFS_LINK_STATUS         "link_status"
+#define DEBUGFS_DBG_REG_SHOW        "dbg_regs_show"
+
+enum air_port_mode {
+	AIR_PORT_MODE_FORCE_100,
+	AIR_PORT_MODE_FORCE_1000,
+	AIR_PORT_MODE_FORCE_2500,
+	AIR_PORT_MODE_AUTONEGO,
+	AIR_PORT_MODE_POWER_DOWN,
+	AIR_PORT_MODE_POWER_UP,
+	AIR_PORT_MODE_FC_UNSUPPORT,
+	AIR_PORT_MODE_FC_SUPPORT,
+	AIR_PORT_MODE_FC_DIS,
+	AIR_PORT_MODE_FC_EN,
+	AIR_PORT_MODE_LAST = 0xFF,
+};
+
+enum air_polarity {
+	AIR_POL_TX_REV_RX_NOR,
+	AIR_POL_TX_NOR_RX_NOR,
+	AIR_POL_TX_REV_RX_REV,
+	AIR_POL_TX_NOR_RX_REV,
+	AIR_POL_TX_NOR_RX_LAST = 0xff,
+};
+
+/* Link mode bit indices */
+enum air_link_mode_bit {
+	AIR_LINK_MODE_10baseT_Half_BIT	 = 0,
+	AIR_LINK_MODE_10baseT_Full_BIT	 = 1,
+	AIR_LINK_MODE_100baseT_Half_BIT	 = 2,
+	AIR_LINK_MODE_100baseT_Full_BIT	 = 3,
+	AIR_LINK_MODE_1000baseT_Full_BIT = 4,
+	AIR_LINK_MODE_2500baseT_Full_BIT = 5,
+};
+
+#ifndef unlikely
+#  define unlikely(x)	(x)
+#endif
+int air_pbus_reg_write(struct phy_device *phydev,
+	unsigned int pbus_address, unsigned int pbus_data);
+int air_mii_cl22_write(struct mii_bus *ebus, int addr,
+	unsigned int phy_register, unsigned int write_data);
+int air_mii_cl22_read(struct mii_bus *ebus,
+	int addr, unsigned int phy_register);
+int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg);
+int air_mii_cl45_write(struct phy_device *phydev,
+	int devad, u16 reg, u16 write_data);
+unsigned int air_buckpbus_reg_read(struct phy_device *phydev,
+	unsigned int pbus_address);
+int air_buckpbus_reg_write(struct phy_device *phydev,
+	unsigned int pbus_address, unsigned int pbus_data);
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+int airphy_debugfs_init(struct phy_device *phydev);
+void air_debugfs_remove(struct phy_device *phydev);
+#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
+#endif /* End of __EN8811H_API_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_main.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_main.c
new file mode 100644
index 0000000..353e226
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/air_en8811h_main.c
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: GPL-2.0
+/*************************************************
+ * FILE NAME:  air_en8811h_main.c
+ * PURPOSE:
+ *      EN8811H PHY Driver for Linux
+ * NOTES:
+ *
+ *  Copyright (C) 2023 Airoha Technology Corp.
+ *************************************************/
+
+/* INCLUDE FILE DECLARATIONS
+*/
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+#include <linux/debugfs.h>
+#include "air_en8811h_api.h"
+#include "air_en8811h.h"
+
+MODULE_DESCRIPTION("Airoha EN8811H PHY Drivers");
+MODULE_AUTHOR("Airoha");
+MODULE_LICENSE("GPL");
+
+/**************************
+ * GPIO5  <-> BASE_T_LED0,
+ * GPIO4  <-> BASE_T_LED1,
+ * GPIO3  <-> BASE_T_LED2,
+ **************************/
+/* User-defined.B */
+#define AIR_LED_SUPPORT
+#ifdef AIR_LED_SUPPORT
+static const struct air_base_t_led_cfg led_cfg[3] = {
+/********************************************************************
+ *Enable,     GPIO,        LED Polarity,    LED ON,     LED Blink
+*********************************************************************/
+	{1,	 AIR_LED0_GPIO5, AIR_ACTIVE_HIGH, AIR_LED0_ON, AIR_LED0_BLK},
+	{1,	 AIR_LED1_GPIO4, AIR_ACTIVE_HIGH, AIR_LED1_ON, AIR_LED1_BLK},
+	{1,	 AIR_LED2_GPIO3, AIR_ACTIVE_HIGH, AIR_LED2_ON, AIR_LED2_BLK},
+};
+static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
+#endif
+/* User-defined.E */
+
+/***********************************************************
+ *                  F U N C T I O N S
+ ***********************************************************/
+
+
+static int MDIOWriteBuf(struct phy_device *phydev,
+		unsigned long address, const struct firmware *fw)
+{
+	unsigned int write_data, offset;
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+	/* page 4 */
+	ret = air_mii_cl22_write(mbus, addr, 0x1F, 4);
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	/* address increment*/
+	ret = air_mii_cl22_write(mbus, addr, 0x10, 0x8000);
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	ret = air_mii_cl22_write(mbus, addr, 0x11,
+					(u32)((address >> 16) & 0xffff));
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	ret = air_mii_cl22_write(mbus, addr, 0x12, (u32)(address & 0xffff));
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+
+	for (offset = 0; offset < fw->size; offset += 4) {
+		write_data = (fw->data[offset + 3] << 8) | fw->data[offset + 2];
+		ret = air_mii_cl22_write(mbus, addr, 0x13, write_data);
+		if (ret < 0) {
+			dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+			return ret;
+		}
+		write_data = (fw->data[offset + 1] << 8) | fw->data[offset];
+		ret = air_mii_cl22_write(mbus, addr, 0x14, write_data);
+		if (ret < 0) {
+			dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+			return ret;
+		}
+	}
+	ret = air_mii_cl22_write(mbus, addr, 0x1F, 0);
+	if (ret < 0) {
+		dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int en8811h_load_firmware(struct phy_device *phydev)
+{
+	struct device *dev = phydev_dev(phydev);
+	const struct firmware *fw;
+	const char *firmware;
+	int ret = 0;
+	u32 pbus_value = 0;
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	struct en8811h_priv *priv = phydev->priv;
+#endif
+	ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x0);
+	if (ret < 0)
+		return ret;
+	pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
+	pbus_value |= BIT(11);
+	ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
+	if (ret < 0)
+		return ret;
+	firmware = EN8811H_MD32_DM;
+	ret = request_firmware_direct(&fw, firmware, dev);
+	if (ret < 0) {
+		dev_err(dev,
+			"failed to load firmware %s, ret: %d\n", firmware, ret);
+		return ret;
+	}
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	priv->dm_crc32 = ~crc32(~0, fw->data, fw->size);
+#endif
+	dev_info(dev, "%s: crc32=0x%x\n",
+		firmware, ~crc32(~0, fw->data, fw->size));
+	/* Download DM */
+	ret = MDIOWriteBuf(phydev, 0x00000000, fw);
+	release_firmware(fw);
+	if (ret < 0) {
+		dev_err(dev,
+			"MDIOWriteBuf 0x00000000 fail, ret: %d\n", ret);
+		return ret;
+	}
+
+	firmware = EN8811H_MD32_DSP;
+	ret = request_firmware_direct(&fw, firmware, dev);
+	if (ret < 0) {
+		dev_info(dev,
+			"failed to load firmware %s, ret: %d\n", firmware, ret);
+		return ret;
+	}
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	priv->dsp_crc32 = ~crc32(~0, fw->data, fw->size);
+#endif
+	dev_info(dev, "%s: crc32=0x%x\n",
+		firmware, ~crc32(~0, fw->data, fw->size));
+	/* Download PM */
+	ret = MDIOWriteBuf(phydev, 0x00100000, fw);
+	release_firmware(fw);
+	if (ret < 0) {
+		dev_err(dev,
+			"MDIOWriteBuf 0x00100000 fail , ret: %d\n", ret);
+		return ret;
+	}
+	pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
+	pbus_value &= ~BIT(11);
+	ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
+	if (ret < 0)
+		return ret;
+	ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+#ifdef AIR_LED_SUPPORT
+static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity,
+				int polar, u16 on_evt, u16 blk_evt)
+{
+	int ret = 0;
+
+	if (polar == AIR_ACTIVE_HIGH)
+		on_evt |= LED_ON_POL;
+	else
+		on_evt &= ~LED_ON_POL;
+
+	ret = air_mii_cl45_write(phydev, 0x1f,
+			LED_ON_CTRL(entity), on_evt | LED_ON_EN);
+	if (ret < 0)
+		return ret;
+	ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
+{
+	u16 cl45_data;
+	int err = 0;
+	struct device *dev = phydev_dev(phydev);
+
+	cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR);
+	switch (mode) {
+	case AIR_LED_MODE_DISABLE:
+		cl45_data &= ~LED_BCR_EXT_CTRL;
+		cl45_data &= ~LED_BCR_MODE_MASK;
+		cl45_data |= LED_BCR_MODE_DISABLE;
+		break;
+	case AIR_LED_MODE_USER_DEFINE:
+		cl45_data |= LED_BCR_EXT_CTRL;
+		cl45_data |= LED_BCR_CLK_EN;
+		break;
+	default:
+		dev_err(dev, "LED mode%d is not supported!\n", mode);
+		return -EINVAL;
+	}
+	err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
+{
+	u16 cl45_data = 0;
+	int err;
+
+	cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
+	if (state == 1)
+		cl45_data |= LED_ON_EN;
+	else
+		cl45_data &= ~LED_ON_EN;
+
+	err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int en8811h_led_init(struct phy_device *phydev)
+{
+
+	unsigned long led_gpio = 0, reg_value = 0;
+	u16 cl45_data = led_dur;
+	int ret = 0, id;
+	struct device *dev = phydev_dev(phydev);
+
+	ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
+	if (ret < 0)
+		return ret;
+	cl45_data >>= 1;
+	ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
+	if (ret < 0)
+		return ret;
+	ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
+	if (ret != 0) {
+		dev_err(dev, "led_set_mode fail(ret:%d)!\n", ret);
+		return ret;
+	}
+	for (id = 0; id < EN8811H_LED_COUNT; id++) {
+		/* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */
+		if (led_cfg[id].gpio != (id + (AIR_LED0_GPIO5 - (2 * id)))) {
+			dev_err(dev, "LED%d uses incorrect GPIO%d !\n",
+							id, led_cfg[id].gpio);
+			return -EINVAL;
+		}
+		ret = airoha_led_set_state(phydev, id, led_cfg[id].en);
+		if (ret != 0) {
+			dev_err(dev, "led_set_state fail(ret:%d)!\n", ret);
+			return ret;
+		}
+		if (led_cfg[id].en == 1) {
+			led_gpio |= BIT(led_cfg[id].gpio);
+			ret = airoha_led_set_usr_def(phydev, id,
+				led_cfg[id].pol, led_cfg[id].on_cfg,
+				led_cfg[id].blk_cfg);
+			if (ret != 0) {
+				dev_err(dev, "led_set_usr_def fail!\n");
+				return ret;
+			}
+		}
+	}
+	reg_value = air_buckpbus_reg_read(phydev, 0xcf8b8) | led_gpio;
+	ret = air_buckpbus_reg_write(phydev, 0xcf8b8, reg_value);
+	if (ret < 0)
+		return ret;
+	dev_info(dev, "LED initialize OK !\n");
+	return 0;
+}
+#endif /* AIR_LED_SUPPORT */
+#if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE)
+static int en8811h_get_features(struct phy_device *phydev)
+{
+	int ret;
+	struct device *dev = phydev_dev(phydev);
+
+	dev_dbg(dev, "%s()\n", __func__);
+	ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
+	if (ret < 0)
+		return ret;
+	ret = genphy_read_abilities(phydev);
+	if (ret)
+		return ret;
+	/* EN8811H supports 100M/1G/2.5G speed. */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+			phydev->supported);
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+			phydev->supported);
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+			phydev->supported);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+			phydev->supported);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+			phydev->supported);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+			phydev->supported);
+	return 0;
+}
+#endif
+
+static int en8811h_probe(struct phy_device *phydev)
+{
+	int ret = 0;
+	int reg_value, pid1 = 0, pid2 = 0;
+	u32 retry, pbus_value = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct mii_bus *mbus = phydev_mdio_bus(phydev);
+	int addr = phydev_addr(phydev);
+
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	struct en8811h_priv *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	phydev->priv = priv;
+#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
+	ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
+	if (ret < 0)
+		return ret;
+	pid1 = air_mii_cl22_read(mbus, addr, MII_PHYSID1);
+	if (pid1 < 0)
+		return pid1;
+	pid2 = air_mii_cl22_read(mbus, addr, MII_PHYSID2);
+	if (pid2 < 0)
+		return pid2;
+	dev_info(dev, "PHY = %x - %x\n", pid1, pid2);
+	if ((pid1 != EN8811H_PHY_ID1) || (pid2 != EN8811H_PHY_ID2)) {
+		dev_err(dev, "EN8811H dose not exist !\n");
+		return -ENODEV;
+	}
+	pbus_value = air_buckpbus_reg_read(phydev, 0xcf914);
+	dev_info(dev, "Bootmode: %s\n",
+			(GET_BIT(pbus_value, 24) ? "Flash" : "Download Code"));
+
+	ret = en8811h_load_firmware(phydev);
+	if (ret < 0) {
+		dev_err(dev, "EN8811H load firmware fail.\n");
+		return ret;
+	}
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	ret = airphy_debugfs_init(phydev);
+	if (ret < 0) {
+		dev_err(dev, "air_debug_procfs_init fail. (ret=%d)\n", ret);
+		air_debugfs_remove(phydev);
+		kfree(priv);
+		return ret;
+	}
+#endif /* CONFIG_AIROHA_EN8811H_PHY_DEBUGFS */
+	retry = MAX_RETRY;
+	do {
+		mdelay(300);
+		reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009);
+		if (reg_value == EN8811H_PHY_READY) {
+			dev_info(dev, "EN8811H PHY ready!\n");
+			break;
+		}
+		retry--;
+	} while (retry);
+	if (retry == 0) {
+		dev_err(dev, "MD32 FW is not ready.(Status 0x%x)\n", reg_value);
+		pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
+		dev_err(dev,
+			"Check MD32 FW Version(0x3b3c) : %08x\n", pbus_value);
+		dev_err(dev,
+			"EN8811H initialize fail!\n");
+		return 0;
+	}
+	/* Mode selection*/
+	dev_info(dev, "EN8811H Mode 1 !\n");
+	ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
+	if (ret < 0)
+		return ret;
+	ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
+	if (ret < 0)
+		return ret;
+	ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
+	if (ret < 0)
+		return ret;
+	ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
+	if (ret < 0)
+		return ret;
+	/* Serdes polarity */
+	pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
+	pbus_value &= ~0x3;
+	pbus_value |= (EN8811H_RX_POL_NORMAL | EN8811H_TX_POL_NORMAL);
+	ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value);
+	if (ret < 0)
+		return ret;
+	pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
+	dev_info(dev, "Tx, Rx Polarity : %08x\n", pbus_value);
+	pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
+	dev_info(dev, "MD32 FW Version : %08x\n", pbus_value);
+#if defined(AIR_LED_SUPPORT)
+	ret = en8811h_led_init(phydev);
+	if (ret < 0) {
+		dev_err(dev, "en8811h_led_init fail. (ret=%d)\n", ret);
+		return ret;
+	}
+#endif
+	dev_info(dev, "EN8811H initialize OK! (%s)\n", EN8811H_DRIVER_VERSION);
+	return 0;
+}
+void en8811h_remove(struct phy_device *phydev)
+{
+#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
+	struct en8811h_priv *priv = phydev->priv;
+	struct device *dev = phydev_dev(phydev);
+
+	dev_dbg(dev, "%s: start\n", __func__);
+	if (priv) {
+		dev_info(dev, "%s: air_debugfs_remove\n", __func__);
+		air_debugfs_remove(phydev);
+		kfree(priv);
+	}
+#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
+}
+
+static struct phy_driver en8811h_driver[] = {
+{
+	.phy_id         = EN8811H_PHY_ID,
+	.name           = "Airoha EN8811H",
+	.phy_id_mask    = 0x0ffffff0,
+	.probe          = en8811h_probe,
+	.remove         = en8811h_remove,
+#if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE)
+	.get_features   = en8811h_get_features,
+	.read_mmd       = air_mii_cl45_read,
+	.write_mmd      = air_mii_cl45_write,
+#endif
+} };
+
+int __init en8811h_phy_driver_register(void)
+{
+	int ret;
+#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
+	ret = phy_driver_register(en8811h_driver);
+#else
+	ret = phy_driver_register(en8811h_driver, THIS_MODULE);
+#endif
+	if (!ret)
+		return 0;
+
+	phy_driver_unregister(en8811h_driver);
+	return ret;
+}
+
+void __exit en8811h_phy_driver_unregister(void)
+{
+	phy_driver_unregister(en8811h_driver);
+}
+
+module_init(en8811h_phy_driver_register);
+module_exit(en8811h_phy_driver_unregister);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3000-mtketh-add-threaded-napi-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3000-mtketh-add-threaded-napi-support.patch
deleted file mode 100755
index 99e3ecd..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3000-mtketh-add-threaded-napi-support.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index b6bdb978..9de1b0b6 100755
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3696,6 +3696,9 @@ static int mtk_probe(struct platform_device *pdev)
- 	 * for NAPI to work
- 	 */
- 	init_dummy_netdev(&eth->dummy_dev);
-+	eth->dummy_dev.threaded = 1;
-+	strcpy(eth->dummy_dev.name, "mtk_eth");
-+
- 	netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx,
- 		       MTK_NAPI_WEIGHT);
- 	netif_napi_add(&eth->dummy_dev, &eth->rx_napi[0].napi, mtk_napi_rx,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mtketh-add-qdma-sw-solution-for-mac80211-sdk.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mtketh-add-qdma-sw-solution-for-mac80211-sdk.patch
deleted file mode 100755
index 2089f78..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mtketh-add-qdma-sw-solution-for-mac80211-sdk.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 28cda15..d34a3a0 100755
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -24,6 +24,7 @@
- #include "mtk_eth_soc.h"
- #include "mtk_eth_dbg.h"
- #include "mtk_eth_reset.h"
-+#include "mtk_hnat/hnat.h"
- 
- #if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE)
- #include "mtk_hnat/nf_hnat_mtk.h"
-@@ -3722,6 +3722,10 @@ static int mtk_probe(struct platform_device *pdev)
-	timer_setup(&eth->mtk_dma_monitor_timer, mtk_dma_monitor, 0);
-	eth->mtk_dma_monitor_timer.expires = jiffies;
- 	add_timer(&eth->mtk_dma_monitor_timer);
--#endif
-+
-+	mtk_w32(eth, 0x00000404, MTK_QTX_CFG(MTK_QDMA_GMAC2_QID));
-+	mtk_w32(eth, 0x40000000, MTK_QTX_SCH(MTK_QDMA_GMAC2_QID));
-+	mtk_w32(eth, 0x80008000, QDMA_TX_4SCH_BASE(0));
-+#endif
-
- 	return 0;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3003-mt7622-backport-nf-hw-offload-framework-and-ups.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3003-mt7622-backport-nf-hw-offload-framework-and-ups.patch
index b52782f..9ccf9ce 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3003-mt7622-backport-nf-hw-offload-framework-and-ups.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3003-mt7622-backport-nf-hw-offload-framework-and-ups.patch
@@ -237,7 +237,7 @@
 index 000000000..66298e223
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -0,0 +1,509 @@
+@@ -0,0 +1,510 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
 +
@@ -714,6 +714,7 @@
 +	val = MTK_PPE_GLO_CFG_EN |
 +	      MTK_PPE_GLO_CFG_IP4_L4_CS_DROP |
 +	      MTK_PPE_GLO_CFG_IP4_CS_DROP |
++	      MTK_PPE_GLO_CFG_MCAST_TB_EN |
 +	      MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE;
 +	ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
 +
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3005-add-wed.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3005-add-wed.patch
index 201a629..fe2dc53 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3005-add-wed.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3005-add-wed.patch
@@ -138,7 +138,7 @@
 index 819d8a0be..2121335a1
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -20,12 +21,14 @@
+@@ -20,11 +21,13 @@
  #include <linux/pinctrl/devinfo.h>
  #include <linux/phylink.h>
  #include <linux/gpio/consumer.h>
@@ -148,7 +148,6 @@
  #include "mtk_eth_soc.h"
  #include "mtk_eth_dbg.h"
  #include "mtk_eth_reset.h"
- #include "mtk_hnat/hnat.h"
 +#include "mtk_wed.h"
  
  #if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE)
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3006-ethernet-update-ppe-from-mt7622-to-mt7986.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3006-ethernet-update-ppe-from-mt7622-to-mt7986.patch
index c0efd12..dcfd527 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3006-ethernet-update-ppe-from-mt7622-to-mt7986.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3006-ethernet-update-ppe-from-mt7622-to-mt7986.patch
@@ -49,7 +49,7 @@
 @@ -3926,13 +3937,14 @@ static const struct mtk_soc_data mt7986_data = {
  	.required_clks = MT7986_CLKS_BITMAP,
  	.required_pctl = false,
- 	.has_sram = true,
+ 	.has_sram = false,
 +	.offload_version = 2,
 	.rss_num = 0,
  	.txrx = {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
index ee370b6..9957670 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
@@ -1,25 +1,25 @@
-From a59cb5c770a694cb34ab179ec59e91ba5c39908b Mon Sep 17 00:00:00 2001
-From: Bo Jiao <Bo.Jiao@mediatek.com>
-Date: Mon, 27 Jun 2022 14:48:35 +0800
-Subject: [PATCH 6/8] 9995-flow-offload-add-mkhnat-dual-ppe-new-v2
+From 8341e1b35ca14f303f828f96f5e63719f80be88e Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Wed, 23 Aug 2023 11:36:05 +0800
+Subject: [PATCH] 999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2
 
 ---
  arch/arm64/boot/dts/mediatek/mt7986a.dtsi     |  1 +
  drivers/net/ethernet/mediatek/mtk_eth_soc.c   | 67 ++++++++++++++-----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h   | 10 ++-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   | 14 +++-
  drivers/net/ethernet/mediatek/mtk_ppe.c       |  5 +-
  drivers/net/ethernet/mediatek/mtk_ppe.h       |  7 +-
  .../net/ethernet/mediatek/mtk_ppe_debugfs.c   | 27 ++++++--
- .../net/ethernet/mediatek/mtk_ppe_offload.c   | 45 ++++++++++---
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   | 48 ++++++++++---
  include/linux/netdevice.h                     |  4 ++
- 8 files changed, 125 insertions(+), 41 deletions(-)
+ 8 files changed, 131 insertions(+), 42 deletions(-)
  mode change 100644 => 100755 drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 
 diff --git a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
-index 7f78de6b9..381136c21 100644
+index e5d4e12..0c54e12 100644
 --- a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
 +++ b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
-@@ -479,6 +479,7 @@
+@@ -496,6 +496,7 @@
                  mediatek,ethsys = <&ethsys>;
  		mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>;
  		mediatek,wed = <&wed0>, <&wed1>;
@@ -28,10 +28,10 @@
                  #address-cells = <1>;
                  #size-cells = <0>;
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 01fc1e5c0..3f67bebfe 100644
+index f019c56..81b68d6 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1732,6 +1732,7 @@ static int mtk_poll_rx(struct napi_struc
+@@ -2097,6 +2097,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
  	u8 *data, *new_data;
  	struct mtk_rx_dma_v2 *rxd, trxd;
  	int done = 0;
@@ -39,7 +39,7 @@
  
  	if (unlikely(!ring))
  		goto rx_done;
-@@ -1843,14 +1844,20 @@ static int mtk_poll_rx(struct napi_struc
+@@ -2209,14 +2210,20 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
  
  #if defined(CONFIG_MEDIATEK_NETSYS_RX_V2)
  		reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON_V2, trxd.rxd5);
@@ -66,9 +66,9 @@
  #endif
  
  		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
-@@ -3184,8 +3191,12 @@ static int mtk_open(struct net_device *d
- 	if (!phy_node && eth->xgmii->regmap_sgmii[mac->id])
- 		regmap_write(eth->xgmii->regmap_sgmii[mac->id], SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+@@ -3588,8 +3595,12 @@ static int mtk_open(struct net_device *dev)
+ 		regmap_write(eth->sgmii->pcs[id].regmap,
+ 			     SGMSYS_QPHY_PWR_STATE_CTRL, 0);
  
 -	if (eth->soc->offload_version && mtk_ppe_start(&eth->ppe) == 0)
 -			gdm_config = MTK_GDMA_TO_PPE;
@@ -81,7 +81,7 @@
  
  	mtk_gdm_config(eth, mac->id, gdm_config);
  
-@@ -3268,8 +3279,10 @@ static int mtk_stop(struct net_device *d
+@@ -3671,8 +3682,10 @@ static int mtk_stop(struct net_device *dev)
  
  	mtk_dma_free(eth);
  
@@ -94,7 +94,7 @@
  
  	return 0;
  }
-@@ -4408,15 +4421,35 @@ static int mtk_probe(struct platform_dev
+@@ -4958,15 +4971,35 @@ static int mtk_probe(struct platform_device *pdev)
  	}
  
  	if (eth->soc->offload_version) {
@@ -138,10 +138,10 @@
  
  	for (i = 0; i < MTK_MAX_DEVS; i++) {
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-index fce1a7172..b4de7c0c6 100644
+index 8675c86..fad3e73 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -118,7 +118,12 @@
+@@ -130,7 +130,12 @@
  #define MTK_GDMA_UCS_EN		BIT(20)
  #define MTK_GDMA_STRP_CRC	BIT(16)
  #define MTK_GDMA_TO_PDMA	0x0
@@ -155,7 +155,7 @@
  #define MTK_GDMA_DROP_ALL	0x7777
  
  /* GDM Egress Control Register */
-@@ -1612,7 +1617,8 @@ struct mtk_eth {
+@@ -1856,7 +1861,8 @@ struct mtk_eth {
  	spinlock_t			syscfg0_lock;
  	struct timer_list		mtk_dma_monitor_timer;
  
@@ -165,8 +165,8 @@
  	struct rhashtable		flow_table;
  };
  
-@@ -1668,9 +1674,11 @@ int mtk_gmac_usxgmii_path_setup(struct m
- void mtk_usxgmii_reset(struct mtk_xgmii *ss, int mac_id);
+@@ -1918,9 +1924,11 @@ int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r);
+ int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r);
  int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range);
  
 -int mtk_eth_offload_init(struct mtk_eth *eth);
@@ -179,10 +179,10 @@
 +int mtk_ppe_debugfs_init(struct mtk_eth *eth);
  #endif /* MTK_ETH_H */
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
-index d46e91178..3d6ff30ba 100755
+index 7506670..0ff1fef 100755
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -677,7 +677,7 @@ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+@@ -696,7 +696,7 @@ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
  }
  
  struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
@@ -191,7 +191,7 @@
  {
  	struct device *dev = eth->dev;
  	struct mtk_foe_entry *foe;
-@@ -696,6 +696,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+@@ -715,6 +715,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
  	ppe->eth = eth;
  	ppe->dev = dev;
  	ppe->version = version;
@@ -199,7 +199,7 @@
  
  	foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
  				  &ppe->foe_phys, GFP_KERNEL);
-@@ -704,8 +705,6 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+@@ -723,8 +724,6 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
  
  	ppe->foe_table = foe;
  
@@ -209,11 +209,10 @@
  }
  
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-index a76f4b0ac..21cc55145 100644
+index 7012351..86bbac8 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -8,10 +8,12 @@
- #include <linux/bitfield.h>
+@@ -9,8 +9,10 @@
  #include <linux/rhashtable.h>
  
  #if defined(CONFIG_MEDIATEK_NETSYS_V2)
@@ -224,8 +223,7 @@
  #define MTK_ETH_PPE_BASE		0xc00
  #endif
  
- #define MTK_PPE_ENTRIES_SHIFT		3
-@@ -253,6 +255,7 @@ struct mtk_flow_entry {
+@@ -299,6 +301,7 @@ struct mtk_flow_entry {
  		};
  	};
  	u8 type;
@@ -233,7 +231,7 @@
  	s8 wed_index;
  	u16 hash;
  	union {
-@@ -272,6 +275,7 @@ struct mtk_ppe {
+@@ -318,6 +321,7 @@ struct mtk_ppe {
  	struct device *dev;
  	void __iomem *base;
  	int version;
@@ -241,7 +239,7 @@
  
  	struct mtk_foe_entry *foe_table;
  	dma_addr_t foe_phys;
-@@ -284,7 +288,7 @@ struct mtk_ppe {
+@@ -330,7 +334,7 @@ struct mtk_ppe {
  	void *acct_table;
  };
  
@@ -250,7 +248,7 @@
  int mtk_ppe_start(struct mtk_ppe *ppe);
  int mtk_ppe_stop(struct mtk_ppe *ppe);
  
-@@ -335,6 +339,5 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+@@ -381,6 +385,5 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
@@ -258,7 +256,7 @@
  
  #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-index a591ab1fd..f4ebe5944 100644
+index a591ab1..f4ebe59 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
 @@ -73,9 +73,8 @@ mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
@@ -332,10 +330,10 @@
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 old mode 100644
 new mode 100755
-index 5a4201447..2f7d76d3b
+index 77594f3..18f6333
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -226,8 +226,10 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+@@ -229,9 +229,12 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
  	struct flow_action_entry *act;
  	struct mtk_flow_data data = {};
  	struct mtk_foe_entry foe;
@@ -345,9 +343,11 @@
 +	struct net_device_path_ctx ctx = {};
 +	struct net_device_path path = {};
  	int offload_type = 0;
++	int ppe_index = 0;
  	int wed_index = -1;
  	u16 addr_type = 0;
-@@ -242,6 +244,10 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+ 	u8 l4proto = 0;
+@@ -245,6 +248,10 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
  		struct flow_match_meta match;
  
  		flow_rule_match_meta(rule, &match);
@@ -358,45 +358,49 @@
  	} else {
  		return -EOPNOTSUPP;
  	}
-@@ -435,11 +441,27 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
- 	if (!entry)
- 		return -ENOMEM;
+@@ -347,6 +354,20 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+ 	if (err)
+ 		return err;
  
-+	i = 0;
 +#if defined(CONFIG_MEDIATEK_NETSYS_V2)
 +	if (idev && idev->netdev_ops->ndo_fill_receive_path) {
 +		ctx.dev = idev;
 +		idev->netdev_ops->ndo_fill_receive_path(&ctx, &path);
-+		i = path.mtk_wdma.wdma_idx;
-+		if (i >= eth->ppe_num) {
++		ppe_index = path.mtk_wdma.wdma_idx;
++		if (ppe_index >= eth->ppe_num) {
 +			if (printk_ratelimit())
-+				pr_info("[%s] PPE%d doesn't exist, please check mtketh-ppe-num in dts !\n", __func__, i);
++				pr_info("[%s] PPE%d doesn't exist, please check mtketh-ppe-num in dts !\n", __func__, ppe_index);
 +
 +			return -EINVAL;
 +		}
 +	}
 +#endif
 +
+ 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+ 		struct flow_match_ports ports;
+ 
+@@ -440,9 +461,10 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+ 
  	entry->cookie = f->cookie;
  	memcpy(&entry->data, &foe, sizeof(entry->data));
-+	entry->ppe_index = i;
++	entry->ppe_index = ppe_index;
  	entry->wed_index = wed_index;
  
 -	if (mtk_foe_entry_commit(eth->ppe, entry) < 0)
-+	if (mtk_foe_entry_commit(eth->ppe[i], entry) < 0)
++	if (mtk_foe_entry_commit(eth->ppe[ppe_index], entry) < 0)
  		goto free;
  
  	err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
-@@ -450,7 +470,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+@@ -453,7 +475,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
  	return 0;
  
  clear:
 -	mtk_foe_entry_clear(eth->ppe, entry);
-+	mtk_foe_entry_clear(eth->ppe[i], entry);
++	mtk_foe_entry_clear(eth->ppe[ppe_index], entry);
  free:
  	kfree(entry);
  	if (wed_index >= 0)
-@@ -462,13 +482,15 @@ static int
+@@ -465,13 +487,15 @@ static int
  mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
  {
  	struct mtk_flow_entry *entry;
@@ -413,7 +417,7 @@
  	rhashtable_remove_fast(&eth->flow_table, &entry->node,
  			       mtk_flow_ht_params);
  	if (entry->wed_index >= 0)
-@@ -483,13 +505,15 @@ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
+@@ -486,13 +510,15 @@ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
  {
  	struct mtk_flow_entry *entry;
  	u32 idle;
@@ -430,7 +434,7 @@
  	f->stats.lastused = jiffies - idle * HZ;
  
  	return 0;
-@@ -540,10 +564,12 @@ mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
+@@ -543,10 +569,12 @@ mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
  	static LIST_HEAD(block_cb_list);
  	struct flow_block_cb *block_cb;
  	flow_setup_cb_t *cb;
@@ -446,7 +450,7 @@
  
  	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
  		return -EOPNOTSUPP;
-@@ -591,9 +618,9 @@ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
+@@ -603,9 +631,9 @@ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
  	}
  }
  
@@ -459,10 +463,10 @@
  
  	return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
-index 35998b1a7..0ada2461b 100644
+index 59a3e96..0967dc2 100644
 --- a/include/linux/netdevice.h
 +++ b/include/linux/netdevice.h
-@@ -1302,6 +1302,8 @@ struct tlsdev_ops;
+@@ -1311,6 +1311,8 @@ struct tlsdev_ops;
   *	rtnl_lock is not held.
   * int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, struct net_device_path *path);
   *     Get the forwarding path to reach the real device from the HW destination address
@@ -471,7 +475,7 @@
   */
  struct net_device_ops {
  	int			(*ndo_init)(struct net_device *dev);
-@@ -1501,6 +1503,8 @@ struct net_device_ops {
+@@ -1510,6 +1512,8 @@ struct net_device_ops {
  	struct devlink_port *	(*ndo_get_devlink_port)(struct net_device *dev);
  	int                     (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx,
                                                           struct net_device_path *path);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3008-add-wed-tx-support-for-mt7986.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3008-add-wed-tx-support-for-mt7986.patch
old mode 100755
new mode 100644
index 7734ce2..f6fcae3
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3008-add-wed-tx-support-for-mt7986.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3008-add-wed-tx-support-for-mt7986.patch
@@ -6,12 +6,12 @@
 ---
  arch/arm64/boot/dts/mediatek/mt7986a.dtsi     |   2 +
  arch/arm64/boot/dts/mediatek/mt7986b.dtsi     |   2 +
- drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   8 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c   |  17 +-
  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   5 +
- drivers/net/ethernet/mediatek/mtk_wed.c       | 502 +++++++++++++-----
+ drivers/net/ethernet/mediatek/mtk_wed.c       | 496 +++++++++++++-----
  drivers/net/ethernet/mediatek/mtk_wed.h       |  18 +-
  .../net/ethernet/mediatek/mtk_wed_debugfs.c   |   3 +
- drivers/net/ethernet/mediatek/mtk_wed_regs.h  | 127 ++++-
+ drivers/net/ethernet/mediatek/mtk_wed_regs.h  | 130 ++++-
  include/linux/soc/mediatek/mtk_wed.h          |  29 +-
  9 files changed, 546 insertions(+), 150 deletions(-)
 
@@ -59,7 +59,7 @@
 index 3f67bebfe..ac021e2ed 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3579,6 +3579,7 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -4807,6 +4807,7 @@ static int mtk_probe(struct platform_device *pdev)
  {
  	struct device_node *mac_np;
  	struct mtk_eth *eth;
@@ -67,15 +67,26 @@
  	int err, i;
  
  	eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
-@@ -3594,7 +3595,6 @@ static int mtk_probe(struct platform_device *pdev)
- 		return PTR_ERR(eth->base);
+@@ -4827,13 +4828,12 @@ static int mtk_probe(struct platform_device *pdev)
+ 			return PTR_ERR(eth->sram_base);
+ 	}
  
- 	if(eth->soc->has_sram) {
+-	if(eth->soc->has_sram) {
 -		struct resource *res;
- 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- 		if (unlikely(!res))
- 			return -EINVAL;
-@@ -3682,12 +3682,16 @@ static int mtk_probe(struct platform_device *pdev)
+-		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+-		if (unlikely(!res))
+-			return -EINVAL;
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (unlikely(!res))
++		return -EINVAL;
++
++	if(eth->soc->has_sram)
+ 		eth->phy_scratch_ring = res->start + MTK_ETH_SRAM_OFFSET;
+-	}
+ 
+ 	mtk_get_hwver(eth);
+ 
+@@ -4929,12 +4929,15 @@ static int mtk_probe(struct platform_device *pdev)
  			MTK_WDMA1_BASE
  		};
  		void __iomem *wdma;
@@ -86,8 +97,7 @@
  
  		wdma = eth->base + wdma_regs[i];
 -		mtk_wed_add_hw(np, eth, wdma, i);
-+		if (res)
-+			wdma_phy = res->start + wdma_regs[i];
++		wdma_phy = res->start + wdma_regs[i];
 +
 +		mtk_wed_add_hw(np, eth, wdma, wdma_phy, i);
  	}
@@ -832,20 +842,7 @@
  	}
  
  	return 0;
-@@ -706,10 +935,8 @@ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
- 	val = wed_r32(dev, MTK_WED_EXT_INT_STATUS);
- 	wed_w32(dev, MTK_WED_EXT_INT_STATUS, val);
- 	val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK;
--	if (!dev->hw->num_flows)
--		val &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
--	if (val && net_ratelimit())
--		pr_err("mtk_wed%d: error status=%08x\n", dev->hw->index, val);
-+	WARN_RATELIMIT(val, "mtk_wed%d: error status=%08x\n",
-+		       dev->hw->index, val);
- 
- 	val = wed_r32(dev, MTK_WED_INT_STATUS);
- 	val &= mask;
-@@ -780,7 +1007,8 @@ out:
+@@ -780,7 +1009,8 @@ out:
  }
  
  void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
@@ -855,7 +852,7 @@
  {
  	static const struct mtk_wed_ops wed_ops = {
  		.attach = mtk_wed_attach,
-@@ -830,21 +1058,27 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -830,21 +1060,27 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  	hw->eth = eth;
  	hw->dev = &pdev->dev;
  	hw->wdma = wdma;
@@ -992,7 +989,7 @@
  #define MTK_WED_RESET					0x008
  #define MTK_WED_RESET_TX_BM				BIT(0)
  #define MTK_WED_RESET_TX_FREE_AGENT			BIT(4)
-@@ -41,6 +51,7 @@ struct mtk_wdma_desc {
+@@ -41,6 +55,7 @@ struct mtk_wdma_desc {
  #define MTK_WED_CTRL_RESERVE_EN				BIT(12)
  #define MTK_WED_CTRL_RESERVE_BUSY			BIT(13)
  #define MTK_WED_CTRL_FINAL_DIDX_READ			BIT(24)
@@ -1000,7 +997,7 @@
  #define MTK_WED_CTRL_MIB_READ_CLEAR			BIT(28)
  
  #define MTK_WED_EXT_INT_STATUS				0x020
-@@ -49,6 +60,10 @@ struct mtk_wdma_desc {
+@@ -49,6 +64,10 @@ struct mtk_wdma_desc {
  #define MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID	BIT(4)
  #define MTK_WED_EXT_INT_STATUS_TX_FBUF_LO_TH		BIT(8)
  #define MTK_WED_EXT_INT_STATUS_TX_FBUF_HI_TH		BIT(9)
@@ -1011,7 +1008,7 @@
  #define MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH		BIT(12)
  #define MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH		BIT(13)
  #define MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR	BIT(16)
-@@ -57,16 +71,23 @@ struct mtk_wdma_desc {
+@@ -57,16 +76,23 @@ struct mtk_wdma_desc {
  #define MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN	BIT(19)
  #define MTK_WED_EXT_INT_STATUS_RX_DRV_BM_DMAD_COHERENT	BIT(20)
  #define MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR	BIT(21)
@@ -1038,7 +1035,7 @@
  
  #define MTK_WED_EXT_INT_MASK				0x028
  
-@@ -80,10 +103,6 @@ struct mtk_wdma_desc {
+@@ -80,10 +106,6 @@ struct mtk_wdma_desc {
  
  #define MTK_WED_TX_BM_BASE				0x084
  
@@ -1049,7 +1046,7 @@
  #define MTK_WED_TX_BM_BUF_LEN				0x08c
  
  #define MTK_WED_TX_BM_INTF				0x09c
-@@ -93,9 +112,38 @@ struct mtk_wdma_desc {
+@@ -93,9 +115,38 @@ struct mtk_wdma_desc {
  #define MTK_WED_TX_BM_INTF_TKID_READ			BIT(29)
  
  #define MTK_WED_TX_BM_DYN_THR				0x0a0
@@ -1088,7 +1085,7 @@
  #define MTK_WED_INT_STATUS				0x200
  #define MTK_WED_INT_MASK				0x204
  
-@@ -125,6 +173,7 @@ struct mtk_wdma_desc {
+@@ -125,6 +176,7 @@ struct mtk_wdma_desc {
  #define MTK_WED_RESET_IDX_RX				GENMASK(17, 16)
  
  #define MTK_WED_TX_MIB(_n)				(0x2a0 + (_n) * 4)
@@ -1096,7 +1093,7 @@
  
  #define MTK_WED_RING_TX(_n)				(0x300 + (_n) * 0x10)
  
-@@ -139,6 +188,19 @@ struct mtk_wdma_desc {
+@@ -139,6 +191,19 @@ struct mtk_wdma_desc {
  #define MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY		BIT(1)
  #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN			BIT(2)
  #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY		BIT(3)
@@ -1116,7 +1113,7 @@
  #define MTK_WED_WPDMA_GLO_CFG_RX_BT_SIZE		GENMASK(5, 4)
  #define MTK_WED_WPDMA_GLO_CFG_TX_WB_DDONE		BIT(6)
  #define MTK_WED_WPDMA_GLO_CFG_BIG_ENDIAN		BIT(7)
-@@ -152,24 +214,54 @@ struct mtk_wdma_desc {
+@@ -152,24 +217,54 @@ struct mtk_wdma_desc {
  #define MTK_WED_WPDMA_GLO_CFG_FIRST_TOKEN_ONLY		BIT(26)
  #define MTK_WED_WPDMA_GLO_CFG_OMIT_RX_INFO		BIT(27)
  #define MTK_WED_WPDMA_GLO_CFG_OMIT_TX_INFO		BIT(28)
@@ -1172,7 +1169,7 @@
  
  #define MTK_WED_WPDMA_TX_MIB(_n)			(0x5a0 + (_n) * 4)
  #define MTK_WED_WPDMA_TX_COHERENT_MIB(_n)		(0x5d0 + (_n) * 4)
-@@ -203,14 +295,22 @@ struct mtk_wdma_desc {
+@@ -203,14 +298,22 @@ struct mtk_wdma_desc {
  #define MTK_WED_WDMA_RESET_IDX_RX			GENMASK(17, 16)
  #define MTK_WED_WDMA_RESET_IDX_DRV			GENMASK(25, 24)
  
@@ -1196,7 +1193,7 @@
  
  #define MTK_WED_WDMA_RX_MIB(_n)				(0xae0 + (_n) * 4)
  #define MTK_WED_WDMA_RX_RECYCLE_MIB(_n)			(0xae8 + (_n) * 4)
-@@ -221,14 +321,21 @@ struct mtk_wdma_desc {
+@@ -221,14 +324,21 @@ struct mtk_wdma_desc {
  #define MTK_WED_RING_OFS_CPU_IDX			0x08
  #define MTK_WED_RING_OFS_DMA_IDX			0x0c
  
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-ethernet-update-ppe-backward-compatible-two-way-hash.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-ethernet-update-ppe-backward-compatible-two-way-hash.patch
index cfe29e7..554a0ee 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-ethernet-update-ppe-backward-compatible-two-way-hash.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-ethernet-update-ppe-backward-compatible-two-way-hash.patch
@@ -47,7 +47,7 @@
 @@ -4635,6 +4640,7 @@ static const struct mtk_soc_data mt7986_
  	.required_clks = MT7986_CLKS_BITMAP,
  	.required_pctl = false,
- 	.has_sram = true,
+ 	.has_sram = false,
 +	.hash_way = 4,
  	.offload_version = 2,
 	.rss_num = 0,
@@ -55,7 +55,7 @@
 @@ -4651,6 +4657,8 @@ static const struct mtk_soc_data mt7981_
  	.required_clks = MT7981_CLKS_BITMAP,
  	.required_pctl = false,
- 	.has_sram = true,
+ 	.has_sram = false,
 +	.hash_way = 4,
 +	.offload_version = 2,
 	.rss_num = 0,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3013-flow-offload-add-mtkhnat-flow-accounting.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3013-flow-offload-add-mtkhnat-flow-accounting.patch
index ed90d2c..b29a907 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3013-flow-offload-add-mtkhnat-flow-accounting.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3013-flow-offload-add-mtkhnat-flow-accounting.patch
@@ -55,7 +55,7 @@
 @@ -4138,6 +4144,7 @@ static const struct mtk_soc_data mt7986_data = {
  	.required_clks = MT7986_CLKS_BITMAP,
  	.required_pctl = false,
- 	.has_sram = true,
+ 	.has_sram = false,
 +	.has_accounting = true,
  	.hash_way = 4,
  	.offload_version = 2,
@@ -63,7 +63,7 @@
 @@ -4155,6 +4162,7 @@ static const struct mtk_soc_data mt7981_data = {
  	.required_clks = MT7981_CLKS_BITMAP,
  	.required_pctl = false,
- 	.has_sram = true,
+ 	.has_sram = false,
 +	.has_accounting = true,
  	.hash_way = 4,
  	.offload_version = 2,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3014-flow-offload-add-mtkhnat-qdma-qos.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3014-flow-offload-add-mtkhnat-qdma-qos.patch
index fd9a5d4..74a937e 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3014-flow-offload-add-mtkhnat-qdma-qos.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3014-flow-offload-add-mtkhnat-qdma-qos.patch
@@ -1,5 +1,23 @@
+From e2277f2ea6c9657b727b082f7baa967ef94861dc Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Sat, 26 Aug 2023 00:38:09 +0800
+Subject: [PATCH] 999-3014-flow-offload-add-mtkhnat-qdma-qos
+
+---
+ drivers/net/ethernet/mediatek/Makefile        |   2 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c   |  10 +
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  63 ++-
+ drivers/net/ethernet/mediatek/mtk_ppe.c       |  48 +-
+ drivers/net/ethernet/mediatek/mtk_ppe.h       |   4 +
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   |  28 +-
+ .../net/ethernet/mediatek/mtk_qdma_debugfs.c  | 439 ++++++++++++++++++
+ include/net/flow_offload.h                    |   1 +
+ net/netfilter/nf_flow_table_offload.c         |   4 +-
+ 9 files changed, 593 insertions(+), 6 deletions(-)
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c
+
 diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
-index 0c724a5..93cd55f 100644
+index fdbb90f..c7d2296 100644
 --- a/drivers/net/ethernet/mediatek/Makefile
 +++ b/drivers/net/ethernet/mediatek/Makefile
 @@ -5,7 +5,7 @@
@@ -12,10 +30,10 @@
  ifdef CONFIG_DEBUG_FS
  mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index ca76047..809c735 100644
+index 9102144..a370547 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4787,6 +4787,8 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -5085,6 +5085,8 @@ static int mtk_probe(struct platform_device *pdev)
  		}
  
  		mtk_ppe_debugfs_init(eth);
@@ -24,7 +42,7 @@
  	}
  
  	for (i = 0; i < MTK_MAX_DEVS; i++) {
-@@ -4901,6 +4903,7 @@ static const struct mtk_soc_data mt2701_data = {
+@@ -5197,6 +5199,7 @@ static const struct mtk_soc_data mt2701_data = {
  		.rx_dma_l4_valid = RX_DMA_L4_VALID,
  		.dma_max_len = MTK_TX_DMA_BUF_LEN,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
@@ -32,7 +50,7 @@
  	},
  };
  
-@@ -4920,6 +4923,7 @@ static const struct mtk_soc_data mt7621_data = {
+@@ -5217,6 +5220,7 @@ static const struct mtk_soc_data mt7621_data = {
  		.rxd_size = sizeof(struct mtk_rx_dma),
  		.dma_max_len = MTK_TX_DMA_BUF_LEN,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
@@ -40,7 +58,7 @@
  	},
  };
  
-@@ -4940,6 +4944,7 @@ static const struct mtk_soc_data mt7622_data = {
+@@ -5238,6 +5242,7 @@ static const struct mtk_soc_data mt7622_data = {
  		.rx_dma_l4_valid = RX_DMA_L4_VALID,
  		.dma_max_len = MTK_TX_DMA_BUF_LEN,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
@@ -48,7 +66,7 @@
  	},
  };
  
-@@ -4959,6 +4964,7 @@ static const struct mtk_soc_data mt7623_data = {
+@@ -5258,6 +5263,7 @@ static const struct mtk_soc_data mt7623_data = {
  		.rx_dma_l4_valid = RX_DMA_L4_VALID,
  		.dma_max_len = MTK_TX_DMA_BUF_LEN,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
@@ -56,7 +74,7 @@
  	},
  };
  
-@@ -4997,6 +5003,7 @@ static const struct mtk_soc_data mt7986_data = {
+@@ -5298,6 +5304,7 @@ static const struct mtk_soc_data mt7986_data = {
  		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
  		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
@@ -64,7 +82,7 @@
  	},
  };
  
-@@ -5017,6 +5024,7 @@ static const struct mtk_soc_data mt7981_data = {
+@@ -5319,6 +5326,7 @@ static const struct mtk_soc_data mt7981_data = {
  		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
  		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
@@ -72,7 +90,7 @@
  	},
  };
  
-@@ -5034,6 +5042,7 @@ static const struct mtk_soc_data mt7988_data = {
+@@ -5337,6 +5345,7 @@ static const struct mtk_soc_data mt7988_data = {
  		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
  		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
@@ -80,7 +98,7 @@
  	},
  };
  
-@@ -5051,6 +5060,7 @@ static const struct mtk_soc_data rt5350_data = {
+@@ -5355,6 +5364,7 @@ static const struct mtk_soc_data rt5350_data = {
  		.rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA,
  		.dma_max_len = MTK_TX_DMA_BUF_LEN,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
@@ -89,10 +107,10 @@
  };
  
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-index c6afff5..bd73c27 100644
+index db139a1..a7892e2 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -385,10 +385,21 @@
+@@ -398,10 +398,21 @@
  
  /* QDMA TX Queue Configuration Registers */
  #define MTK_QTX_CFG(x)		(QDMA_BASE + (x * 0x10))
@@ -114,7 +132,7 @@
  
  /* QDMA RX Base Pointer Register */
  #define MTK_QRX_BASE_PTR0	(QDMA_BASE + 0x100)
-@@ -406,7 +417,9 @@
+@@ -419,7 +430,9 @@
  #define MTK_QRX_DRX_IDX0	(QDMA_BASE + 0x10c)
  
  /* QDMA Page Configuration Register */
@@ -125,7 +143,7 @@
  
  /* QDMA Global Configuration Register */
  #define MTK_QDMA_GLO_CFG	(QDMA_BASE + 0x204)
-@@ -443,6 +456,9 @@
+@@ -456,6 +469,9 @@
  #define FC_THRES_DROP_EN	(7 << 16)
  #define FC_THRES_MIN		0x4444
  
@@ -134,8 +152,8 @@
 +
  /* QDMA Interrupt Status Register */
  #define MTK_QDMA_INT_STATUS	(QDMA_BASE + 0x218)
- #if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
-@@ -478,6 +494,11 @@
+ #if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
+@@ -492,6 +508,11 @@
  /* QDMA Interrupt Mask Register */
  #define MTK_QDMA_HRED2		(QDMA_BASE + 0x244)
  
@@ -147,7 +165,7 @@
  /* QDMA TX Forward CPU Pointer Register */
  #define MTK_QTX_CTX_PTR		(QDMA_BASE +0x300)
  
-@@ -505,6 +526,14 @@
+@@ -519,6 +540,14 @@
  /* QDMA FQ Free Page Buffer Length Register */
  #define MTK_QDMA_FQ_BLEN	(QDMA_BASE +0x32c)
  
@@ -162,7 +180,7 @@
  /* WDMA Registers */
  #define MTK_WDMA_CTX_PTR(x)	(WDMA_BASE(x) + 0x8)
  #define MTK_WDMA_DTX_PTR(x)	(WDMA_BASE(x) + 0xC)
-@@ -1596,6 +1625,7 @@ struct mtk_soc_data {
+@@ -1682,6 +1711,7 @@ struct mtk_soc_data {
  		u32	rx_dma_l4_valid;
  		u32	dma_max_len;
  		u32	dma_len_offset;
@@ -170,7 +188,7 @@
  	} txrx;
  };
  
-@@ -1736,6 +1766,7 @@ struct mtk_eth {
+@@ -1868,6 +1898,7 @@ struct mtk_eth {
  	spinlock_t			syscfg0_lock;
  	struct timer_list		mtk_dma_monitor_timer;
  
@@ -178,18 +196,62 @@
  	u8				ppe_num;
  	struct mtk_ppe			*ppe[MTK_MAX_PPE_NUM];
  	struct rhashtable		flow_table;
-@@ -1815,4 +1846,6 @@ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
- void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
+@@ -1906,6 +1937,34 @@ extern const struct of_device_id of_mtk_match[];
+ extern u32 mtk_hwlro_stats_ebl;
+ extern u32 dbg_show_level;
+ 
++static inline void mtk_set_ib1_sp(struct mtk_eth *eth, struct mtk_foe_entry *foe, u32 val)
++{
++	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++		foe->ib1 |= FIELD_PREP(MTK_FOE_IB1_UNBIND_SRC_PORT, val);
++}
++
++static inline u32 mtk_get_ib1_sp(struct mtk_eth *eth, struct mtk_foe_entry *foe)
++{
++	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++		return FIELD_GET(MTK_FOE_IB1_UNBIND_SRC_PORT, foe->ib1);
++
++	return 0;
++}
++
++static inline int
++mtk_ppe_check_pppq_path(struct mtk_eth *eth, struct mtk_foe_entry *foe, int dsa_port)
++{
++	u32 sp = mtk_get_ib1_sp(eth, foe);
++
++	if ((dsa_port >= 0 && dsa_port <= 4) ||
++	    (dsa_port == 5 && (sp == PSE_WDMA0_PORT ||
++			       sp == PSE_WDMA1_PORT ||
++			       sp == PSE_WDMA2_PORT)))
++		return 1;
++
++	return 0;
++}
++
+ /* read the hardware status register */
+ void mtk_stats_update_mac(struct mtk_mac *mac);
+ 
+@@ -1938,4 +1997,6 @@ void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
+ u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
  
  int mtk_ppe_debugfs_init(struct mtk_eth *eth);
 +
 +int mtk_qdma_debugfs_init(struct mtk_eth *eth);
  #endif /* MTK_ETH_H */
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
-index a49275f..1767823 100755
+index 107f5a1..0d4ae28 100755
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -406,6 +406,16 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+@@ -128,7 +128,7 @@ static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable)
+ 		enable * MTK_PPE_CACHE_CTL_EN);
+ }
+ 
+-static u32 mtk_ppe_hash_entry(struct mtk_ppe *ppe, struct mtk_foe_entry *e)
++u32 mtk_ppe_hash_entry(struct mtk_ppe *ppe, struct mtk_foe_entry *e)
+ {
+ 	u32 hv1, hv2, hv3;
+ 	u32 hash;
+@@ -420,12 +420,58 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
  	return 0;
  }
  
@@ -206,20 +268,68 @@
  static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
  {
  	return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
+ 	       FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
+ }
+ 
++bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data)
++{
++	int type, len;
++
++	if ((data->ib1 ^ entry->ib1) & MTK_FOE_IB1_UDP)
++		return false;
++
++	type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++	if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++		len = offsetof(struct mtk_foe_entry, ipv6._rsv);
++	else
++		len = offsetof(struct mtk_foe_entry, ipv4.ib2);
++
++	return !memcmp(&entry->data, &data->data, len - 4);
++}
++
++int mtk_foe_entry_set_sp(struct mtk_ppe *ppe, struct mtk_foe_entry *entry)
++{
++	struct mtk_foe_entry *hwe;
++	u32 hash, sp = 0;
++	int i;
++
++	hash = mtk_ppe_hash_entry(ppe, entry);
++	for (i = 0; i < ppe->way; i++) {
++		hwe = &ppe->foe_table[hash + i];
++		if (mtk_foe_entry_match(hwe, entry)) {
++			sp = mtk_get_ib1_sp(ppe->eth, hwe);
++			break;
++		}
++	}
++
++	mtk_set_ib1_sp(ppe->eth, entry, sp);
++
++	return 0;
++}
++
+ static bool
+ mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data)
+ {
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-index 8076e5d..c46c4d9 100644
+index 86288b0..5ab864f 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -356,6 +356,7 @@ int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
+@@ -403,9 +403,13 @@ int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
  int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
  int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
  			   int bss, int wcid);
 +int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid);
++bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data);
++int mtk_foe_entry_set_sp(struct mtk_ppe *ppe, struct mtk_foe_entry *entry);
  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+ struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, struct mtk_foe_accounting *diff);
++u32 mtk_ppe_hash_entry(struct mtk_ppe *ppe, struct mtk_foe_entry *e);
+ 
+ #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-index f258539..3b17819 100755
+index b80f72d..3bc50a4 100755
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 @@ -9,6 +9,8 @@
@@ -244,7 +354,7 @@
  	if (dsa_port >= 0)
  		mtk_foe_entry_set_dsa(foe, dsa_port);
  
-+	if (eth->qos_toggle == 2 && dsa_port >= 0)
++	if (eth->qos_toggle == 2 && mtk_ppe_check_pppq_path(eth, foe, dsa_port))
 +		mtk_foe_entry_set_qid(foe, dsa_port & MTK_QDMA_TX_MASK);
 +
  	if (dev == eth->netdev[0])
@@ -274,60 +384,23 @@
  	mtk_foe_entry_set_pse_port(foe, pse_port);
  
  	return 0;
-@@ -432,7 +455,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+@@ -447,7 +469,9 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
  	if (data.pppoe.num == 1)
  		mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
  
 -	err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest,
++	mtk_foe_entry_set_sp(eth->ppe[ppe_index], &foe);
++
 +	err = mtk_flow_set_output_device(eth, &foe, odev, f->flow->ct, data.eth.h_dest,
  					 &wed_index);
  	if (err)
  		return err;
-diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h
-index 59b8736..7261b6d 100644
---- a/include/net/flow_offload.h
-+++ b/include/net/flow_offload.h
-@@ -365,6 +378,7 @@ struct flow_cls_offload {
- 	struct flow_cls_common_offload common;
- 	enum flow_cls_command command;
- 	unsigned long cookie;
-+	struct flow_offload *flow;
- 	struct flow_rule *rule;
- 	struct flow_stats stats;
- 	u32 classid;
-diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
-index d94c6fb..886ced5 100644
---- a/net/netfilter/nf_flow_table_offload.c
-+++ b/net/netfilter/nf_flow_table_offload.c
-@@ -810,11 +810,13 @@ static int nf_flow_offload_alloc(const struct flow_offload_work *offload,
- }
- 
- static void nf_flow_offload_init(struct flow_cls_offload *cls_flow,
-+				 struct flow_offload *flow,
- 				 __be16 proto, int priority,
- 				 enum flow_cls_command cmd,
- 				 const struct flow_offload_tuple *tuple,
- 				 struct netlink_ext_ack *extack)
- {
-+	cls_flow->flow = flow;
- 	cls_flow->common.protocol = proto;
- 	cls_flow->common.prio = priority;
- 	cls_flow->common.extack = extack;
-@@ -836,7 +838,7 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable,
- 	__be16 proto = ETH_P_ALL;
- 	int err, i = 0;
- 
--	nf_flow_offload_init(&cls_flow, proto, priority, cmd,
-+	nf_flow_offload_init(&cls_flow, flow, proto, priority, cmd,
- 			     &flow->tuplehash[dir].tuple, &extack);
- 	if (cmd == FLOW_CLS_REPLACE)
- 		cls_flow.rule = flow_rule->rule;
 diff --git a/drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c b/drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c
 new file mode 100644
-index 0000000..198b924
+index 0000000..3a7c585
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c
-@@ -0,0 +1,435 @@
+@@ -0,0 +1,439 @@
 +/* SPDX-License-Identifier: GPL-2.0
 + *
 + * Copyright (c) 2022 MediaTek Inc.
@@ -435,8 +508,12 @@
 +{
 +	struct mtk_eth *eth = m->private;
 +
-+	seq_printf(m, "value=%d, HQoS is %s now!\n",
-+		   eth->qos_toggle, (eth->qos_toggle) ? "enabled" : "disabled");
++	if (eth->qos_toggle == 0)
++		pr_info("HQoS is disabled now!\n");
++	else if (eth->qos_toggle == 1)
++		pr_info("HQoS is enabled now!\n");
++	else if (eth->qos_toggle == 2)
++		pr_info("Per-port-per-queue mode is enabled!\n");
 +
 +	return 0;
 +}
@@ -763,3 +840,45 @@
 +
 +	return 0;
 +}
+diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h
+index 59b8736..c4eb45c 100644
+--- a/include/net/flow_offload.h
++++ b/include/net/flow_offload.h
+@@ -365,6 +365,7 @@ struct flow_cls_offload {
+ 	struct flow_cls_common_offload common;
+ 	enum flow_cls_command command;
+ 	unsigned long cookie;
++	struct flow_offload *flow;
+ 	struct flow_rule *rule;
+ 	struct flow_stats stats;
+ 	u32 classid;
+diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
+index 50f2f2e..ba34572 100644
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -810,11 +810,13 @@ static int nf_flow_offload_alloc(const struct flow_offload_work *offload,
+ }
+ 
+ static void nf_flow_offload_init(struct flow_cls_offload *cls_flow,
++				 struct flow_offload *flow,
+ 				 __be16 proto, int priority,
+ 				 enum flow_cls_command cmd,
+ 				 const struct flow_offload_tuple *tuple,
+ 				 struct netlink_ext_ack *extack)
+ {
++	cls_flow->flow = flow;
+ 	cls_flow->common.protocol = proto;
+ 	cls_flow->common.prio = priority;
+ 	cls_flow->common.extack = extack;
+@@ -836,7 +838,7 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable,
+ 	__be16 proto = ETH_P_ALL;
+ 	int err, i = 0;
+ 
+-	nf_flow_offload_init(&cls_flow, proto, priority, cmd,
++	nf_flow_offload_init(&cls_flow, flow, proto, priority, cmd,
+ 			     &flow->tuplehash[dir].tuple, &extack);
+ 	if (cmd == FLOW_CLS_REPLACE)
+ 		cls_flow.rule = flow_rule->rule;
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3017-ethernet-update-ppe-from-mt7986-to-mt7988.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3017-ethernet-update-ppe-from-mt7986-to-mt7988.patch
index d9af44d..872a279 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3017-ethernet-update-ppe-from-mt7986-to-mt7988.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3017-ethernet-update-ppe-from-mt7986-to-mt7988.patch
@@ -1,8 +1,21 @@
+From 06c3b8d434290affb720808a38315a78e94c9923 Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Sat, 26 Aug 2023 00:45:40 +0800
+Subject: [PATCH] 999-3017-ethernet-update-ppe-from-mt7986-to-mt7988
+
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c   | 14 ++++---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  9 +++--
+ drivers/net/ethernet/mediatek/mtk_ppe.c       | 18 ++++++---
+ drivers/net/ethernet/mediatek/mtk_ppe.h       | 38 ++++++++++++++++---
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   |  6 ++-
+ 5 files changed, 64 insertions(+), 21 deletions(-)
+
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 4075ec2..524c5d9 100644
+index a370547..239c25d 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1796,17 +1796,17 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+@@ -2272,17 +2272,17 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
  			skb_checksum_none_assert(skb);
  		skb->protocol = eth_type_trans(skb, netdev);
  
@@ -24,7 +37,7 @@
  		reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON_V2, trxd.rxd5);
  		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) {
  			for (i = 0; i < eth->ppe_num; i++) {
-@@ -4448,7 +4448,8 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -5071,7 +5071,8 @@ static int mtk_probe(struct platform_device *pdev)
  
  		for (i = 0; i < eth->ppe_num; i++) {
  			eth->ppe[i] = mtk_ppe_init(eth,
@@ -34,28 +47,21 @@
  						   2, eth->soc->hash_way, i,
  						   eth->soc->has_accounting);
  			if (!eth->ppe[i]) {
-@@ -4626,13 +4626,16 @@ static const struct mtk_soc_data mt7988_data = {
+@@ -5338,6 +5339,9 @@ static const struct mtk_soc_data mt7988_data = {
  	.required_clks = MT7988_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = true,
 +	.has_accounting = true,
 +	.hash_way = 4,
 +	.offload_version = 2,
-	.rss_num = 4,
+ 	.rss_num = 4,
  	.txrx = {
  		.txd_size = sizeof(struct mtk_tx_dma_v2),
- 		.rxd_size = sizeof(struct mtk_rx_dma_v2),
- 		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
- 		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
- 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
- 		.qdma_tx_sch = 4,
- 	},
- };
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-index 5b39d87..94bd423 100644
+index a7892e2..cd19c8d 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -118,7 +118,8 @@
+@@ -130,9 +130,10 @@
  #define MTK_GDMA_UCS_EN		BIT(20)
  #define MTK_GDMA_STRP_CRC	BIT(16)
  #define MTK_GDMA_TO_PDMA	0x0
@@ -65,8 +71,28 @@
  #define MTK_GDMA_TO_PPE1	0x4444
 +#define MTK_GMAC_TO_PPE2	0xcccc
  #else
+ #define MTK_GDMA_TO_PPE0	0x4444
+ #endif
+@@ -1939,13 +1940,15 @@ extern u32 dbg_show_level;
+ 
+ static inline void mtk_set_ib1_sp(struct mtk_eth *eth, struct mtk_foe_entry *foe, u32 val)
+ {
+-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ||
++	    MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
+ 		foe->ib1 |= FIELD_PREP(MTK_FOE_IB1_UNBIND_SRC_PORT, val);
+ }
+ 
+ static inline u32 mtk_get_ib1_sp(struct mtk_eth *eth, struct mtk_foe_entry *foe)
+ {
+-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ||
++	    MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3))
+ 		return FIELD_GET(MTK_FOE_IB1_UNBIND_SRC_PORT, foe->ib1);
+ 
+ 	return 0;
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
-index 98f61fe..bd504d4 100755
+index 0d4ae28..308d5a1 100755
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 @@ -211,7 +211,7 @@ int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
@@ -104,7 +130,7 @@
  
  	return 0;
  }
-@@ -867,13 +867,16 @@ int mtk_ppe_start(struct mtk_ppe *ppe)
+@@ -922,13 +927,16 @@ int mtk_ppe_start(struct mtk_ppe *ppe)
  	mtk_ppe_init_foe_table(ppe);
  	ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
  
@@ -123,7 +149,7 @@
  	      MTK_PPE_TB_CFG_INFO_SEL |
  #endif
  	      FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS,
-@@ -937,7 +940,7 @@ int mtk_ppe_start(struct mtk_ppe *ppe)
+@@ -992,7 +1000,7 @@ int mtk_ppe_start(struct mtk_ppe *ppe)
  
  	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
  
@@ -133,7 +159,7 @@
  	ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
  #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-index 703b2bd..03b4dfb 100644
+index 5ab864f..5529d64 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 @@ -8,7 +8,10 @@
@@ -148,7 +174,7 @@
  #define MTK_MAX_PPE_NUM			2
  #define MTK_ETH_PPE_BASE		0x2000
  #else
-@@ -22,7 +22,7 @@
+@@ -22,7 +25,7 @@
  #define MTK_PPE_WAIT_TIMEOUT_US		1000000
  
  #define MTK_FOE_IB1_UNBIND_TIMESTAMP	GENMASK(7, 0)
@@ -157,7 +183,7 @@
  #define MTK_FOE_IB1_UNBIND_SRC_PORT	GENMASK(11, 8)
  #define MTK_FOE_IB1_UNBIND_PACKETS	GENMASK(19, 12)
  #define MTK_FOE_IB1_UNBIND_PREBIND	BIT(22)
-@@ -70,7 +70,7 @@ enum {
+@@ -70,7 +73,7 @@ enum {
  	MTK_PPE_PKT_TYPE_IPV6_6RD = 7,
  };
  
@@ -166,7 +192,7 @@
  #define MTK_FOE_IB2_QID			GENMASK(6, 0)
  #define MTK_FOE_IB2_PORT_MG		BIT(7)
  #define MTK_FOE_IB2_PSE_QOS		BIT(8)
-@@ -98,7 +98,18 @@ enum {
+@@ -98,7 +101,18 @@ enum {
  
  #define MTK_FOE_IB2_DSCP		GENMASK(31, 24)
  
@@ -186,7 +212,7 @@
  #define MTK_FOE_WINFO_BSS		GENMASK(5, 0)
  #define MTK_FOE_WINFO_WCID		GENMASK(15, 6)
  #else
-@@ -128,7 +139,17 @@ struct mtk_foe_mac_info {
+@@ -128,7 +142,17 @@ struct mtk_foe_mac_info {
  	u16 pppoe_id;
  	u16 src_mac_lo;
  
@@ -205,7 +231,7 @@
  	u16 minfo;
  	u16 winfo;
  #endif
-@@ -249,7 +265,9 @@ struct mtk_foe_entry {
+@@ -249,7 +273,9 @@ struct mtk_foe_entry {
  		struct mtk_foe_ipv4_dslite dslite;
  		struct mtk_foe_ipv6 ipv6;
  		struct mtk_foe_ipv6_6rd ipv6_6rd;
@@ -217,7 +243,7 @@
  #else
  		u32 data[19];
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-index a5bf090..0e41ff2 100755
+index 3bc50a4..f0c63da 100755
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 @@ -195,7 +195,7 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
@@ -229,7 +255,7 @@
  		if (info.wdma_idx == 0)
  			pse_port = PSE_WDMA0_PORT;
  		else if (info.wdma_idx == 1)
-@@ -220,6 +220,8 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
+@@ -218,6 +218,8 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
  		pse_port = PSE_GDM1_PORT;
  	else if (dev == eth->netdev[1])
  		pse_port = PSE_GDM2_PORT;
@@ -238,12 +264,15 @@
  	else
  		return -EOPNOTSUPP;
  
-@@ -452,7 +452,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
- 		return -ENOMEM;
+@@ -376,7 +378,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+ 	if (err)
+ 		return err;
  
- 	i = 0;
 -#if defined(CONFIG_MEDIATEK_NETSYS_V2)
 +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
  	if (idev && idev->netdev_ops->ndo_fill_receive_path) {
  		ctx.dev = idev;
  		idev->netdev_ops->ndo_fill_receive_path(&ctx, &path);
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3019-flow-offload-add-mtkhnat-dscp.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3019-flow-offload-add-mtkhnat-dscp.patch
index fe278a7..a43261a 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3019-flow-offload-add-mtkhnat-dscp.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3019-flow-offload-add-mtkhnat-dscp.patch
@@ -1,5 +1,19 @@
+From 72dd3b798e3a08122e9a769d6f0197b0f357478e Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Wed, 23 Aug 2023 14:03:52 +0800
+Subject: [PATCH] 999-3019-flow-offload-add-mtkhnat-dscp
+
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.c       | 11 +++++++
+ drivers/net/ethernet/mediatek/mtk_ppe.h       |  1 +
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   | 12 +++++++
+ include/net/netfilter/nf_flow_table.h         |  2 ++
+ net/netfilter/nf_flow_table_offload.c         |  7 +++-
+ net/netfilter/xt_FLOWOFFLOAD.c                | 32 +++++++++++++++++++
+ 6 files changed, 64 insertions(+), 1 deletion(-)
+
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
-index c2416b1..bc13a9b 100755
+index 4c17a4e..2860e99 100755
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 @@ -435,6 +435,17 @@ int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid)
@@ -21,7 +35,7 @@
  {
  	return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-index e7ecbf7..df10040 100644
+index 4abed85..57251d3 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 @@ -430,6 +430,7 @@ int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
@@ -29,14 +43,14 @@
  			   int bss, int wcid);
  int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid);
 +int mtk_foe_entry_set_dscp(struct mtk_foe_entry *entry, int dscp);
+ bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data);
  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
- int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-index 23d2048..9bc0857 100644
+index 989b412..f89875d 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -246,6 +246,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+@@ -262,6 +262,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
  	int wed_index = -1;
  	u16 addr_type = 0;
  	u8 l4proto = 0;
@@ -44,7 +58,7 @@
  	int err = 0;
  	int i;
  
-@@ -282,6 +283,15 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+@@ -298,6 +299,15 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
  		return -EOPNOTSUPP;
  	}
  
@@ -60,17 +74,17 @@
  	switch (addr_type) {
  	case 0:
  		offload_type = MTK_PPE_PKT_TYPE_BRIDGE;
-@@ -441,6 +451,8 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+@@ -471,6 +481,8 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
  	if (data.pppoe.num == 1)
  		mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
  
 +	mtk_foe_entry_set_dscp(&foe, dscp);
 +
+ 	mtk_foe_entry_set_sp(eth->ppe[ppe_index], &foe);
+ 
  	err = mtk_flow_set_output_device(eth, &foe, odev, f->flow->ct, data.eth.h_dest,
- 					 &wed_index);
- 	if (err)
 diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
-index 55359dd..1a23c03 100644
+index feac793..97a277d 100644
 --- a/include/net/netfilter/nf_flow_table.h
 +++ b/include/net/netfilter/nf_flow_table.h
 @@ -36,6 +36,7 @@ struct nf_flow_key {
@@ -90,7 +104,7 @@
  
  struct flow_offload_tuple_rhash {
 diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
-index 61cc518..c1a5f64 100644
+index ba34572..b8b2fa6 100644
 --- a/net/netfilter/nf_flow_table_offload.c
 +++ b/net/netfilter/nf_flow_table_offload.c
 @@ -104,6 +104,7 @@ static int nf_flow_rule_match(struct nf_flow_match *match,
@@ -118,7 +132,7 @@
  }
  
 diff --git a/net/netfilter/xt_FLOWOFFLOAD.c b/net/netfilter/xt_FLOWOFFLOAD.c
-index 2d5c3cc..b231dd7 100644
+index 1defb15..d4aecab 100644
 --- a/net/netfilter/xt_FLOWOFFLOAD.c
 +++ b/net/netfilter/xt_FLOWOFFLOAD.c
 @@ -49,6 +49,35 @@ static DEFINE_SPINLOCK(hooks_lock);
@@ -157,7 +171,7 @@
  static unsigned int
  xt_flowoffload_net_hook(void *priv, struct sk_buff *skb,
  			const struct nf_hook_state *state)
-@@ -599,6 +628,9 @@ flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
+@@ -617,6 +646,9 @@ flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
  	if (flow_offload_route_init(flow, &route) < 0)
  		goto err_flow_add;
  
@@ -167,3 +181,6 @@
  	if (tcph) {
  		ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
  		ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-1022-backport-linux-6.2-xfrm-packet-mode.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-1022-backport-linux-6.2-xfrm-packet-mode.patch
new file mode 100644
index 0000000..9eb9347
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-1022-backport-linux-6.2-xfrm-packet-mode.patch
@@ -0,0 +1,980 @@
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -919,6 +919,10 @@ struct xfrmdev_ops {
+ 	bool	(*xdo_dev_offload_ok) (struct sk_buff *skb,
+ 				       struct xfrm_state *x);
+ 	void	(*xdo_dev_state_advance_esn) (struct xfrm_state *x);
++	void	(*xdo_dev_state_update_curlft) (struct xfrm_state *x);
++	int	(*xdo_dev_policy_add) (struct xfrm_policy *x);
++	void	(*xdo_dev_policy_delete) (struct xfrm_policy *x);
++	void	(*xdo_dev_policy_free) (struct xfrm_policy *x);
+ };
+ #endif
+ 
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -125,11 +125,25 @@ struct xfrm_state_walk {
+ 	struct xfrm_address_filter *filter;
+ };
+ 
++enum {
++	XFRM_DEV_OFFLOAD_IN = 1,
++	XFRM_DEV_OFFLOAD_OUT,
++	XFRM_DEV_OFFLOAD_FWD,
++};
++
++enum {
++	XFRM_DEV_OFFLOAD_UNSPECIFIED,
++	XFRM_DEV_OFFLOAD_CRYPTO,
++	XFRM_DEV_OFFLOAD_PACKET,
++};
++
+ struct xfrm_state_offload {
+ 	struct net_device	*dev;
+ 	unsigned long		offload_handle;
+ 	unsigned int		num_exthdrs;
+ 	u8			flags;
++	u8			dir : 2;
++	u8			type : 2;
+ };
+ 
+ struct xfrm_mode {
+@@ -527,6 +541,8 @@ struct xfrm_policy {
+ 	struct xfrm_tmpl       	xfrm_vec[XFRM_MAX_DEPTH];
+ 	struct hlist_node	bydst_inexact_list;
+ 	struct rcu_head		rcu;
++
++	struct xfrm_state_offload xdo;
+ };
+ 
+ static inline struct net *xp_net(const struct xfrm_policy *xp)
+@@ -1084,6 +1100,29 @@ xfrm_state_addr_cmp(const struct xfrm_tm
+ }
+ 
+ #ifdef CONFIG_XFRM
++static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb)
++{
++	struct sec_path *sp = skb_sec_path(skb);
++
++	return sp->xvec[sp->len - 1];
++}
++#endif
++
++static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb)
++{
++#ifdef CONFIG_XFRM
++	struct sec_path *sp = skb_sec_path(skb);
++
++	if (!sp || !sp->olen || sp->len != sp->olen)
++		return NULL;
++
++	return &sp->ovec[sp->olen - 1];
++#else
++	return NULL;
++#endif
++}
++
++#ifdef CONFIG_XFRM
+ int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb,
+ 			unsigned short family);
+ 
+@@ -1093,10 +1132,19 @@ static inline int __xfrm_policy_check2(s
+ {
+ 	struct net *net = dev_net(skb->dev);
+ 	int ndir = dir | (reverse ? XFRM_POLICY_MASK + 1 : 0);
++	struct xfrm_offload *xo = xfrm_offload(skb);
++	struct xfrm_state *x;
+ 
+ 	if (sk && sk->sk_policy[XFRM_POLICY_IN])
+ 		return __xfrm_policy_check(sk, ndir, skb, family);
+ 
++	if (xo) {
++		x = xfrm_input_state(skb);
++		if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++			return (xo->flags & CRYPTO_DONE) &&
++			       (xo->status & CRYPTO_SUCCESS);
++	}
++
+ 	return	(!net->xfrm.policy_count[dir] && !secpath_exists(skb)) ||
+ 		(skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) ||
+ 		__xfrm_policy_check(sk, ndir, skb, family);
+@@ -1490,6 +1538,23 @@ struct xfrm_state *xfrm_stateonly_find(s
+ struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi,
+ 					      unsigned short family);
+ int xfrm_state_check_expire(struct xfrm_state *x);
++#ifdef CONFIG_XFRM_OFFLOAD
++static inline void xfrm_dev_state_update_curlft(struct xfrm_state *x)
++{
++	struct xfrm_state_offload *xdo = &x->xso;
++	struct net_device *dev = xdo->dev;
++
++	if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
++		return;
++
++	if (dev && dev->xfrmdev_ops &&
++	    dev->xfrmdev_ops->xdo_dev_state_update_curlft)
++		dev->xfrmdev_ops->xdo_dev_state_update_curlft(x);
++
++}
++#else
++static inline void xfrm_dev_state_update_curlft(struct xfrm_state *x) {}
++#endif
+ void xfrm_state_insert(struct xfrm_state *x);
+ int xfrm_state_add(struct xfrm_state *x);
+ int xfrm_state_update(struct xfrm_state *x);
+@@ -1539,6 +1604,8 @@ struct xfrm_state *xfrm_find_acq_byseq(s
+ int xfrm_state_delete(struct xfrm_state *x);
+ int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
+ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
++int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
++			  bool task_valid);
+ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
+ void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
+ u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
+@@ -1820,29 +1887,6 @@ static inline void xfrm_states_delete(st
+ }
+ #endif
+ 
+-#ifdef CONFIG_XFRM
+-static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb)
+-{
+-	struct sec_path *sp = skb_sec_path(skb);
+-
+-	return sp->xvec[sp->len - 1];
+-}
+-#endif
+-
+-static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb)
+-{
+-#ifdef CONFIG_XFRM
+-	struct sec_path *sp = skb_sec_path(skb);
+-
+-	if (!sp || !sp->olen || sp->len != sp->olen)
+-		return NULL;
+-
+-	return &sp->ovec[sp->olen - 1];
+-#else
+-	return NULL;
+-#endif
+-}
+-
+ void __init xfrm_dev_init(void);
+ 
+ #ifdef CONFIG_XFRM_OFFLOAD
+@@ -1851,6 +1895,9 @@ void xfrm_dev_backlog(struct softnet_dat
+ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again);
+ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
+ 		       struct xfrm_user_offload *xuo);
++int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
++			struct xfrm_user_offload *xuo, u8 dir,
++			struct netlink_ext_ack *extack);
+ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
+ 
+ static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
+@@ -1899,6 +1946,27 @@ static inline void xfrm_dev_state_free(s
+ 		dev_put(dev);
+ 	}
+ }
++
++static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
++{
++	struct xfrm_state_offload *xdo = &x->xdo;
++	struct net_device *dev = xdo->dev;
++
++	if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_policy_delete)
++		dev->xfrmdev_ops->xdo_dev_policy_delete(x);
++}
++
++static inline void xfrm_dev_policy_free(struct xfrm_policy *x)
++{
++	struct xfrm_state_offload *xdo = &x->xdo;
++	struct net_device *dev = xdo->dev;
++
++	if (dev && dev->xfrmdev_ops) {
++		if (dev->xfrmdev_ops->xdo_dev_policy_free)
++			dev->xfrmdev_ops->xdo_dev_policy_free(x);
++		xdo->dev = NULL;
++	}
++}
+ #else
+ static inline void xfrm_dev_resume(struct sk_buff *skb)
+ {
+@@ -1931,6 +1999,21 @@ static inline bool xfrm_dev_offload_ok(s
+ 	return false;
+ }
+ 
++static inline int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
++				      struct xfrm_user_offload *xuo, u8 dir,
++				      struct netlink_ext_ack *extack)
++{
++	return 0;
++}
++
++static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
++{
++}
++
++static inline void xfrm_dev_policy_free(struct xfrm_policy *x)
++{
++}
++
+ static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
+ {
+ }
+--- a/include/uapi/linux/xfrm.h
++++ b/include/uapi/linux/xfrm.h
+@@ -512,6 +512,12 @@ struct xfrm_user_offload {
+  */
+ #define XFRM_OFFLOAD_IPV6	1
+ #define XFRM_OFFLOAD_INBOUND	2
++/* Two bits above are relevant for state path only, while
++ * offload is used for both policy and state flows.
++ *
++ * In policy offload mode, they are free and can be safely reused.
++ */
++#define XFRM_OFFLOAD_PACKET	4
+ 
+ #ifndef __KERNEL__
+ /* backwards compatibility for userspace */
+--- a/net/xfrm/xfrm_device.c
++++ b/net/xfrm/xfrm_device.c
+@@ -80,6 +80,7 @@ struct sk_buff *validate_xmit_xfrm(struc
+ 	struct softnet_data *sd;
+ 	netdev_features_t esp_features = features;
+ 	struct xfrm_offload *xo = xfrm_offload(skb);
++	struct net_device *dev = skb->dev;
+ 	struct sec_path *sp;
+ 
+ 	if (!xo || (xo->flags & XFRM_XMIT))
+@@ -93,6 +94,17 @@ struct sk_buff *validate_xmit_xfrm(struc
+ 	if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND)
+ 		return skb;
+ 
++	/* The packet was sent to HW IPsec packet offload engine,
++	 * but to wrong device. Drop the packet, so it won't skip
++	 * XFRM stack.
++	 */
++	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET && x->xso.dev != dev) {
++		kfree_skb(skb);
++		//dev_core_stats_tx_dropped_inc(dev);
++		atomic_long_inc(&dev->tx_dropped);
++		return NULL;
++	}
++
+ 	local_irq_save(flags);
+ 	sd = this_cpu_ptr(&softnet_data);
+ 	err = !skb_queue_empty(&sd->xfrm_backlog);
+@@ -198,6 +210,7 @@ int xfrm_dev_state_add(struct net *net,
+ 	struct xfrm_state_offload *xso = &x->xso;
+ 	xfrm_address_t *saddr;
+ 	xfrm_address_t *daddr;
++	bool is_packet_offload;
+ 
+ 	if (!x->type_offload)
+ 		return -EINVAL;
+@@ -206,9 +219,11 @@ int xfrm_dev_state_add(struct net *net,
+ 	if (x->encap || x->tfcpad)
+ 		return -EINVAL;
+ 
+-	if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND))
++	if (xuo->flags &
++	    ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND | XFRM_OFFLOAD_PACKET))
+ 		return -EINVAL;
+ 
++	is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET;
+ 	dev = dev_get_by_index(net, xuo->ifindex);
+ 	if (!dev) {
+ 		if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) {
+@@ -223,7 +238,7 @@ int xfrm_dev_state_add(struct net *net,
+ 					x->props.family,
+ 					xfrm_smark_get(0, x));
+ 		if (IS_ERR(dst))
+-			return 0;
++			return (is_packet_offload) ? -EINVAL : 0;
+ 
+ 		dev = dst->dev;
+ 
+@@ -234,7 +249,7 @@ int xfrm_dev_state_add(struct net *net,
+ 	if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) {
+ 		xso->dev = NULL;
+ 		dev_put(dev);
+-		return 0;
++		return (is_packet_offload) ? -EINVAL : 0;
+ 	}
+ 
+ 	if (x->props.flags & XFRM_STATE_ESN &&
+@@ -249,14 +264,28 @@ int xfrm_dev_state_add(struct net *net,
+ 	/* Don't forward bit that is not implemented */
+ 	xso->flags = xuo->flags & ~XFRM_OFFLOAD_IPV6;
+ 
++	if (is_packet_offload)
++		xso->type = XFRM_DEV_OFFLOAD_PACKET;
++	else
++		xso->type = XFRM_DEV_OFFLOAD_CRYPTO;
++
+ 	err = dev->xfrmdev_ops->xdo_dev_state_add(x);
+ 	if (err) {
+ 		xso->num_exthdrs = 0;
+ 		xso->flags = 0;
+ 		xso->dev = NULL;
+ 		dev_put(dev);
++		xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
+ 
+-		if (err != -EOPNOTSUPP)
++		/* User explicitly requested packet offload mode and configured
++		 * policy in addition to the XFRM state. So be civil to users,
++		 * and return an error instead of taking fallback path.
++		 *
++		 * This WARN_ON() can be seen as a documentation for driver
++		 * authors to do not return -EOPNOTSUPP in packet offload mode.
++		 */
++		WARN_ON(err == -EOPNOTSUPP && is_packet_offload);
++		if (err != -EOPNOTSUPP || is_packet_offload)
+ 			return err;
+ 	}
+ 
+@@ -264,6 +293,65 @@ int xfrm_dev_state_add(struct net *net,
+ }
+ EXPORT_SYMBOL_GPL(xfrm_dev_state_add);
+ 
++int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
++			struct xfrm_user_offload *xuo, u8 dir,
++			struct netlink_ext_ack *extack)
++{
++	struct xfrm_state_offload *xdo = &xp->xdo;
++	struct net_device *dev;
++	int err;
++
++	if (!xuo->flags || xuo->flags & ~XFRM_OFFLOAD_PACKET) {
++		/* We support only packet offload mode and it means
++		 * that user must set XFRM_OFFLOAD_PACKET bit.
++		 */
++		NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request");
++		return -EINVAL;
++	}
++
++	dev = dev_get_by_index(net, xuo->ifindex);
++	if (!dev)
++		return -EINVAL;
++
++	if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_policy_add) {
++		xdo->dev = NULL;
++		dev_put(dev);
++		NL_SET_ERR_MSG(extack, "Policy offload is not supported");
++		return -EINVAL;
++	}
++
++	xdo->dev = dev;
++	xdo->type = XFRM_DEV_OFFLOAD_PACKET;
++	switch (dir) {
++	case XFRM_POLICY_IN:
++		xdo->dir = XFRM_DEV_OFFLOAD_IN;
++		break;
++	case XFRM_POLICY_OUT:
++		xdo->dir = XFRM_DEV_OFFLOAD_OUT;
++		break;
++	case XFRM_POLICY_FWD:
++		xdo->dir = XFRM_DEV_OFFLOAD_FWD;
++		break;
++	default:
++		xdo->dev = NULL;
++		dev_put(dev);
++		NL_SET_ERR_MSG(extack, "Unrecognized oflload direction");
++		return -EINVAL;
++	}
++
++	err = dev->xfrmdev_ops->xdo_dev_policy_add(xp);
++	if (err) {
++		xdo->dev = NULL;
++		xdo->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
++		xdo->dir = 0;
++		NL_SET_ERR_MSG(extack, "Device failed to offload this policy");
++		return err;
++	}
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(xfrm_dev_policy_add);
++
+ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
+ {
+ 	int mtu;
+@@ -274,8 +362,9 @@ bool xfrm_dev_offload_ok(struct sk_buff
+ 	if (!x->type_offload || x->encap)
+ 		return false;
+ 
+-	if ((!dev || (dev == xfrm_dst_path(dst)->dev)) &&
+-	    (!xdst->child->xfrm)) {
++	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET ||
++	    ((!dev || (dev == xfrm_dst_path(dst)->dev)) &&
++	     !xdst->child->xfrm)) {
+ 		mtu = xfrm_state_mtu(x, xdst->child_mtu_cached);
+ 		if (skb->len <= mtu)
+ 			goto ok;
+@@ -376,8 +465,10 @@ static int xfrm_dev_feat_change(struct n
+ 
+ static int xfrm_dev_down(struct net_device *dev)
+ {
+-	if (dev->features & NETIF_F_HW_ESP)
++	if (dev->features & NETIF_F_HW_ESP) {
+ 		xfrm_dev_state_flush(dev_net(dev), dev, true);
++		xfrm_dev_policy_flush(dev_net(dev), dev, true);
++	}
+ 
+ 	return NOTIFY_DONE;
+ }
+--- a/net/xfrm/xfrm_output.c
++++ b/net/xfrm/xfrm_output.c
+@@ -410,7 +410,7 @@ static int xfrm_output_one(struct sk_buf
+ 	struct xfrm_state *x = dst->xfrm;
+ 	struct net *net = xs_net(x);
+ 
+-	if (err <= 0)
++	if (err <= 0 || x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
+ 		goto resume;
+ 
+ 	do {
+@@ -568,6 +568,16 @@ int xfrm_output(struct sock *sk, struct
+ 	struct xfrm_state *x = skb_dst(skb)->xfrm;
+ 	int err;
+ 
++	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) {
++		if (!xfrm_dev_offload_ok(skb, x)) {
++			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
++			kfree_skb(skb);
++			return -EHOSTUNREACH;
++		}
++
++		return xfrm_output_resume(skb, 0);
++	}
++
+ 	secpath_reset(skb);
+ 
+ 	if (xfrm_dev_offload_ok(skb, x)) {
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -423,6 +423,7 @@ void xfrm_policy_destroy(struct xfrm_pol
+ 	if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer))
+ 		BUG();
+ 
++	xfrm_dev_policy_free(policy);
+ 	call_rcu(&policy->rcu, xfrm_policy_destroy_rcu);
+ }
+ EXPORT_SYMBOL(xfrm_policy_destroy);
+@@ -533,7 +534,7 @@ redo:
+ 		__get_hash_thresh(net, pol->family, dir, &dbits, &sbits);
+ 		h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
+ 				pol->family, nhashmask, dbits, sbits);
+-		if (!entry0) {
++		if (!entry0 || pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
+ 			hlist_del_rcu(&pol->bydst);
+ 			hlist_add_head_rcu(&pol->bydst, ndsttable + h);
+ 			h0 = h;
+@@ -864,7 +865,7 @@ static void xfrm_policy_inexact_list_rei
+ 				break;
+ 		}
+ 
+-		if (newpos)
++		if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET)
+ 			hlist_add_behind_rcu(&policy->bydst, newpos);
+ 		else
+ 			hlist_add_head_rcu(&policy->bydst, &n->hhead);
+@@ -1345,7 +1346,7 @@ static void xfrm_hash_rebuild(struct wor
+ 			else
+ 				break;
+ 		}
+-		if (newpos)
++		if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET)
+ 			hlist_add_behind_rcu(&policy->bydst, newpos);
+ 		else
+ 			hlist_add_head_rcu(&policy->bydst, chain);
+@@ -1522,7 +1523,7 @@ static void xfrm_policy_insert_inexact_l
+ 			break;
+ 	}
+ 
+-	if (newpos)
++	if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET)
+ 		hlist_add_behind_rcu(&policy->bydst_inexact_list, newpos);
+ 	else
+ 		hlist_add_head_rcu(&policy->bydst_inexact_list, chain);
+@@ -1559,9 +1560,12 @@ static struct xfrm_policy *xfrm_policy_i
+ 			break;
+ 	}
+ 
+-	if (newpos)
++	if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET)
+ 		hlist_add_behind_rcu(&policy->bydst, &newpos->bydst);
+ 	else
++		/* Packet offload policies enter to the head
++		 * to speed-up lookups.
++		 */
+ 		hlist_add_head_rcu(&policy->bydst, chain);
+ 
+ 	return delpol;
+@@ -1767,12 +1771,41 @@ xfrm_policy_flush_secctx_check(struct ne
+ 	}
+ 	return err;
+ }
++
++static inline int xfrm_dev_policy_flush_secctx_check(struct net *net,
++						     struct net_device *dev,
++						     bool task_valid)
++{
++	struct xfrm_policy *pol;
++	int err = 0;
++
++	list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
++		if (pol->walk.dead ||
++		    xfrm_policy_id2dir(pol->index) >= XFRM_POLICY_MAX ||
++		    pol->xdo.dev != dev)
++			continue;
++
++		err = security_xfrm_policy_delete(pol->security);
++		if (err) {
++			xfrm_audit_policy_delete(pol, 0, task_valid);
++			return err;
++		}
++	}
++	return err;
++}
+ #else
+ static inline int
+ xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
+ {
+ 	return 0;
+ }
++
++static inline int xfrm_dev_policy_flush_secctx_check(struct net *net,
++						     struct net_device *dev,
++						     bool task_valid)
++{
++	return 0;
++}
+ #endif
+ 
+ int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
+@@ -1812,6 +1845,44 @@ out:
+ }
+ EXPORT_SYMBOL(xfrm_policy_flush);
+ 
++int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
++			  bool task_valid)
++{
++	int dir, err = 0, cnt = 0;
++	struct xfrm_policy *pol;
++
++	spin_lock_bh(&net->xfrm.xfrm_policy_lock);
++
++	err = xfrm_dev_policy_flush_secctx_check(net, dev, task_valid);
++	if (err)
++		goto out;
++
++again:
++	list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
++		dir = xfrm_policy_id2dir(pol->index);
++		if (pol->walk.dead ||
++		    dir >= XFRM_POLICY_MAX ||
++		    pol->xdo.dev != dev)
++			continue;
++
++		__xfrm_policy_unlink(pol, dir);
++		spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
++		cnt++;
++		xfrm_audit_policy_delete(pol, 1, task_valid);
++		xfrm_policy_kill(pol);
++		spin_lock_bh(&net->xfrm.xfrm_policy_lock);
++		goto again;
++	}
++	if (cnt)
++		__xfrm_policy_inexact_flush(net);
++	else
++		err = -ESRCH;
++out:
++	spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
++	return err;
++}
++EXPORT_SYMBOL(xfrm_dev_policy_flush);
++
+ int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
+ 		     int (*func)(struct xfrm_policy *, int, int, void*),
+ 		     void *data)
+@@ -2113,6 +2184,9 @@ static struct xfrm_policy *xfrm_policy_l
+ 			break;
+ 		}
+ 	}
++	if (ret && ret->xdo.type == XFRM_DEV_OFFLOAD_PACKET)
++		goto skip_inexact;
++
+ 	bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id);
+ 	if (!bin || !xfrm_policy_find_inexact_candidates(&cand, bin, saddr,
+ 							 daddr))
+@@ -2246,6 +2320,7 @@ int xfrm_policy_delete(struct xfrm_polic
+ 	pol = __xfrm_policy_unlink(pol, dir);
+ 	spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
+ 	if (pol) {
++		xfrm_dev_policy_delete(pol);
+ 		xfrm_policy_kill(pol);
+ 		return 0;
+ 	}
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -78,6 +78,25 @@ xfrm_spi_hash(struct net *net, const xfr
+ 	return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
+ }
+ 
++#define XFRM_STATE_INSERT(by, _n, _h, _type)                               \
++	{                                                                  \
++		struct xfrm_state *_x = NULL;                              \
++									   \
++		if (_type != XFRM_DEV_OFFLOAD_PACKET) {                    \
++			hlist_for_each_entry_rcu(_x, _h, by) {             \
++				if (_x->xso.type == XFRM_DEV_OFFLOAD_PACKET) \
++					continue;                          \
++				break;                                     \
++			}                                                  \
++		}                                                          \
++									   \
++		if (!_x || _x->xso.type == XFRM_DEV_OFFLOAD_PACKET)        \
++			/* SAD is empty or consist from HW SAs only */     \
++			hlist_add_head_rcu(_n, _h);                        \
++		else                                                       \
++			hlist_add_before_rcu(_n, &_x->by);                 \
++	}
++
+ static void xfrm_hash_transfer(struct hlist_head *list,
+ 			       struct hlist_head *ndsttable,
+ 			       struct hlist_head *nsrctable,
+@@ -93,18 +112,20 @@ static void xfrm_hash_transfer(struct hl
+ 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
+ 				    x->props.reqid, x->props.family,
+ 				    nhashmask);
+-		hlist_add_head_rcu(&x->bydst, ndsttable + h);
++		XFRM_STATE_INSERT(bydst, &x->bydst, ndsttable + h, x->xso.type);
+ 
+ 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
+ 				    x->props.family,
+ 				    nhashmask);
+-		hlist_add_head_rcu(&x->bysrc, nsrctable + h);
++		XFRM_STATE_INSERT(bysrc, &x->bysrc, nsrctable + h, x->xso.type);
+ 
+ 		if (x->id.spi) {
+ 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
+ 					    x->id.proto, x->props.family,
+ 					    nhashmask);
+ 			hlist_add_head_rcu(&x->byspi, nspitable + h);
++			XFRM_STATE_INSERT(byspi, &x->byspi, nspitable + h,
++					  x->xso.type);
+ 		}
+ 	}
+ }
+@@ -527,6 +548,8 @@ static enum hrtimer_restart xfrm_timer_h
+ 	int err = 0;
+ 
+ 	spin_lock(&x->lock);
++	xfrm_dev_state_update_curlft(x);
++
+ 	if (x->km.state == XFRM_STATE_DEAD)
+ 		goto out;
+ 	if (x->km.state == XFRM_STATE_EXPIRED)
+@@ -923,6 +946,49 @@ xfrm_init_tempstate(struct xfrm_state *x
+ 	x->props.family = tmpl->encap_family;
+ }
+ 
++static struct xfrm_state *__xfrm_state_lookup_all(struct net *net, u32 mark,
++						  const xfrm_address_t *daddr,
++						  __be32 spi, u8 proto,
++						  unsigned short family,
++						  struct xfrm_state_offload *xdo)
++{
++	unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family);
++	struct xfrm_state *x;
++
++	hlist_for_each_entry_rcu(x, net->xfrm.state_byspi + h, byspi) {
++#ifdef CONFIG_XFRM_OFFLOAD
++		if (xdo->type == XFRM_DEV_OFFLOAD_PACKET) {
++			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
++				/* HW states are in the head of list, there is
++				 * no need to iterate further.
++				 */
++				break;
++
++			/* Packet offload: both policy and SA should
++			 * have same device.
++			 */
++			if (xdo->dev != x->xso.dev)
++				continue;
++		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++			/* Skip HW policy for SW lookups */
++			continue;
++#endif
++		if (x->props.family != family ||
++		    x->id.spi       != spi ||
++		    x->id.proto     != proto ||
++		    !xfrm_addr_equal(&x->id.daddr, daddr, family))
++			continue;
++
++		if ((mark & x->mark.m) != x->mark.v)
++			continue;
++		if (!xfrm_state_hold_rcu(x))
++			continue;
++		return x;
++	}
++
++	return NULL;
++}
++
+ static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark,
+ 					      const xfrm_address_t *daddr,
+ 					      __be32 spi, u8 proto,
+@@ -1062,6 +1128,23 @@ xfrm_state_find(const xfrm_address_t *da
+ 	rcu_read_lock();
+ 	h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
+ 	hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) {
++#ifdef CONFIG_XFRM_OFFLOAD
++		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
++			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
++				/* HW states are in the head of list, there is
++				 * no need to iterate further.
++				 */
++				break;
++
++			/* Packet offload: both policy and SA should
++			 * have same device.
++			 */
++			if (pol->xdo.dev != x->xso.dev)
++				continue;
++		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++			/* Skip HW policy for SW lookups */
++			continue;
++#endif
+ 		if (x->props.family == encap_family &&
+ 		    x->props.reqid == tmpl->reqid &&
+ 		    (mark & x->mark.m) == x->mark.v &&
+@@ -1079,6 +1162,23 @@ xfrm_state_find(const xfrm_address_t *da
+ 
+ 	h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family);
+ 	hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h_wildcard, bydst) {
++#ifdef CONFIG_XFRM_OFFLOAD
++		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
++			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
++				/* HW states are in the head of list, there is
++				 * no need to iterate further.
++				 */
++				break;
++
++			/* Packet offload: both policy and SA should
++			 * have same device.
++			 */
++			if (pol->xdo.dev != x->xso.dev)
++				continue;
++		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++			/* Skip HW policy for SW lookups */
++			continue;
++#endif
+ 		if (x->props.family == encap_family &&
+ 		    x->props.reqid == tmpl->reqid &&
+ 		    (mark & x->mark.m) == x->mark.v &&
+@@ -1096,8 +1196,10 @@ found:
+ 	x = best;
+ 	if (!x && !error && !acquire_in_progress) {
+ 		if (tmpl->id.spi &&
+-		    (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi,
+-					      tmpl->id.proto, encap_family)) != NULL) {
++		    (x0 = __xfrm_state_lookup_all(net, mark, daddr,
++						  tmpl->id.spi, tmpl->id.proto,
++						  encap_family,
++						  &pol->xdo)) != NULL) {
+ 			to_put = x0;
+ 			error = -EEXIST;
+ 			goto out;
+@@ -1131,17 +1233,42 @@ found:
+ 			x = NULL;
+ 			goto out;
+ 		}
+-
++#ifdef CONFIG_XFRM_OFFLOAD
++		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
++			struct xfrm_state_offload *xdo = &pol->xdo;
++			struct xfrm_state_offload *xso = &x->xso;
++
++			xso->type = XFRM_DEV_OFFLOAD_PACKET;
++			xso->dir = xdo->dir;
++			xso->dev = xdo->dev;
++			error = xso->dev->xfrmdev_ops->xdo_dev_state_add(x);
++			if (error) {
++				xso->dir = 0;
++				xso->dev = NULL;
++				xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
++				x->km.state = XFRM_STATE_DEAD;
++				to_put = x;
++				x = NULL;
++				goto out;
++			}
++		}
++#endif
+ 		if (km_query(x, tmpl, pol) == 0) {
+ 			spin_lock_bh(&net->xfrm.xfrm_state_lock);
+ 			x->km.state = XFRM_STATE_ACQ;
+ 			list_add(&x->km.all, &net->xfrm.state_all);
+-			hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h);
++			XFRM_STATE_INSERT(bydst, &x->bydst,
++					  net->xfrm.state_bydst + h,
++					  x->xso.type);
+ 			h = xfrm_src_hash(net, daddr, saddr, encap_family);
+-			hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h);
++			XFRM_STATE_INSERT(bysrc, &x->bysrc,
++					  net->xfrm.state_bysrc + h,
++					  x->xso.type);
+ 			if (x->id.spi) {
+ 				h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
+-				hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
++				XFRM_STATE_INSERT(byspi, &x->byspi,
++						  net->xfrm.state_byspi + h,
++						  x->xso.type);
+ 			}
+ 			x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
+ 			hrtimer_start(&x->mtimer,
+@@ -1151,6 +1278,16 @@ found:
+ 			xfrm_hash_grow_check(net, x->bydst.next != NULL);
+ 			spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+ 		} else {
++#ifdef CONFIG_XFRM_OFFLOAD
++			struct xfrm_state_offload *xso = &x->xso;
++
++			if (xso->type == XFRM_DEV_OFFLOAD_PACKET) {
++				xso->dev->xfrmdev_ops->xdo_dev_state_delete(x);
++				xso->dir = 0;
++				xso->dev = NULL;
++				xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
++			}
++#endif
+ 			x->km.state = XFRM_STATE_DEAD;
+ 			to_put = x;
+ 			x = NULL;
+@@ -1246,16 +1383,19 @@ static void __xfrm_state_insert(struct x
+ 
+ 	h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr,
+ 			  x->props.reqid, x->props.family);
+-	hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h);
++	XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h,
++			  x->xso.type);
+ 
+ 	h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family);
+-	hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h);
++	XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h,
++			  x->xso.type);
+ 
+ 	if (x->id.spi) {
+ 		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto,
+ 				  x->props.family);
+ 
+-		hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
++		XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h,
++				  x->xso.type);
+ 	}
+ 
+ 	hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT);
+@@ -1369,9 +1509,11 @@ static struct xfrm_state *__find_acq_cor
+ 			      ktime_set(net->xfrm.sysctl_acq_expires, 0),
+ 			      HRTIMER_MODE_REL_SOFT);
+ 		list_add(&x->km.all, &net->xfrm.state_all);
+-		hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h);
++		XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h,
++				  x->xso.type);
+ 		h = xfrm_src_hash(net, daddr, saddr, family);
+-		hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h);
++		XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h,
++				  x->xso.type);
+ 
+ 		net->xfrm.state_num++;
+ 
+@@ -1742,6 +1884,8 @@ EXPORT_SYMBOL(xfrm_state_update);
+ 
+ int xfrm_state_check_expire(struct xfrm_state *x)
+ {
++	xfrm_dev_state_update_curlft(x);
++
+ 	if (!x->curlft.use_time)
+ 		x->curlft.use_time = ktime_get_real_seconds();
+ 
+@@ -2043,7 +2187,8 @@ int xfrm_alloc_spi(struct xfrm_state *x,
+ 		spin_lock_bh(&net->xfrm.xfrm_state_lock);
+ 		x->id.spi = newspi;
+ 		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family);
+-		hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
++		XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h,
++				  x->xso.type);
+ 		spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+ 
+ 		err = 0;
+--- a/net/xfrm/xfrm_user.c
++++ b/net/xfrm/xfrm_user.c
+@@ -844,6 +844,8 @@ static int copy_user_offload(struct xfrm
+ 	memset(xuo, 0, sizeof(*xuo));
+ 	xuo->ifindex = xso->dev->ifindex;
+ 	xuo->flags = xso->flags;
++	if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
++		xuo->flags |= XFRM_OFFLOAD_PACKET;
+ 
+ 	return 0;
+ }
+@@ -1634,6 +1636,15 @@ static struct xfrm_policy *xfrm_policy_c
+ 	if (attrs[XFRMA_IF_ID])
+ 		xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
+ 
++	/* configure the hardware if offload is requested */
++	if (attrs[XFRMA_OFFLOAD_DEV]) {
++		err = xfrm_dev_policy_add(net, xp,
++					  nla_data(attrs[XFRMA_OFFLOAD_DEV]),
++					  p->dir, 0);
++		if (err)
++			goto error;
++	}
++
+ 	return xp;
+  error:
+ 	*errp = err;
+@@ -1672,6 +1683,7 @@ static int xfrm_add_policy(struct sk_buf
+ 	xfrm_audit_policy_add(xp, err ? 0 : 1, true);
+ 
+ 	if (err) {
++		xfrm_dev_policy_delete(xp);
+ 		security_xfrm_policy_free(xp->security);
+ 		kfree(xp);
+ 		return err;
+@@ -1783,6 +1795,8 @@ static int dump_one_policy(struct xfrm_p
+ 		err = xfrm_mark_put(skb, &xp->mark);
+ 	if (!err)
+ 		err = xfrm_if_id_put(skb, xp->if_id);
++	if (!err && xp->xdo.dev)
++		err = copy_user_offload(&xp->xdo, skb);
+ 	if (err) {
+ 		nlmsg_cancel(skb, nlh);
+ 		return err;
+@@ -2958,6 +2972,8 @@ static int build_acquire(struct sk_buff
+ 		err = xfrm_mark_put(skb, &xp->mark);
+ 	if (!err)
+ 		err = xfrm_if_id_put(skb, xp->if_id);
++	if (!err && xp->xdo.dev)
++		err = copy_user_offload(&xp->xdo, skb);
+ 	if (err) {
+ 		nlmsg_cancel(skb, nlh);
+ 		return err;
+@@ -3076,6 +3092,8 @@ static int build_polexpire(struct sk_buf
+ 		err = xfrm_mark_put(skb, &xp->mark);
+ 	if (!err)
+ 		err = xfrm_if_id_put(skb, xp->if_id);
++	if (!err && xp->xdo.dev)
++		err = copy_user_offload(&xp->xdo, skb);
+ 	if (err) {
+ 		nlmsg_cancel(skb, nlh);
+ 		return err;
+@@ -3159,6 +3177,8 @@ static int xfrm_notify_policy(struct xfr
+ 		err = xfrm_mark_put(skb, &xp->mark);
+ 	if (!err)
+ 		err = xfrm_if_id_put(skb, xp->if_id);
++	if (!err && xp->xdo.dev)
++		err = copy_user_offload(&xp->xdo, skb);
+ 	if (err)
+ 		goto out_free_skb;
+ 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2728-xfrm-extend-packet-mode-to-support-esp-tunnel-mode.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2728-xfrm-extend-packet-mode-to-support-esp-tunnel-mode.patch
new file mode 100644
index 0000000..ba9bc98
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2728-xfrm-extend-packet-mode-to-support-esp-tunnel-mode.patch
@@ -0,0 +1,28 @@
+--- a/net/xfrm/xfrm_output.c
++++ b/net/xfrm/xfrm_output.c
+@@ -410,7 +410,7 @@ static int xfrm_output_one(struct sk_buf
+ 	struct xfrm_state *x = dst->xfrm;
+ 	struct net *net = xs_net(x);
+ 
+-	if (err <= 0 || x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++	if (err <= 0)
+ 		goto resume;
+ 
+ 	do {
+@@ -568,16 +568,6 @@ int xfrm_output(struct sock *sk, struct
+ 	struct xfrm_state *x = skb_dst(skb)->xfrm;
+ 	int err;
+ 
+-	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) {
+-		if (!xfrm_dev_offload_ok(skb, x)) {
+-			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
+-			kfree_skb(skb);
+-			return -EHOSTUNREACH;
+-		}
+-
+-		return xfrm_output_resume(skb, 0);
+-	}
+-
+ 	secpath_reset(skb);
+ 
+ 	if (xfrm_dev_offload_ok(skb, x)) {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4100-mtk-tops-tunnel-offload-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4100-mtk-tunnel-offload-support.patch
similarity index 81%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4100-mtk-tops-tunnel-offload-support.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4100-mtk-tunnel-offload-support.patch
index a4a6d95..57a1dde 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4100-mtk-tops-tunnel-offload-support.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4100-mtk-tunnel-offload-support.patch
@@ -10,7 +10,7 @@
  void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
  {
  	__raw_writel(val, eth->base + reg);
-@@ -2089,6 +2092,7 @@ static int mtk_poll_rx(struct napi_struc
+@@ -2171,6 +2174,7 @@ static int mtk_poll_rx(struct napi_struc
  	u64 addr64 = 0;
  	u8 *data, *new_data;
  	struct mtk_rx_dma_v2 *rxd, trxd;
@@ -18,7 +18,7 @@
  	int done = 0;
  
  	if (unlikely(!ring))
-@@ -2132,11 +2136,20 @@ static int mtk_poll_rx(struct napi_struc
+@@ -2214,11 +2218,20 @@ static int mtk_poll_rx(struct napi_struc
  				      0 : RX_DMA_GET_SPORT(trxd.rxd4) - 1;
  		}
  
@@ -43,7 +43,7 @@
  
  		if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
  			goto release_desc;
-@@ -2221,6 +2234,8 @@ static int mtk_poll_rx(struct napi_struc
+@@ -2303,6 +2316,8 @@ static int mtk_poll_rx(struct napi_struc
  		skb_hnat_alg(skb) = 0;
  		skb_hnat_filled(skb) = 0;
  		skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
@@ -94,16 +94,16 @@
  {
 --- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
 +++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
-@@ -1085,6 +1085,8 @@ enum FoeIpAct {
- #define NR_WDMA0_PORT 8
+@@ -1087,6 +1087,8 @@ enum FoeIpAct {
  #define NR_WDMA1_PORT 9
+ #define NR_WDMA2_PORT 13
  #define NR_GMAC3_PORT 15
 +#define NR_TDMA_TPORT 4
 +#define NR_TDMA_QDMA_TPORT 5
  #define LAN_DEV_NAME hnat_priv->lan
  #define LAN2_DEV_NAME hnat_priv->lan2
  #define IS_WAN(dev)                                                            \
-@@ -1208,6 +1210,8 @@ static inline bool hnat_dsa_is_enable(st
+@@ -1210,6 +1212,8 @@ static inline bool hnat_dsa_is_enable(st
  }
  #endif
  
@@ -112,7 +112,7 @@
  void hnat_deinit_debugfs(struct mtk_hnat *h);
  int hnat_init_debugfs(struct mtk_hnat *h);
  int hnat_register_nf_hooks(void);
-@@ -1224,6 +1228,9 @@ extern int qos_ul_toggle;
+@@ -1226,6 +1230,9 @@ extern int qos_ul_toggle;
  extern int hook_toggle;
  extern int mape_toggle;
  extern int qos_toggle;
@@ -192,7 +192,7 @@
  	rcu_read_unlock_bh();
  
  	return 0;
-@@ -1202,6 +1233,37 @@ static struct ethhdr *get_ipv6_ipip_ethh
+@@ -1202,6 +1233,81 @@ static struct ethhdr *get_ipv6_ipip_ethh
  	return eth;
  }
  
@@ -205,10 +205,53 @@
 +	memcpy(entry,
 +	       &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)],
 +	       sizeof(*entry));
++
++#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
++	entry->bfib1.mc = 0;
++#endif /* defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) */
++	entry->bfib1.ka = 0;
++	entry->bfib1.vlan_layer = 0;
++	entry->bfib1.psn = 0;
++	entry->bfib1.vpm = 0;
++	entry->bfib1.ps = 0;
++}
++
++static inline void hnat_qos_tnl(u32 id, const struct net_device *dev)
++{
++	u32 cfg;
++	u32 max_man = 0;
++	u32 max_exp = 0;
++	const struct mtk_mac *mac;
++
++	if (!dev)
++		return;
++	mac = netdev_priv(dev);
++
++	switch (mac->speed) {
++	case SPEED_100:
++	case SPEED_1000:
++	case SPEED_2500:
++	case SPEED_5000:
++	case SPEED_10000:
++		max_man = mac->speed / SPEED_100;
++		max_exp = 5;
++		break;
++	default:
++		return;
++	}
++
++	cfg = QTX_SCH_MIN_RATE_EN | QTX_SCH_MAX_RATE_EN;
++	cfg |= (1 << QTX_SCH_MIN_RATE_MAN_OFFSET) |
++	       (4 << QTX_SCH_MIN_RATE_EXP_OFFSET) |
++	       (max_man << QTX_SCH_MAX_RATE_MAN_OFFSET) |
++	       (max_exp << QTX_SCH_MAX_RATE_EXP_OFFSET) |
++	       (4 << QTX_SCH_MAX_RATE_WGHT_OFFSET);
++	writel(cfg, hnat_priv->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));
 +}
 +
 +static inline void hnat_fill_offload_engine_entry(struct sk_buff *skb,
-+						  struct foe_entry *entry)
++						  struct foe_entry *entry,
++						  const struct net_device *dev)
 +{
 +#if defined(CONFIG_MEDIATEK_NETSYS_V3)
 +	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) {
@@ -224,13 +267,14 @@
 +	}
 +
 +	entry->ipv4_hnapt.iblk2.qid = 12; /* offload engine use QID 12 */
++	hnat_qos_tnl(12, dev); /* set rate limit to line rate */
 +#endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */
 +}
 +
  static unsigned int skb_to_hnat_info(struct sk_buff *skb,
  				     const struct net_device *dev,
  				     struct foe_entry *foe,
-@@ -1237,6 +1299,11 @@ static unsigned int skb_to_hnat_info(str
+@@ -1238,6 +1344,11 @@ static unsigned int skb_to_hnat_info(str
  	if (whnat && is_hnat_pre_filled(foe))
  		return 0;
  
@@ -242,15 +286,7 @@
  	entry.bfib1.pkt_type = foe->udib1.pkt_type; /* Get packte type state*/
  	entry.bfib1.state = foe->udib1.state;
  
-@@ -1247,6 +1314,7 @@ static unsigned int skb_to_hnat_info(str
- 	switch (ntohs(eth->h_proto)) {
- 	case ETH_P_IP:
- 		iph = ip_hdr(skb);
-+
- 		switch (iph->protocol) {
- 		case IPPROTO_UDP:
- 			udp = 1;
-@@ -1628,6 +1696,10 @@ static unsigned int skb_to_hnat_info(str
+@@ -1633,6 +1744,10 @@ static unsigned int skb_to_hnat_info(str
  	/* Fill Layer2 Info.*/
  	entry = ppe_fill_L2_info(eth, entry, hw_path);
  
@@ -261,12 +297,12 @@
  	/* Fill Info Blk*/
  	entry = ppe_fill_info_blk(eth, entry, hw_path);
  
-@@ -1806,7 +1878,20 @@ static unsigned int skb_to_hnat_info(str
+@@ -1833,7 +1948,20 @@ static unsigned int skb_to_hnat_info(str
  			entry.ipv6_5t_route.act_dp |= UDF_HNAT_PRE_FILLED;
  	}
  
 +#if defined(CONFIG_MEDIATEK_NETSYS_V3)
-+	hnat_fill_offload_engine_entry(skb, &entry);
++	hnat_fill_offload_engine_entry(skb, &entry, dev);
 +#endif
 +
 +hnat_entry_skip_bind:
@@ -282,7 +318,7 @@
  	memcpy(foe, &entry, sizeof(entry));
  	/*reset statistic for this entry*/
  	if (hnat_priv->data->per_flow_accounting &&
-@@ -1859,6 +1944,7 @@ int mtk_sw_nat_hook_tx(struct sk_buff *s
+@@ -1886,6 +2014,7 @@ int mtk_sw_nat_hook_tx(struct sk_buff *s
  		return NF_ACCEPT;
  
  	eth = eth_hdr(skb);
@@ -290,7 +326,7 @@
  	memcpy(&bfib1_tx, &entry->bfib1, sizeof(entry->bfib1));
  
  	/*not bind multicast if PPE mcast not enable*/
-@@ -1878,6 +1964,12 @@ int mtk_sw_nat_hook_tx(struct sk_buff *s
+@@ -1905,6 +2034,12 @@ int mtk_sw_nat_hook_tx(struct sk_buff *s
  	switch ((int)bfib1_tx.pkt_type) {
  	case IPV4_HNAPT:
  	case IPV4_HNAT:
@@ -303,18 +339,18 @@
  		entry->ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source));
  		entry->ipv4_hnapt.smac_lo = swab16(*((u16 *)&eth->h_source[4]));
  		break;
-@@ -2037,6 +2129,10 @@ int mtk_sw_nat_hook_tx(struct sk_buff *s
+@@ -2066,6 +2201,10 @@ int mtk_sw_nat_hook_tx(struct sk_buff *s
  		entry->ipv6_5t_route.iblk2.dp = gmac_no;
  	}
  
 +#if defined(CONFIG_MEDIATEK_NETSYS_V3)
-+	hnat_fill_offload_engine_entry(skb, entry);
++	hnat_fill_offload_engine_entry(skb, entry, NULL);
 +#endif
 +
  	bfib1_tx.ttl = 1;
  	bfib1_tx.state = BIND;
  	wmb();
-@@ -2058,6 +2154,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
+@@ -2087,6 +2226,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
  	}
  
  	skb_hnat_alg(skb) = 0;
@@ -322,7 +358,7 @@
  	skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
  
  	if (skb_hnat_iface(skb) == FOE_MAGIC_WED0)
-@@ -2504,6 +2601,7 @@ static unsigned int mtk_hnat_nf_post_rou
+@@ -2535,6 +2675,7 @@ static unsigned int mtk_hnat_nf_post_rou
  	struct flow_offload_hw_path hw_path = { .dev = (struct net_device*)out,
  						.virt_dev = (struct net_device*)out };
  	const struct net_device *arp_dev = out;
@@ -330,14 +366,25 @@
  
  	if (xlat_toggle && !mtk_464xlat_post_process(skb, out))
  		return 0;
-@@ -2524,10 +2622,18 @@ static unsigned int mtk_hnat_nf_post_rou
+@@ -2551,10 +2692,29 @@ static unsigned int mtk_hnat_nf_post_rou
  
  	if (out->netdev_ops->ndo_flow_offload_check) {
  		out->netdev_ops->ndo_flow_offload_check(&hw_path);
 +
  		out = (IS_GMAC1_MODE) ? hw_path.virt_dev : hw_path.dev;
-+		if (hw_path.flags & FLOW_OFFLOAD_PATH_TNL && mtk_tnl_encap_offload)
-+			skb_hnat_set_tops(skb, hw_path.tnl_type + 1);
++		if (hw_path.flags & FLOW_OFFLOAD_PATH_TNL && mtk_tnl_encap_offload) {
++			if (ntohs(skb->protocol) == ETH_P_IP
++			    && ip_hdr(skb)->protocol == IPPROTO_TCP) {
++				skb_hnat_set_tops(skb, hw_path.tnl_type + 1);
++			} else {
++				/*
++				 * we are not support protocols other than IPv4 TCP
++				 * for tunnel protocol offload yet
++				 */
++				skb_hnat_alg(skb) = 1;
++				return 0;
++			}
++		}
  	}
  
  	if (!IS_LAN_GRP(out) && !IS_WAN(out) && !IS_EXT(out))
@@ -349,7 +396,7 @@
  		return 0;
  
  	trace_printk("[%s] case hit, %x-->%s, reason=%x\n", __func__,
-@@ -2547,9 +2653,18 @@ static unsigned int mtk_hnat_nf_post_rou
+@@ -2574,9 +2734,18 @@ static unsigned int mtk_hnat_nf_post_rou
  		if (fn && !mtk_hnat_accel_type(skb))
  			break;
  
@@ -369,7 +416,7 @@
  		skb_to_hnat_info(skb, out, entry, &hw_path);
  		break;
  	case HIT_BIND_KEEPALIVE_DUP_OLD_HDR:
-@@ -2820,7 +2935,7 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
+@@ -2847,7 +3016,7 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
  	if (iph->protocol == IPPROTO_IPV6) {
  		entry->udib1.pkt_type = IPV6_6RD;
  		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
@@ -438,7 +485,7 @@
  	u8 eth_dest[ETH_ALEN];
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1868,6 +1868,9 @@ extern const struct of_device_id of_mtk_
+@@ -1874,6 +1874,9 @@ extern const struct of_device_id of_mtk_
  extern u32 mtk_hwlro_stats_ebl;
  extern u32 dbg_show_level;
  
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4101-mtk-tops-network-service-error-recover-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4101-mtk-tunnel-network-service-error-recover-support.patch
similarity index 96%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4101-mtk-tops-network-service-error-recover-support.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4101-mtk-tunnel-network-service-error-recover-support.patch
index fbb46ac..9f9e26a 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4101-mtk-tops-network-service-error-recover-support.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4101-mtk-tunnel-network-service-error-recover-support.patch
@@ -38,7 +38,7 @@
  
  #define MTK_ETHTOOL_STAT(x) { #x, \
  			      offsetof(struct mtk_hw_stats, x) / sizeof(u64) }
-@@ -4057,6 +4058,8 @@ static void mtk_pending_work(struct work
+@@ -4141,6 +4142,8 @@ static void mtk_pending_work(struct work
  				}
  			pr_warn("wait for MTK_FE_START_RESET\n");
  		}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4102-mtk-crypto-offload-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4102-mtk-crypto-offload-support.patch
new file mode 100644
index 0000000..75880ed
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4102-mtk-crypto-offload-support.patch
@@ -0,0 +1,183 @@
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2316,6 +2316,7 @@ static int mtk_poll_rx(struct napi_struc
+ 
+ 		skb_hnat_alg(skb) = 0;
+ 		skb_hnat_filled(skb) = 0;
++		skb_hnat_set_cdrt(skb, 0);
+ 		skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
+ 		skb_hnat_set_tops(skb, 0);
+ 		skb_hnat_set_is_decap(skb, 0);
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+@@ -1076,6 +1076,9 @@ static unsigned int hnat_ipv4_get_nextho
+ 		return 0;
+ 	}
+ 
++	if (!skb_hnat_cdrt(skb) && dst && dst_xfrm(dst))
++		return 0;
++
+ 	rcu_read_lock_bh();
+ 	nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr);
+ 	neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
+@@ -1097,7 +1100,7 @@ static unsigned int hnat_ipv4_get_nextho
+ 	 * outer header, we must update its outer mac header pointer
+ 	 * before filling outer mac or it may screw up inner mac
+ 	 */
+-	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) {
++	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) || skb_hnat_cdrt(skb)) {
+ 		skb_push(skb, sizeof(struct ethhdr));
+ 		skb_reset_mac_header(skb);
+ 	}
+@@ -1105,7 +1108,7 @@ static unsigned int hnat_ipv4_get_nextho
+ 	memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN);
+ 	memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN);
+ 
+-	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
++	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) || skb_hnat_cdrt(skb))
+ 		skb_pull(skb, sizeof(struct ethhdr));
+ 
+ 	rcu_read_unlock_bh();
+@@ -1299,6 +1302,9 @@ static inline void hnat_fill_offload_eng
+ 		 */
+ 		entry->ipv4_hnapt.tport_id = NR_TDMA_QDMA_TPORT;
+ 		entry->ipv4_hnapt.tops_entry = skb_hnat_tops(skb);
++	} else if (skb_hnat_cdrt(skb)) {
++		entry->ipv4_hnapt.tport_id = NR_EIP197_QDMA_TPORT;
++		entry->ipv4_hnapt.cdrt_id = skb_hnat_cdrt(skb);
+ 	} else {
+ 		return;
+ 	}
+@@ -1344,7 +1350,8 @@ static unsigned int skb_to_hnat_info(str
+ 	if (whnat && is_hnat_pre_filled(foe))
+ 		return 0;
+ 
+-	if (skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL)) {
++	if ((skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL))
++	    || (skb_hnat_cdrt(skb) && skb_dst(skb) && !dst_xfrm(skb_dst(skb)))) {
+ 		hnat_get_filled_unbind_entry(skb, &entry);
+ 		goto hnat_entry_bind;
+ 	}
+@@ -1744,7 +1751,8 @@ static unsigned int skb_to_hnat_info(str
+ 	/* Fill Layer2 Info.*/
+ 	entry = ppe_fill_L2_info(eth, entry, hw_path);
+ 
+-	if (skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL)
++	if ((skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL)
++	    || (!skb_hnat_cdrt(skb) && skb_dst(skb) && dst_xfrm(skb_dst(skb))))
+ 		goto hnat_entry_skip_bind;
+ 
+ hnat_entry_bind:
+@@ -1950,6 +1958,8 @@ hnat_entry_bind:
+ 
+ #if defined(CONFIG_MEDIATEK_NETSYS_V3)
+ 	hnat_fill_offload_engine_entry(skb, &entry, dev);
++	if (skb_hnat_cdrt(skb))
++		entry = ppe_fill_L2_info(eth, entry, hw_path);
+ #endif
+ 
+ hnat_entry_skip_bind:
+@@ -2227,6 +2237,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
+ 
+ 	skb_hnat_alg(skb) = 0;
+ 	skb_hnat_set_tops(skb, 0);
++	skb_hnat_set_cdrt(skb, 0);
+ 	skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
+ 
+ 	if (skb_hnat_iface(skb) == FOE_MAGIC_WED0)
+@@ -2313,7 +2324,8 @@ static unsigned int mtk_hnat_accel_type(
+ 	 * is from local_out which is also filtered in sanity check.
+ 	 */
+ 	dst = skb_dst(skb);
+-	if (dst && dst_xfrm(dst))
++	if (dst && dst_xfrm(dst)
++	    && (!mtk_crypto_offloadable || !mtk_crypto_offloadable(skb)))
+ 		return 0;
+ 
+ 	ct = nf_ct_get(skb, &ctinfo);
+@@ -2709,6 +2721,14 @@ static unsigned int mtk_hnat_nf_post_rou
+ 		}
+ 	}
+ 
++	/* we are not support protocols other than IPv4 TCP for crypto offload yet */
++	if (skb_hnat_is_decrypt(skb)
++	    && (ntohs(skb->protocol) != ETH_P_IP
++		|| ip_hdr(skb)->protocol != IPPROTO_TCP)) {
++		skb_hnat_alg(skb) = 1;
++		return 0;
++	}
++
+ 	if (!IS_LAN_GRP(out) && !IS_WAN(out) && !IS_EXT(out))
+ 		is_virt_dev = true;
+ 
+@@ -3016,7 +3036,10 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
+ 	if (iph->protocol == IPPROTO_IPV6) {
+ 		entry->udib1.pkt_type = IPV6_6RD;
+ 		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
+-	} else if (!skb_hnat_tops(skb)) {
++	} else if (is_magic_tag_valid(skb)
++		   && (skb_hnat_cdrt(skb) || skb_hnat_tops(skb))) {
++		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
++	} else {
+ 		hnat_set_head_frags(state, skb, 1, hnat_set_alg);
+ 	}
+ 
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
+@@ -46,7 +46,8 @@ struct hnat_desc {
+ 	u32 amsdu : 1;
+ 	u32 tops : 6;
+ 	u32 is_decap : 1;
+-	u32 resv3 : 12;
++	u32 cdrt : 8;
++	u32 resv3 : 4;
+ 	u32 magic_tag_protect : 16;
+ } __packed;
+ #elif defined(CONFIG_MEDIATEK_NETSYS_RX_V2)
+@@ -99,12 +100,16 @@ struct hnat_desc {
+ #define skb_hnat_is_encap(skb) (!skb_hnat_is_decap(skb))
+ #define skb_hnat_set_tops(skb, tops) ((skb_hnat_tops(skb)) = (tops))
+ #define skb_hnat_set_is_decap(skb, is_decap) ((skb_hnat_is_decap(skb)) = (is_decap))
++#define skb_hnat_cdrt(skb) (((struct hnat_desc *)((skb)->head))->cdrt)
++#define skb_hnat_set_cdrt(skb, cdrt) ((skb_hnat_cdrt(skb)) = (cdrt))
+ #else /* !defined(CONFIG_MEDIATEK_NETSYS_V3) */
+ #define skb_hnat_tops(skb) (0)
+ #define skb_hnat_is_decap(skb) (0)
+ #define skb_hnat_is_encap(skb) (0)
+ #define skb_hnat_set_tops(skb, tops)
+ #define skb_hnat_set_is_decap(skb, is_decap)
++#define skb_hnat_cdrt(skb) (0)
++#define skb_hnat_set_cdrt(skb, cdrt)
+ #endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */
+ #define skb_hnat_magic(skb) (((struct hnat_desc *)(skb->head))->magic)
+ #define skb_hnat_reason(skb) (((struct hnat_desc *)(skb->head))->crsn)
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+@@ -49,6 +49,8 @@ int (*mtk_tnl_decap_offload)(struct sk_b
+ EXPORT_SYMBOL(mtk_tnl_decap_offload);
+ bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb) = NULL;
+ EXPORT_SYMBOL(mtk_tnl_decap_offloadable);
++bool (*mtk_crypto_offloadable)(struct sk_buff *skb) = NULL;
++EXPORT_SYMBOL(mtk_crypto_offloadable);
+ 
+ static void hnat_sma_build_entry(struct timer_list *t)
+ {
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+@@ -1087,6 +1087,8 @@ enum FoeIpAct {
+ #define NR_WDMA1_PORT 9
+ #define NR_WDMA2_PORT 13
+ #define NR_GMAC3_PORT 15
++#define NR_EIP197_TPORT 2
++#define NR_EIP197_QDMA_TPORT 3
+ #define NR_TDMA_TPORT 4
+ #define NR_TDMA_QDMA_TPORT 5
+ #define LAN_DEV_NAME hnat_priv->lan
+@@ -1233,6 +1235,7 @@ extern int qos_toggle;
+ extern int (*mtk_tnl_encap_offload)(struct sk_buff *skb);
+ extern int (*mtk_tnl_decap_offload)(struct sk_buff *skb);
+ extern bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb);
++extern bool (*mtk_crypto_offloadable)(struct sk_buff *skb);
+ 
+ int ext_if_add(struct extdev_entry *ext_entry);
+ int ext_if_del(struct extdev_entry *ext_entry);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4103-mtk-tunnel-crypto-offload-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4103-mtk-tunnel-crypto-offload-support.patch
new file mode 100644
index 0000000..b2cc240
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4103-mtk-tunnel-crypto-offload-support.patch
@@ -0,0 +1,211 @@
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+@@ -1091,6 +1091,8 @@ enum FoeIpAct {
+ #define NR_EIP197_QDMA_TPORT 3
+ #define NR_TDMA_TPORT 4
+ #define NR_TDMA_QDMA_TPORT 5
++#define NR_TDMA_EIP197_TPORT 8
++#define NR_TDMA_EIP197_QDMA_TPORT 9
+ #define LAN_DEV_NAME hnat_priv->lan
+ #define LAN2_DEV_NAME hnat_priv->lan2
+ #define IS_WAN(dev)                                                            \
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+@@ -1099,7 +1099,8 @@ static unsigned int hnat_ipv4_get_nextho
+ 	 * outer header, we must update its outer mac header pointer
+ 	 * before filling outer mac or it may screw up inner mac
+ 	 */
+-	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) || skb_hnat_cdrt(skb)) {
++	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
++	    || (skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb))) {
+ 		skb_push(skb, sizeof(struct ethhdr));
+ 		skb_reset_mac_header(skb);
+ 	}
+@@ -1107,7 +1108,8 @@ static unsigned int hnat_ipv4_get_nextho
+ 	memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN);
+ 	memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN);
+ 
+-	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) || skb_hnat_cdrt(skb))
++	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
++	    || (skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb)))
+ 		skb_pull(skb, sizeof(struct ethhdr));
+ 
+ 	rcu_read_unlock_bh();
+@@ -1255,6 +1257,38 @@ static inline void hnat_get_filled_unbin
+ 	entry->bfib1.ps = 0;
+ }
+ 
++/*
++ * check offload engine data is prepared
++ * return 0 for packets not related to offload engine
++ * return positive value for offload engine prepared data done
++ * return negative value for data is still constructing
++ */
++static inline int hnat_offload_engine_done(struct sk_buff *skb,
++					   struct flow_offload_hw_path *hw_path)
++{
++	struct dst_entry *dst = skb_dst(skb);
++
++	if ((skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL))) {
++		/* tunnel encap'ed */
++		if (dst && dst_xfrm(dst))
++			/*
++			 * skb not ready to bind since it is still needs
++			 * to be encrypted
++			 */
++			return -1;
++
++		/* nothing need to be done further for this skb */
++		return 1;
++	}
++
++	if (skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb) && dst && !dst_xfrm(dst))
++		/* crypto encrypted done */
++		return 1;
++
++	/* no need for tunnel encapsulation or crypto encryption */
++	return 0;
++}
++
+ static inline void hnat_qos_tnl(u32 id, const struct net_device *dev)
+ {
+ 	u32 cfg;
+@@ -1299,9 +1333,15 @@ static inline void hnat_fill_offload_eng
+ 		 * we fill in hnat tport and tops_entry for tunnel encapsulation
+ 		 * offloading
+ 		 */
+-		entry->ipv4_hnapt.tport_id = NR_TDMA_QDMA_TPORT;
++		if (skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb)) {
++			entry->ipv4_hnapt.tport_id = NR_TDMA_EIP197_QDMA_TPORT;
++			entry->ipv4_hnapt.cdrt_id = skb_hnat_cdrt(skb);
++		} else {
++			entry->ipv4_hnapt.tport_id = NR_TDMA_QDMA_TPORT;
++		}
+ 		entry->ipv4_hnapt.tops_entry = skb_hnat_tops(skb);
+-	} else if (skb_hnat_cdrt(skb)) {
++
++	} else if (skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb)) {
+ 		entry->ipv4_hnapt.tport_id = NR_EIP197_QDMA_TPORT;
+ 		entry->ipv4_hnapt.cdrt_id = skb_hnat_cdrt(skb);
+ 	} else {
+@@ -1333,6 +1373,7 @@ static unsigned int skb_to_hnat_info(str
+ 	u32 port_id = 0;
+ 	u32 payload_len = 0;
+ 	int mape = 0;
++	int ret;
+ 
+ 	ct = nf_ct_get(skb, &ctinfo);
+ 
+@@ -1349,10 +1390,12 @@ static unsigned int skb_to_hnat_info(str
+ 	if (whnat && is_hnat_pre_filled(foe))
+ 		return 0;
+ 
+-	if ((skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL))
+-	    || (skb_hnat_cdrt(skb) && skb_dst(skb) && !dst_xfrm(skb_dst(skb)))) {
++	ret = hnat_offload_engine_done(skb, hw_path);
++	if (ret == 1) {
+ 		hnat_get_filled_unbind_entry(skb, &entry);
+ 		goto hnat_entry_bind;
++	} else if (ret == -1) {
++		return 0;
+ 	}
+ 
+ 	entry.bfib1.pkt_type = foe->udib1.pkt_type; /* Get packte type state*/
+@@ -1751,7 +1794,8 @@ static unsigned int skb_to_hnat_info(str
+ 	entry = ppe_fill_L2_info(eth, entry, hw_path);
+ 
+ 	if ((skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL)
+-	    || (!skb_hnat_cdrt(skb) && skb_dst(skb) && dst_xfrm(skb_dst(skb))))
++	    || (!skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb)
++	        && skb_dst(skb) && dst_xfrm(skb_dst(skb))))
+ 		goto hnat_entry_skip_bind;
+ 
+ hnat_entry_bind:
+@@ -1957,7 +2001,7 @@ hnat_entry_bind:
+ 
+ #if defined(CONFIG_MEDIATEK_NETSYS_V3)
+ 	hnat_fill_offload_engine_entry(skb, &entry, dev);
+-	if (skb_hnat_cdrt(skb))
++	if (skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb))
+ 		entry = ppe_fill_L2_info(eth, entry, hw_path);
+ #endif
+ 
+@@ -2237,6 +2281,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
+ 	skb_hnat_alg(skb) = 0;
+ 	skb_hnat_set_tops(skb, 0);
+ 	skb_hnat_set_cdrt(skb, 0);
++	skb_hnat_set_is_decrypt(skb, 0);
+ 	skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
+ 
+ 	if (skb_hnat_iface(skb) == FOE_MAGIC_WED0)
+@@ -3037,7 +3082,8 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
+ 		entry->udib1.pkt_type = IPV6_6RD;
+ 		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
+ 	} else if (is_magic_tag_valid(skb)
+-		   && (skb_hnat_cdrt(skb) || skb_hnat_tops(skb))) {
++		   && ((skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb))
++		       || skb_hnat_tops(skb))) {
+ 		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
+ 	} else {
+ 		hnat_set_head_frags(state, skb, 1, hnat_set_alg);
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2324,10 +2324,11 @@ static int mtk_poll_rx(struct napi_struc
+ 
+ 		skb_hnat_alg(skb) = 0;
+ 		skb_hnat_filled(skb) = 0;
+-		skb_hnat_set_cdrt(skb, 0);
++		skb_hnat_set_cdrt(skb, RX_DMA_GET_CDRT(trxd.rxd7));
+ 		skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
+ 		skb_hnat_set_tops(skb, 0);
+ 		skb_hnat_set_is_decap(skb, 0);
++		skb_hnat_set_is_decrypt(skb, (skb_hnat_cdrt(skb) ? 1 : 0));
+ 
+ 		if (skb_hnat_reason(skb) == HIT_BIND_FORCE_TO_CPU) {
+ 			trace_printk("[%s] reason=0x%x(force to CPU) from WAN to Ext\n",
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -640,6 +640,9 @@
+ #define RX_DMA_GET_AGG_CNT_V2(_x)	(((_x) >> 16) & 0xff)
+ #define RX_DMA_GET_TOPS_CRSN(_x)	(((_x) >> 24) & 0xff)
+ 
++/* PDMA V2 descriptor rxd7 */
++#define RX_DMA_GET_CDRT(_x)		(((_x) >> 8) & 0xff)
++
+ /* PHY Polling and SMI Master Control registers */
+ #define MTK_PPSC		0x10000
+ #define PPSC_MDC_CFG		GENMASK(29, 24)
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
+@@ -47,7 +47,8 @@ struct hnat_desc {
+ 	u32 tops : 6;
+ 	u32 is_decap : 1;
+ 	u32 cdrt : 8;
+-	u32 resv3 : 4;
++	u32 is_decrypt : 1;
++	u32 resv3 : 3;
+ 	u32 magic_tag_protect : 16;
+ } __packed;
+ #elif defined(CONFIG_MEDIATEK_NETSYS_RX_V2)
+@@ -101,7 +102,10 @@ struct hnat_desc {
+ #define skb_hnat_set_tops(skb, tops) ((skb_hnat_tops(skb)) = (tops))
+ #define skb_hnat_set_is_decap(skb, is_decap) ((skb_hnat_is_decap(skb)) = (is_decap))
+ #define skb_hnat_cdrt(skb) (((struct hnat_desc *)((skb)->head))->cdrt)
++#define skb_hnat_is_decrypt(skb) (((struct hnat_desc *)((skb)->head))->is_decrypt)
++#define skb_hnat_is_encrypt(skb) (!skb_hnat_is_decrypt(skb))
+ #define skb_hnat_set_cdrt(skb, cdrt) ((skb_hnat_cdrt(skb)) = (cdrt))
++#define skb_hnat_set_is_decrypt(skb, is_dec) ((skb_hnat_is_decrypt(skb)) = is_dec)
+ #else /* !defined(CONFIG_MEDIATEK_NETSYS_V3) */
+ #define skb_hnat_tops(skb) (0)
+ #define skb_hnat_is_decap(skb) (0)
+@@ -109,7 +113,10 @@ struct hnat_desc {
+ #define skb_hnat_set_tops(skb, tops)
+ #define skb_hnat_set_is_decap(skb, is_decap)
+ #define skb_hnat_cdrt(skb) (0)
++#define skb_hnat_is_decrypt(skb) (0)
++#define skb_hnat_is_encrypt(skb) (0)
+ #define skb_hnat_set_cdrt(skb, cdrt)
++#define skb_hnat_set_is_decrypt(skb, is_dec)
+ #endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */
+ #define skb_hnat_magic(skb) (((struct hnat_desc *)(skb->head))->magic)
+ #define skb_hnat_reason(skb) (((struct hnat_desc *)(skb->head))->crsn)
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4500-mtk-tops-gre-offload-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4500-mtk-gre-offload-support.patch
similarity index 100%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4500-mtk-tops-gre-offload-support.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4500-mtk-gre-offload-support.patch
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4500-mtk-tops-l2tp-offload-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4500-mtk-l2tp-offload-support.patch
similarity index 100%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4500-mtk-tops-l2tp-offload-support.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-4500-mtk-l2tp-offload-support.patch
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1033-backport-pinctrl-spinlock.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1033-backport-pinctrl-spinlock.patch
new file mode 100644
index 0000000..875bd7b
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1033-backport-pinctrl-spinlock.patch
@@ -0,0 +1,29 @@
+--- a/drivers/pinctrl/mediatek/pinctrl-moore.c
++++ b/drivers/pinctrl/mediatek/pinctrl-moore.c
+@@ -662,6 +662,7 @@ int mtk_moore_pinctrl_probe(struct platf
+ 	}
+ 
+ 	hw->nbase = hw->soc->nbase_names;
++	spin_lock_init(&hw->lock);
+ 
+ 	/* Copy from internal struct mtk_pin_desc to register to the core */
+ 	pins = devm_kmalloc_array(&pdev->dev, hw->soc->npins, sizeof(*pins),
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
+@@ -55,11 +55,16 @@ static u32 mtk_r32(struct mtk_pinctrl *p
+ void mtk_rmw(struct mtk_pinctrl *pctl, u8 i, u32 reg, u32 mask, u32 set)
+ {
+ 	u32 val;
++	unsigned long flags;
++
++	spin_lock_irqsave(&pctl->lock, flags);
+ 
+ 	val = mtk_r32(pctl, i, reg);
+ 	val &= ~mask;
+ 	val |= set;
+ 	mtk_w32(pctl, i, reg, val);
++
++	spin_unlock_irqrestore(&pctl->lock, flags);
+ }
+ 
+ static int mtk_hw_pin_field_lookup(struct mtk_pinctrl *hw,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2704-en8811h-2p5gphy-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2704-en8811h-2p5gphy-support.patch
index 5e3c619..366414c 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2704-en8811h-2p5gphy-support.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2704-en8811h-2p5gphy-support.patch
@@ -5,19 +5,15 @@
  [backport-networking-drivers][999-1703-en8811h-2p5gphy-support.patch]
 
 ---
- drivers/net/phy/Kconfig       |   5 +
- drivers/net/phy/Makefile      |   1 +
- drivers/net/phy/air_en8811h.c | 702 ++++++++++++++++++++++++++++++++++
- drivers/net/phy/air_en8811h.h | 151 ++++++++
- 4 files changed, 859 insertions(+)
- create mode 100644 drivers/net/phy/air_en8811h.c
- create mode 100644 drivers/net/phy/air_en8811h.h
+ drivers/net/phy/Kconfig       |  11 +
+ drivers/net/phy/Makefile      |   2 +
+ 2 files changed, 13 insertions(+)
 
 diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
 index 170dd00cd..5eeccfee2 100644
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -345,6 +345,11 @@ config SFP
+@@ -345,6 +345,17 @@ config SFP
  	depends on HWMON || HWMON=n
  	select MDIO_I2C
  
@@ -26,6 +22,12 @@
 +	---help---
 +	  Currently supports the Airoha EN8811H PHY.
 +
++config AIROHA_EN8811H_PHY_DEBUGFS
++	bool "EN8811H debugfs support"
++	depends on AIROHA_EN8811H_PHY
++	---help---
++	  Enable creation of debugfs files for the EN8811H drivers.
++
  config ADIN_PHY
  	tristate "Analog Devices Industrial Ethernet PHYs"
  	help
@@ -33,879 +35,15 @@
 index 2368c1d19..8ea612a85 100644
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -67,6 +67,7 @@ aquantia-objs			+= aquantia_main.o
+@@ -67,6 +67,8 @@ aquantia-objs			+= aquantia_main.o
  ifdef CONFIG_HWMON
  aquantia-objs			+= aquantia_hwmon.o
  endif
++air_en8811h-y := air_en8811h_main.o air_en8811h_api.o
 +obj-$(CONFIG_AIROHA_EN8811H_PHY)	+= air_en8811h.o
  obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia.o
  obj-$(CONFIG_AX88796B_PHY)	+= ax88796b.o
  obj-$(CONFIG_AT803X_PHY)	+= at803x.o
-diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
-new file mode 100644
-index 000000000..cf564fc04
---- /dev/null
-+++ b/drivers/net/phy/air_en8811h.c
-@@ -0,0 +1,702 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+
-+/* FILE NAME:  air_en8811h.c
-+ * PURPOSE:
-+ *      EN8811H phy driver for Linux
-+ * NOTES:
-+ *
-+ */
-+
-+/* INCLUDE FILE DECLARATIONS
-+ */
-+#include <linux/kernel.h>
-+#include <linux/errno.h>
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/mii.h>
-+#include <linux/phy.h>
-+#include <linux/delay.h>
-+#include <linux/ethtool.h>
-+#include <linux/delay.h>
-+#include <linux/version.h>
-+#include <linux/firmware.h>
-+#include <linux/crc32.h>
-+
-+#include "air_en8811h.h"
-+
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0))
-+#define phydev_mdio_bus(_dev) (_dev->bus)
-+#define phydev_addr(_dev) (_dev->addr)
-+#define phydev_dev(_dev) (&_dev->dev)
-+#else
-+#define phydev_mdio_bus(_dev) (_dev->mdio.bus)
-+#define phydev_addr(_dev) (_dev->mdio.addr)
-+#define phydev_dev(_dev) (&_dev->mdio.dev)
-+#endif
-+
-+MODULE_DESCRIPTION("Airoha EN8811H PHY drivers");
-+MODULE_AUTHOR("Airoha");
-+MODULE_LICENSE("GPL");
-+
-+/*
-+GPIO5  <-> BASE_T_LED0,
-+GPIO4  <-> BASE_T_LED1,
-+GPIO3  <-> BASE_T_LED2,
-+*/
-+/* User-defined.B */
-+#define AIR_LED_SUPPORT
-+#ifdef AIR_LED_SUPPORT
-+static const AIR_BASE_T_LED_CFG_T led_cfg[3] =
-+{
-+    /*
-+     *    LED Enable,            GPIO,            LED Polarity,            LED ON,               LED Blink
-+     */
-+         {LED_ENABLE,      AIR_LED0_GPIO5,       AIR_ACTIVE_HIGH,     BASE_T_LED0_ON_CFG,    BASE_T_LED0_BLK_CFG}, /* BASE-T LED0 */
-+         {LED_ENABLE,      AIR_LED1_GPIO4,       AIR_ACTIVE_HIGH,     BASE_T_LED1_ON_CFG,    BASE_T_LED1_BLK_CFG}, /* BASE-T LED1 */
-+         {LED_ENABLE,      AIR_LED2_GPIO3,       AIR_ACTIVE_HIGH,     BASE_T_LED2_ON_CFG,    BASE_T_LED2_BLK_CFG}, /* BASE-T LED2 */
-+};
-+static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
-+#endif
-+/* User-defined.E */
-+
-+/************************************************************************
-+*                  F U N C T I O N S
-+************************************************************************/
-+#if 0
-+/* Airoha MII read function */
-+static int air_mii_cl22_read(struct mii_bus *ebus, unsigned int phy_addr,unsigned int phy_register)
-+{
-+    int read_data;
-+    read_data = mdiobus_read(ebus, phy_addr, phy_register);
-+    return read_data;
-+}
-+#endif
-+/* Airoha MII write function */
-+static int air_mii_cl22_write(struct mii_bus *ebus, unsigned int phy_addr, unsigned int phy_register,unsigned int write_data)
-+{
-+    int ret = 0;
-+    ret = mdiobus_write(ebus, phy_addr, phy_register, write_data);
-+    return ret;
-+}
-+
-+static int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
-+{
-+    int ret = 0;
-+    int data;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, devad);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, reg);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    data = phy_read(phydev, MII_MMD_ADDR_DATA_REG);
-+    return data;
-+}
-+
-+static int air_mii_cl45_write(struct phy_device *phydev, int devad, u16 reg, u16 write_data)
-+{
-+    int ret = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, devad);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, reg);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, write_data);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
-+}
-+/* Use default PBUS_PHY_ID */
-+/* EN8811H PBUS write function */
-+static int air_pbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned long pbus_data)
-+{
-+    struct mii_bus *mbus = phydev_mdio_bus(phydev);
-+    int addr = phydev_addr(phydev);
-+    int ret = 0;
-+    ret = air_mii_cl22_write(mbus, (addr + 8), 0x1F, (unsigned int)(pbus_address >> 6));
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl22_write(mbus, (addr + 8), (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF));
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl22_write(mbus, (addr + 8), 0x10, (unsigned int)(pbus_data >> 16));
-+    AIR_RTN_ERR(ret);
-+    return 0;
-+}
-+
-+/* EN8811H BUCK write function */
-+static int air_buckpbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned int pbus_data)
-+{
-+    int ret = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, 0x1F, (unsigned int)4);        /* page 4 */
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x10, (unsigned int)0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x11, (unsigned int)((pbus_address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x12, (unsigned int)(pbus_address & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x13, (unsigned int)((pbus_data >> 16) & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x14, (unsigned int)(pbus_data & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x1F, 0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
-+}
-+
-+/* EN8811H BUCK read function */
-+static unsigned int air_buckpbus_reg_read(struct phy_device *phydev, unsigned long pbus_address)
-+{
-+    unsigned int pbus_data = 0, pbus_data_low, pbus_data_high;
-+    int ret = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, 0x1F, (unsigned int)4);        /* page 4 */
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, 0x10, (unsigned int)0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, 0x15, (unsigned int)((pbus_address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, 0x16, (unsigned int)(pbus_address & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+
-+    pbus_data_high = phy_read(phydev, 0x17);
-+    pbus_data_low = phy_read(phydev, 0x18);
-+    pbus_data = (pbus_data_high << 16) + pbus_data_low;
-+    ret = phy_write(phydev, 0x1F, 0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return pbus_data;
-+}
-+
-+static int MDIOWriteBuf(struct phy_device *phydev, unsigned long address, const struct firmware *fw)
-+{
-+    unsigned int write_data, offset ;
-+    int ret = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = phy_write(phydev, 0x1F, (unsigned int)4);            /* page 4 */
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x10, (unsigned int)0x8000);        /* address increment*/
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x11, (unsigned int)((address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, 0x12, (unsigned int)(address & 0xffff));
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+
-+    for (offset = 0; offset < fw->size; offset += 4)
-+    {
-+        write_data = (fw->data[offset + 3] << 8) | fw->data[offset + 2];
-+        ret = phy_write(phydev, 0x13, write_data);
-+        if (ret < 0) {
-+            dev_err(dev, "phy_write, ret: %d\n", ret);
-+            return ret;
-+        }
-+        write_data = (fw->data[offset + 1] << 8) | fw->data[offset];
-+        ret = phy_write(phydev, 0x14, write_data);
-+        if (ret < 0) {
-+            dev_err(dev, "phy_write, ret: %d\n", ret);
-+            return ret;
-+        }
-+    }
-+    ret = phy_write(phydev, 0x1F, (unsigned int)0);
-+    if (ret < 0) {
-+        dev_err(dev, "phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
-+}
-+
-+static int en8811h_load_firmware(struct phy_device *phydev)
-+{
-+    struct device *dev = phydev_dev(phydev);
-+    const struct firmware *fw;
-+    const char *firmware;
-+    int ret = 0;
-+    unsigned int crc32;
-+    u32 pbus_value = 0;
-+
-+    ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x0);
-+    AIR_RTN_ERR(ret);
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
-+    pbus_value |= BIT(11);
-+    ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
-+    AIR_RTN_ERR(ret);
-+    firmware = EN8811H_MD32_DM;
-+    ret = request_firmware_direct(&fw, firmware, dev);
-+    if (ret < 0) {
-+        dev_info(dev, "failed to load firmware %s, ret: %d\n", firmware, ret);
-+        return ret;
-+    }
-+    crc32 = ~crc32(~0, fw->data, fw->size);
-+    dev_info(dev, "%s: crc32=0x%x\n", firmware, crc32);
-+    /* Download DM */
-+    ret = MDIOWriteBuf(phydev, 0x00000000, fw);
-+    if (ret < 0) {
-+        dev_info(dev, "MDIOWriteBuf 0x00000000 fail, ret: %d\n", ret);
-+        return ret;
-+    }
-+    release_firmware(fw);
-+
-+    firmware = EN8811H_MD32_DSP;
-+    ret = request_firmware_direct(&fw, firmware, dev);
-+    if (ret < 0) {
-+        dev_info(dev, "failed to load firmware %s, ret: %d\n", firmware, ret);
-+        return ret;
-+    }
-+    crc32 = ~crc32(~0, fw->data, fw->size);
-+    dev_info(dev, "%s: crc32=0x%x\n", firmware, crc32);
-+    /* Download PM */
-+    ret = MDIOWriteBuf(phydev, 0x00100000, fw);
-+    if (ret < 0) {
-+            dev_info(dev, "MDIOWriteBuf 0x00100000 fail , ret: %d\n", ret);
-+            return ret;
-+    }
-+    release_firmware(fw);
-+
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
-+    pbus_value &= ~BIT(11);
-+    ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
-+    AIR_RTN_ERR(ret);
-+    ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
-+    AIR_RTN_ERR(ret);
-+    return 0;
-+}
-+
-+#ifdef  AIR_LED_SUPPORT
-+static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity, int polar,
-+                                   u16 on_evt, u16 blk_evt)
-+{
-+    int ret = 0;
-+    if (AIR_ACTIVE_HIGH == polar) {
-+        on_evt |= LED_ON_POL;
-+    } else {
-+        on_evt &= ~LED_ON_POL ;
-+    }
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), on_evt | LED_ON_EN);
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
-+    AIR_RTN_ERR(ret);
-+    return 0;
-+}
-+
-+static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
-+{
-+    u16 cl45_data;
-+    int err = 0;
-+    struct device *dev = phydev_dev(phydev);
-+    cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR);
-+    switch (mode) {
-+    case AIR_LED_MODE_DISABLE:
-+        cl45_data &= ~LED_BCR_EXT_CTRL;
-+        cl45_data &= ~LED_BCR_MODE_MASK;
-+        cl45_data |= LED_BCR_MODE_DISABLE;
-+        break;
-+    case AIR_LED_MODE_USER_DEFINE:
-+        cl45_data |= LED_BCR_EXT_CTRL;
-+        cl45_data |= LED_BCR_CLK_EN;
-+        break;
-+    default:
-+        dev_err(dev, "LED mode%d is not supported!\n", mode);
-+        return -EINVAL;
-+    }
-+    err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
-+    AIR_RTN_ERR(err);
-+    return 0;
-+}
-+
-+static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
-+{
-+    u16 cl45_data = 0;
-+    int err;
-+
-+    cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
-+    if (LED_ENABLE == state) {
-+        cl45_data |= LED_ON_EN;
-+    } else {
-+        cl45_data &= ~LED_ON_EN;
-+    }
-+
-+    err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
-+    AIR_RTN_ERR(err);
-+    return 0;
-+}
-+
-+static int en8811h_led_init(struct phy_device *phydev)
-+{
-+
-+    unsigned long led_gpio = 0, reg_value = 0;
-+    u16 cl45_data = led_dur;
-+    int ret = 0, led_id;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
-+    AIR_RTN_ERR(ret);
-+    cl45_data >>= 1;
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
-+    AIR_RTN_ERR(ret);
-+    ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
-+    if (ret != 0) {
-+        dev_err(dev, "LED fail to set mode, ret %d !\n", ret);
-+        return ret;
-+    }
-+    for(led_id = 0; led_id < EN8811H_LED_COUNT; led_id++)
-+    {
-+        /* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */
-+        if (led_cfg[led_id].gpio != (led_id + (AIR_LED0_GPIO5 - (2 * led_id))))
-+        {
-+            dev_err(dev, "LED%d uses incorrect GPIO%d !\n", led_id, led_cfg[led_id].gpio);
-+            return -EINVAL;
-+        }
-+        ret = airoha_led_set_state(phydev, led_id, led_cfg[led_id].en);
-+        if (ret != 0)
-+        {
-+            dev_err(dev, "LED fail to set state, ret %d !\n", ret);
-+            return ret;
-+        }
-+        if (LED_ENABLE == led_cfg[led_id].en)
-+        {
-+            led_gpio |= BIT(led_cfg[led_id].gpio);
-+            ret = airoha_led_set_usr_def(phydev, led_id, led_cfg[led_id].pol, led_cfg[led_id].on_cfg, led_cfg[led_id].blk_cfg);
-+            if (ret != 0)
-+            {
-+                dev_err(dev, "LED fail to set default, ret %d !\n", ret);
-+                return ret;
-+            }
-+        }
-+    }
-+    reg_value = air_buckpbus_reg_read(phydev, 0xcf8b8) | led_gpio;
-+    ret = air_buckpbus_reg_write(phydev, 0xcf8b8, reg_value);
-+    AIR_RTN_ERR(ret);
-+
-+    dev_info(dev, "LED initialize OK !\n");
-+    return 0;
-+}
-+#endif /* AIR_LED_SUPPORT */
-+#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 5, 0))
-+static int en8811h_get_features(struct phy_device *phydev)
-+{
-+    int ret;
-+    struct device *dev = phydev_dev(phydev);
-+    dev_info(dev, "%s()\n", __func__);
-+    ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0);
-+    AIR_RTN_ERR(ret);
-+    ret = genphy_read_abilities(phydev);
-+    if (ret)
-+        return ret;
-+
-+    /* EN8811H supports 100M/1G/2.5G speed. */
-+    linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
-+               phydev->supported);
-+    linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
-+               phydev->supported);
-+    linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-+               phydev->supported);
-+    linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-+               phydev->supported);
-+    linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-+               phydev->supported);
-+    linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
-+               phydev->supported);
-+    return 0;
-+}
-+#endif
-+static int en8811h_phy_probe(struct phy_device *phydev)
-+{
-+    int ret = 0;
-+    int reg_value, pid1 = 0, pid2 = 0;
-+    u32 pbus_value = 0, retry;
-+    struct device *dev = phydev_dev(phydev);
-+    ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0);
-+    AIR_RTN_ERR(ret);
-+    pid1 = phy_read(phydev, MII_PHYSID1);
-+    if (pid1 < 0)
-+        return pid1;
-+    pid2 = phy_read(phydev, MII_PHYSID2);
-+    if (pid2 < 0)
-+        return pid2;
-+    dev_info(dev, "PHY = %x - %x\n", pid1, pid2);
-+    if ((EN8811H_PHY_ID1 != pid1) || (EN8811H_PHY_ID2 != pid2))
-+    {
-+        dev_err(dev, "EN8811H dose not exist !\n");
-+        return -ENODEV;
-+    }
-+    ret = en8811h_load_firmware(phydev);
-+    if (ret)
-+    {
-+        dev_err(dev,"EN8811H load firmware fail.\n");
-+        return ret;
-+    }
-+    retry = MAX_RETRY;
-+    do {
-+        mdelay(300);
-+        reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009);
-+        if (EN8811H_PHY_READY == reg_value)
-+        {
-+            dev_info(dev, "EN8811H PHY ready!\n");
-+            break;
-+        }
-+        retry--;
-+    } while (retry);
-+    if (0 == retry)
-+    {
-+        dev_err(dev, "EN8811H initialize fail ! reg: 0x%x\n", reg_value);
-+        return -EIO;
-+    }
-+    /* Mode selection*/
-+    dev_info(dev, "EN8811H Mode 1 !\n");
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
-+    AIR_RTN_ERR(ret);
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
-+    AIR_RTN_ERR(ret);
-+
-+    /* Serdes polarity */
-+    pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
-+    pbus_value = (pbus_value & 0xfffffffc) | EN8811H_RX_POLARITY_NORMAL | EN8811H_TX_POLARITY_NORMAL;
-+    ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value);
-+    AIR_RTN_ERR(ret);
-+    pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
-+    dev_info(dev, "0xca0f8= 0x%x\n", pbus_value);
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
-+    dev_info(dev, "Version(0x3b3c)= %x\n", pbus_value);
-+#if defined(AIR_LED_SUPPORT)
-+    ret = en8811h_led_init(phydev);
-+    if (ret < 0)
-+    {
-+        dev_err(dev, "en8811h_led_init fail. (ret=%d)\n", ret);
-+        return ret;
-+    }
-+#endif
-+    dev_info(dev, "EN8811H initialize OK ! (%s)\n", EN8811H_FW_VERSION);
-+    return 0;
-+}
-+
-+static int en8811h_get_autonego(struct phy_device *phydev, int *an)
-+{
-+    int reg;
-+    reg = phy_read(phydev, MII_BMCR);
-+    if (reg < 0)
-+        return -EINVAL;
-+    if (reg & BMCR_ANENABLE)
-+        *an = AUTONEG_ENABLE;
-+    else
-+        *an = AUTONEG_DISABLE;
-+    return 0;
-+}
-+
-+static int en8811h_read_status(struct phy_device *phydev)
-+{
-+    int ret = 0, lpagb = 0, lpa = 0, common_adv_gb = 0, common_adv = 0, advgb = 0, adv = 0, reg = 0, an = AUTONEG_DISABLE, bmcr = 0;
-+    int old_link = phydev->link;
-+    u32 pbus_value = 0;
-+    struct device *dev = phydev_dev(phydev);
-+	ret = genphy_update_link(phydev);
-+	if (ret)
-+    {
-+        dev_err(dev, "ret %d!\n", ret);
-+		return ret;
-+    }
-+
-+	if (old_link && phydev->link)
-+		return 0;
-+
-+	phydev->speed = SPEED_UNKNOWN;
-+	phydev->duplex = DUPLEX_UNKNOWN;
-+	phydev->pause = 0;
-+	phydev->asym_pause = 0;
-+
-+    reg = phy_read(phydev, MII_BMSR);
-+    if (reg < 0)
-+    {
-+        dev_err(dev, "MII_BMSR reg %d!\n", reg);
-+		return reg;
-+    }
-+    reg = phy_read(phydev, MII_BMSR);
-+    if (reg < 0)
-+    {
-+        dev_err(dev, "MII_BMSR reg %d!\n", reg);
-+		return reg;
-+    }
-+    if(reg & BMSR_LSTATUS)
-+    {
-+        pbus_value = air_buckpbus_reg_read(phydev, 0x109D4);
-+        if (0x10 & pbus_value) {
-+            phydev->speed = SPEED_2500;
-+            phydev->duplex = DUPLEX_FULL;
-+        }
-+        else
-+        {
-+            ret = en8811h_get_autonego(phydev, &an);
-+            if ((AUTONEG_ENABLE == an) && (0 == ret))
-+            {
-+                dev_dbg(dev, "AN mode!\n");
-+                dev_dbg(dev, "SPEED 1000/100!\n");
-+                lpagb = phy_read(phydev, MII_STAT1000);
-+                if (lpagb < 0 )
-+                    return lpagb;
-+                advgb = phy_read(phydev, MII_CTRL1000);
-+                if (adv < 0 )
-+                    return adv;
-+                common_adv_gb = (lpagb & (advgb << 2));
-+
-+                lpa = phy_read(phydev, MII_LPA);
-+                if (lpa < 0 )
-+                    return lpa;
-+                adv = phy_read(phydev, MII_ADVERTISE);
-+                if (adv < 0 )
-+                    return adv;
-+                common_adv = (lpa & adv);
-+
-+                phydev->speed = SPEED_10;
-+                phydev->duplex = DUPLEX_HALF;
-+                if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF))
-+                {
-+                    phydev->speed = SPEED_1000;
-+                    if (common_adv_gb & LPA_1000FULL)
-+
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+                else if (common_adv & (LPA_100FULL | LPA_100HALF))
-+                {
-+                    phydev->speed = SPEED_100;
-+                    if (common_adv & LPA_100FULL)
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+                else
-+                {
-+                    if (common_adv & LPA_10FULL)
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+            }
-+            else
-+            {
-+                dev_dbg(dev, "Force mode!\n");
-+                bmcr = phy_read(phydev, MII_BMCR);
-+
-+                if (bmcr < 0)
-+                    return bmcr;
-+
-+                if (bmcr & BMCR_FULLDPLX)
-+                    phydev->duplex = DUPLEX_FULL;
-+                else
-+                    phydev->duplex = DUPLEX_HALF;
-+
-+                if (bmcr & BMCR_SPEED1000)
-+                    phydev->speed = SPEED_1000;
-+                else if (bmcr & BMCR_SPEED100)
-+                    phydev->speed = SPEED_100;
-+                else
-+                    phydev->speed = SPEED_UNKNOWN;
-+            }
-+        }
-+    }
-+
-+	return ret;
-+}
-+static struct phy_driver en8811h_driver[] = {
-+{
-+    .phy_id         = EN8811H_PHY_ID,
-+    .name           = "Airoha EN8811H",
-+    .phy_id_mask    = 0x0ffffff0,
-+    .probe          = en8811h_phy_probe,
-+    .read_status    = en8811h_read_status,
-+#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 5, 0))
-+    .get_features   = en8811h_get_features,
-+    .read_mmd       = air_mii_cl45_read,
-+    .write_mmd      = air_mii_cl45_write,
-+#endif
-+} };
-+
-+int __init en8811h_phy_driver_register(void)
-+{
-+    int ret;
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0))
-+    ret = phy_driver_register(en8811h_driver);
-+#else
-+    ret = phy_driver_register(en8811h_driver, THIS_MODULE);
-+#endif
-+    if (!ret)
-+        return 0;
-+
-+    phy_driver_unregister(en8811h_driver);
-+    return ret;
-+}
-+
-+void __exit en8811h_phy_driver_unregister(void)
-+{
-+    phy_driver_unregister(en8811h_driver);
-+}
-+
-+module_init(en8811h_phy_driver_register);
-+module_exit(en8811h_phy_driver_unregister);
-diff --git a/drivers/net/phy/air_en8811h.h b/drivers/net/phy/air_en8811h.h
-new file mode 100644
-index 000000000..1c9162795
---- /dev/null
-+++ b/drivers/net/phy/air_en8811h.h
-@@ -0,0 +1,151 @@
-+#ifndef __EN8811H_H
-+#define __EN8811H_H
-+
-+#define EN8811H_MD32_DM             "EthMD32.dm.bin"
-+#define EN8811H_MD32_DSP            "EthMD32.DSP.bin"
-+
-+#define EN8811H_PHY_ID1             0x03a2
-+#define EN8811H_PHY_ID2             0xa411
-+#define EN8811H_PHY_ID              ((EN8811H_PHY_ID1 << 16) | EN8811H_PHY_ID2)
-+#define EN8811H_PHY_READY           0x02
-+#define MAX_RETRY                   5
-+
-+#define EN8811H_TX_POLARITY_NORMAL   0x1
-+#define EN8811H_TX_POLARITY_REVERSE  0x0
-+
-+#define EN8811H_RX_POLARITY_REVERSE  (0x1 << 1)
-+#define EN8811H_RX_POLARITY_NORMAL   (0x0 << 1)
-+
-+
-+/*
-+The following led_cfg example is for reference only.
-+LED0 Link 2500/Blink 2500 TxRx   (GPIO5)    <-> BASE_T_LED0,
-+LED1 Link 1000/Blink 1000 TxRx   (GPIO4)    <-> BASE_T_LED1,
-+LED2 Link 100 /Blink 100  TxRx   (GPIO3)    <-> BASE_T_LED2,
-+*/
-+/* User-defined.B */
-+#define BASE_T_LED0_ON_CFG      (LED_ON_EVT_LINK_2500M)
-+#define BASE_T_LED0_BLK_CFG     (LED_BLK_EVT_2500M_TX_ACT | LED_BLK_EVT_2500M_RX_ACT)
-+#define BASE_T_LED1_ON_CFG      (LED_ON_EVT_LINK_1000M)
-+#define BASE_T_LED1_BLK_CFG     (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT)
-+#define BASE_T_LED2_ON_CFG      (LED_ON_EVT_LINK_100M)
-+#define BASE_T_LED2_BLK_CFG     (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT)
-+/* User-defined.E */
-+
-+/* CL45 MDIO control */
-+#define MII_MMD_ACC_CTL_REG         0x0d
-+#define MII_MMD_ADDR_DATA_REG       0x0e
-+#define MMD_OP_MODE_DATA            BIT(14)
-+
-+#define EN8811H_FW_VERSION          "1.1.3"
-+
-+#define LED_ON_CTRL(i)              (0x024 + ((i)*2))
-+#define LED_ON_EN                   (1 << 15)
-+#define LED_ON_POL                  (1 << 14)
-+#define LED_ON_EVT_MASK             (0x1ff)
-+/* LED ON Event Option.B */
-+#define LED_ON_EVT_LINK_2500M       (1 << 8)
-+#define LED_ON_EVT_FORCE            (1 << 6)
-+#define LED_ON_EVT_LINK_DOWN        (1 << 3)
-+#define LED_ON_EVT_LINK_100M        (1 << 1)
-+#define LED_ON_EVT_LINK_1000M       (1 << 0)
-+/* LED ON Event Option.E */
-+
-+#define LED_BLK_CTRL(i)             (0x025 + ((i)*2))
-+#define LED_BLK_EVT_MASK            (0xfff)
-+/* LED Blinking Event Option.B*/
-+#define LED_BLK_EVT_2500M_RX_ACT    (1 << 11)
-+#define LED_BLK_EVT_2500M_TX_ACT    (1 << 10)
-+#define LED_BLK_EVT_FORCE           (1 << 9)
-+#define LED_BLK_EVT_100M_RX_ACT     (1 << 3)
-+#define LED_BLK_EVT_100M_TX_ACT     (1 << 2)
-+#define LED_BLK_EVT_1000M_RX_ACT    (1 << 1)
-+#define LED_BLK_EVT_1000M_TX_ACT    (1 << 0)
-+/* LED Blinking Event Option.E*/
-+#define LED_ENABLE                  1
-+#define LED_DISABLE                 0
-+
-+#define EN8811H_LED_COUNT           3
-+
-+#define LED_BCR                     (0x021)
-+#define LED_BCR_EXT_CTRL            (1 << 15)
-+#define LED_BCR_CLK_EN              (1 << 3)
-+#define LED_BCR_TIME_TEST           (1 << 2)
-+#define LED_BCR_MODE_MASK           (3)
-+#define LED_BCR_MODE_DISABLE        (0)
-+
-+#define LED_ON_DUR                  (0x022)
-+#define LED_ON_DUR_MASK             (0xffff)
-+
-+#define LED_BLK_DUR                 (0x023)
-+#define LED_BLK_DUR_MASK            (0xffff)
-+
-+#define UNIT_LED_BLINK_DURATION     1024
-+
-+#define AIR_RTN_ON_ERR(cond, err)  \
-+    do { if ((cond)) return (err); } while(0)
-+
-+#define AIR_RTN_ERR(err)            AIR_RTN_ON_ERR(err < 0, err)
-+
-+#define LED_SET_EVT(reg, cod, result, bit) do         \
-+    {                                                 \
-+        if(reg & cod) {                               \
-+            result |= bit;                            \
-+        }                                             \
-+    } while(0)
-+
-+#define LED_SET_GPIO_SEL(gpio, led, val) do           \
-+    {                                                 \
-+        val |= (led << (8 * (gpio % 4)));         \
-+    } while(0)
-+
-+#define INVALID_DATA                0xffff
-+#define PBUS_INVALID_DATA           0xffffffff
-+
-+typedef struct AIR_BASE_T_LED_CFG_S
-+{
-+    u16 en;
-+    u16 gpio;
-+    u16 pol;
-+    u16 on_cfg;
-+    u16 blk_cfg;
-+}AIR_BASE_T_LED_CFG_T;
-+typedef enum
-+{
-+    AIR_LED2_GPIO3 = 3,
-+    AIR_LED1_GPIO4,
-+    AIR_LED0_GPIO5,
-+    AIR_LED_LAST
-+} AIR_LED_GPIO;
-+
-+typedef enum {
-+    AIR_BASE_T_LED0,
-+    AIR_BASE_T_LED1,
-+    AIR_BASE_T_LED2,
-+    AIR_BASE_T_LED3
-+}AIR_BASE_T_LED;
-+
-+typedef enum
-+{
-+    AIR_LED_BLK_DUR_32M,
-+    AIR_LED_BLK_DUR_64M,
-+    AIR_LED_BLK_DUR_128M,
-+    AIR_LED_BLK_DUR_256M,
-+    AIR_LED_BLK_DUR_512M,
-+    AIR_LED_BLK_DUR_1024M,
-+    AIR_LED_BLK_DUR_LAST
-+} AIR_LED_BLK_DUT_T;
-+
-+typedef enum
-+{
-+    AIR_ACTIVE_LOW,
-+    AIR_ACTIVE_HIGH,
-+} AIR_LED_POLARITY;
-+typedef enum
-+{
-+    AIR_LED_MODE_DISABLE,
-+    AIR_LED_MODE_USER_DEFINE,
-+    AIR_LED_MODE_LAST
-+} AIR_LED_MODE_T;
-+
-+#endif /* End of __EN8811H_MD32_H */
 -- 
 2.34.1
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2716-en8801sc-gphy-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2716-en8801sc-gphy-support.patch
index 47168b9..27ba8be 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2716-en8801sc-gphy-support.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2716-en8801sc-gphy-support.patch
@@ -33,6 +33,7 @@
  aquantia-objs			+= aquantia_hwmon.o
  endif
 +obj-$(CONFIG_AIROHA_EN8801SC_PHY)	+= en8801sc.o
+ air_en8811h-y := air_en8811h_main.o air_en8811h_api.o
  obj-$(CONFIG_AIROHA_EN8811H_PHY)	+= air_en8811h.o
  obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia.o
  obj-$(CONFIG_AX88796B_PHY)	+= ax88796b.o
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2719-net-phy-aquantia-add-firmware-download.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2719-net-phy-aquantia-add-firmware-download.patch
index 170b841..407896c 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2719-net-phy-aquantia-add-firmware-download.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2719-net-phy-aquantia-add-firmware-download.patch
@@ -140,7 +140,7 @@
  #endif
 +
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD
-+enum {
++enum aqr_fw_dl_mode {
 +	FW_DL_SINGLE = 0,
 +	FW_DL_GNAGLOAD,
 +};
@@ -153,7 +153,7 @@
 index 000000000..d2828aad4
 --- /dev/null
 +++ b/drivers/net/phy/aquantia_firmware.c
-@@ -0,0 +1,1100 @@
+@@ -0,0 +1,1109 @@
 +// SPDX-License-Identifier: GPL-2.0
 +/* FW download driver for Aquantia PHY
 + */
@@ -301,8 +301,9 @@
 +struct phy_device *gangload_phydevs[MAX_GANGLOAD_DEVICES];
 +static unsigned long gangload_timeout = 0;
 +static int gangload = 0;
++static DEFINE_SPINLOCK(gangload_lock);
 +
-+static int aqr_firmware_download_single(struct phy_device *phydev);
++static int aqr_firmware_download_single(struct phy_device *phydev, bool force_reload);
 +
 +void AQ_API_EnableMDIO_BootLoadMode
 +(
@@ -1068,9 +1069,7 @@
 +		if (priv->fw_initialized == true &&
 +		    aqr_firmware_check_heartbeat(phydev) == 1) {
 +			dev_err(dev, "Detect heartbeat stopped, start to realod firmware...\n");
-+			priv->fw_initialized = false;
-+			priv->fw_dl_mode = FW_DL_SINGLE;
-+			aqr_firmware_download_single(phydev);
++			aqr_firmware_download_single(phydev, true);
 +		}
 +
 +		set_current_state(TASK_INTERRUPTIBLE);
@@ -1092,12 +1091,14 @@
 +	if (!fw)
 +		return;
 +
++	spin_lock(&gangload_lock);
++
 +	num_phydevs = priv->fw_dl_mode == FW_DL_GNAGLOAD ?
 +		      gangload : 1;
 +
 +retry:
-+	if (gandload_phydev->state == PHY_HALTED) {
-+		dev_info(dev, "Detect PHY power down, stop to reload firmware...\n");
++	if (gandload_phydev->state == PHY_HALTED && priv->fw_initialized == true) {
++		dev_info(dev, "Detect PHY power down, stop to download firmware...\n");
 +		goto out;
 +	}
 +
@@ -1139,23 +1140,28 @@
 +			}
 +		}
 +	}
++
 +out:
 +	release_firmware(fw);
++
++	spin_unlock(&gangload_lock);
 +}
 +
-+static int aqr_firmware_download_single(struct phy_device *phydev)
++static int aqr_firmware_download_single(struct phy_device *phydev, bool force_reload)
 +{
 +	struct aqr107_priv *priv = phydev->priv;
 +	struct device *dev = &phydev->mdio.dev;
 +	const struct firmware *fw;
 +	int ret = 0;
 +
-+	if (priv->fw_initialized == true)
++	if (priv->fw_initialized == true && force_reload == false)
 +		return 0;
 +
-+	spin_lock_init(&priv->lock);
++	if (priv->phydevs[0] != phydev) {
++		priv->phydevs[0] = phydev;
++		spin_lock_init(&priv->lock);
++	}
 +	priv->fw_dl_mode = FW_DL_SINGLE;
-+	priv->phydevs[0] = phydev;
 +	priv->heartbeat = -1;
 +
 +	ret = request_firmware(&fw, AQR_FIRMWARE, dev);
@@ -1224,18 +1230,21 @@
 +		wake_up_process(gangload_kthread);
 +	}
 +
-+	/* fall back to single mode if timeout is reached */
-+	if (time_after(jiffies, gangload_timeout))
-+		return aqr_firmware_download_single(phydev);
-+
 +	for (i = 0; i < gangload; i++) {
 +		if (gangload_phydevs[i] == phydev) {
 +			dev_warn(dev, "Detect duplicate gangload phydev\n");
-+			return -EINVAL;
++			return 0;
 +		}
 +	}
 +
-+	spin_lock_init(&priv->lock);
++	/* fall back to single mode if timeout is reached */
++	if (time_after(jiffies, gangload_timeout))
++		return aqr_firmware_download_single(phydev, false);
++
++	if (priv->phydevs[0] != phydev) {
++		priv->phydevs[0] = phydev;
++		spin_lock_init(&priv->lock);
++	}
 +	priv->fw_dl_mode = FW_DL_GNAGLOAD;
 +	priv->heartbeat = -1;
 +	gangload_phydevs[gangload] = phydev;
@@ -1248,7 +1257,7 @@
 +{
 +	int ret = 0;
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_SINGLE
-+	ret = aqr_firmware_download_single(phydev);
++	ret = aqr_firmware_download_single(phydev, false);
 +#elif CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_GANG
 +	ret = aqr_firmware_download_gang(phydev);
 +#endif
@@ -1383,20 +1392,21 @@
  
  	return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
  }
-@@ -574,12 +559,41 @@ static void aqr107_link_change_notify(struct phy_device *phydev)
+@@ -574,12 +559,43 @@ static void aqr107_link_change_notify(struct phy_device *phydev)
  
  static int aqr107_suspend(struct phy_device *phydev)
  {
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD
 +	struct aqr107_priv *priv = phydev->priv;
 +
-+	spin_lock(&priv->lock);
-+	if (priv->heartbeat_thread) {
-+		kthread_stop(priv->heartbeat_thread);
-+		priv->heartbeat_thread = NULL;
-+		priv->heartbeat = -1;
++	if (spin_trylock(&priv->lock)) {
++		if (priv->heartbeat_thread) {
++			kthread_stop(priv->heartbeat_thread);
++			priv->heartbeat_thread = NULL;
++			priv->heartbeat = -1;
++		}
++		spin_unlock(&priv->lock);
 +	}
-+	spin_unlock(&priv->lock);
 +#endif
  	return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
  				MDIO_CTRL1_LPOWER);
@@ -1407,20 +1417,21 @@
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD
 +	struct aqr107_priv *priv = phydev->priv;
 +
-+	spin_lock(&priv->lock);
-+	if (!priv->heartbeat_thread) {
-+		priv->heartbeat_thread = kthread_create(aqr_firmware_heartbeat_thread,
-+							phydev,
-+							"aqr_firmware_heartbeat_thread");
-+		if (IS_ERR(priv->heartbeat_thread)) {
-+			phydev_err(phydev,
-+				   "Failed to create aqr_firmware_heartbeat_thread(%ld)\n",
-+				    PTR_ERR(priv->heartbeat_thread));
-+			priv->heartbeat_thread = NULL;
-+		} else
-+			wake_up_process(priv->heartbeat_thread);
++	if (spin_trylock(&priv->lock)) {
++		if (!priv->heartbeat_thread) {
++			priv->heartbeat_thread = kthread_create(aqr_firmware_heartbeat_thread,
++								phydev,
++								"aqr_firmware_heartbeat_thread");
++			if (IS_ERR(priv->heartbeat_thread)) {
++				phydev_err(phydev,
++					   "Failed to create aqr_firmware_heartbeat_thread(%ld)\n",
++					    PTR_ERR(priv->heartbeat_thread));
++				priv->heartbeat_thread = NULL;
++			} else
++				wake_up_process(priv->heartbeat_thread);
++		}
++		spin_unlock(&priv->lock);
 +	}
-+	spin_unlock(&priv->lock);
 +#endif
  	return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
  				  MDIO_CTRL1_LPOWER);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2729-net-phy-remove-reporting-line-rate-to-mac.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2729-net-phy-remove-reporting-line-rate-to-mac.patch
new file mode 100644
index 0000000..4f62103
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2729-net-phy-remove-reporting-line-rate-to-mac.patch
@@ -0,0 +1,14 @@
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -686,7 +686,10 @@ static void phylink_link_up(struct phyli
+ 		 * the link_state) to the interface speed, and will send
+ 		 * pause frames to the MAC to limit its transmission speed.
+ 		 */
+-		speed = phylink_interface_max_speed(link_state.interface);
++		/* For tunnel HW offload, we need to get true link rate to
++		 * set QDMA rate limit as link rate.
++		 */
++		// speed = phylink_interface_max_speed(link_state.interface);
+ 		duplex = DUPLEX_FULL;
+ 		rx_pause = true;
+ 		break;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2730-net-phy-sfp-change-shared-mod-def0.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2730-net-phy-sfp-change-shared-mod-def0.patch
new file mode 100644
index 0000000..479f58b
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2730-net-phy-sfp-change-shared-mod-def0.patch
@@ -0,0 +1,43 @@
+From c576ce67488bc8b0285933f315cb183c9171f199 Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Thu, 7 Sep 2023 12:01:57 +0800
+Subject: [PATCH] 999-2730-net-phy-sfp-change-shared-mod-def0
+
+---
+ drivers/net/phy/sfp.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index 0c335b1..d49a825 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -152,7 +152,7 @@ static const char *gpio_of_names[] = {
+ };
+ 
+ static const enum gpiod_flags gpio_flags[] = {
+-	GPIOD_IN,
++	GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE,
+ 	GPIOD_IN,
+ 	GPIOD_IN,
+ 	GPIOD_ASIS,
+@@ -512,7 +512,7 @@ static unsigned int sfp_gpio_get_state(struct sfp *sfp)
+ 	unsigned int i, state, v;
+ 
+ 	for (i = state = 0; i < GPIO_MAX; i++) {
+-		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
++		if (((gpio_flags[i] & GPIOD_IN) != GPIOD_IN) || !sfp->gpio[i])
+ 			continue;
+ 
+ 		v = gpiod_get_value_cansleep(sfp->gpio[i]);
+@@ -2757,7 +2757,7 @@ static int sfp_probe(struct platform_device *pdev)
+ 	}
+ 
+ 	for (i = 0; i < GPIO_MAX; i++) {
+-		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
++		if (((gpio_flags[i] & GPIOD_IN) != GPIOD_IN) || !sfp->gpio[i])
+ 			continue;
+ 
+ 		sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
+-- 
+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 8f19163..e4bde13 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
@@ -36,6 +36,7 @@
     file://1020-spi-nor-w25q512jv.patch \
     file://1021-ubnt-ledbar-driver.patch \
     file://999-1000-backport-pinctrl-pinconf-setting-combo.patch \
+    file://999-1033-backport-pinctrl-spinlock.patch \
     file://999-1600-mdiobus-add-c45.patch \
     file://999-1700-macsec-revert-async-support.patch \
     file://999-1701-add-default-setting-to-dsa-unused-port.patch \
@@ -147,6 +148,8 @@
     file://999-2725-iwconfig-wireless-rate-fix.patch;apply=no \
     file://999-2727-net-phy-sfp-add-debug-info.patch.patch \
     file://999-2728-net-phy-aquantia-add-mib-read.patch \
+    file://999-2729-net-phy-remove-reporting-line-rate-to-mac.patch;apply=no \
+    file://999-2730-net-phy-sfp-change-shared-mod-def0.patch \
     file://999-2800-misc-add-mtk-platform.patch \
     file://999-2900-dts-mt7622-enable-new-mtk-snand-for-ubi.patch \
     file://999-2901-dts-mt7622-remove-cooling-device.patch \
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/wed3/999-3021-mtk-wed-add-wed3-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/wed3/999-3021-mtk-wed-add-wed3-support.patch
index 69df648..cda6afa 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/wed3/999-3021-mtk-wed-add-wed3-support.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/wed3/999-3021-mtk-wed-add-wed3-support.patch
@@ -1,9 +1,8 @@
-From 400f8349a31ffc48538aa7df64a88111de9a738b Mon Sep 17 00:00:00 2001
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Thu, 13 Apr 2023 15:51:08 +0800
+From e8b3ef23ae83316d6b1d7d544708cc2a076ba571 Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Mon, 11 Sep 2023 11:25:07 +0800
 Subject: [PATCH] mtk:wed:add wed3 support
 
-Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
 ---
  arch/arm64/boot/dts/mediatek/mt7988.dtsi      |  152 ++-
  .../dts/mediatek/mt7988a-dsa-10g-spim-nor.dts |   16 +-
@@ -13,7 +12,7 @@
  drivers/net/ethernet/mediatek/mtk_ppe.c       |   17 +-
  drivers/net/ethernet/mediatek/mtk_ppe.h       |    2 +-
  .../net/ethernet/mediatek/mtk_ppe_offload.c   |   13 +-
- drivers/net/ethernet/mediatek/mtk_wed.c       | 1148 +++++++++++++----
+ drivers/net/ethernet/mediatek/mtk_wed.c       | 1155 +++++++++++++----
  drivers/net/ethernet/mediatek/mtk_wed.h       |   25 +-
  .../net/ethernet/mediatek/mtk_wed_debugfs.c   |  584 ++++++++-
  drivers/net/ethernet/mediatek/mtk_wed_mcu.c   |   13 +-
@@ -21,14 +20,14 @@
  drivers/net/ethernet/mediatek/mtk_wed_regs.h  |  338 ++++-
  include/linux/netdevice.h                     |    7 +
  include/linux/soc/mediatek/mtk_wed.h          |   81 +-
- 16 files changed, 1446 insertions(+), 333 deletions(-)
+ 16 files changed, 2045 insertions(+), 387 deletions(-)
  mode change 100755 => 100644 drivers/net/ethernet/mediatek/mtk_ppe.c
 
 diff --git a/arch/arm64/boot/dts/mediatek/mt7988.dtsi b/arch/arm64/boot/dts/mediatek/mt7988.dtsi
-index 364deef..f9a0120 100644
+index 561450e9ba48..8995ea30d4f7 100644
 --- a/arch/arm64/boot/dts/mediatek/mt7988.dtsi
 +++ b/arch/arm64/boot/dts/mediatek/mt7988.dtsi
-@@ -191,44 +191,49 @@
+@@ -205,44 +205,49 @@
  		status = "disabled";
  	};
  
@@ -113,7 +112,7 @@
  	};
  
  	wdma: wdma@15104800 {
-@@ -238,15 +243,25 @@
+@@ -252,15 +257,25 @@
  		      <0 0x15105000 0 0x400>;
  	};
  
@@ -147,7 +146,7 @@
  	};
  
  	wocpu0_ilm: wocpu0_ilm@151E0000 {
-@@ -254,31 +269,53 @@
+@@ -268,31 +283,53 @@
  		reg = <0 0x151E0000 0 0x8000>;
  	};
  
@@ -215,7 +214,7 @@
  	};
  
  	reserved-memory {
-@@ -827,6 +864,7 @@
+@@ -902,6 +939,7 @@
  					 <&topckgen CK_TOP_CB_SGM_325M>;
  		mediatek,ethsys = <&ethsys>;
  		mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>;
@@ -224,10 +223,10 @@
  		mediatek,xfi_pextp = <&xfi_pextp0>, <&xfi_pextp1>;
  		mediatek,xfi_pll = <&xfi_pll>;
 diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts b/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
-index 7db5164..0a6db8b 100644
+index 70a7554473f6..bed27b4b3488 100644
 --- a/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
 +++ b/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
-@@ -341,9 +341,23 @@
+@@ -369,9 +369,23 @@
  	status = "okay";
  };
  
@@ -254,10 +253,10 @@
 +};
 \ No newline at end of file
 diff --git a/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts b/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
-index 67c6508..c407b33 100644
+index e8e3a69290c8..5dd481b72ea8 100644
 --- a/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
 +++ b/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
-@@ -325,9 +325,23 @@
+@@ -379,9 +379,23 @@
  	status = "okay";
  };
  
@@ -284,10 +283,10 @@
 +};
 \ No newline at end of file
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 388982c..d59c29f 100644
+index 6fabc5758fb6..655928cc7462 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4865,7 +4865,8 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -5094,7 +5094,8 @@ static int mtk_probe(struct platform_device *pdev)
  							  "mediatek,wed", i);
  		static const u32 wdma_regs[] = {
  			MTK_WDMA0_BASE,
@@ -298,10 +297,10 @@
  		void __iomem *wdma;
  		u32 wdma_phy;
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-index a9feaed..70e8377 100644
+index 80f4c4b32a30..06f2b5d98b81 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -605,9 +605,12 @@
+@@ -611,9 +611,12 @@
  #define RX_DMA_SPORT_MASK       0x7
  #define RX_DMA_SPORT_MASK_V2    0xf
  
@@ -318,7 +317,7 @@
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
 old mode 100755
 new mode 100644
-index bc13a9b..3910163
+index 384e811d6946..eda23c2afbbf
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 @@ -9,6 +9,7 @@
@@ -367,7 +366,7 @@
  }
  
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-index df10040..9e7d5aa 100644
+index 2a8b6ef225a2..66c7f10170d2 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 @@ -428,7 +428,7 @@ int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
@@ -378,9 +377,9 @@
 +			   int bss, int wcid, bool amsdu_en);
  int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid);
  int mtk_foe_entry_set_dscp(struct mtk_foe_entry *entry, int dscp);
- int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+ bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data);
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-index 9bc0857..86fc9a1 100644
+index 95174b795cf0..eab9e9de7193 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 @@ -112,6 +112,7 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
@@ -408,7 +407,7 @@
  		else
  			return -EOPNOTSUPP;
  #endif
-@@ -458,8 +461,8 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+@@ -490,8 +493,8 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
  	if (err)
  		return err;
  
@@ -419,8 +418,8 @@
  
  	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
  	if (!entry)
-@@ -499,8 +502,8 @@ clear:
- 	mtk_foe_entry_clear(eth->ppe[i], entry);
+@@ -516,8 +519,8 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+ 	mtk_foe_entry_clear(eth->ppe[ppe_index], entry);
  free:
  	kfree(entry);
 -	if (wed_index >= 0)
@@ -431,7 +430,7 @@
  }
  
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
-index 37a86c3..e3809db 100644
+index 3e760f7c5f3d..6ac4aa8d4e3c 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 @@ -28,7 +28,7 @@ struct wo_cmd_ring {
@@ -1018,10 +1017,10 @@
 -#define MTK_WFMDA_RX_DMA_EN 	BIT(2)
 +	if (!mtk_wed_get_rx_capa(dev))
 +		return 0;
- 
++
 +	wed_set(dev, MTK_WED_RRO_PG_BM_RX_DMAM,
 +		FIELD_PREP(MTK_WED_RRO_PG_BM_RX_SDL0, 128));
-+
+ 
 +	wed_w32(dev, MTK_WED_RRO_PG_BM_BASE,
 +		dev->rx_page_buf_ring.desc_phys);
 +
@@ -1323,12 +1322,12 @@
 -		value = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
 -		wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180);
 -		wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184);
--
--		value = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
--		value = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
 +			MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER |
 +			FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, dev->hw->index));
  
+-		value = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
+-		value = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
+-
 -		wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
 -		wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER);
 -
@@ -2088,16 +2087,24 @@
  static u32
  mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
  {
-@@ -1660,6 +2335,8 @@ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
+@@ -1659,9 +2334,13 @@ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
+ 
  	val = wed_r32(dev, MTK_WED_EXT_INT_STATUS);
  	wed_w32(dev, MTK_WED_EXT_INT_STATUS, val);
- 	val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK;
-+	if (dev->hw->version == 3)
+-	val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK;
+-	if (!dev->hw->num_flows)
+-		val &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
++	if (dev->hw->version == 3) {
 +		val &= MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT;
- 	WARN_RATELIMIT(val, "mtk_wed%d: error status=%08x\n",
- 		       dev->hw->index, val);
++	} else {
++		val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK;	
++		if (!dev->hw->num_flows)
++			val &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;	
++	}
+ 	if (val && net_ratelimit())
+ 		pr_err("mtk_wed%d: error status=%08x\n", dev->hw->index, val);
  
-@@ -1752,6 +2429,9 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1754,6 +2433,9 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  		.tx_ring_setup = mtk_wed_tx_ring_setup,
  		.txfree_ring_setup = mtk_wed_txfree_ring_setup,
  		.rx_ring_setup = mtk_wed_rx_ring_setup,
@@ -2107,7 +2114,7 @@
  		.msg_update = mtk_wed_send_msg,
  		.start = mtk_wed_start,
  		.stop = mtk_wed_stop,
-@@ -1763,6 +2443,7 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1765,6 +2447,7 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  		.detach = mtk_wed_detach,
  		.setup_tc = mtk_wed_eth_setup_tc,
  		.ppe_check = mtk_wed_ppe_check,
@@ -2115,7 +2122,7 @@
  	};
  	struct device_node *eth_np = eth->dev->of_node;
  	struct platform_device *pdev;
-@@ -1802,9 +2483,10 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1804,9 +2487,10 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  	hw->wdma_phy = wdma_phy;
  	hw->index = index;
  	hw->irq = irq;
@@ -2128,7 +2135,7 @@
  		hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
  							     "mediatek,pcie-mirror");
  		hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
-@@ -1819,7 +2501,6 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1821,7 +2505,6 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  			regmap_write(hw->mirror, 0, 0);
  			regmap_write(hw->mirror, 4, 0);
  		}
@@ -2137,7 +2144,7 @@
  
  	mtk_wed_hw_add_debugfs(hw);
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed.h b/drivers/net/ethernet/mediatek/mtk_wed.h
-index 490873c..fcf7bd0 100644
+index 490873cc58ac..fcf7bd0a6f66 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed.h
 +++ b/drivers/net/ethernet/mediatek/mtk_wed.h
 @@ -10,10 +10,13 @@
@@ -2204,7 +2211,7 @@
  
  #ifdef CONFIG_NET_MEDIATEK_SOC_WED
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-index 4a9e684..51e3d7c 100644
+index 4a9e684edbdb..51e3d7c94d86 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 @@ -11,9 +11,11 @@ struct reg_dump {
@@ -2901,7 +2908,7 @@
  	}
  }
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-index 96e30a3..055594d 100644
+index 96e30a320ed7..055594de1c98 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 @@ -242,7 +242,7 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
@@ -2939,7 +2946,7 @@
  	wo_w32(wo, WOX_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
  
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.h b/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
-index 19e1199..c07bdb6 100644
+index 19e1199783f4..c07bdb6ca153 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
 @@ -16,8 +16,9 @@
@@ -2955,7 +2962,7 @@
  #define WOCPU_EMI_DEV_NODE		"mediatek,wocpu_emi"
  #define WOCPU_ILM_DEV_NODE		"mediatek,wocpu_ilm"
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-index 403a36b..4e619ff 100644
+index 403a36bb9d5e..25be547b98fa 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 @@ -20,6 +20,9 @@
@@ -3458,10 +3465,10 @@
 +#define MTK_WED_PCIE_BASE2			0x11290000
  #endif
 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
-index 58b5ce6..5e51790 100644
+index 0967dc24060f..3211f344847e 100644
 --- a/include/linux/netdevice.h
 +++ b/include/linux/netdevice.h
-@@ -873,6 +873,13 @@ struct net_device_path {
+@@ -875,6 +875,13 @@ struct net_device_path {
  			u8 queue;
  			u16 wcid;
  			u8 bss;
@@ -3476,7 +3483,7 @@
  	};
  };
 diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h
-index 27cf284..60336e0 100644
+index 27cf284b4af3..2b389e8a813c 100644
 --- a/include/linux/soc/mediatek/mtk_wed.h
 +++ b/include/linux/soc/mediatek/mtk_wed.h
 @@ -5,11 +5,14 @@
diff --git a/recipes-kernel/linux/linux-mediatek_5.4.bb b/recipes-kernel/linux/linux-mediatek_5.4.bb
index 6dc242d..6e4cf79 100644
--- a/recipes-kernel/linux/linux-mediatek_5.4.bb
+++ b/recipes-kernel/linux/linux-mediatek_5.4.bb
@@ -103,6 +103,7 @@
             patch -p1 < ${WORKDIR}/999-1712-net-phy-add-phylink-rate-matching-support.patch
             patch -p1 < ${WORKDIR}/999-2702-v5.9-net-phy-add-support-for-a-common-probe-between-shared-PHYs.patch
             patch -p1 < ${WORKDIR}/999-2725-iwconfig-wireless-rate-fix.patch
+            patch -p1 < ${WORKDIR}/999-2729-net-phy-remove-reporting-line-rate-to-mac.patch
             if [ $DISTRO_secure_boot_ENABLED = 'true' ]; then
                 patch -p1 < ${WORKDIR}/0404-mtdsplit-dm-verity.patch
                 patch -p1 < ${WORKDIR}/0800-dm-verity-redo-hash-for-safexel-sha256.patch