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

[Description]
95600331 [openwrt-24][common][bsp][Update readme for unified autobuild bpi-r4]
62c20e63 [openwrt-24][common][config][change unified autobuild bpi to bpi-r4]
49bdf917 [openwrt-24][Mac80211][2024-10-10 Update]
8884902b [kernel][mt7988][eth][Change the PSE_NO_DROP configuration to disable No Drop on the P1]
8b6360c1 [kernel][mt7988][eth][Revert memory high threshold setting for the CDM2]
df681693 [openwrt-24][mac80211][hnat][Add hwpath support]
ae31fde8 [openwrt][mt7988][tops][change tops encap_offload api]
fab0da5f [[Kernel][common][hnat][Fix the way of recording SMAC and DMAC in get_nexthop API]]
733f3ad8 [kernel][common][hnat][Add PPE pkt_type condition to entry delete by bssid/wcid API]
43dd7b8f [kernel][common][eth][Fix traffic stuck issue with EN8811H after the network restart]
73bd3ebc [kernel][mt7987][eth][Refactor initial function of the xfi_pll and toprgu for the USXGMII/SGMII]
67543ffc [kernel][common][eth][Add sanity check for the USXGMII/SGMII pointer]
a2a093b5 [kernel][mt7988][eth][Refactor the MTK_HWLRO enabling method by relocating it to the MT7988_CAPS]
507dd381 [kernel][common][eth][Fix register dump failure in mt753x switch]
7be881ef [kernel][common][eth][Fix code flow mismatch when non-mt753x switch using 0x1f as mdio address]
bb2adac3 [kernel][common][eth][Refactor GMAC jumbo frame support]
bb02ae35 [HIGH][kernel][common][eth][Update Airoha AN8801SB 1G PHY driver to v1.1.4]
de41e559 [kernel][common][app][Refactor the mtk_ppe support for the mtkhnat_util]
5c3be07b [kernel][mt7988][eth][net: phy: mediatek: mtk-2p5ge: Fix fw loading flow]
60dc45d5 [openwrt][common][app][Fix compiler warnings to avoid build failures]
c0b80498 [openwrt][common][app][Add ethswbox MR1 V1.3.0.0 for Mxl862xx]
087e5e2c [kernel][mt7988][switch][Add mxl862xx DSA driver GA v1.2.0.0]
8d4b4b2b [openwrt-24][common][config][Fix unified autobuild flow]
f3d78036 [openwrt-24][Release][Fix release build fail of Wi-Fi7 MT76]
2fa6e425 [openwrt-24][mt7988][eth][Fix the issue of firmware loss after toggling the interface up and down for the Aquantia 10G PHY]
a4642659 [opewnrt-24][common][bsp][Unified autobuild framework update]
c7a608e1 [openwrt-24][common][bsp][Disable building initramfs for bpi boards]
99aadf4d [openwrt-24][common][config][Remove unused file from unified autobuild]
f80d2252 [openwrt-24][common][config][Add BananaPi boards for unified autobuild]
504c3540 [openwrt-24][common][config][Allow bypass cleaning feeds for unified autobuild]
00f65ebc [openwrt-24][common][config][Update defconfig files of unified autobuild]
a37a8ecd [openwrt-24][common][config][Rewrite defconfig manipulation of unified autobuild]
b5cea6aa [openwrt-trunk][mt798x][config][Enable iptables, switch, and tcpdump packages]
39410d47 [MAC80211][hnat][Fix patch conflict issue]
0119aee2 [kernel][common][eth][Store NETSYS SER event in the eth reset structure instead of using static variables]
7d0f0b78 [openwrt-24][Release][Fix build fail of Wi-Fi7 MT76]
653936cc [kernel][mt7988][eth][Fix patches conflict issue]
0f9be39d [openwrt][common][eth][Add proprietary debugfs for the ETH]
10b1d1a4 [kernel][common][eth][Remove the unused variable in the mtk_mac_link_down() function]
765d2af7 [openwrt-24][common][bsp][Fix band 1 register failed issue]
2ae8c177 [openwrt-24][Release][Fix build fail of Wi-Fi7 MT76]
2b1b6dab [openwrt][mt7988][crypto][Change ESP offload config to default]
fa120419 [kernel][mt7988][eth][Add Aquantia AQR113C 10G PHY firmware package]
046cd633 [kernel][mt7987][eth][net: phy: mediatek: mtk-2p5ge: Force phy to enter AN state at beginning]
97d78ca1 [kernel][mt7987][eth][net: phy: mediatek: Add support for mt7987 built-in 2.5Gphy]
da9e1c24 [MAC80211][hnat][Fix patch conflict issue]
71a595f0 [kernel][common][eth][Decrease the USXGMII dumping range for the NETSYS SER]
8d606e49 [kernel][common][eth][Change the wait time for SER handshake with WiFi timeout to 10 seconds, and refactor the debugging messages]
51be0f13 [kernel][common][switch][Add 2500base-x for mt753x mac port setting]
17e01dcd [kernel][mt7988][eth][Refactor QDMA rate limit for the TOPS]
e04b73bf [MAC80211][hnat][Fix issue of PPE entry deletion by enhancing roaming handler]
167e53b9 [kernel][common][hnat][Add event to delete HNAT entries of bond slave when physical link is down]
e80a7ea9 [openwrt-24][common][bsp][Enable updating feeds with rebase for unified autobuild]
127b6a3c [openwrt-24][common][bsp][Update patch of script/feeds of unified autobuild]
27a5eed5 [openwrt][mt7988][tops][change maintenance for eth and hnat from patch way to files-5.4]
de310f02 [MAC80211][hnat][Fix patch conflict issue]
b7acb168 [kernel][mt7988][eth][Refactor the infrastructure of the NETSYS SER monitor and enhance the detection rules for ADMA_RX, GDM_RX, and QDMA_TX]
117ff793 [HIGH][mac80211][eth][Fix patch conflict issue]
05351d23 [HIGH][kernel][mt7986/81][eth][Change RX DMA L4 valid bit definition]
68562c64 [MAC80211][hnat][Fix double kfree wo issue when init wo fail]
b6d2ddbf [openwrt][common][config][Fix default kernel config for mxl862xx DSA]
d645f89f [openwrt-24][common][bsp][Add eeprom loading support for MT76]
2f52b080 [kernel][common][hnat][Fix coverity scan issue]
260804eb [kernel][common][hnat][Add HNAT DSA tag for MXL862_8021Q protocol]
c297a74d [openwrt][mt7987][crypto][Add package for eip196]
231f4714 [kernel][mt7988][switch][add mxl862xx SDK build]
3d24d1ec [kernel][mt7988][switch][add mxl862xx switch DSA driver]
05668a4d [kernel][common][hnat][Fix patch conflict issue]
189af907 [kernel][common][hnat][Add support to identify bond slave device]
0e62d3ef [openwrt][openwrt][app][Fix BridgePortConfigSet issue of ethswbox]
c69491cb [openwrt][app][Fix DEBUG_RMON_PORT issue of ethswbox]
df967857 [openwrt][Fix msdc source clock]
bb04bb14 [openwrt][common][Fix Bug in kernel upgrade from 5.4.271 to 5.4.281]
43d789fd [openwrt-24][common][image][Check symlinks for unified autobuild list function]
5e6b93c0 [openwrt][mt7987][app][Update mtk_factory_rw.sh]
43bdf85b [kernel][common][eth][Add Ethernet jumbo frame support]
45c7811b [openwrt-24][common][image][Add unified autobuild framework]
c96b6eea [openwrt-24][common][bsp][Add rtl8621n phy driver for unified autobuild]
7777d989 [openwrt-24][common][bsp][Add initial filogic target for unified autobuild]
8b607077 [kernel][mt7988][eth][Change the interface type to INTERNAL for the internal 2.5G PHY]
969a1c36 [High][kernel][mt7988][eth][dts: Change internal 2.5G phy mode to internal]
a4bc41d9 [kernel][common][eth][Fix Coverity defects in the USXGMII/SGMII]
9c76e0fd [[Kernel][common][hnat][Fix trace_printk and printk_limited debug log]]
1ca27815 [High][kernel][mt7981/mt7988][eth][net: phy: mediatek: Introduce mtk-phy-lib and add 2.5Gphy support]
fec7dd55 [High][kernel][mt7981/mt7988][eth][net: phy: Fix and get ready to integrate mediatek's internal ethernet phy]

[Release-log]

Change-Id: I1f66986e6db8a07330c79fd523089be254f5794a
diff --git a/recipes-bsp/airoha-eth-firmware/airoha-eth-firmware.bb b/recipes-bsp/airoha-eth-firmware/airoha-eth-firmware.bb
new file mode 100644
index 0000000..e05e6b7
--- /dev/null
+++ b/recipes-bsp/airoha-eth-firmware/airoha-eth-firmware.bb
@@ -0,0 +1,26 @@
+SUMMARY = "Airoha Ethernet firmware files"
+DESCRIPTION = "Firmware for Airoha 2.5G phy. "
+
+LICENSE = "CLOSED"
+
+SRC_URI = " \
+    file://EthMD32.dm.bin \
+    file://EthMD32.DSP.bin \
+"
+S = "${WORKDIR}"
+
+inherit allarch
+
+do_install() {
+    install -d ${D}/${base_libdir}/firmware/
+    install -m 644 ${WORKDIR}/EthMD32.dm.bin ${D}${base_libdir}/firmware/
+    install -m 644 ${WORKDIR}/EthMD32.DSP.bin ${D}${base_libdir}/firmware/
+}
+
+FILES_${PN} += "${base_libdir}/firmware/*"
+
+# Make Mediatek-eth-firmware depend on all of the split-out packages.
+python populate_packages_prepend () {
+    firmware_pkgs = oe.utils.packages_filter_out_system(d)
+    d.appendVar('RDEPENDS_mediatek-eth-firmware', ' ' + ' '.join(firmware_pkgs))
+}
diff --git a/recipes-bsp/airoha-eth-firmware/files/EthMD32.DSP.bin b/recipes-bsp/airoha-eth-firmware/files/EthMD32.DSP.bin
new file mode 100644
index 0000000..dc9418a
--- /dev/null
+++ b/recipes-bsp/airoha-eth-firmware/files/EthMD32.DSP.bin
Binary files differ
diff --git a/recipes-bsp/airoha-eth-firmware/files/EthMD32.dm.bin b/recipes-bsp/airoha-eth-firmware/files/EthMD32.dm.bin
new file mode 100644
index 0000000..32aeb21
--- /dev/null
+++ b/recipes-bsp/airoha-eth-firmware/files/EthMD32.dm.bin
Binary files differ
diff --git a/recipes-bsp/mediatek-eth-firmware/files/mt7987/i2p5ge-phy-DSPBitTb.bin b/recipes-bsp/mediatek-eth-firmware/files/mt7987/i2p5ge-phy-DSPBitTb.bin
new file mode 100644
index 0000000..5b0e44c
--- /dev/null
+++ b/recipes-bsp/mediatek-eth-firmware/files/mt7987/i2p5ge-phy-DSPBitTb.bin
Binary files differ
diff --git a/recipes-bsp/mediatek-eth-firmware/files/mt7987/i2p5ge-phy-pmb.bin b/recipes-bsp/mediatek-eth-firmware/files/mt7987/i2p5ge-phy-pmb.bin
new file mode 100644
index 0000000..991c319
--- /dev/null
+++ b/recipes-bsp/mediatek-eth-firmware/files/mt7987/i2p5ge-phy-pmb.bin
Binary files differ
diff --git a/recipes-bsp/mediatek-eth-firmware/files/i2p5ge-phy-pmb.bin b/recipes-bsp/mediatek-eth-firmware/files/mt7988/i2p5ge-phy-pmb.bin
similarity index 100%
rename from recipes-bsp/mediatek-eth-firmware/files/i2p5ge-phy-pmb.bin
rename to recipes-bsp/mediatek-eth-firmware/files/mt7988/i2p5ge-phy-pmb.bin
Binary files differ
diff --git a/recipes-bsp/mediatek-eth-firmware/mediatek-eth-firmware.bb b/recipes-bsp/mediatek-eth-firmware/mediatek-eth-firmware.bb
index d8ccad1..72f2a64 100644
--- a/recipes-bsp/mediatek-eth-firmware/mediatek-eth-firmware.bb
+++ b/recipes-bsp/mediatek-eth-firmware/mediatek-eth-firmware.bb
@@ -3,19 +3,27 @@
 
 LICENSE = "CLOSED"
 
-SRC_URI = " \
-    file://i2p5ge-phy-pmb.bin \
+SRC_URI_mt7988 += " \
+    file://mt7988 \
+"
+SRC_URI_mt7987 += " \
+    file://mt7987 \
 "
 S = "${WORKDIR}"
 
 inherit allarch
 
-do_install() {
+do_install_mt7988() {
     install -d ${D}/${base_libdir}/firmware/mediatek/mt7988/
-    install -m 644 ${WORKDIR}/i2p5ge-phy-pmb.bin ${D}${base_libdir}/firmware/mediatek/mt7988/
+    install -m 644 ${WORKDIR}/mt7988/* ${D}${base_libdir}/firmware/mediatek/mt7988/
+}
+
+do_install_mt7987() {
+    install -d ${D}/${base_libdir}/firmware/mediatek/mt7987/
+    install -m 644 ${WORKDIR}/mt7987/* ${D}${base_libdir}/firmware/mediatek/mt7987/
 }
 
-FILES_${PN} += "${base_libdir}/firmware/mediatek/mt7988/*"
+FILES_${PN} += "${base_libdir}/firmware/mediatek/*"
 
 # Make Mediatek-eth-firmware depend on all of the split-out packages.
 python populate_packages_prepend () {
diff --git a/recipes-devtools/mtk-factory-rw/files/mtk_factory_rw.sh b/recipes-devtools/mtk-factory-rw/files/mtk_factory_rw.sh
index 3124088..22c0a7c 100644
--- a/recipes-devtools/mtk-factory-rw/files/mtk_factory_rw.sh
+++ b/recipes-devtools/mtk-factory-rw/files/mtk_factory_rw.sh
@@ -46,7 +46,7 @@
 		lan_mac_offset=0x1F800
 		wan_mac_offset=0x1F806
 		;;
-	*7988*)
+	*7987*|*7988*)
 		#1024k - 18 byte
 		lan2_mac_offset=0xFFFEE
 		lan_mac_offset=0xFFFF4
diff --git a/recipes-devtools/smp/files/smp-mt76.sh b/recipes-devtools/smp/files/smp-mt76.sh
index 4830806..b00d665 100644
--- a/recipes-devtools/smp/files/smp-mt76.sh
+++ b/recipes-devtools/smp/files/smp-mt76.sh
@@ -395,6 +395,9 @@
 	elif [[ $board == *"7622"* ]]; then
 		dbg "setup_model: MT7622 NUM_WIFI_CARD=$num_of_wifi"
 		MT7622 $num_of_wifi
+	elif [[ $board == *"bpi-r4"* ]]; then
+		dbg "setup_model: bpi-r4 NUM_WIFI_CARD=$num_of_wifi"
+		MT7988 $num_of_wifi
 	fi
 }
 
diff --git a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_cmd.c b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_cmd.c
index 6dfe416..ffe932d 100644
--- a/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_cmd.c
+++ b/recipes-devtools/switch/files/src/an8855_sdk/api/src/air_cmd.c
@@ -4389,7 +4389,7 @@
 		 if (AIR_E_OK != rc)
 		 {
 			AIR_PRINT("***Error***,get port=%u error\n", i);
-			return rc;
+			return;
 		 }
 
          if(session.flags & AIR_MIR_SESSION_FLAGS_DIR_TX)
diff --git a/recipes-devtools/switch/files/src/switch_fun.h b/recipes-devtools/switch/files/src/switch_fun.h
index 7ca1b0a..81f336b 100644
--- a/recipes-devtools/switch/files/src/switch_fun.h
+++ b/recipes-devtools/switch/files/src/switch_fun.h
@@ -6,8 +6,6 @@
 
 #include <stdbool.h>
 
-void (*pf_chip_func)(int argc, char *argv[]);
-
 struct switch_func_s {
 	void (*pf_table_dump)(int argc, char *argv[]);
 	void (*pf_table_clear)(int argc, char *argv[]);
diff --git a/recipes-devtools/switch/switch_1.0.bb b/recipes-devtools/switch/switch_1.0.bb
index 133b669..a16e0d6 100644
--- a/recipes-devtools/switch/switch_1.0.bb
+++ b/recipes-devtools/switch/switch_1.0.bb
@@ -26,6 +26,5 @@
 
 do_install() {
     install -d ${D}/usr/sbin
-    install -d ${D}/lib/network
     install -m 0755 ${S}/switch ${D}/usr/sbin
 }
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/001-rdkb-eth-mtk-change-ifname-for.patch b/recipes-kernel/linux/linux-mediatek-5.4/001-rdkb-eth-mtk-change-ifname-for.patch
index 50eea64..36d4c4b 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/001-rdkb-eth-mtk-change-ifname-for.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/001-rdkb-eth-mtk-change-ifname-for.patch
@@ -1,12 +1,12 @@
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 1bf43b3..3001525 100755
+index bf6b6f8120c1..6a5bff8b0bbc 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3395,6 +3395,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
- 
- 	eth->netdev[id]->irq = eth->irq[0];
+@@ -5935,6 +5935,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ 	eth->netdev[id]->irq = eth->irq_fe[0];
  	eth->netdev[id]->dev.of_node = np;
+ 	eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
 +	sprintf (eth->netdev[id]->name, "eth%d",id+1);
  
- 	return 0;
- 
+ 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+ 		mac->device_notifier.notifier_call = mtk_device_event;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
index 987a4f6..cac496e 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
@@ -115,3 +115,35 @@
  	call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
  	add_device_randomness(dev->dev_addr, dev->addr_len);
  	return 0;
+--- a/include/linux/etherdevice.h
++++ b/include/linux/etherdevice.h
+@@ -531,6 +531,18 @@ static inline unsigned long compare_ethe
+ #endif
+ }
+ 
++static inline bool
++eth_check_local_mask(const void *addr1, const void *addr2, const void *mask)
++{
++	const u16 *a1 = addr1;
++	const u16 *a2 = addr2;
++	const u16 *m = mask;
++
++	return (((a1[0] ^ a2[0]) & ~m[0]) |
++		((a1[1] ^ a2[1]) & ~m[1]) |
++		((a1[2] ^ a2[2]) & ~m[2]));
++}
++
+ /**
+  * eth_skb_pkt_type - Assign packet type if destination address does not match
+  * @skb: Assigned a packet type if address does not match @dev address
+@@ -553,6 +565,10 @@ static inline void eth_skb_pkt_type(stru
+ 		} else {
+ 			skb->pkt_type = PACKET_OTHERHOST;
+ 		}
++
++		if (eth_check_local_mask(eth->h_dest, dev->dev_addr,
++					 dev->local_addr_mask))
++			skb->gro_skip = 1;
+ 	}
+ }
+ 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-sd-rfb.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-sd-rfb.dts
index f0d4639..c38ea11 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-sd-rfb.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-sd-rfb.dts
@@ -33,15 +33,17 @@
 };
 
 &mmc0 {
-         pinctrl-names = "default", "state_uhs";
-         pinctrl-0 = <&mmc0_pins_default>;
-         pinctrl-1 = <&mmc0_pins_uhs>;
-         bus-width = <4>;
-         max-frequency = <52000000>;
-         cap-sd-highspeed;
-         vmmc-supply = <&reg_3p3v>;
-         vqmmc-supply = <&reg_3p3v>;
-         status = "okay";
+	assigned-clock-parents = <&topckgen CK_TOP_CB_NET2_D4>,
+				 <&topckgen CK_TOP_CB_NET2_D2>;
+	pinctrl-names = "default", "state_uhs";
+	pinctrl-0 = <&mmc0_pins_default>;
+	pinctrl-1 = <&mmc0_pins_uhs>;
+	bus-width = <4>;
+	max-frequency = <50000000>;
+	cap-sd-highspeed;
+	vmmc-supply = <&reg_3p3v>;
+	vqmmc-supply = <&reg_3p3v>;
+	status = "okay";
 };
 
 &eth {
@@ -196,6 +198,18 @@
 			function = "flash";
 			groups = "emmc_45";
 		};
+		conf-cmd-dat {
+			pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO",
+				"SPI0_CS", "SPI1_MISO";
+			input-enable;
+			drive-strength = <MTK_DRIVE_4mA>;
+			bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
+		};
+		conf-clk {
+			pins = "SPI1_CS";
+			drive-strength = <MTK_DRIVE_8mA>;
+			bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
+		};
 	};
 
 	mmc0_pins_uhs: mmc0-pins-uhs {
@@ -203,6 +217,18 @@
 			function = "flash";
 			groups = "emmc_45";
 		};
+		conf-cmd-dat {
+			pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO",
+				"SPI0_CS", "SPI1_MISO";
+			input-enable;
+			drive-strength = <MTK_DRIVE_4mA>;
+			bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
+		};
+		conf-clk {
+			pins = "SPI1_CS";
+			drive-strength = <MTK_DRIVE_8mA>;
+			bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
+		};
 	};
 };
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981.dtsi b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981.dtsi
index 91415e4..f119cde 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981.dtsi
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981.dtsi
@@ -475,20 +475,21 @@
 	};
 
 	mmc0: mmc@11230000 {
-                   compatible = "mediatek,mt7986-mmc",
-                                "mediatek,mt7981-mmc";
-                   reg = <0 0x11230000 0 0x1000>, <0 0x11c20000 0 0x1000>;
-                   interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
-                   clocks = <&topckgen CK_TOP_EMMC_208M>,
-                             <&topckgen CK_TOP_EMMC_400M>,
-                             <&infracfg_ao CK_INFRA_MSDC_CK>;
-                   assigned-clocks = <&topckgen CK_TOP_EMMC_208M_SEL>,
-                                      <&topckgen CK_TOP_EMMC_400M_SEL>;
-                   assigned-clock-parents = <&topckgen CK_TOP_CB_M_D2>,
-                                             <&topckgen CK_TOP_CB_NET2_D2>;
-                   clock-names = "source", "hclk", "source_cg";
-                   status = "disabled";
-        };
+		compatible = "mediatek,mt7986-mmc",
+			     "mediatek,mt7981-mmc";
+		reg = <0 0x11230000 0 0x1000>, <0 0x11c20000 0 0x1000>;
+		interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&topckgen CK_TOP_EMMC_208M>,
+			 <&topckgen CK_TOP_EMMC_400M>,
+			 <&infracfg_ao CK_INFRA_MSDC_66M_CK>,
+			 <&infracfg_ao CK_INFRA_MSDC_CK>;
+		assigned-clocks = <&topckgen CK_TOP_EMMC_208M_SEL>,
+				  <&topckgen CK_TOP_EMMC_400M_SEL>;
+		assigned-clock-parents = <&topckgen CK_TOP_CB_M_D2>,
+					 <&topckgen CK_TOP_CB_NET2_D2>;
+		clock-names = "source", "hclk", "bus_clk", "source_cg";
+		status = "disabled";
+	};
 
 	wbsys: wbsys@18000000 {
 		compatible = "mediatek,wbsys",
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 f38b2b7..b2b2fde 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
@@ -471,12 +471,6 @@
 		#reset-cells = <1>;
 	};
 
-	phyfw: phy-firmware@f000000 {
-		compatible = "mediatek,2p5gphy-fw";
-		reg = <0 0x0f100000 0 0x20000>,
-		      <0 0x0f0f0018 0 0x20>;
-	};
-
 	boottrap: boottrap@1001f6f0 {
 		compatible = "mediatek,boottrap";
 		reg = <0 0x1001f6f0 0 0x20>;
@@ -817,7 +811,7 @@
 	};
 
 	pio: pinctrl@1001f000 {
-		compatible = "mediatek,mt7988-pinctrl";
+		compatible = "mediatek,mt7988-pinctrl", "syscon";
 		reg = <0 0x1001f000 0 0x1000>,
 		      <0 0x11c10000 0 0x1000>,
 		      <0 0x11d00000 0 0x1000>,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts
index c558535..8398be6 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts
@@ -521,6 +521,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-emmc.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-emmc.dts
index f07aa6e..6a6bcb5 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-emmc.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-emmc.dts
@@ -623,6 +623,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-nand.dts
index f4511b9..caed45a 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-nand.dts
@@ -626,6 +626,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-sd.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-sd.dts
index ef6e6ec..6291a1b 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-sd.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-sd.dts
@@ -614,6 +614,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-emmc.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-emmc.dts
index 1fb43e5..11913fe 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-emmc.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-emmc.dts
@@ -363,6 +363,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sd.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sd.dts
index c41aa57..5bee34a 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sd.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sd.dts
@@ -354,6 +354,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sfp-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sfp-spim-nand.dts
index 8fbe9c5..87f30e7 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sfp-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sfp-spim-nand.dts
@@ -440,6 +440,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-snfi-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-snfi-nand.dts
index cde918b..af7eaec 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-snfi-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-snfi-nand.dts
@@ -392,6 +392,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand-CASAN.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand-CASAN.dts
index 834eb41..e4a586b 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand-CASAN.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand-CASAN.dts
@@ -516,6 +516,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand.dts
index 224583a..6468c9d 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand.dts
@@ -516,6 +516,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
index 24c7799..e0f1326 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
@@ -384,6 +384,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-e2p5g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-e2p5g-spim-nand.dts
index 4562413..b9d8ac7 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-e2p5g-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-e2p5g-spim-nand.dts
@@ -423,6 +423,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-i2p5g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-i2p5g-spim-nand.dts
index 2803b7b..08389ef 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-i2p5g-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-i2p5g-spim-nand.dts
@@ -300,7 +300,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -321,7 +321,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -386,6 +386,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-mxl86252-10g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-mxl86252-10g-spim-nand.dts
new file mode 100644
index 0000000..e0b931e
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-mxl86252-10g-spim-nand.dts
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (C) 2021 MediaTek Inc.
+ * Author: Sam.Shih <sam.shih@mediatek.com>
+ */
+
+/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
+#include "mt7988.dtsi"
+
+/ {
+	model = "MediaTek MT7988A DSA 10G SPIM-NAND RFB";
+	compatible = "mediatek,mt7988a-dsa-mxl86252-10g-spim-snand",
+		     /* Reserve this for DVFS if creating new dts */
+		     "mediatek,mt7988";
+
+	chosen {
+		bootargs = "console=ttyS0,115200n1 loglevel=8  \
+			    earlycon=uart8250,mmio32,0x11000000 \
+			    pci=pcie_bus_perf";
+	};
+
+	memory {
+		reg = <0 0x40000000 0 0x10000000>;
+	};
+
+	nmbm_spim_nand {
+		compatible = "generic,nmbm";
+
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		lower-mtd-device = <&spi_nand>;
+		forced-create;
+
+		partitions {
+			compatible = "fixed-partitions";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			partition@0 {
+				label = "BL2";
+				reg = <0x00000 0x0100000>;
+				read-only;
+			};
+
+			partition@100000 {
+				label = "u-boot-env";
+				reg = <0x0100000 0x0080000>;
+			};
+
+			factory: partition@180000 {
+				label = "Factory";
+				reg = <0x180000 0x0400000>;
+			};
+
+			partition@580000 {
+				label = "FIP";
+				reg = <0x580000 0x0200000>;
+			};
+
+			partition@780000 {
+				label = "ubi";
+				reg = <0x780000 0x7080000>;
+			};
+		};
+	};
+
+	wsys_adie: wsys_adie@0 {
+	// fpga cases need to manual change adie_id / sku_type for dvt only
+		compatible = "mediatek,rebb-mt7988-adie";
+		adie_id = <7976>;
+		sku_type = <3000>;
+	};
+
+	sound_wm8960 {
+		compatible = "mediatek,mt7986-wm8960-sound";
+		audio-routing = "Headphone", "HP_L",
+				"Headphone", "HP_R",
+				"LINPUT1", "AMIC",
+				"RINPUT1", "AMIC";
+
+		status = "disabled";
+
+		platform {
+			sound-dai = <&afe>;
+		};
+
+		codec {
+			sound-dai = <&wm8960>;
+		};
+	};
+
+	sound_si3218x {
+		compatible = "mediatek,mt7986-si3218x-sound";
+		status = "disabled";
+
+		platform {
+			sound-dai = <&afe>;
+		};
+
+		codec {
+			sound-dai = <&proslic_spi>;
+		};
+	};
+};
+
+&fan {
+	pwms = <&pwm 0 50000>;
+	status = "okay";
+};
+
+&afe {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcm_pins>;
+	status = "okay";
+};
+
+&pwm {
+	status = "okay";
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_pins>;
+	status = "okay";
+};
+
+&i2c0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c0_pins>;
+	status = "okay";
+
+	rt5190a_64: rt5190a@64 {
+		compatible = "richtek,rt5190a";
+		reg = <0x64>;
+		/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
+		vin2-supply = <&rt5190_buck1>;
+		vin3-supply = <&rt5190_buck1>;
+		vin4-supply = <&rt5190_buck1>;
+
+		regulators {
+			rt5190_buck1: buck1 {
+				regulator-name = "rt5190a-buck1";
+				regulator-min-microvolt = <5090000>;
+				regulator-max-microvolt = <5090000>;
+				regulator-allowed-modes =
+				<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
+				regulator-boot-on;
+			};
+			buck2 {
+				regulator-name = "vcore";
+				regulator-min-microvolt = <600000>;
+				regulator-max-microvolt = <1400000>;
+				regulator-boot-on;
+			};
+			buck3 {
+				regulator-name = "proc";
+				regulator-min-microvolt = <600000>;
+				regulator-max-microvolt = <1400000>;
+				regulator-boot-on;
+			};
+			buck4 {
+				regulator-name = "rt5190a-buck4";
+				regulator-min-microvolt = <850000>;
+				regulator-max-microvolt = <850000>;
+				regulator-allowed-modes =
+				<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
+				regulator-boot-on;
+			};
+			ldo {
+				regulator-name = "rt5190a-ldo";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+				regulator-boot-on;
+			};
+		};
+	};
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	status = "okay";
+
+	wm8960: wm8960@1a {
+		compatible = "wlf,wm8960";
+		reg = <0x1a>;
+	};
+
+	zts8032: zts8032@77 {
+		compatible = "zilltek,zts8032";
+		reg = <0x76>;
+	};
+
+	dps368: dps368@77 {
+		compatible = "infineon,dps310";
+		reg = <0x77>;
+	};
+
+	rtq6056: rtq6056@40 {
+		compatible = "richtek,rtq6056";
+		reg = <0x40>;
+		shunt-resistor-micro-ohms = <10000>;
+		#io-channel-cells = <1>;
+	};
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_flash_pins>;
+	status = "okay";
+
+	spi_nand: spi_nand@0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "spi-nand";
+		spi-cal-enable;
+		spi-cal-mode = "read-data";
+		spi-cal-datalen = <7>;
+		spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>;
+		spi-cal-addrlen = <5>;
+		spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>;
+		reg = <0>;
+		spi-max-frequency = <52000000>;
+		spi-tx-bus-width = <4>;
+		spi-rx-bus-width = <4>;
+	};
+};
+
+&spi1 {
+	pinctrl-names = "default";
+	/* pin shared with snfi */
+	pinctrl-0 = <&spic_pins>;
+	status = "disabled";
+
+	proslic_spi: proslic_spi@0 {
+		compatible = "silabs,proslic_spi";
+		reg = <0>;
+		spi-max-frequency = <10000000>;
+		spi-cpha = <1>;
+		spi-cpol = <1>;
+		channel_count = <1>;
+		debug_level = <4>;       /* 1 = TRC, 2 = DBG, 4 = ERR */
+		reset_gpio = <&pio 54 0>;
+		ig,enable-spi = <1>;     /* 1: Enable, 0: Disable */
+	};
+};
+
+&pcie0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcie0_pins>;
+	wifi-reset-gpios = <&pio 7 GPIO_ACTIVE_HIGH>;
+	wifi-reset-msleep = <100>;
+	status = "okay";
+};
+
+&pcie1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcie1_pins>;
+	status = "okay";
+};
+
+&pcie2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcie2_pins>;
+	status = "disabled";
+};
+
+&pcie3 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcie3_pins>;
+	status = "okay";
+};
+
+&pio {
+	mdio0_pins: mdio0-pins {
+		mux {
+			function = "mdio";
+			groups = "mdc_mdio0";
+		};
+
+		conf {
+			groups = "mdc_mdio0";
+			drive-strength = <MTK_DRIVE_10mA>;
+		};
+	};
+
+	gbe0_led0_pins: gbe0-pins {
+		mux {
+			function = "led";
+			groups = "gbe0_led0";
+		};
+	};
+
+	gbe1_led0_pins: gbe1-pins {
+		mux {
+			function = "led";
+			groups = "gbe1_led0";
+		};
+	};
+
+	gbe2_led0_pins: gbe2-pins {
+		mux {
+			function = "led";
+			groups = "gbe2_led0";
+		};
+	};
+
+	gbe3_led0_pins: gbe3-pins {
+		mux {
+			function = "led";
+			groups = "gbe3_led0";
+		};
+	};
+
+	i2c0_pins: i2c0-pins-g0 {
+		mux {
+			function = "i2c";
+			groups = "i2c0_1";
+		};
+	};
+
+	pcie0_pins: pcie0-pins {
+		mux {
+			function = "pcie";
+			groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0";
+		};
+	};
+
+	pcie1_pins: pcie1-pins {
+		mux {
+			function = "pcie";
+			groups = "pcie_2l_1_pereset", "pcie_clk_req_n1",
+				 "pcie_wake_n1_0";
+		};
+	};
+
+	pcie2_pins: pcie2-pins {
+		mux {
+			function = "pcie";
+			groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0",
+				 "pcie_wake_n2_0";
+		};
+	};
+
+	pcie3_pins: pcie3-pins {
+		mux {
+			function = "pcie";
+			groups = "pcie_1l_1_pereset", "pcie_clk_req_n3",
+				 "pcie_wake_n3_0";
+		};
+	};
+
+	spi0_flash_pins: spi0-pins {
+		mux {
+			function = "spi";
+			groups = "spi0", "spi0_wp_hold";
+		};
+	};
+
+	spic_pins: spi1-pins {
+		mux {
+			function = "spi";
+			groups = "spi1";
+		};
+	};
+
+	i2c1_pins: i2c1-pins {
+		mux {
+			function = "i2c";
+			groups = "i2c1_0";
+		};
+	};
+
+	i2s_pins: i2s-pins {
+		mux {
+			function = "audio";
+			groups = "i2s";
+		};
+	};
+
+	pcm_pins: pcm-pins {
+		mux {
+			function = "audio";
+			groups = "pcm";
+		};
+	};
+
+	uart1_pins: uart1-pins {
+		mux {
+			function = "uart";
+			groups = "uart1_2";
+		};
+	};
+};
+
+&watchdog {
+	status = "disabled";
+};
+
+&eth {
+	pinctrl-names = "default";
+	pinctrl-0 = <&mdio0_pins>;
+	status = "okay";
+
+	gmac0: mac@0 {
+		compatible = "mediatek,eth-mac";
+		reg = <0>;
+		mtd-mac-address = <&factory 0xFFFF4>;
+		mac-type = "xgdm";
+		phy-mode = "10gbase-kr";
+
+		fixed-link {
+			speed = <10000>;
+			full-duplex;
+			pause;
+		};
+	};
+
+	gmac1: mac@1 {
+		compatible = "mediatek,eth-mac";
+		reg = <1>;
+		mtd-mac-address = <&factory 0xFFFFA>;
+		mac-type = "xgdm";
+		phy-mode = "usxgmii";
+		phy-handle = <&phy0>;
+	};
+
+	gmac2: mac@2 {
+		compatible = "mediatek,eth-mac";
+		reg = <2>;
+		/* mtd-mac-address = <&factory 0xFFFEE>; */
+		mac-type = "xgdm";
+		phy-mode = "10gbase-kr";
+
+		fixed-link {
+			speed = <10000>;
+			full-duplex;
+			pause;
+		};
+	};
+
+	mdio: mdio-bus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clock-frequency = <10500000>;
+
+		phy0: ethernet-phy@0 {
+			reg = <0>;
+			compatible = "ethernet-phy-ieee802.3-c45";
+			reset-gpios = <&pio 72 1>;
+			reset-assert-us = <100000>;
+			reset-deassert-us = <221000>;
+			mdi-reversal = /bits/ 16 <1>;
+		};
+
+		switch16: switch@16 {
+			compatible = "mxl,86252";
+			reg = <16>;
+			dsa,member = <0 0>;
+
+			ports {
+				port0: port@0 {
+					reg = <0>;
+					label = "mxl_lan0";
+					phy-handle = <&switchphy0>;
+					phy-mode = "internal";
+					status = "okay";
+				};
+
+				port1: port@1 {
+					reg = <1>;
+					label = "mxl_lan1";
+					phy-handle = <&switchphy1>;
+					phy-mode = "internal";
+					status = "okay";
+				};
+
+				port2: port@2 {
+					reg = <2>;
+					label = "mxl_lan2";
+					phy-handle = <&switchphy2>;
+					phy-mode = "internal";
+					status = "okay";
+				};
+
+				port3: port@3 {
+					reg = <3>;
+					label = "mxl_lan3";
+					phy-handle = <&switchphy3>;
+					phy-mode = "internal";
+					status = "okay";
+				};
+
+				port4: port@4 {
+					reg = <4>;
+					label = "mxl_lan4";
+					phy-handle = <&switchphy4>;
+					phy-mode = "internal";
+					status = "okay";
+				};
+
+				port8: port@8 {
+					reg = <8>;
+					label = "cpu";
+					phy-mode = "usxgmii";
+					ethernet = <&gmac2>;
+					dsa-tag-protocol = "mxl862_8021q";
+
+					fixed-link {
+						speed = <10000>;
+						full-duplex;
+					};
+				};
+			};
+
+			mdio {
+				switchphy0:switchphy@0 {
+					reg= <0>;
+				};
+				switchphy1:switchphy@1 {
+					reg= <1>;
+				};
+				switchphy2:switchphy@2 {
+					reg= <2>;
+				};
+				switchphy3:switchphy@3 {
+					reg= <3>;
+				};
+				switchphy4:switchphy@4 {
+					reg= <4>;
+				};
+			};
+		};
+
+		switch@31 {
+			compatible = "mediatek,mt7988";
+			reg = <31>;
+			dsa,member = <1 0>;
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					reg = <0>;
+					label = "lan0";
+					phy-mode = "gmii";
+					phy-handle = <&sphy0>;
+				};
+
+				port@1 {
+					reg = <1>;
+					label = "lan1";
+					phy-mode = "gmii";
+					phy-handle = <&sphy1>;
+				};
+
+				port@2 {
+					reg = <2>;
+					label = "lan2";
+					phy-mode = "gmii";
+					phy-handle = <&sphy2>;
+				};
+
+				port@3 {
+					reg = <3>;
+					label = "lan3";
+					phy-mode = "gmii";
+					phy-handle = <&sphy3>;
+				};
+
+				port@6 {
+					reg = <6>;
+					label = "cpu";
+					ethernet = <&gmac0>;
+					phy-mode = "10gbase-kr";
+
+					fixed-link {
+						speed = <10000>;
+						full-duplex;
+						pause;
+					};
+				};
+			};
+
+			mdio {
+				compatible = "mediatek,dsa-slave-mdio";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				mediatek,pio = <&pio>;
+
+				sphy0: switch_phy0@0 {
+					compatible = "ethernet-phy-id03a2.9481";
+					reg = <0>;
+					pinctrl-names = "gbe-led";
+					pinctrl-0 = <&gbe0_led0_pins>;
+					nvmem-cells = <&phy_calibration_p0>;
+					nvmem-cell-names = "phy-cal-data";
+				};
+
+				sphy1: switch_phy1@1 {
+					compatible = "ethernet-phy-id03a2.9481";
+					reg = <1>;
+					pinctrl-names = "gbe-led";
+					pinctrl-0 = <&gbe1_led0_pins>;
+					nvmem-cells = <&phy_calibration_p1>;
+					nvmem-cell-names = "phy-cal-data";
+				};
+
+				sphy2: switch_phy2@2 {
+					compatible = "ethernet-phy-id03a2.9481";
+					reg = <2>;
+					pinctrl-names = "gbe-led";
+					pinctrl-0 = <&gbe2_led0_pins>;
+					nvmem-cells = <&phy_calibration_p2>;
+					nvmem-cell-names = "phy-cal-data";
+				};
+
+				sphy3: switch_phy3@3 {
+					compatible = "ethernet-phy-id03a2.9481";
+					reg = <3>;
+					pinctrl-names = "gbe-led";
+					pinctrl-0 = <&gbe3_led0_pins>;
+					nvmem-cells = <&phy_calibration_p3>;
+					nvmem-cell-names = "phy-cal-data";
+				};
+			};
+		};
+	};
+};
+
+&hnat {
+	mtketh-wan = "eth1";
+	mtketh-lan = "lan";
+	mtketh-lan2 = "mxl";
+	mtketh-max-gmac = <3>;
+	mtketh-ppe-num = <3>;
+	status = "okay";
+};
+
+&slot0 {
+	mt7996@0,0 {
+		reg = <0x0000 0 0 0 0>;
+		device_type = "pci";
+		mediatek,mtd-eeprom = <&factory 0x0>;
+	};
+};
+
+&slot1 {
+	mt7992@0,0 {
+		reg = <0x0000 0 0 0 0>;
+		device_type = "pci";
+		mediatek,mtd-eeprom = <&factory 0x0>;
+	};
+};
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-sfp-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-sfp-spim-nand.dts
index a922d57..db2f9ec 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-sfp-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-sfp-spim-nand.dts
@@ -404,6 +404,7 @@
 	mdio1: mdio-bus {
 		#address-cells = <1>;
 		#size-cells = <0>;
+		mediatek,pio = <&pio>;
 
 		gsw_phy0: ethernet-phy@0 {
 			compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand-4pcie.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand-4pcie.dts
index 4ee16fd..e534f9b 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand-4pcie.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand-4pcie.dts
@@ -409,6 +409,7 @@
 	mdio1: mdio-bus {
 		#address-cells = <1>;
 		#size-cells = <0>;
+		mediatek,pio = <&pio>;
 
 		gsw_phy0: ethernet-phy@0 {
 			compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand.dts
index b084812..f7252ec 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand.dts
@@ -441,6 +441,7 @@
 	mdio1: mdio-bus {
 		#address-cells = <1>;
 		#size-cells = <0>;
+		mediatek,pio = <&pio>;
 
 		gsw_phy0: ethernet-phy@0 {
 			compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-emmc.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-emmc.dts
index 2f32e45..9ffc06d 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-emmc.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-emmc.dts
@@ -286,7 +286,7 @@
 		compatible = "mediatek,eth-mac";
 		reg = <1>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -308,7 +308,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -373,6 +373,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-sd.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-sd.dts
index d7e41c5..5182eb7 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-sd.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-sd.dts
@@ -277,7 +277,7 @@
 		compatible = "mediatek,eth-mac";
 		reg = <1>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -299,7 +299,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -364,6 +364,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-sfp-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-sfp-spim-nand.dts
index 356ff3a..dfd10a9 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-sfp-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-sfp-spim-nand.dts
@@ -351,7 +351,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -374,7 +374,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		switch@0 {
@@ -430,6 +430,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-snfi-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-snfi-nand.dts
index bd4326f..d9d1ab0 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-snfi-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-snfi-nand.dts
@@ -314,7 +314,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -337,7 +337,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -402,6 +402,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand-CASAN.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand-CASAN.dts
index 42efe94..cdb1870 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand-CASAN.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand-CASAN.dts
@@ -408,7 +408,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -431,7 +431,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -496,6 +496,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts
index 3e2c564..c4f33a3 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nand.dts
@@ -408,7 +408,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -431,7 +431,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -496,6 +496,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
index f72c5d3..c486d62 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
@@ -306,7 +306,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -329,7 +329,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -394,6 +394,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-wan-emmc.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-wan-emmc.dts
index 71c2e3d..6bdd410 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-wan-emmc.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-wan-emmc.dts
@@ -286,7 +286,7 @@
 		compatible = "mediatek,eth-mac";
 		reg = <1>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -308,7 +308,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -373,6 +373,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-wan-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-wan-spim-nand.dts
index 7a3d9a6..a0d5b61 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-wan-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-wan-spim-nand.dts
@@ -408,7 +408,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -431,7 +431,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -496,6 +496,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-e2p5g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-e2p5g-spim-nand.dts
index 78847ef..c64c3ce 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-e2p5g-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-dsa-e2p5g-spim-nand.dts
@@ -326,7 +326,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -348,7 +348,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy5: phy@5 {
@@ -412,6 +412,7 @@
 				compatible = "mediatek,dsa-slave-mdio";
 				#address-cells = <1>;
 				#size-cells = <0>;
+				mediatek,pio = <&pio>;
 
 				sphy0: switch_phy0@0 {
 					compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-gsw-10g-sfp-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-gsw-10g-sfp-spim-nand.dts
index 18bbfcc..ff99edc 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-gsw-10g-sfp-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-gsw-10g-sfp-spim-nand.dts
@@ -340,7 +340,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -363,7 +363,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 	};
@@ -400,6 +400,7 @@
 	mdio1: mdio-bus {
 		#address-cells = <1>;
 		#size-cells = <0>;
+		mediatek,pio = <&pio>;
 
 		gsw_phy0: ethernet-phy@0 {
 			compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-gsw-10g-spim-nand.dts b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-gsw-10g-spim-nand.dts
index 1f5ea1f..36e01ae 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-gsw-10g-spim-nand.dts
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988d-gsw-10g-spim-nand.dts
@@ -370,7 +370,7 @@
 		reg = <1>;
 		mtd-mac-address = <&factory 0xFFFFA>;
 		mac-type = "xgdm";
-		phy-mode = "xgmii";
+		phy-mode = "internal";
 		phy-handle = <&phy0>;
 	};
 
@@ -393,7 +393,7 @@
 			pinctrl-0 = <&i2p5gbe_led0_pins>;
 			reg = <15>;
 			compatible = "ethernet-phy-ieee802.3-c45";
-			phy-mode = "xgmii";
+			phy-mode = "internal";
 		};
 
 		phy1: ethernet-phy@8 {
@@ -438,6 +438,7 @@
 	mdio1: mdio-bus {
 		#address-cells = <1>;
 		#size-cells = <0>;
+		mediatek,pio = <&pio>;
 
 		gsw_phy0: ethernet-phy@0 {
 			compatible = "ethernet-phy-id03a2.9481";
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/misc/mediatek/reset-boot-count/reset-boot-count.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/misc/mediatek/reset-boot-count/reset-boot-count.c
index 2b82d89..e199a9f 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/misc/mediatek/reset-boot-count/reset-boot-count.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/misc/mediatek/reset-boot-count/reset-boot-count.c
@@ -17,8 +17,8 @@
 

 #define RBC "reset_boot_count"

 

-#define MTK_SIP_READ_NONRST_REG			0xC2000550

-#define MTK_SIP_WRITE_NONRST_REG		0xC2000551

+#define MTK_SIP_READ_NONRST_REG			0xC2000570

+#define MTK_SIP_WRITE_NONRST_REG		0xC2000571

 

 static struct proc_dir_entry *rbc_entry;

 

diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/Kconfig b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/Kconfig
new file mode 100644
index 0000000..5b4e051
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config NET_DSA_MXL862
+	tristate "MaxLinear MxL862xx"
+	depends on NET_DSA
+	select NET_DSA_TAG_MXL862
+	help
+	  This enables support for the MaxLinear MxL862xx switch family.
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/Makefile b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/Makefile
new file mode 100644
index 0000000..62473cd
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o
+mxl862xx_dsa-y := mxl862xx.o host_api/mxl862xx_api.o host_api/mxl862xx_host_api_impl.o host_api/mxl862xx_mdio_relay.o
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c
new file mode 100644
index 0000000..92a74df
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_api.c - dsa driver for Maxlinear mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include "mxl862xx_host_api_impl.h"
+#include "mxl862xx_api.h"
+#include "mxl862xx_mmd_apis.h"
+
+int sys_misc_fw_version(const mxl862xx_device_t *dev,
+			struct sys_fw_image_version *sys_img_ver)
+{
+	return mxl862xx_api_wrap(dev, SYS_MISC_FW_VERSION, sys_img_ver,
+			    sizeof(*sys_img_ver), 0, sizeof(*sys_img_ver));
+}
+
+int mxl862xx_register_mod(const mxl862xx_device_t *dev, mxl862xx_register_mod_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_COMMON_REGISTERMOD, parm, sizeof(*parm), 0,
+			    0);
+}
+
+int mxl862xx_port_link_cfg_set(const mxl862xx_device_t *dev, mxl862xx_port_link_cfg_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTLINKCFGSET, parm, sizeof(*parm),
+			    MXL862XX_COMMON_PORTLINKCFGGET, 0);
+}
+
+int mxl862xx_port_cfg_get(const mxl862xx_device_t *dev, mxl862xx_port_cfg_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTCFGGET, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_port_cfg_set(const mxl862xx_device_t *dev, mxl862xx_port_cfg_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTCFGSET, parm, sizeof(*parm),
+			    MXL862XX_COMMON_PORTCFGGET, 0);
+}
+
+int mxl862xx_bridge_alloc(const mxl862xx_device_t *dev, mxl862xx_bridge_alloc_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_ALLOC, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+int mxl862xx_bridge_free(const mxl862xx_device_t *dev, mxl862xx_bridge_alloc_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_FREE, parm, sizeof(*parm), 0, 0);
+}
+
+int mxl862xx_bridge_config_set(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_CONFIGSET, parm, sizeof(*parm),
+			    MXL862XX_BRIDGE_CONFIGGET, 0);
+}
+
+int mxl862xx_bridge_config_get(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_CONFIGGET, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_bridge_port_alloc(const mxl862xx_device_t *dev, mxl862xx_bridge_port_alloc_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_ALLOC, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_bridge_port_free(const mxl862xx_device_t *dev, mxl862xx_bridge_port_alloc_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_FREE, parm, sizeof(*parm), 0, 0);
+}
+
+int mxl862xx_bridge_port_config_set(const mxl862xx_device_t *dev,
+				    mxl862xx_bridge_port_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_CONFIGSET, parm, sizeof(*parm),
+			    MXL862XX_BRIDGEPORT_CONFIGGET, 0);
+}
+
+int mxl862xx_bridge_port_config_get(const mxl862xx_device_t *dev,
+				    mxl862xx_bridge_port_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_CONFIGGET, parm, sizeof(*parm),
+			    0, sizeof(*parm));
+}
+
+int mxl862xx_debug_rmon_port_get(const mxl862xx_device_t *dev,
+				 mxl862xx_debug_rmon_port_cnt_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_DEBUG_RMON_PORT_GET, parm, sizeof(*parm),
+			    0, sizeof(*parm));
+}
+
+int mxl862xx_mac_table_clear(const mxl862xx_device_t *dev)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLECLEAR, NULL, 0, 0, 0);
+}
+
+int mxl862xx_mac_table_clear_cond(const mxl862xx_device_t *dev,
+				  mxl862xx_mac_table_clear_cond_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLECLEARCOND, parm, sizeof(*parm), 0,
+			    0);
+}
+
+int mxl862xx_mac_table_entry_read(const mxl862xx_device_t *dev,
+				  mxl862xx_mac_table_read_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYREAD, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_mac_table_entry_add(const mxl862xx_device_t *dev,
+				 mxl862xx_mac_table_add_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYADD, parm, sizeof(*parm), 0,
+			    0);
+}
+
+int mxl862xx_mac_table_entry_remove(const mxl862xx_device_t *dev,
+				    mxl862xx_mac_table_remove_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYREMOVE, parm, sizeof(*parm),
+			    0, 0);
+}
+
+int mxl862xx_stp_port_cfg_set(const mxl862xx_device_t *dev, mxl862xx_stp_port_cfg_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_STP_PORTCFGSET, parm, sizeof(*parm),
+			    MXL862XX_STP_PORTCFGGET, 0);
+}
+
+int mxl862xx_ss_sp_tag_get(const mxl862xx_device_t *dev, mxl862xx_ss_sp_tag_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_SS_SPTAG_GET, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_ss_sp_tag_set(const mxl862xx_device_t *dev, mxl862xx_ss_sp_tag_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_SS_SPTAG_SET, parm, sizeof(*parm),
+			    MXL862XX_SS_SPTAG_GET, sizeof(*parm));
+}
+
+int mxl862xx_ctp_port_config_get(const mxl862xx_device_t *dev,
+				 mxl862xx_ctp_port_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTCONFIGGET, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_ctp_port_config_set(const mxl862xx_device_t *dev,
+				 mxl862xx_ctp_port_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTCONFIGSET, parm, sizeof(*parm),
+			    MXL862XX_CTP_PORTCONFIGGET, 0);
+}
+
+int mxl862xx_ctp_port_assignment_set(const mxl862xx_device_t *dev,
+				     mxl862xx_ctp_port_assignment_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTASSIGNMENTSET, parm, sizeof(*parm),
+			    MXL862XX_CTP_PORTASSIGNMENTGET, 0);
+}
+
+int mxl862xx_ctp_port_assignment_get(const mxl862xx_device_t *dev,
+				     mxl862xx_ctp_port_assignment_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTASSIGNMENTGET, parm, sizeof(*parm),
+			    0, sizeof(*parm));
+}
+
+int mxl862xx_monitor_port_cfg_get(const mxl862xx_device_t *dev,
+				  mxl862xx_monitor_port_cfg_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_COMMON_MONITORPORTCFGGET, parm,
+			    sizeof(*parm), 0, sizeof(*parm));
+}
+
+int mxl862xx_monitor_port_cfg_set(const mxl862xx_device_t *dev,
+				  mxl862xx_monitor_port_cfg_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_COMMON_MONITORPORTCFGSET, parm,
+			    sizeof(*parm), MXL862XX_COMMON_MONITORPORTCFGGET, 0);
+}
+
+int mxl862xx_extended_vlan_alloc(const mxl862xx_device_t *dev,
+				 mxl862xx_extendedvlan_alloc_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_ALLOC, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_extended_vlan_set(const mxl862xx_device_t *dev,
+			       mxl862xx_extendedvlan_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_SET, parm, sizeof(*parm),
+			    MXL862XX_EXTENDEDVLAN_GET, 0);
+}
+
+int mxl862xx_extended_vlan_get(const mxl862xx_device_t *dev,
+			       mxl862xx_extendedvlan_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_GET, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_extended_vlan_free(const mxl862xx_device_t *dev,
+				mxl862xx_extendedvlan_alloc_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_FREE, parm, sizeof(*parm), 0,
+			    0);
+}
+
+int mxl862xx_vlan_filter_alloc(const mxl862xx_device_t *dev,
+			       mxl862xx_vlanfilter_alloc_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_ALLOC, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_vlan_filter_set(const mxl862xx_device_t *dev,
+			     mxl862xx_vlanfilter_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_SET, parm, sizeof(*parm),
+			    MXL862XX_VLANFILTER_GET, 0);
+}
+
+int mxl862xx_vlan_filter_get(const mxl862xx_device_t *dev,
+			     mxl862xx_vlanfilter_config_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_GET, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_vlan_filter_free(const mxl862xx_device_t *dev,
+			      mxl862xx_vlanfilter_alloc_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_FREE, parm, sizeof(*parm), 0,
+			    0);
+}
+
+int mxl862xx_cfg_get(const mxl862xx_device_t *dev, mxl862xx_cfg_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_COMMON_CFGGET, parm, sizeof(*parm), 0,
+			    sizeof(*parm));
+}
+
+int mxl862xx_cfg_set(const mxl862xx_device_t *dev, mxl862xx_cfg_t *parm)
+{
+	return mxl862xx_api_wrap(dev, MXL862XX_COMMON_CFGSET, parm, sizeof(*parm),
+				MXL862XX_COMMON_CFGGET, 0);
+}
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h
new file mode 100644
index 0000000..d222952
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h
@@ -0,0 +1,1826 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_api.h - DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_API_H_
+#define _MXL862XX_API_H_
+
+#include "mxl862xx_types.h"
+#include "mxl862xx_rmon.h"
+
+#define MxL862XX_SDMA_PCTRLp(p) (0xBC0 + ((p) * 0x6))
+#define MXL862XX_SDMA_PCTRL_EN BIT(0) /* SDMA Port Enable */
+
+/* Ethernet Switch Fetch DMA Port Control Register */
+#define MxL862XX_FDMA_PCTRLp(p) (0xA80 + ((p) * 0x6))
+#define MXL862XX_FDMA_PCTRL_EN BIT(0) /* FDMA Port Enable */
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+//#define SCK_FW_1_0_41
+
+/** \brief Spanning Tree Protocol port states.
+    Used by \ref MXL862XX_STP_port_cfg_t. */
+typedef enum {
+	/** Forwarding state. The port is allowed to transmit and receive
+	    all packets. Address Learning is allowed. */
+	MXL862XX_STP_PORT_STATE_FORWARD = 0,
+	/** Disabled/Discarding state. The port entity will not transmit
+	    and receive any packets. Learning is disabled in this state. */
+	MXL862XX_STP_PORT_STATE_DISABLE = 1,
+	/** Learning state. The port entity will only transmit and receive
+	    Spanning Tree Protocol packets (BPDU). All other packets are discarded.
+	    MAC table address learning is enabled for all good frames. */
+	MXL862XX_STP_PORT_STATE_LEARNING = 2,
+	/** Blocking/Listening. Only the Spanning Tree Protocol packets will
+	    be received and transmitted. All other packets are discarded by
+	    the port entity. MAC table address learning is disabled in this
+	    state. */
+	MXL862XX_STP_PORT_STATE_BLOCKING = 3
+} mxl862xx_stp_port_state_t;
+
+/** \brief Bridge Port Allocation.
+    Used by \ref MXL862XX_BRIDGE_PORT_ALLOC and \ref MXL862XX_BRIDGE_PORT_FREE. */
+typedef struct {
+	/** If \ref MXL862XX_BRIDGE_PORT_ALLOC is successful, a valid ID will be returned
+	    in this field. Otherwise, \ref INVALID_HANDLE is returned in this field.
+	    For \ref MXL862XX_BRIDGE_PORT_FREE, this field should be valid ID returned by
+	    \ref MXL862XX_BRIDGE_PORT_ALLOC. ID 0 is special for CPU port in PRX300
+	    by mapping to CTP 0 (Logical Port 0 with Sub-interface ID 0), and
+	    pre-alloced during initialization. */
+	u16 bridge_port_id;
+} mxl862xx_bridge_port_alloc_t;
+
+/** \brief Color Remarking Mode
+    Used by \ref MXL862XX_CTP_port_config_t. */
+typedef enum {
+	/** values from last process stage */
+	MXL862XX_REMARKING_NONE = 0,
+	/** DEI mark mode */
+	MXL862XX_REMARKING_DEI = 2,
+	/** PCP 8P0D mark mode */
+	MXL862XX_REMARKING_PCP_8P0D = 3,
+	/** PCP 7P1D mark mode */
+	MXL862XX_REMARKING_PCP_7P1D = 4,
+	/** PCP 6P2D mark mode */
+	MXL862XX_REMARKING_PCP_6P2D = 5,
+	/** PCP 5P3D mark mode */
+	MXL862XX_REMARKING_PCP_5P3D = 6,
+	/** DSCP AF class */
+	MXL862XX_REMARKING_DSCP_AF = 7
+} mxl862xx_color_remarking_mode_t;
+
+/** \brief Meters for various egress traffic type.
+    Used by \ref MXL862XX_BRIDGE_port_config_t. */
+typedef enum {
+	/** Index of broadcast traffic meter */
+	MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST = 0,
+	/** Index of known multicast traffic meter */
+	MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST = 1,
+	/** Index of unknown multicast IP traffic meter */
+	MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP = 2,
+	/** Index of unknown multicast non-IP traffic meter */
+	MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP = 3,
+	/** Index of unknown unicast traffic meter */
+	MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC = 4,
+	/** Index of traffic meter for other types */
+	MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS = 5,
+	/** Number of index */
+	MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX = 6
+} mxl862xx_bridge_port_egress_meter_t;
+
+/** \brief P-mapper Mapping Mode
+    Used by \ref MXL862XX_CTP_port_config_t. */
+typedef enum {
+	/** Use PCP for VLAN tagged packets to derive sub interface ID group.
+
+	    \remarks
+	    P-mapper table entry 1-8. */
+	MXL862XX_PMAPPER_MAPPING_PCP = 0,
+	/** Use LAG Index for Pmapper access (regardless of IP and VLAN packet)to
+	    derive sub interface ID group.
+
+	    \remarks
+	    P-mapper table entry 9-72. */
+	MXL862XX_PMAPPER_MAPPING_LAG = 1,
+	/** Use DSCP for VLAN tagged IP packets to derive sub interface ID group.
+
+	    \remarks
+	    P-mapper table entry 9-72. */
+	MXL862XX_PMAPPER_MAPPING_DSCP = 2,
+} mxl862xx_pmapper_mapping_mode_t;
+
+/** \brief P-mapper Configuration
+    Used by \ref MXL862XX_CTP_port_config_t, MXL862XX_BRIDGE_port_config_t.
+    In case of LAG, it is user's responsibility to provide the mapped entries
+    in given P-mapper table. In other modes the entries are auto mapped from
+    input packet. */
+typedef struct {
+	/** Index of P-mapper <0-31>. */
+	u16 pmapper_id;
+
+	/** Sub interface ID group.
+
+	    \remarks
+	    Entry 0 is for non-IP and non-VLAN tagged packets. Entries 1-8 are PCP
+	    mapping entries for VLAN tagged packets with \ref MXL862XX_PMAPPER_MAPPING_PCP
+	    selected. Entries 9-72 are DSCP or LAG mapping entries for IP packets without
+	    VLAN tag or VLAN tagged packets with \ref MXL862XX_PMAPPER_MAPPING_DSCP or
+	    MXL862XX_PMAPPER_MAPPING_LAG selected. When LAG is selected this 8bit field is
+	    decoded as Destination sub-interface ID group field bits 3:0, Destination
+	    logical port ID field bits 7:4 */
+	u8 dest_sub_if_id_group[73];
+} mxl862xx_pmapper_t;
+
+/** \brief Bridge Port configuration mask.
+    Used by \ref MXL862XX_BRIDGE_port_config_t. */
+typedef enum {
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_bridge_id */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID = 0x00000001,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_extended_vlan_enable and
+	    \ref MXL862XX_BRIDGE_port_config_t::n_ingress_extended_vlan_block_id */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN = 0x00000002,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_extended_vlan_enable and
+	    \ref MXL862XX_BRIDGE_port_config_t::n_egress_extended_vlan_block_id */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN = 0x00000004,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::e_ingress_marking_mode */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING = 0x00000008,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::e_egress_remarking_mode */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING = 0x00000010,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_metering_enable and
+	    \ref MXL862XX_BRIDGE_port_config_t::n_ingress_traffic_meter_id */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER = 0x00000020,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_sub_metering_enable and
+	    \ref MXL862XX_BRIDGE_port_config_t::n_egress_traffic_sub_meter_id */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER = 0x00000040,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_dest_logical_port_id,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_pmapper_enable,
+	    \ref MXL862XX_BRIDGE_port_config_t::n_dest_sub_if_id_group,
+	    \ref MXL862XX_BRIDGE_port_config_t::e_pmapper_mapping_mode,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_pmapper_id_valid and
+	    \ref MXL862XX_BRIDGE_port_config_t::s_pmapper. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING = 0x00000080,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_bridge_port_map */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP = 0x00000100,
+
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mc_dest_ip_lookup_enable. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP = 0x00000200,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mc_src_ip_lookup_enable. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP = 0x00000400,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_dest_mac_lookup_enable. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP = 0x00000800,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_src_mac_learning_enable. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING = 0x00001000,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mac_spoofing_detect_enable. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING = 0x00002000,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_port_lock_enable. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK = 0x00004000,
+
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mac_learning_limit_enable and
+	    \ref MXL862XX_BRIDGE_port_config_t::n_mac_learning_limit. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT = 0x00008000,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_mac_learning_count */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT = 0x00010000,
+
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_vlan_filter_enable and
+	    \ref MXL862XX_BRIDGE_port_config_t::n_ingress_vlan_filter_block_id. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER = 0x00020000,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_bypass_egress_vlan_filter1,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter1Enable
+	    and \ref MXL862XX_BRIDGE_port_config_t::n_egress_vlan_filter1Block_id. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 = 0x00040000,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter2Enable and
+	    \ref MXL862XX_BRIDGE_port_config_t::n_egress_vlan_filter2Block_id. */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2 = 0x00080000,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_vlan_tag_selection,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_priority_enable,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_dEIEnable,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_vid_enable,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_priority_enable,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_dEIEnable,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_vid_enable */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING = 0x00100000,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_priority_enable,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_dEIEnable,
+	    \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_vid_enable */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP = 0x00200000,
+	/** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_loop_violation_count */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER = 0x00400000,
+	/** Enable all */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF,
+	/** Bypass any check for debug purpose */
+	MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE = 0x80000000
+} mxl862xx_bridge_port_config_mask_t;
+
+/** \brief Color Marking Mode
+    Used by \ref MXL862XX_CTP_port_config_t. */
+typedef enum {
+	/** mark packets (except critical) to green */
+	MXL862XX_MARKING_ALL_GREEN = 0,
+	/** do not change color and priority */
+	MXL862XX_MARKING_INTERNAL_MARKING = 1,
+	/** DEI mark mode */
+	MXL862XX_MARKING_DEI = 2,
+	/** PCP 8P0D mark mode */
+	MXL862XX_MARKING_PCP_8P0D = 3,
+	/** PCP 7P1D mark mode */
+	MXL862XX_MARKING_PCP_7P1D = 4,
+	/** PCP 6P2D mark mode */
+	MXL862XX_MARKING_PCP_6P2D = 5,
+	/** PCP 5P3D mark mode */
+	MXL862XX_MARKING_PCP_5P3D = 6,
+	/** DSCP AF class */
+	MXL862XX_MARKING_DSCP_AF = 7
+} mxl862xx_color_marking_mode_t;
+
+/** \brief Bridge Port Configuration.
+    Used by \ref MXL862XX_BRIDGE_PORT_CONFIG_SET and \ref MXL862XX_BRIDGE_PORT_CONFIG_GET. */
+typedef struct {
+	/** Bridge Port ID allocated by \ref MXL862XX_BRIDGE_PORT_ALLOC.
+
+	    \remarks
+	    If \ref MXL862XX_BRIDGE_port_config_t::e_mask has
+	    \ref MXL862XX_Bridge_port_config_mask_t::MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE, this
+	    field is absolute index of Bridge Port in hardware for debug purpose,
+	    bypassing any check. */
+	u16 bridge_port_id;
+
+	/** Mask for updating/retrieving fields. */
+	mxl862xx_bridge_port_config_mask_t mask;
+
+	/** Bridge ID (FID) to which this bridge port is associated. A default
+	    bridge (ID 0) should be always available. */
+	u16 bridge_id;
+
+	/** Enable extended VLAN processing for ingress non-IGMP traffic. */
+	bool ingress_extended_vlan_enable;
+	/** Extended VLAN block allocated for ingress non-IGMP traffic. It defines
+	    extended VLAN process for ingress non-IGMP traffic. Valid when
+	    b_ingress_extended_vlan_enable is TRUE. */
+	u16 ingress_extended_vlan_block_id;
+	/** Extended VLAN block size for ingress non-IGMP traffic. This is optional.
+	    If it is 0, the block size of n_ingress_extended_vlan_block_id will be used.
+	    Otherwise, this field will be used. */
+	u16 ingress_extended_vlan_block_size;
+
+	/** Enable extended VLAN processing enabled for egress non-IGMP traffic. */
+	bool egress_extended_vlan_enable;
+	/** Extended VLAN block allocated for egress non-IGMP traffic. It defines
+	    extended VLAN process for egress non-IGMP traffic. Valid when
+	    b_egress_extended_vlan_enable is TRUE. */
+	u16 egress_extended_vlan_block_id;
+	/** Extended VLAN block size for egress non-IGMP traffic. This is optional.
+	    If it is 0, the block size of n_egress_extended_vlan_block_id will be used.
+	    Otherwise, this field will be used. */
+	u16 egress_extended_vlan_block_size;
+
+	/** Ingress color marking mode for ingress traffic. */
+	mxl862xx_color_marking_mode_t ingress_marking_mode;
+
+	/** Color remarking for egress traffic. */
+	mxl862xx_color_remarking_mode_t egress_remarking_mode;
+
+	/** Traffic metering on ingress traffic applies. */
+	bool ingress_metering_enable;
+	/** Meter for ingress Bridge Port process.
+
+	    \remarks
+	    Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before Bridge
+	    port configuration. If this Bridge port is re-set, the last used meter
+	    should be released. */
+	u16 ingress_traffic_meter_id;
+
+	/** Traffic metering on various types of egress traffic (such as broadcast,
+	    multicast, unknown unicast, etc) applies. */
+	bool egress_sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX];
+	/** Meter for egress Bridge Port process with specific type (such as
+	    broadcast, multicast, unknown unicast, etc). Need pre-allocated for each
+	    type. */
+	u16 egress_traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX];
+
+	/** This field defines destination logical port. */
+	u8 dest_logical_port_id;
+	/** This field indicates whether to enable P-mapper. */
+	bool pmapper_enable;
+	/** When b_pmapper_enable is FALSE, this field defines destination sub
+	    interface ID group. */
+	u16 dest_sub_if_id_group;
+	/** When b_pmapper_enable is TRUE, this field selects either DSCP or PCP to
+	    derive sub interface ID. */
+	mxl862xx_pmapper_mapping_mode_t pmapper_mapping_mode;
+	/** When b_pmapper_enable is TRUE, P-mapper is used. This field determines
+	    whether s_pmapper.n_pmapper_id is valid. If this field is TRUE, the
+	    P-mapper is re-used and no allocation of new P-mapper or value
+	    change in the P-mapper. If this field is FALSE, allocation is
+	    taken care in the API implementation. */
+	bool pmapper_id_valid;
+	/** When b_pmapper_enable is TRUE, P-mapper is used. if b_pmapper_id_valid is
+	    FALSE, API implementation need take care of P-mapper allocation,
+	    and maintain the reference counter of P-mapper used multiple times.
+	    If b_pmapper_id_valid is TRUE, only s_pmapper.n_pmapper_id is used to
+	    associate the P-mapper, and there is no allocation of new P-mapper
+	    or value change in the P-mapper. */
+	mxl862xx_pmapper_t pmapper;
+
+	/** Port map define broadcast domain.
+
+	    \remarks
+	    Each bit is one bridge port. Bridge port ID is index * 16 + bit offset.
+	    For example, bit 1 of n_bridge_port_map[1] is bridge port ID 17. */
+	u16 bridge_port_map[8]; /* max can be 16 */
+
+	/** Multicast IP table is searched if this field is FALSE and traffic is IP
+	    multicast. */
+	bool mc_dest_ip_lookup_disable;
+	/** Multicast IP table is searched if this field is TRUE and traffic is IP
+	    multicast. */
+	bool mc_src_ip_lookup_enable;
+
+	/** Default is FALSE. Packet is treated as "unknown" if it's not
+	    broadcast/multicast packet. */
+	bool dest_mac_lookup_disable;
+
+	/** Default is FALSE. Source MAC address is learned. */
+	bool src_mac_learning_disable;
+
+	/** If this field is TRUE and MAC address which is already learned in another
+	    bridge port appears on this bridge port, port locking violation is
+	    detected. */
+	bool mac_spoofing_detect_enable;
+
+	/** If this field is TRUE and MAC address which is already learned in this
+	    bridge port appears on another bridge port, port locking violation is
+	    detected. */
+	bool port_lock_enable;
+
+	/** Enable MAC learning limitation. */
+	bool mac_learning_limit_enable;
+	/** Max number of MAC can be learned from this bridge port. */
+	u16 mac_learning_limit;
+
+	/** Get number of Loop violation counter from this bridge port. */
+	u16 loop_violation_count;
+
+	/** Get number of MAC address learned from this bridge port. */
+	u16 mac_learning_count;
+
+	/** Enable ingress VLAN filter */
+	bool ingress_vlan_filter_enable;
+	/** VLAN filter block of ingress traffic if
+	    \ref MXL862XX_BRIDGE_port_config_t::b_ingress_vlan_filter_enable is TRUE. */
+	u16 ingress_vlan_filter_block_id;
+	/** VLAN filter block size. This is optional.
+	    If it is 0, the block size of n_ingress_vlan_filter_block_id will be used.
+	    Otherwise, this field will be used. */
+	u16 ingress_vlan_filter_block_size;
+	/** For ingress traffic, bypass VLAN filter 1 at egress bridge port
+	    processing. */
+	bool bypass_egress_vlan_filter1;
+	/** Enable egress VLAN filter 1 */
+	bool egress_vlan_filter1enable;
+	/** VLAN filter block 1 of egress traffic if
+	    \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter1Enable is TRUE. */
+	u16 egress_vlan_filter1block_id;
+	/** VLAN filter block 1 size. This is optional.
+	    If it is 0, the block size of n_egress_vlan_filter1Block_id will be used.
+	    Otherwise, this field will be used. */
+	u16 egress_vlan_filter1block_size;
+	/** Enable egress VLAN filter 2 */
+	bool egress_vlan_filter2enable;
+	/** VLAN filter block 2 of egress traffic if
+	    \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter2Enable is TRUE. */
+	u16 egress_vlan_filter2block_id;
+	/** VLAN filter block 2 size. This is optional.
+	    If it is 0, the block size of n_egress_vlan_filter2Block_id will be used.
+	    Otherwise, this field will be used. */
+	u16 egress_vlan_filter2block_size;
+#ifdef SCK_FW_1_0_41
+	/** Enable Ingress VLAN Based Mac Learning */
+	bool ingress_vlan_based_mac_learning_enable;
+	/** 0 - Intermediate outer VLAN
+	    tag is used for MAC address/multicast
+	    learning, lookup and filtering.
+	    1 - Original outer VLAN tag is used
+	    for MAC address/multicast learning, lookup
+	    and filtering. */
+#endif
+	bool vlan_tag_selection;
+	/** 0 - Disable, VLAN Priority field is not used
+	    and value 0 is used for source MAC address
+	    learning and filtering.
+	    1 - Enable, VLAN Priority field is used for
+	    source MAC address learning and filtering. */
+	bool vlan_src_mac_priority_enable;
+	/** 0 - Disable, VLAN DEI/CFI field is not used
+	    and value 0 is used for source MAC address
+	    learning and filtering.
+	    1 -	 Enable, VLAN DEI/CFI field is used for
+	    source MAC address learning and filtering */
+	bool vlan_src_mac_dei_enable;
+	/** 0 - Disable, VLAN ID field is not used and
+	    value 0 is used for source MAC address
+	    learning and filtering
+	    1 - Enable, VLAN ID field is used for source
+	    MAC address learning and filtering. */
+	bool vlan_src_mac_vid_enable;
+	/** 0 - Disable, VLAN Priority field is not used
+	    and value 0 is used for destination MAC
+	    address look up and filtering.
+	    1 - Enable, VLAN Priority field is used for
+	    destination MAC address look up and
+	    filtering */
+	bool vlan_dst_mac_priority_enable;
+	/** 0 - Disable, VLAN CFI/DEI field is not used
+	    and value 0 is used for destination MAC
+	    address lookup and filtering.
+	    1 - Enable, VLAN CFI/DEI field is used for
+	    destination MAC address look up and
+	    filtering. */
+	bool vlan_dst_mac_dei_enable;
+	/** 0 - Disable, VLAN ID field is not used and
+	    value 0 is used for destination MAC address
+	    look up and filtering.
+	    1 - Enable, VLAN ID field is destination for
+	    destination MAC address look up and
+	    filtering. */
+	bool vlan_dst_mac_vid_enable;
+#ifdef SCK_FW_1_0_41
+	/** Enable, VLAN Based Multicast Lookup */
+	bool vlan_based_multi_cast_lookup;
+	/** 0 - Disable, VLAN Priority field is not used
+	    and value 0 is used for IP multicast lookup.
+	    1 - Enable, VLAN Priority field is used for IP
+	    multicast lookup. */
+#endif
+	bool vlan_multicast_priority_enable;
+	/** 0 - Disable, VLAN CFI/DEI field is not used
+	    and value 0 is used for IP multicast lookup.
+	    1 - Enable, VLAN CFI/DEI field is used for IP
+	    multicast lookup. */
+	bool vlan_multicast_dei_enable;
+	/** 0 - Disable, VLAN ID field is not used and
+	    value 0 is used for IP multicast lookup.
+	    1 - Enable, VLAN ID field is destination for IP
+	    multicast lookup. */
+	bool vlan_multicast_vid_enable;
+} mxl862xx_bridge_port_config_t;
+
+/** \brief Bridge Allocation.
+    Used by \ref MXL862XX_BRIDGE_ALLOC and \ref MXL862XX_BRIDGE_FREE. */
+typedef struct {
+	/** If \ref MXL862XX_BRIDGE_ALLOC is successful, a valid ID will be returned
+	    in this field. Otherwise, \ref INVALID_HANDLE is returned in this field.
+	    For \ref MXL862XX_BRIDGE_FREE, this field should be valid ID returned by
+	    \ref MXL862XX_BRIDGE_ALLOC. ID 0 is special Bridge created during
+	    initialization. */
+	u16 bridge_id;
+} mxl862xx_bridge_alloc_t;
+
+/** \brief Ethernet port speed mode.
+    For certain generations of GSWIP, a port might support only a subset of the possible settings.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+	/** 10 Mbit/s */
+	MXL862XX_PORT_SPEED_10,
+	/** 100 Mbit/s */
+	MXL862XX_PORT_SPEED_100,
+	/** 200 Mbit/s */
+	MXL862XX_PORT_SPEED_200,
+	/** 1000 Mbit/s */
+	MXL862XX_PORT_SPEED_1000,
+	/** 2.5 Gbit/s */
+	MXL862XX_PORT_SPEED_2500,
+	/** 5 Gbit/s */
+	MXL862XX_PORT_SPEED_5000,
+	/** 10 Gbit/s */
+	MXL862XX_PORT_SPEED_10000,
+	/** Auto speed for XGMAC */
+	MXL862XX_PORT_SPEED_AUTO,
+} mxl862xx_port_speed_t;
+
+/** \brief Ethernet port duplex status.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+	/** Port operates in full-duplex mode */
+	MXL862XX_DUPLEX_FULL = 0,
+	/** Port operates in half-duplex mode */
+	MXL862XX_DUPLEX_HALF = 1,
+	/** Port operates in Auto mode */
+	MXL862XX_DUPLEX_AUTO = 2,
+} mxl862xx_port_duplex_t;
+
+/** \brief Force the MAC and PHY link modus.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+	/** Link up. Any connected LED
+	    still behaves based on the real PHY status. */
+	MXL862XX_PORT_LINK_UP = 0,
+	/** Link down. */
+	MXL862XX_PORT_LINK_DOWN = 1,
+	/** Link Auto. */
+	MXL862XX_PORT_LINK_AUTO = 2,
+} mxl862xx_port_link_t;
+
+/** \brief Ethernet port interface mode.
+    A port might support only a subset of the possible settings.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+	/** Normal PHY interface (twisted pair), use the internal MII Interface. */
+	MXL862XX_PORT_HW_MII = 0,
+	/** Reduced MII interface in normal mode. */
+	MXL862XX_PORT_HW_RMII = 1,
+	/** GMII or MII, depending upon the speed. */
+	MXL862XX_PORT_HW_GMII = 2,
+	/** RGMII mode. */
+	MXL862XX_PORT_HW_RGMII = 3,
+	/** XGMII mode. */
+	MXL862XX_PORT_HW_XGMII = 4,
+} mxl862xx_mii_mode_t;
+
+/** \brief Ethernet port configuration for PHY or MAC mode.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+	/** MAC Mode. The Ethernet port is configured to work in MAC mode. */
+	MXL862XX_PORT_MAC = 0,
+	/** PHY Mode. The Ethernet port is configured to work in PHY mode. */
+	MXL862XX_PORT_PHY = 1
+} mxl862xx_mii_type_t;
+
+/** \brief Ethernet port clock source configuration.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+	/** Clock Mode not applicable. */
+	MXL862XX_PORT_CLK_NA = 0,
+	/** Clock Master Mode. The port is configured to provide the clock as output signal. */
+	MXL862XX_PORT_CLK_MASTER = 1,
+	/** Clock Slave Mode. The port is configured to use the input clock signal. */
+	MXL862XX_PORT_CLK_SLAVE = 2
+} mxl862xx_clk_mode_t;
+
+/** \brief Ethernet port link, speed status and flow control status.
+    Used by \ref MXL862XX_PORT_LINK_CFG_GET and \ref MXL862XX_PORT_LINK_CFG_SET. */
+typedef struct {
+	/** Ethernet Port number (zero-based counting). The valid range is hardware
+	    dependent. An error code is delivered if the selected port is not
+	    available. */
+	u16 port_id;
+	/** Force Port Duplex Mode.
+
+	    - 0: Negotiate Duplex Mode. Auto-negotiation mode. Negotiated
+	      duplex mode given in 'e_duplex'
+	      during MXL862XX_PORT_LINK_CFG_GET calls.
+	    - 1: Force Duplex Mode. Force duplex mode in 'e_duplex'.
+	*/
+	bool duplex_force;
+	/** Port Duplex Status. */
+	mxl862xx_port_duplex_t duplex;
+	/** Force Link Speed.
+
+	    - 0: Negotiate Link Speed. Negotiated speed given in
+	      'e_speed' during MXL862XX_PORT_LINK_CFG_GET calls.
+	    - 1: Force Link Speed. Forced speed mode in 'e_speed'.
+	*/
+	bool speed_force;
+	/** Ethernet port link up/down and speed status. */
+	mxl862xx_port_speed_t speed;
+	/** Force Link.
+
+	     - 0: Auto-negotiate Link. Current link status is given in
+	       'e_link' during MXL862XX_PORT_LINK_CFG_GET calls.
+	     - 1: Force Duplex Mode. Force duplex mode in 'e_link'.
+	 */
+	bool link_force;
+	/** Link Status. Read out the current link status.
+	    Note that the link could be forced by setting 'b_link_force'. */
+	mxl862xx_port_link_t link;
+	/** Selected interface mode (MII/RMII/RGMII/GMII/XGMII). */
+	mxl862xx_mii_mode_t mii_mode;
+	/** Select MAC or PHY mode (PHY = Reverse x_mII). */
+	mxl862xx_mii_type_t mii_type;
+	/** Interface Clock mode (used for RMII mode). */
+	mxl862xx_clk_mode_t clk_mode;
+	/** 'Low Power Idle' Support for 'Energy Efficient Ethernet'.
+	    Only enable this feature in case the attached PHY also supports it. */
+	bool lpi;
+} mxl862xx_port_link_cfg_t;
+
+/** \brief Port Enable Type Selection.
+    Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+	/** The port is disabled in both directions. */
+	MXL862XX_PORT_DISABLE = 0,
+	/** The port is enabled in both directions (ingress and egress). */
+	MXL862XX_PORT_ENABLE_RXTX = 1,
+	/** The port is enabled in the receive (ingress) direction only. */
+	MXL862XX_PORT_ENABLE_RX = 2,
+	/** The port is enabled in the transmit (egress) direction only. */
+	MXL862XX_PORT_ENABLE_TX = 3
+} mxl862xx_port_enable_t;
+
+/** \brief Ethernet flow control status.
+    Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+	/** Automatic flow control mode selection through auto-negotiation. */
+	MXL862XX_FLOW_AUTO = 0,
+	/** Receive flow control only */
+	MXL862XX_FLOW_RX = 1,
+	/** Transmit flow control only */
+	MXL862XX_FLOW_TX = 2,
+	/** Receive and Transmit flow control */
+	MXL862XX_FLOW_RXTX = 3,
+	/** No flow control */
+	MXL862XX_FLOW_OFF = 4
+} mxl862xx_port_flow_t;
+
+/** \brief Port Mirror Options.
+    Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+	/** Mirror Feature is disabled. Normal port usage. */
+	MXL862XX_PORT_MONITOR_NONE = 0,
+	/** Port Ingress packets are mirrored to the monitor port. */
+	MXL862XX_PORT_MONITOR_RX = 1,
+	/** Port Egress packets are mirrored to the monitor port. */
+	MXL862XX_PORT_MONITOR_TX = 2,
+	/** Port Ingress and Egress packets are mirrored to the monitor port. */
+	MXL862XX_PORT_MONITOR_RXTX = 3,
+	/** Packet mirroring of 'unknown VLAN violation' frames. */
+	MXL862XX_PORT_MONITOR_VLAN_UNKNOWN = 4,
+	/** Packet mirroring of 'VLAN ingress or egress membership violation' frames. */
+	MXL862XX_PORT_MONITOR_VLAN_MEMBERSHIP = 16,
+	/** Packet mirroring of 'port state violation' frames. */
+	MXL862XX_PORT_MONITOR_PORT_STATE = 32,
+	/** Packet mirroring of 'MAC learning limit violation' frames. */
+	MXL862XX_PORT_MONITOR_LEARNING_LIMIT = 64,
+	/** Packet mirroring of 'port lock violation' frames. */
+	MXL862XX_PORT_MONITOR_PORT_LOCK = 128
+} mxl862xx_port_monitor_t;
+
+/** \brief Interface RMON Counter Mode - (FID, SUBID or FLOWID) Config - GSWIP-3.0 only.
+    Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+	/** FID based Interface RMON counters Usage */
+	MXL862XX_IF_RMON_FID = 0,
+	/** Sub-Interface Id based Interface RMON counters Usage */
+	MXL862XX_IF_RMON_SUBID = 1,
+	/** Flow Id (LSB bits 3 to 0) based Interface RMON counters Usage */
+	MXL862XX_IF_RMON_FLOWID_LSB = 2,
+	/** Flow Id (MSB bits 7 to 4) based Interface RMON counters Usage */
+	MXL862XX_IF_RMON_FLOWID_MSB = 3
+} mxl862xx_if_rmon_mode_t;
+
+/** \brief Port Configuration Parameters.
+    Used by \ref MXL862XX_PORT_CFG_GET and \ref MXL862XX_PORT_CFG_SET. */
+typedef struct {
+	/** Port Type. This gives information which type of port is configured.
+	    n_port_id should be based on this field. */
+	mxl862xx_port_type_t port_type;
+
+	/** Ethernet Port number (zero-based counting). The valid range is hardware
+	    dependent. An error code is delivered if the selected port is not
+	    available. */
+	u16 port_id;
+	/** Enable Port (ingress only, egress only, both directions, or disabled).
+	    This parameter is used for Spanning Tree Protocol and 802.1X applications. */
+	mxl862xx_port_enable_t enable;
+	/** Drop unknown unicast packets.
+	    Do not send out unknown unicast packets on this port,
+	    if the boolean parameter is enabled. By default packets of this type
+	    are forwarded to this port. */
+	bool unicast_unknown_drop;
+	/** Drop unknown multicast packets.
+	    Do not send out unknown multicast packets on this port,
+	    if boolean parameter is enabled. By default packets of this type
+	    are forwarded to this port.
+	    Some platforms also drop broadcast packets. */
+	bool multicast_unknown_drop;
+	/** Drop reserved packet types
+	    (destination address from '01 80 C2 00 00 00' to
+	    '01 80 C2 00 00 2F') received on this port. */
+	bool reserved_packet_drop;
+	/** Drop Broadcast packets received on this port. By default packets of this
+	  type are forwarded to this port. */
+	bool broadcast_drop;
+	/** Enables MAC address table aging.
+	    The MAC table entries learned on this port are removed after the
+	    aging time has expired.
+	    The aging time is a global parameter, common to all ports. */
+	bool aging;
+	/** MAC address table learning on the port specified by 'n_port_id'.
+	    By default this parameter is always enabled. */
+	bool learning;
+	/** Automatic MAC address table learning locking on the port specified
+	    by 'n_port_id'.
+	    This parameter is only taken into account when 'b_learning' is enabled. */
+	bool learning_mac_port_lock;
+	/** Automatic MAC address table learning limitation on this port.
+	    The learning functionality is disabled when the limit value is zero.
+	    The value 0x_fFFF to allow unlimited learned address.
+	    This parameter is only taken into account when 'b_learning' is enabled. */
+	u16 learning_limit;
+	/** MAC spoofing detection. Identifies ingress packets that carry
+	    a MAC source address which was previously learned on a different ingress
+	    port (learned by MAC bridging table). This also applies to static added
+	    entries. Those violated packets could be accepted or discarded,
+	    depending on the global switch configuration 'b_mAC_Spoofing_action'.
+	    This parameter is only taken into account when 'b_learning' is enabled. */
+	bool mac_spoofing_detection;
+	/** Port Flow Control Status. Enables the flow control function. */
+	mxl862xx_port_flow_t flow_ctrl;
+	/** Port monitor feature. Allows forwarding of egress and/or ingress
+	    packets to the monitor port. If enabled, the monitor port gets
+	    a copy of the selected packet type. */
+	mxl862xx_port_monitor_t port_monitor;
+	/** Assign Interface RMON Counters for this Port - GSWIP-3.0 */
+	bool if_counters;
+	/** Interface RMON Counters Start Index - GSWIP-3.0.
+	    Value of (-1) denotes unassigned Interface Counters.
+	    Valid range : 0-255 available to be shared amongst ports in desired way*/
+	int if_count_start_idx;
+	/** Interface RMON Counters Mode - GSWIP-3.0 */
+	mxl862xx_if_rmon_mode_t if_rmonmode;
+} mxl862xx_port_cfg_t;
+
+#define MXL862XX_ERROR_BASE 1024
+/** \brief Enumeration for function status return. The upper four bits are reserved for error classification */
+typedef enum {
+	/** Correct or Expected Status */
+	MXL862XX_STATUS_OK = 0,
+	/** Generic or unknown error occurred */
+	MXL862XX_STATUS_ERR = -1,
+	/** Invalid function parameter */
+	MXL862XX_STATUS_PARAM = -(MXL862XX_ERROR_BASE + 2),
+	/** No space left in VLAN table */
+	MXL862XX_STATUS_VLAN_SPACE = -(MXL862XX_ERROR_BASE + 3),
+	/** Requested VLAN ID not found in table */
+	MXL862XX_STATUS_VLAN_ID = -(MXL862XX_ERROR_BASE + 4),
+	/** Invalid ioctl */
+	MXL862XX_STATUS_INVAL_IOCTL = -(MXL862XX_ERROR_BASE + 5),
+	/** Operation not supported by hardware */
+	MXL862XX_STATUS_NO_SUPPORT = -(MXL862XX_ERROR_BASE + 6),
+	/** Timeout */
+	MXL862XX_STATUS_TIMEOUT = -(MXL862XX_ERROR_BASE + 7),
+	/** At least one value is out of range */
+	MXL862XX_STATUS_VALUE_RANGE = -(MXL862XX_ERROR_BASE + 8),
+	/** The port_id/queue_id/meter_id/etc. is not available in this hardware or the
+	    selected feature is not available on this port */
+	MXL862XX_STATUS_PORT_INVALID = -(MXL862XX_ERROR_BASE + 9),
+	/** The interrupt is not available in this hardware */
+	MXL862XX_STATUS_IRQ_INVALID = -(MXL862XX_ERROR_BASE + 10),
+	/** The MAC table is full, an entry could not be added */
+	MXL862XX_STATUS_MAC_TABLE_FULL = -(MXL862XX_ERROR_BASE + 11),
+	/** Locking failed - SWAPI is busy */
+	MXL862XX_STATUS_LOCK_FAILED = -(MXL862XX_ERROR_BASE + 12),
+	/** Multicast Forwarding table entry not found */
+	MXL862XX_STATUS_ENTRY_NOT_FOUND = -(MXL862XX_ERROR_BASE + 13),
+} mxl862xx_return_t;
+
+/** \brief MAC Table Clear Type
+    Used by \ref MXL862XX_MAC_table_clear_cond_t */
+typedef enum {
+	/** Clear dynamic entries based on Physical Port */
+	MXL862XX_MAC_CLEAR_PHY_PORT = 0,
+	/** Clear all dynamic entries */
+	MXL862XX_MAC_CLEAR_DYNAMIC = 1,
+} mxl862xx_mac_clear_type_t;
+
+/** \brief MAC Table Clear based on given condition.
+    Used by \ref MXL862XX_MAC_TABLE_CLEAR_COND. */
+typedef struct {
+	/** MAC table clear type \ref MXL862XX_Mac_clear_type_t */
+	u8 type;
+	union {
+		/** Physical port id (0~16) if \ref e_type is
+		    ref MXL862XX_MAC_CLEAR_PHY_PORT. */
+		u8 port_id;
+	};
+} mxl862xx_mac_table_clear_cond_t;
+
+/** \brief Configures the Spanning Tree Protocol state of an Ethernet port.
+    Used by \ref MXL862XX_STP_PORT_CFG_SET
+    and \ref MXL862XX_STP_PORT_CFG_GET. */
+typedef struct {
+	/** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From
+	    GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware
+	    dependent. An error code is delivered if the selected port is not
+	    available. */
+	u16 port_id;
+	/** Filtering Identifier (FID) (not supported by all switches).
+	    The FID allows to keep multiple STP states per physical Ethernet port.
+	    Multiple CTAG VLAN groups could be a assigned to one FID and therefore
+	    share the same STP port state. Switch API ignores the FID value
+	    in case the switch device does not support it or switch CTAG VLAN
+	    awareness is disabled. */
+	u16 fid;
+	/** Spanning Tree Protocol state of the port. */
+	mxl862xx_stp_port_state_t port_state;
+} mxl862xx_stp_port_cfg_t;
+
+/** \brief Special tag Ethertype mode */
+typedef enum {
+	/** The ether_type field of the Special Tag of egress packets is always set
+	    to a prefined value. This same defined value applies for all
+	    switch ports. */
+	MXL862XX_CPU_ETHTYPE_PREDEFINED = 0,
+	/** The Ethertype field of the Special Tag of egress packets is set to
+	    the flow_iD parameter, which is a results of the switch flow
+	    classification result. The switch flow table rule provides this
+	    flow_iD as action parameter. */
+	MXL862XX_CPU_ETHTYPE_FLOWID = 1
+} mxl862xx_cpu_special_tag_eth_type_t;
+
+/** \brief Parser Flags and Offsets Header settings on CPU Port for GSWIP-3.0.
+    Used by \ref MXL862XX_CPU_Port_cfg_t. */
+typedef enum {
+	/** No Parsing Flags or Offsets accompanying to CPU Port for this combination */
+	MXL862XX_CPU_PARSER_NIL = 0,
+	/** 8-Bytes Parsing Flags (Bit 63:0) accompanying to CPU Port for this combination */
+	MXL862XX_CPU_PARSER_FLAGS = 1,
+	/** 40-Bytes Offsets (Offset-0 to -39) + 8-Bytes Parsing Flags (Bit 63:0) accompanying to CPU Port for this combination */
+	MXL862XX_CPU_PARSER_OFFSETS_FLAGS = 2,
+	/** Reserved - for future use */
+	MXL862XX_CPU_PARSER_RESERVED = 3
+} mxl862xx_cpu_parser_header_cfg_t;
+
+/** \brief FCS and Pad Insertion operations for GSWIP 3.1
+    Used by \ref MXL862XX_CPU_Port_cfg_set/Get. */
+typedef enum {
+	/** CRC Pad Insertion Enable */
+	MXL862XX_CRC_PAD_INS_EN = 0,
+	/** CRC Insertion Enable Pad Insertion Disable */
+	MXL862XX_CRC_EN_PAD_DIS = 1,
+	/** CRC Pad Insertion Disable */
+	MXL862XX_CRC_PAD_INS_DIS = 2
+} mxl862xx_fcs_tx_ops_t;
+
+/** \brief Defines one port that is directly connected to the CPU and its applicable settings.
+    Used by \ref MXL862XX_CPU_PORT_CFG_SET and \ref MXL862XX_CPU_PORT_CFG_GET. */
+typedef struct {
+	/** Ethernet Port number (zero-based counting) set to CPU Port. The valid number is hardware
+	    dependent. (E.g. Port number 0 for GSWIP-3.0 or 6 for GSWIP-2.x). An error code is delivered if the selected port is not
+	    available. */
+	u16 port_id;
+	/** CPU port validity.
+	    Set command: set true to define a CPU port, set false to undo the setting.
+	    Get command: true if defined as CPU, false if not defined as CPU port. */
+	bool cPU_Port_valid;
+	/** Special tag enable in ingress direction. */
+	bool special_tag_ingress;
+	/** Special tag enable in egress direction. */
+	bool special_tag_egress;
+	/** Enable FCS check
+
+	    - false: No check, forward all frames
+	    - 1: Check FCS, drop frames with errors
+	*/
+	bool fcs_check;
+	/** Enable FCS generation
+
+	    - false: Forward packets without FCS
+	    - 1: Generate FCS for all frames
+	*/
+	bool fcs_generate;
+	/** Special tag Ethertype mode. Not applicable to GSWIP-3.1. */
+	mxl862xx_cpu_special_tag_eth_type_t special_tag_eth_type;
+	/** GSWIP-3.0 specific Parser Header Config for no MPE flags (i.e. MPE1=0, MPE2=0). */
+	mxl862xx_cpu_parser_header_cfg_t no_mpeparser_cfg;
+	/** GSWIP-3.0 specific Parser Header Config for MPE-1 set flag (i.e. MPE1=1, MPE2=0). */
+	mxl862xx_cpu_parser_header_cfg_t mpe1parser_cfg;
+	/** GSWIP-3.0 specific Parser Header Config for MPE-2 set flag (i.e. MPE1=0, MPE2=1). */
+	mxl862xx_cpu_parser_header_cfg_t mpe2parser_cfg;
+	/** GSWIP-3.0 specific Parser Header Config for both MPE-1 and MPE-2 set flag (i.e. MPE1=1, MPE2=1). */
+	mxl862xx_cpu_parser_header_cfg_t mpe1mpe2parser_cfg;
+	/** GSWIP-3.1 FCS tx Operations. */
+	mxl862xx_fcs_tx_ops_t fcs_tx_ops;
+	/** GSWIP-3.2 Time Stamp Field Removal for PTP Packet
+	    0 - DIS Removal is disabled
+	    1 - EN Removal is enabled
+	*/
+	bool ts_ptp;
+	/** GSWIP-3.2 Time Stamp Field Removal for Non-PTP Packet
+	    0 - DIS Removal is disabled
+	    1 - EN Removal is enabled
+	*/
+	bool ts_nonptp;
+} mxl862xx_cpu_port_cfg_t;
+
+typedef struct {
+	/* port ID (1~16) */
+	u8 pid;
+
+	/* bit value 1 to indicate valid field
+	 *   bit 0 - rx
+	 *       1 - tx
+	 *       2 - rx_pen
+	 *       3 - tx_pen
+	 */
+	u8 mask;
+
+	/* RX special tag mode
+	 *  value 0 - packet does NOT have special tag and special tag is
+	 *            NOT inserted
+	 *        1 - packet does NOT have special tag and special tag is
+	 *            inserted
+	 *        2 - packet has special tag and special tag is NOT inserted
+	 */
+	u8 rx;
+
+	/* TX special tag mode
+	 *  value 0 - packet does NOT have special tag and special tag is
+	 *            NOT removed
+	 *        1 - packet has special tag and special tag is replaced
+	 *        2 - packet has special tag and special tag is NOT removed
+	 *        3 - packet has special tag and special tag is removed
+	 */
+	u8 tx;
+
+	/* RX special tag info over preamble
+	 *  value 0 - special tag info inserted from byte 2 to 7 are all 0
+	 *        1 - special tag byte 5 is 16, other bytes from 2 to 7 are 0
+	 *        2 - special tag byte 5 is from preamble field, others are 0
+	 *        3 - special tag byte 2 to 7 are from preabmle field
+	 */
+	u8 rx_pen;
+
+	/* TX special tag info over preamble
+	 *  value 0 - disabled
+	 *        1 - enabled
+	 */
+	u8 tx_pen;
+} mxl862xx_ss_sp_tag_t;
+
+/** \brief Port monitor configuration.
+    Used by \ref MXL862XX_MONITOR_PORT_CFG_GET and \ref MXL862XX_MONITOR_PORT_CFG_SET. */
+typedef struct {
+	/** Ethernet Port number (zero-based counting). The valid range is hardware
+	    dependent. An error code is delivered if the selected port is not
+	    available. */
+	u8 port_id;
+	/* Monitoring Sub-IF id */
+	u16 sub_if_id;
+	/* Out of use. */
+	bool monitor_port;
+} mxl862xx_monitor_port_cfg_t;
+
+/** \brief VLAN Filter TCI Mask.
+    Used by \ref MXL862XX_VLANFILTER_config_t */
+typedef enum {
+	MXL862XX_VLAN_FILTER_TCI_MASK_VID = 0,
+	MXL862XX_VLAN_FILTER_TCI_MASK_PCP = 1,
+	MXL862XX_VLAN_FILTER_TCI_MASK_TCI = 2
+} mxl862xx_vlan_filter_tci_mask_t;
+
+typedef enum {
+	MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1 = 0,
+	MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2 = 1,
+	MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3 = 2,
+	MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4 = 3
+} mxl862xx_extended_vlan_4_tpid_mode_t;
+
+/** \brief Extended VLAN Filter TPID Field.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */
+typedef enum {
+	/** Do not filter. */
+	MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER = 0,
+	/** TPID is 0x8100. */
+	MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q = 1,
+	/** TPID is global configured value. */
+	MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE = 2
+} mxl862xx_extended_vlan_filter_tpid_t;
+
+/** \brief Extended VLAN Treatment Set TPID.
+   Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+	/** TPID is copied from inner VLAN tag of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID = 0,
+	/** TPID is copied from outer VLAN tag of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID = 1,
+	/** TPID is global configured value. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE = 2,
+	/** TPID is 0x8100. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q = 3
+} mxl862xx_extended_vlan_treatment_tpid_t;
+
+/** \brief Extended VLAN Filter DEI Field.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */
+typedef enum {
+	/** Do not filter. */
+	MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER = 0,
+	/** DEI is 0. */
+	MXL862XX_EXTENDEDVLAN_FILTER_DEI_0 = 1,
+	/** DEI is 1. */
+	MXL862XX_EXTENDEDVLAN_FILTER_DEI_1 = 2
+} mxl862xx_extended_vlan_filter_dei_t;
+
+/** \brief Extended VLAN Treatment Set DEI.
+   Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+	/** DEI (if applicable) is copied from inner VLAN tag of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI = 0,
+	/** DEI (if applicable) is copied from outer VLAN tag of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI = 1,
+	/** DEI is 0. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0 = 2,
+	/** DEI is 1. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1 = 3
+} mxl862xx_extended_vlan_treatment_dei_t;
+
+/** \brief Extended VLAN Filter Type.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */
+typedef enum {
+	/** There is tag and criteria applies. */
+	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL = 0,
+	/** There is tag but no criteria. */
+	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER = 1,
+	/** Default entry if no other rule applies. */
+	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT = 2,
+	/** There is no tag. */
+	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG = 3,
+	/** Block invalid*/
+	MXL862XX_EXTENDEDVLAN_BLOCK_INVALID = 4
+} mxl862xx_extended_vlan_filter_type_t;
+
+/** \brief Extended VLAN Filter ether_type.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */
+typedef enum {
+	/** Do not filter. */
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER = 0,
+	/** IPo_e frame (Ethertyp is 0x0800). */
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE = 1,
+	/** PPPo_e frame (Ethertyp is 0x8863 or 0x8864). */
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE = 2,
+	/** ARP frame (Ethertyp is 0x0806). */
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP = 3,
+	/** IPv6 IPo_e frame (Ethertyp is 0x86DD). */
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE = 4,
+	/** EAPOL (Ethertyp is 0x888E). */
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL = 5,
+	/** DHCPV4 (UDP DESTINATION PORT 67&68). */
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4 = 6,
+	/** DHCPV6 (UDP DESTINATION PORT 546&547). */
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6 = 7
+} mxl862xx_extended_vlan_filter_ethertype_t;
+
+/** \brief Extended VLAN Treatment Set Priority.
+   Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+	/** Set priority with given value. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0,
+	/** Prority value is copied from inner VLAN tag of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1,
+	/** Prority value is copied from outer VLAN tag of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2,
+	/** Prority value is derived from DSCP field of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3
+} mxl862xx_extended_vlan_treatment_priority_t;
+
+/** \brief Extended VLAN Treatment Set VID.
+   Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+	/** Set VID with given value. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL = 0,
+	/** VID is copied from inner VLAN tag of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID = 1,
+	/** VID is copied from outer VLAN tag of received packet. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID = 2,
+} mxl862xx_extended_vlan_treatment_vid_t;
+
+/** \brief Extended VLAN Treatment Remove Tag.
+    Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+	/** Do not remove VLAN tag. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG = 0,
+	/** Remove 1 VLAN tag following DA/SA. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG = 1,
+	/** Remove 2 VLAN tag following DA/SA. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG = 2,
+	/** Discard upstream traffic. */
+	MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM = 3,
+} mxl862xx_extended_vlan_treatment_remove_tag_t;
+
+/** \brief Extended VLAN Filter VLAN Tag.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_t. */
+typedef struct {
+	/** Filter Type: normal filter, default rule, or no tag */
+	mxl862xx_extended_vlan_filter_type_t type;
+	/** Enable priority field filtering. */
+	bool priority_enable;
+	/** Filter priority value if b_priority_enable is TRUE. */
+	u32 priority_val;
+	/** Enable VID filtering. */
+	bool vid_enable;
+	/** Filter VID if b_vid_enable is TRUE. */
+	u32 vid_val;
+	/** Mode to filter TPID of VLAN tag. */
+	mxl862xx_extended_vlan_filter_tpid_t tpid;
+	/** Mode to filter DEI of VLAN tag. */
+	mxl862xx_extended_vlan_filter_dei_t dei;
+} mxl862xx_extendedvlan_filter_vlan_t;
+
+/** \brief Extended VLAN Treatment VLAN Tag.
+    Used by \ref MXL862XX_EXTENDEDVLAN_treatment_t. */
+typedef struct {
+	/** Select source of priority field of VLAN tag. */
+	mxl862xx_extended_vlan_treatment_priority_t priority_mode;
+	/** If \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::priority_mode is
+	    \ref MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL, use this value for
+	    priority field of VLAN tag. */
+	u32 priority_val;
+	/** Select source of VID field of VLAN tag. */
+	mxl862xx_extended_vlan_treatment_vid_t vid_mode;
+	/** If \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::e_vid_mode is
+	    \ref MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL, use this value for VID field
+	    of VLAN tag. */
+	u32 vid_val;
+	/** Select source of TPID field of VLAN tag. */
+	mxl862xx_extended_vlan_treatment_tpid_t tpid;
+	/** Select source of DEI field of VLAN tag. */
+	mxl862xx_extended_vlan_treatment_dei_t dei;
+} mxl862xx_extendedvlan_treatment_vlan_t;
+
+/** \brief Extended VLAN Filter.
+    Used by \ref MXL862XX_EXTENDEDVLAN_config_t. */
+typedef struct {
+	/** Filter on Original Packet. */
+	bool original_packet_filter_mode;
+	mxl862xx_extended_vlan_4_tpid_mode_t filter_4_tpid_mode;
+	/** Filter for outer VLAN tag. */
+	mxl862xx_extendedvlan_filter_vlan_t outer_vlan;
+	/** Filter for inner VLAN tag. */
+	mxl862xx_extendedvlan_filter_vlan_t inner_vlan;
+	/** Filter ether_type. */
+	mxl862xx_extended_vlan_filter_ethertype_t ether_type;
+} mxl862xx_extendedvlan_filter_t;
+
+/** \brief Extended VLAN Allocation.
+    Used by \ref MXL862XX_EXTENDEDVLAN_ALLOC and \ref MXL862XX_EXTENDEDVLAN_FREE. */
+typedef struct {
+	/** Total number of extended VLAN entries are requested. Proper value should
+	    be given for \ref MXL862XX_EXTENDEDVLAN_ALLOC. This field is ignored for
+	    \ref MXL862XX_EXTENDEDVLAN_FREE. */
+	u16 number_of_entries;
+
+	/** If \ref MXL862XX_EXTENDEDVLAN_ALLOC is successful, a valid ID will be returned
+	    in this field. Otherwise, \ref INVALID_HANDLE is returned in this field.
+	    For \ref MXL862XX_EXTENDEDVLAN_FREE, this field should be valid ID returned by
+	    \ref MXL862XX_EXTENDEDVLAN_ALLOC. */
+	u16 extended_vlan_block_id;
+} mxl862xx_extendedvlan_alloc_t;
+
+/** \brief Extended VLAN Treatment.
+    Used by \ref MXL862XX_EXTENDEDVLAN_config_t. */
+typedef struct {
+	/** Number of VLAN tag(s) to remove. */
+	mxl862xx_extended_vlan_treatment_remove_tag_t remove_tag;
+
+	mxl862xx_extended_vlan_4_tpid_mode_t treatment_4_tpid_mode;
+
+	/** Enable outer VLAN tag add/modification. */
+	bool add_outer_vlan;
+	/** If b_add_outer_vlan is TRUE, add or modify outer VLAN tag. */
+	mxl862xx_extendedvlan_treatment_vlan_t outer_vlan;
+
+	/** Enable inner VLAN tag add/modification. */
+	bool add_inner_vlan;
+	/** If b_add_inner_vlan is TRUE, add or modify inner VLAN tag. */
+	mxl862xx_extendedvlan_treatment_vlan_t inner_vlan;
+
+	/** Enable re-assignment of bridge port. */
+	bool reassign_bridge_port;
+	/** If b_reassign_bridge_port is TRUE, use this value for bridge port. */
+	u16 new_bridge_port_id;
+
+	/** Enable new DSCP. */
+	bool new_dscp_enable;
+	/** If b_new_dscp_enable is TRUE, use this value for DSCP. */
+	u16 new_dscp;
+
+	/** Enable new traffic class. */
+	bool new_traffic_class_enable;
+	/** If b_new_traffic_class_enable is TRUE, use this value for traffic class. */
+	u8 new_traffic_class;
+
+	/** Enable new meter. */
+	bool new_meter_enable;
+	/** New meter ID.
+
+	    \remarks
+	    Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before extended
+	    VLAN treatment is added. If this extended VLAN treatment is deleted,
+	    this meter should be released with \ref MXL862XX_QOS_METER_FREE. */
+	u16 s_new_traffic_meter_id;
+
+	/** DSCP to PCP mapping, if
+	    \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::e_priority_mode in
+	    \ref MXL862XX_EXTENDEDVLAN_treatment_t::s_outer_vlan.e_priority_mode or
+	    \ref MXL862XX_EXTENDEDVLAN_treatment_t::s_inner_vlan.e_priority_mode is
+	    \ref MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP.
+
+	    \remarks
+	    The index of array stands for DSCP value. Each byte of the array is 3-bit
+	    PCP value. For implementation, if DSCP2PCP is separate hardware table,
+	    a resource management mechanism should be implemented. Allocation happens
+	    when extended VLAN treatment added, and release happens when the
+	    treatment is deleted. For debug, the DSCP2PCP table can be dumped with
+	    \ref MXL862XX_DSCP2PCP_MAP_GET. */
+	u8 dscp2pcp_map[64];
+
+	/** Enable loopback. */
+	bool loopback_enable;
+	/** Enable destination/source MAC address swap. */
+	bool da_sa_swap_enable;
+	/** Enable traffic mirrored to the monitoring port. */
+	bool mirror_enable;
+} mxl862xx_extendedvlan_treatment_t;
+
+/** \brief Extended VLAN Configuration.
+    Used by \ref MXL862XX_EXTENDEDVLAN_SET and \ref MXL862XX_EXTENDEDVLAN_GET. */
+typedef struct {
+	/** This should be valid ID returned by \ref MXL862XX_EXTENDEDVLAN_ALLOC.
+	    If it is \ref INVALID_HANDLE, \ref MXL862XX_EXTENDEDVLAN_config_t::n_entry_index
+	    is absolute index of Extended VLAN entry in hardware for debug purpose,
+	    bypassing any check. */
+	u16 extended_vlan_block_id;
+
+	/** Index of entry, ranges between 0 and
+	    \ref MXL862XX_EXTENDEDVLAN_alloc_t::n_number_of_entries - 1, to
+	    set (\ref MXL862XX_EXTENDEDVLAN_SET) or get (\ref MXL862XX_EXTENDEDVLAN_GET)
+	    Extended VLAN Configuration entry. For debug purpose, this field could be
+	    absolute index of Entended VLAN entry in hardware, when
+	    \ref MXL862XX_EXTENDEDVLAN_config_t::n_extended_vlan_block_id is
+	    \ref INVALID_HANDLE. */
+	u16 entry_index;
+
+	/** Extended VLAN Filter */
+	mxl862xx_extendedvlan_filter_t filter;
+	/** Extended VLAN Treatment */
+	mxl862xx_extendedvlan_treatment_t treatment;
+} mxl862xx_extendedvlan_config_t;
+
+/** \brief VLAN Filter Allocation.
+    Used by \ref MXL862XX_VLANFILTER_ALLOC and \ref MXL862XX_VLANFILTER_FREE. */
+typedef struct {
+	/** Total number of VLAN Filter entries are requested. Proper value should
+	    be given for \ref MXL862XX_VLANFILTER_ALLOC. This field is ignored for
+	    \ref MXL862XX_VLANFILTER_FREE. */
+	u16 number_of_entries;
+
+	/** If \ref MXL862XX_VLANFILTER_ALLOC is successful, a valid ID will be returned
+	    in this field. Otherwise, \ref INVALID_HANDLE is returned in this field.
+	    For \ref MXL862XX_EXTENDEDVLAN_FREE, this field should be valid ID returned by
+	    \ref MXL862XX_VLANFILTER_ALLOC. */
+	u16 vlan_filter_block_id;
+
+	/** Discard packet without VLAN tag. */
+	bool discard_untagged;
+	/** Discard VLAN tagged packet not matching any filter entry. */
+	bool discard_unmatched_tagged;
+	/** Use default port VLAN ID for VLAN filtering
+
+	    \remarks
+	    This field is not available in PRX300. */
+	bool use_default_port_vid;
+} mxl862xx_vlanfilter_alloc_t;
+
+/** \brief VLAN Filter.
+    Used by \ref MXL862XX_VLANFILTER_SET and \ref MXL862XX_VLANFILTER_GET */
+typedef struct {
+	/** This should be valid ID return by \ref MXL862XX_VLANFILTER_ALLOC.
+	    If it is \ref INVALID_HANDLE, \ref MXL862XX_VLANFILTER_config_t::n_entry_index
+	    is absolute index of VLAN Filter entry in hardware for debug purpose,
+	    bypassing any check. */
+	u16 vlan_filter_block_id;
+
+	/** Index of entry. ranges between 0 and
+	    \ref MXL862XX_VLANFILTER_alloc_t::n_number_of_entries - 1, to
+	    set (\ref MXL862XX_VLANFILTER_SET) or get (\ref MXL862XX_VLANFILTER_GET)
+	    VLAN FIlter entry. For debug purpose, this field could be absolute index
+	    of VLAN Filter entry in hardware, when
+	    \ref MXL862XX_VLANFILTER_config_t::n_vlan_filter_block_id is
+	    \ref INVALID_HANDLE. */
+	u16 entry_index;
+
+	/** VLAN TCI filter mask mode.
+
+	    \remarks
+	    In GSWIP-3.1, this field of first entry in the block will applies to rest
+	    of entries in the same block. */
+	mxl862xx_vlan_filter_tci_mask_t vlan_filter_mask;
+
+	/** This is value for VLAN filtering. It depends on
+	    \ref MXL862XX_VLANFILTER_config_t::e_vlan_filter_mask.
+	    For MXL862XX_VLAN_FILTER_TCI_MASK_VID, this is 12-bit VLAN ID.
+	    For MXL862XX_VLAN_FILTER_TCI_MASK_PCP, this is 3-bit PCP field of VLAN tag.
+	    For MXL862XX_VLAN_FILTER_TCI_MASK_TCI, this is 16-bit TCI of VLAN tag. */
+	u32 val;
+	/** Discard packet if match. */
+	bool discard_matched;
+} mxl862xx_vlanfilter_config_t;
+
+/* VLAN Rmon Counters */
+typedef enum {
+	MXL862XX_VLAN_RMON_RX = 0,
+	MXL862XX_VLAN_RMON_TX = 1,
+	MXL862XX_VLAN_RMON__PCE_BYPASS = 2,
+} mxl862xx_vlan_rmon_type_t;
+
+/**
+ \brief RMON Counters structure for VLAN. */
+typedef struct {
+	u16 vlan_counter_index;
+	mxl862xx_vlan_rmon_type_t vlan_rmon_type;
+	u32 byte_count_high;
+	u32 byte_count_low;
+	u32 total_pkt_count;
+	u32 multicast_pkt_count;
+	u32 drop_pkt_count;
+	u32 clear_all;
+} mxl862xx_vlan_rmon_cnt_t;
+
+/**
+ \brief RMON Counters control structure for VLAN. */
+typedef struct {
+	bool vlan_rmon_enable;
+	bool include_broad_cast_pkt_counting;
+	u32 vlan_last_entry;
+} mxl862xx_vlan_rmon_control_t;
+
+/** \brief VLAN Counter Mapping. */
+typedef enum {
+	/** VLAN Mapping for Ingress */
+	MXL862XX_VLAN_MAPPING_INGRESS = 0,
+	/** VLAN Mapping for Egress */
+	MXL862XX_VLAN_MAPPING_EGRESS = 1,
+	/** VLAN Mapping for Ingress and Egress */
+	MXL862XX_VLAN_MAPPING_INGRESS_AND_EGRESS = 2
+} mxl862xx_vlan_counter_mapping_type_t;
+
+/** \brief VLAN Counter Mapping Filter. */
+typedef enum {
+	/** There is tag and criteria applies. */
+	MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NORMAL = 0,
+	/** There is tag but no criteria. */
+	MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NO_FILTER = 1,
+	/** Default entry if no other rule applies. */
+	MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_DEFAULT = 2,
+	/** There is no tag. */
+	MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NO_TAG = 3,
+	/** Filter invalid*/
+	MXL862XX_VLANCOUNTERMAP_FILTER_INVALID = 4,
+} mxl862xx_vlan_counter_map_filter_type_t;
+
+/** \brief VLAN Counter Mapping Configuration. */
+typedef struct {
+	/** Counter Index */
+	u8 counter_index;
+	/** Ctp Port Id */
+	u16 ctp_port_id;
+	/** Priority Enable */
+	bool priority_enable;
+	/** Priority Val */
+	u32 priority_val;
+	/** VLAN Id Enable */
+	bool vid_enable;
+	/** VLAN Id Value */
+	u32 vid_val;
+	/** VLAN Tag Selection Value */
+	bool vlan_tag_selection_enable;
+	/** VLAN Counter Mapping Type */
+	mxl862xx_vlan_counter_mapping_type_t vlan_counter_mapping_type;
+	/** VLAN Counter Mapping Filter Type */
+	mxl862xx_vlan_counter_map_filter_type_t vlan_counter_mapping_filter_type;
+} mxl862xx_vlan_counter_mapping_config_t;
+
+/** \brief MAC Table Entry to be added.
+    Used by \ref MXL862XX_MAC_TABLE_ENTRY_ADD. */
+typedef struct {
+	/** Filtering Identifier (FID) (not supported by all switches) */
+	u16 fid;
+	/** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From
+	    GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware
+	    dependent.
+
+	    \remarks
+	    In GSWIP-2.1/2.2/3.0, this field is used as portmap field, when the MSB
+	    bit is set. In portmap mode, every value bit represents an Ethernet port.
+	    LSB represents Port 0 with incrementing counting.
+	    The (MSB - 1) bit represent the last port.
+	    The macro \ref MXL862XX_PORTMAP_FLAG_SET allows to set the MSB bit,
+	    marking it as portmap variable.
+	    Checking the portmap flag can be done by
+	    using the \ref MXL862XX_PORTMAP_FLAG_GET macro.
+	    From GSWIP3.1, if MSB is set, other bits in this field are ignored.
+	    array \ref MXL862XX_MAC_table_read_t::n_port_map is used for bit map. */
+	u32 port_id;
+	/** Bridge Port Map - to support GSWIP-3.1, following field is added
+	    for port map in static entry. It's valid only when MSB of
+	    \ref MXL862XX_MAC_table_read_t::n_port_id is set. Each bit stands for 1 bridge
+	    port. */
+	u16 port_map[8]; /* max can be 16 */
+	/** Sub-Interface Identifier Destination (supported in GSWIP-3.0/3.1 only).
+
+	    \remarks
+	    In GSWIP-3.1, this field is sub interface ID for WLAN logical port. For
+	    Other types, either outer VLAN ID if Nto1Vlan enabled or 0. */
+	u16 sub_if_id;
+	/** Aging Time, given in multiples of 1 second in a range
+	    from 1 s to 1,000,000 s.
+	    The configured value might be rounded that it fits to the given hardware platform. */
+	int age_timer;
+	/** STAG VLAN Id. Only applicable in case SVLAN support is enabled on the device. */
+	u16 vlan_id;
+	/** Static Entry (value will be aged out if the entry is not set to static). The
+	    switch API implementation uses the maximum age timer in case the entry
+	    is not static. */
+	bool static_entry;
+	/** Egress queue traffic class.
+	    The queue index starts counting from zero.   */
+	u8 traffic_class;
+	/** MAC Address to add to the table. */
+	u8 mac[ETH_ALEN];
+	/** Source/Destination MAC address filtering flag (GSWIP-3.1 only)
+	    Value 0 - not filter, 1 - source address filter,
+	    2 - destination address filter, 3 - both source and destination filter.
+
+	    \remarks
+	    Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6
+	    "Source MAC Address Filtering and Destination MAC Address Filtering"
+	    for more detail. */
+	u8 filter_flag;
+	/** Packet is marked as IGMP controlled if destination MAC address matches
+	    MAC in this entry. (GSWIP-3.1 only) */
+	bool igmp_controlled;
+
+	/** Associated Mac address -(GSWIP-3.2)*/
+	u8 associated_mac[ETH_ALEN];
+
+	/** TCI for (GSWIP-3.2) B-Step
+	    Bit [0:11] - VLAN ID
+	    Bit [12] - VLAN CFI/DEI
+	    Bit [13:15] - VLAN PRI */
+	u16 tci;
+} mxl862xx_mac_table_add_t;
+
+/** \brief MAC Table Entry to be read.
+    Used by \ref MXL862XX_MAC_TABLE_ENTRY_READ. */
+typedef struct {
+	/** Restart the get operation from the beginning of the table. Otherwise
+	    return the next table entry (next to the entry that was returned
+	    during the previous get operation). This boolean parameter is set by the
+	    calling application. */
+	bool initial;
+	/** Indicates that the read operation got all last valid entries of the
+	    table. This boolean parameter is set by the switch API
+	    when the Switch API is called after the last valid one was returned already. */
+	bool last;
+	/** Get the MAC table entry belonging to the given Filtering Identifier
+	    (not supported by all switches). */
+	u16 fid;
+	/** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From
+	    GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware
+	    dependent.
+
+	    \remarks
+	    In GSWIP-2.1/2.2/3.0, this field is used as portmap field, when the MSB
+	    bit is set. In portmap mode, every value bit represents an Ethernet port.
+	    LSB represents Port 0 with incrementing counting.
+	    The (MSB - 1) bit represent the last port.
+	    The macro \ref MXL862XX_PORTMAP_FLAG_SET allows to set the MSB bit,
+	    marking it as portmap variable.
+	    Checking the portmap flag can be done by
+	    using the \ref MXL862XX_PORTMAP_FLAG_GET macro.
+	    From GSWIP3.1, if MSB is set, other bits in this field are ignored.
+	    array \ref MXL862XX_MAC_table_read_t::n_port_map is used for bit map. */
+	u32 port_id;
+	/** Bridge Port Map - to support GSWIP-3.1, following field is added
+	    for port map in static entry. It's valid only when MSB of
+	    \ref MXL862XX_MAC_table_read_t::n_port_id is set. Each bit stands for 1 bridge
+	    port. */
+	u16 port_map[8]; /* max can be 16 */
+	/** Aging Time, given in multiples of 1 second in a range from 1 s to 1,000,000 s.
+	    The value read back in a GET command might differ slightly from the value
+	    given in the SET command due to limited hardware timing resolution.
+	    Filled out by the switch API implementation. */
+	int age_timer;
+	/** STAG VLAN Id. Only applicable in case SVLAN support is enabled on the device. */
+	u16 vlan_id;
+	/** Static Entry (value will be aged out after 'n_age_timer' if the entry
+	    is not set to static). */
+	bool static_entry;
+	/** Sub-Interface Identifier Destination (supported in GSWIP-3.0/3.1 only). */
+	u16 sub_if_id;
+	/** MAC Address. Filled out by the switch API implementation. */
+	u8 mac[ETH_ALEN];
+	/** Source/Destination MAC address filtering flag (GSWIP-3.1 only)
+	    Value 0 - not filter, 1 - source address filter,
+	    2 - destination address filter, 3 - both source and destination filter.
+
+	    \remarks
+	    Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6
+	    "Source MAC Address Filtering and Destination MAC Address Filtering"
+	    for more detail. */
+	u8 filter_flag;
+	/** Packet is marked as IGMP controlled if destination MAC address matches
+	    MAC in this entry. (GSWIP-3.1 only) */
+	bool igmp_controlled;
+
+	/** Changed
+	0: the entry is not changed
+	1: the entry is changed and not accessed yet */
+
+	bool entry_changed;
+
+	/** Associated Mac address -(GSWIP-3.2)*/
+	u8 associated_mac[ETH_ALEN];
+	/* MAC Table Hit Status Update (Supported in GSWip-3.1/3.2) */
+	bool hit_status;
+	/** TCI for (GSWIP-3.2) B-Step
+	    Bit [0:11] - VLAN ID
+	    Bit [12] - VLAN CFI/DEI
+	    Bit [13:15] - VLAN PRI */
+	u16 tci;
+	u16 first_bridge_port_id;
+} mxl862xx_mac_table_read_t;
+
+/** \brief MAC Table Entry to be removed.
+    Used by \ref MXL862XX_MAC_TABLE_ENTRY_REMOVE. */
+typedef struct {
+	/** Filtering Identifier (FID) (not supported by all switches) */
+	u16 fid;
+	/** MAC Address to be removed from the table. */
+	u8 mac[ETH_ALEN];
+	/** Source/Destination MAC address filtering flag (GSWIP-3.1 only)
+	    Value 0 - not filter, 1 - source address filter,
+	    2 - destination address filter, 3 - both source and destination filter.
+
+	    \remarks
+	    Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6
+	    "Source MAC Address Filtering and Destination MAC Address Filtering"
+	    for more detail. */
+	u8 filter_flag;
+	/** TCI for (GSWIP-3.2) B-Step
+	    Bit [0:11] - VLAN ID
+	    Bit [12] - VLAN CFI/DEI
+	    Bit [13:15] - VLAN PRI */
+	u16 tci;
+} mxl862xx_mac_table_remove_t;
+
+/** \brief Bridge configuration mask.
+    Used by \ref MXL862XX_BRIDGE_config_t. */
+typedef enum {
+	/** Mask for \ref MXL862XX_BRIDGE_config_t::b_mac_learning_limit_enable
+	    and \ref MXL862XX_BRIDGE_config_t::n_mac_learning_limit. */
+	MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT = 0x00000001,
+	/** Mask for \ref MXL862XX_BRIDGE_config_t::n_mac_learning_count */
+	MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT = 0x00000002,
+	/** Mask for \ref MXL862XX_BRIDGE_config_t::n_learning_discard_event */
+	MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT = 0x00000004,
+	/** Mask for \ref MXL862XX_BRIDGE_config_t::b_sub_metering_enable and
+	    \ref MXL862XX_BRIDGE_config_t::n_traffic_sub_meter_id */
+	MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER = 0x00000008,
+	/** Mask for \ref MXL862XX_BRIDGE_config_t::e_forward_broadcast,
+	    \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_multicast_ip,
+	    \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_multicast_non_ip,
+	    and \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_unicast. */
+	MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE = 0x00000010,
+
+	/** Enable all */
+	MXL862XX_BRIDGE_CONFIG_MASK_ALL = 0x7FFFFFFF,
+	/** Bypass any check for debug purpose */
+	MXL862XX_BRIDGE_CONFIG_MASK_FORCE = 0x80000000
+} mxl862xx_bridge_config_mask_t;
+
+/** \brief Bridge forwarding type of packet.
+    Used by \ref MeXL862XX_BRIDGE_port_config_t. */
+typedef enum {
+	/** Packet is flooded to port members of ingress bridge port */
+	MXL862XX_BRIDGE_FORWARD_FLOOD = 0,
+	/** Packet is dscarded */
+	MXL862XX_BRIDGE_FORWARD_DISCARD = 1,
+	/** Packet is forwarded to logical port 0 CTP port 0 bridge port 0 */
+	MXL862XX_BRIDGE_FORWARD_CPU = 2
+} mxl862xx_bridge_forward_mode_t;
+
+/** \brief Bridge Configuration.
+    Used by \ref MXL862XX_BRIDGE_CONFIG_SET and \ref MXL862XX_BRIDGE_CONFIG_GET. */
+typedef struct {
+	/** Bridge ID (FID) allocated by \ref MXL862XX_BRIDGE_ALLOC.
+
+	    \remarks
+	    If \ref MXL862XX_BRIDGE_config_t::e_mask has
+	    \ref MXL862XX_Bridge_config_mask_t::MXL862XX_BRIDGE_CONFIG_MASK_FORCE, this field is
+	    absolute index of Bridge (FID) in hardware for debug purpose, bypassing
+	    any check. */
+	u16 bridge_id;
+
+	/** Mask for updating/retrieving fields. */
+	mxl862xx_bridge_config_mask_t mask;
+
+	/** Enable MAC learning limitation. */
+	bool mac_learning_limit_enable;
+	/** Max number of MAC can be learned in this bridge (all bridge ports). */
+	u16 mac_learning_limit;
+
+	/** Get number of MAC address learned from this bridge port. */
+	u16 mac_learning_count;
+
+	/** Number of learning discard event due to hardware resource not available.
+
+	    \remarks
+	    This is discard event due to either MAC table full or Hash collision.
+	    Discard due to n_mac_learning_count reached is not counted in this field. */
+	u32 learning_discard_event;
+
+	/** Traffic metering on type of traffic (such as broadcast, multicast,
+	    unknown unicast, etc) applies. */
+	bool sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX];
+	/** Meter for bridge process with specific type (such as broadcast,
+	    multicast, unknown unicast, etc). Need pre-allocated for each type. */
+	u16 traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX];
+
+	/** Forwarding mode of broadcast traffic. */
+	mxl862xx_bridge_forward_mode_t forward_broadcast;
+	/** Forwarding mode of unknown multicast IP traffic. */
+	mxl862xx_bridge_forward_mode_t forward_unknown_multicast_ip;
+	/** Forwarding mode of unknown multicast non-IP traffic. */
+	mxl862xx_bridge_forward_mode_t forward_unknown_multicast_non_ip;
+	/** Forwarding mode of unknown unicast traffic. */
+	mxl862xx_bridge_forward_mode_t forward_unknown_unicast;
+} mxl862xx_bridge_config_t;
+
+/** \brief Aging Timer Value.
+    Used by \ref mxl862xx_cfg_t. */
+typedef enum {
+	/** 1 second aging time */
+	MXL862XX_AGETIMER_1_SEC	= 1,
+	/** 10 seconds aging time */
+	MXL862XX_AGETIMER_10_SEC	= 2,
+	/** 300 seconds aging time */
+	MXL862XX_AGETIMER_300_SEC	= 3,
+	/** 1 hour aging time */
+	MXL862XX_AGETIMER_1_HOUR	= 4,
+	/** 24 hours aging time */
+	MXL862XX_AGETIMER_1_DAY	= 5,
+	/** Custom aging time in seconds */
+	MXL862XX_AGETIMER_CUSTOM  = 6
+} mxl862xx_age_timer_t;
+
+/** \brief Global Switch configuration Attributes.
+    Used by \ref MXL862XX_CFG_SET and \ref MXL862XX_CFG_GET. */
+typedef struct {
+	/** MAC table aging timer. After this timer expires the MAC table
+	    entry is aged out. */
+	mxl862xx_age_timer_t mac_table_age_timer;
+	/** If eMAC_TableAgeTimer = MXL862XX_AGETIMER_CUSTOM, this variable defines
+	    MAC table aging timer in seconds. */
+	u32 age_timer;
+	/** Maximum Ethernet packet length. */
+	u16 max_packet_len;
+	/** Automatic MAC address table learning limitation consecutive action.
+	    These frame addresses are not learned, but there exists control as to whether
+	    the frame is still forwarded or dropped.
+
+	    - False: Drop
+	    - True: Forward
+	*/
+	bool learning_limit_action;
+	/** Accept or discard MAC port locking violation packets.
+	    MAC spoofing detection features identifies ingress packets that carry
+	    a MAC source address which was previously learned on a different
+	    ingress port (learned by MAC bridging table). This also applies to
+	    static added entries. MAC address port locking is configured on
+	    port level by 'bLearningMAC_PortLock'.
+
+	    - False: Drop
+		 - True: Forward
+	*/
+	bool mac_locking_action;
+	/** Accept or discard MAC spoofing and port MAC locking violation packets.
+	    MAC spoofing detection features identifies ingress packets that carry
+	    a MAC source address which was previously learned on a different
+	    ingress port (learned by MAC bridging table). This also applies to
+	    static added entries. MAC spoofing detection is enabled on port
+	    level by 'bMAC_SpoofingDetection'.
+
+	    - False: Drop
+	    - True: Forward
+	*/
+	bool mac_spoofing_action;
+	/** Pause frame MAC source address mode. If enabled, use the alternative
+	    address specified with 'nMAC'. */
+	bool pause_mac_mode_src;
+	/** Pause frame MAC source address. */
+	u8	pause_mac_src[ETH_ALEN];
+} mxl862xx_cfg_t;
+
+/** \brief Sets the portmap flag of a port_iD variable.
+    Some Switch API commands allow to use a port index as portmap variable.
+    This requires that the MSB bit is set to indicate that this variable
+    contains a portmap, instead of a port index.
+    In portmap mode, every value bit represents an Ethernet port.
+    LSB represents Port 0 with incrementing counting.
+    The (MSB - 1) bit represent the last port. */
+#define MXL862XX_PORTMAP_FLAG_SET(var_type) (1 << (sizeof(((var_type *)0)->port_id) * 8 - 1))
+
+/** \brief Checks the portmap flag of a port_iD variable.
+    Some Switch API commands allow to use a port index as portmap variable.
+    This requires that the MSB bit is set to indicate that this variable
+    contains a portmap, instead of a port index.
+    In portmap mode, every value bit represents an Ethernet port.
+    LSB represents Port 0 with incrementing counting.
+    The (MSB - 1) bit represent the last port. */
+#define MXL862XX_PORTMAP_FLAG_GET(var_type) (1 << (sizeof(((var_type *)0)->port_id) * 8 - 1))
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+#include "mxl862xx_ctp.h"
+#include "mxl862xx_flow.h"
+
+struct sys_fw_image_version {
+	uint8_t iv_major;
+	uint8_t iv_minor;
+	uint16_t iv_revision;
+	uint32_t iv_build_num;
+};
+
+int sys_misc_fw_version(const mxl862xx_device_t *dummy,
+			struct sys_fw_image_version *sys_img_ver);
+
+int mxl862xx_register_mod(const mxl862xx_device_t *, mxl862xx_register_mod_t *);
+int mxl862xx_port_link_cfg_set(const mxl862xx_device_t *, mxl862xx_port_link_cfg_t *);
+int mxl862xx_port_link_cfg_get(const mxl862xx_device_t *, mxl862xx_port_link_cfg_t *);
+int mxl862xx_port_cfg_set(const mxl862xx_device_t *, mxl862xx_port_cfg_t *);
+int mxl862xx_port_cfg_get(const mxl862xx_device_t *, mxl862xx_port_cfg_t *);
+/* Bridge */
+int mxl862xx_bridge_alloc(const mxl862xx_device_t *, mxl862xx_bridge_alloc_t *);
+int mxl862xx_bridge_free(const mxl862xx_device_t *, mxl862xx_bridge_alloc_t *);
+int mxl862xx_bridge_config_set(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *);
+int mxl862xx_bridge_config_get(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *);
+/* Bridge Port */
+int mxl862xx_bridge_port_alloc(const mxl862xx_device_t *,
+					 mxl862xx_bridge_port_alloc_t *);
+int mxl862xx_bridge_port_free(const mxl862xx_device_t *,
+					 mxl862xx_bridge_port_alloc_t *);
+int mxl862xx_bridge_port_config_set(const mxl862xx_device_t *,
+				    mxl862xx_bridge_port_config_t *);
+int mxl862xx_bridge_port_config_get(const mxl862xx_device_t *,
+				    mxl862xx_bridge_port_config_t *);
+/* Debug */
+int mxl862xx_debug_rmon_port_get(const mxl862xx_device_t *,
+				 mxl862xx_debug_rmon_port_cnt_t *);
+
+int mxl862xx_mac_table_clear(const mxl862xx_device_t *);
+int mxl862xx_mac_table_clear_cond(const mxl862xx_device_t *,
+				  mxl862xx_mac_table_clear_cond_t *);
+int mxl862xx_mac_table_entry_read(const mxl862xx_device_t *, mxl862xx_mac_table_read_t *);
+int mxl862xx_mac_table_entry_add(const mxl862xx_device_t *, mxl862xx_mac_table_add_t *);
+int mxl862xx_mac_table_entry_remove(const mxl862xx_device_t *,
+              mxl862xx_mac_table_remove_t *);
+int mxl862xx_stp_port_cfg_set(const mxl862xx_device_t *, mxl862xx_stp_port_cfg_t *parm);
+
+int mxl862xx_ss_sp_tag_get(const mxl862xx_device_t *, mxl862xx_ss_sp_tag_t *);
+int mxl862xx_ss_sp_tag_set(const mxl862xx_device_t *, mxl862xx_ss_sp_tag_t *);
+
+int mxl862xx_ctp_port_config_get(const mxl862xx_device_t *, mxl862xx_ctp_port_config_t *);
+int mxl862xx_ctp_port_config_set(const mxl862xx_device_t *, mxl862xx_ctp_port_config_t *);
+int mxl862xx_ctp_port_assignment_set(const mxl862xx_device_t *,
+				     mxl862xx_ctp_port_assignment_t *);
+int mxl862xx_ctp_port_assignment_get(const mxl862xx_device_t *,
+				     mxl862xx_ctp_port_assignment_t *);
+int mxl862xx_monitor_port_cfg_get(const mxl862xx_device_t *, mxl862xx_monitor_port_cfg_t *);
+int mxl862xx_monitor_port_cfg_set(const mxl862xx_device_t *, mxl862xx_monitor_port_cfg_t *);
+
+int mxl862xx_extended_vlan_alloc(const mxl862xx_device_t *,
+				 mxl862xx_extendedvlan_alloc_t *);
+int mxl862xx_extended_vlan_set(const mxl862xx_device_t *,
+			       mxl862xx_extendedvlan_config_t *);
+int mxl862xx_extended_vlan_get(const mxl862xx_device_t *,
+			       mxl862xx_extendedvlan_config_t *);
+int mxl862xx_extended_vlan_free(const mxl862xx_device_t *,
+				mxl862xx_extendedvlan_alloc_t *);
+int mxl862xx_vlan_filter_alloc(const mxl862xx_device_t *, mxl862xx_vlanfilter_alloc_t *);
+int mxl862xx_vlan_filter_set(const mxl862xx_device_t *, mxl862xx_vlanfilter_config_t *);
+int mxl862xx_vlan_filter_get(const mxl862xx_device_t *, mxl862xx_vlanfilter_config_t *);
+int mxl862xx_vlan_filter_free(const mxl862xx_device_t *, mxl862xx_vlanfilter_alloc_t *);
+int mxl862xx_cfg_get(const mxl862xx_device_t *, mxl862xx_cfg_t *);
+int mxl862xx_cfg_set(const mxl862xx_device_t *, mxl862xx_cfg_t *);
+#endif /* _MXL862XX_API_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h
new file mode 100644
index 0000000..683fa8f
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h
@@ -0,0 +1,336 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * drivers/net/dsa/host_api/mxl862xx_ctp.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+#ifndef _MXL862XX_CTP_H_
+#define _MXL862XX_CTP_H_
+
+#include "mxl862xx_types.h"
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+/** \brief Logical port mode.
+    Used by \ref MXL862XX_CTP_port_assignment_t. */
+typedef enum {
+	/** WLAN with 8-bit station ID */
+	MXL862XX_LOGICAL_PORT_8BIT_WLAN = 0,
+	/** WLAN with 9-bit station ID */
+	MXL862XX_LOGICAL_PORT_9BIT_WLAN = 1,
+	/** GPON OMCI context */
+	MXL862XX_LOGICAL_PORT_GPON = 2,
+	/** EPON context */
+	MXL862XX_LOGICAL_PORT_EPON = 3,
+	/** G.INT context */
+	MXL862XX_LOGICAL_PORT_GINT = 4,
+	/** Others (sub interface ID is 0 by default) */
+	MXL862XX_LOGICAL_PORT_OTHER = 0xFF,
+} mxl862xx_logical_port_mode_t;
+
+/** \brief CTP Port configuration mask.
+    Used by \ref MXL862XX_CTP_port_config_t. */
+typedef enum {
+	/** Mask for \ref MXL862XX_CTP_port_config_t::bridge_port_id */
+	MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGE_PORT_ID = 0x00000001,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_forced_traffic_class and
+	    \ref MXL862XX_CTP_port_config_t::n_default_traffic_class */
+	MXL862XX_CTP_PORT_CONFIG_MASK_FORCE_TRAFFIC_CLASS = 0x00000002,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_extended_vlan_enable and
+	    \ref MXL862XX_CTP_port_config_t::n_ingress_extended_vlan_block_id */
+	MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN = 0x00000004,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_extended_vlan_igmp_enable and
+	    \ref MXL862XX_CTP_port_config_t::n_ingress_extended_vlan_block_id_igmp */
+	MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN_IGMP = 0x00000008,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_extended_vlan_enable and
+	    \ref MXL862XX_CTP_port_config_t::n_egress_extended_vlan_block_id */
+	MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN = 0x00000010,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_extended_vlan_igmp_enable and
+	    \ref MXL862XX_CTP_port_config_t::n_egress_extended_vlan_block_id_igmp */
+	MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN_IGMP = 0x00000020,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_nto1Vlan_enable */
+	MXL862XX_CTP_PORT_CONFIG_MASK_INRESS_NTO1_VLAN = 0x00000040,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_nto1Vlan_enable */
+	MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_NTO1_VLAN = 0x00000080,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::e_ingress_marking_mode */
+	MXL862XX_CTP_PORT_CONFIG_INGRESS_MARKING = 0x00000100,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::e_egress_marking_mode */
+	MXL862XX_CTP_PORT_CONFIG_EGRESS_MARKING = 0x00000200,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_marking_override_enable and
+	    \ref MXL862XX_CTP_port_config_t::e_egress_marking_mode_override */
+	MXL862XX_CTP_PORT_CONFIG_EGRESS_MARKING_OVERRIDE = 0x00000400,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::e_egress_remarking_mode */
+	MXL862XX_CTP_PORT_CONFIG_EGRESS_REMARKING = 0x00000800,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_metering_enable and
+	    \ref MXL862XX_CTP_port_config_t::n_ingress_traffic_meter_id */
+	MXL862XX_CTP_PORT_CONFIG_INGRESS_METER = 0x00001000,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_metering_enable and
+	    \ref MXL862XX_CTP_port_config_t::n_egress_traffic_meter_id */
+	MXL862XX_CTP_PORT_CONFIG_EGRESS_METER = 0x00002000,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_bridging_bypass,
+	    \ref MXL862XX_CTP_port_config_t::n_dest_logical_port_id,
+	    \ref MXL862XX_CTP_port_config_t::b_pmapper_enable,
+	    \ref MXL862XX_CTP_port_config_t::n_dest_sub_if_id_group,
+	    \ref MXL862XX_CTP_port_config_t::e_pmapper_mapping_mode
+	    \ref mxl862xx_bridge_port_config_t::b_pmapper_id_valid and
+	    \ref MXL862XX_CTP_port_config_t::s_pmapper */
+	MXL862XX_CTP_PORT_CONFIG_BRIDGING_BYPASS = 0x00004000,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::n_first_flow_entry_index and
+	    \ref MXL862XX_CTP_port_config_t::n_number_of_flow_entries */
+	MXL862XX_CTP_PORT_CONFIG_FLOW_ENTRY = 0x00008000,
+	/** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_loopback_enable,
+	    \ref MXL862XX_CTP_port_config_t::b_ingress_da_sa_swap_enable,
+	    \ref MXL862XX_CTP_port_config_t::b_egress_loopback_enable,
+	    \ref MXL862XX_CTP_port_config_t::b_egress_da_sa_swap_enable,
+	    \ref MXL862XX_CTP_port_config_t::b_ingress_mirror_enable and
+	    \ref MXL862XX_CTP_port_config_t::b_egress_mirror_enable */
+	MXL862XX_CTP_PORT_CONFIG_LOOPBACK_AND_MIRROR = 0x00010000,
+
+	/** Enable all fields */
+	MXL862XX_CTP_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF,
+	/** Bypass any check for debug purpose */
+	MXL862XX_CTP_PORT_CONFIG_MASK_FORCE = 0x80000000
+} mxl862xx_ctp_port_config_mask_t;
+
+/** \brief CTP Port Assignment/association with logical port.
+    Used by \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, \ref MXL862XX_CTP_PORT_ASSIGNMENT_SET
+    and \ref MXL862XX_CTP_PORT_ASSIGNMENT_GET. */
+typedef struct {
+	/** Logical Port Id. The valid range is hardware dependent. */
+	u8 logical_port_id;
+
+	/** First CTP Port ID mapped to above logical port ID.
+
+	    \remarks
+	    For \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, this is output when CTP
+	    allocation is successful. For other APIs, this is input. */
+	u16 first_ctp_port_id;
+	/** Total number of CTP Ports mapped above logical port ID. */
+	u16 number_of_ctp_port;
+
+	/** Logical port mode to define sub interface ID format. */
+	mxl862xx_logical_port_mode_t mode;
+
+	/** Bridge ID (FID)
+
+	    \remarks
+	    For \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, this is input. Each CTP allocated
+	    is mapped to Bridge Port given by this field. The Bridge Port will be
+	    configured to use first CTP
+	    (\ref MXL862XX_CTP_port_assignment_t::n_first_ctp_port_id) as egress CTP.
+	    For other APIs, this is ignored. */
+	u16 bridge_port_id;
+} mxl862xx_ctp_port_assignment_t;
+
+/** \brief CTP Port Configuration.
+    Used by \ref MXL862XX_CTP_PORT_CONFIG_SET and \ref MXL862XX_CTP_PORT_CONFIG_GET. */
+typedef struct {
+	/** Logical Port Id. The valid range is hardware dependent.
+	    If \ref MXL862XX_CTP_port_config_t::e_mask has
+	    \ref MXL862XX_Ctp_port_config_mask_t::MXL862XX_CTP_PORT_CONFIG_MASK_FORCE, this field
+	    is ignored. */
+	u8 logical_port_id;
+
+	/** Sub interface ID group. The valid range is hardware/protocol dependent.
+
+	    \remarks
+	    Sub interface ID group is defined for each of \ref MXL862XX_Logical_port_mode_t.
+	    For both \ref MXL862XX_LOGICAL_PORT_8BIT_WLAN and
+	    \ref MXL862XX_LOGICAL_PORT_9BIT_WLAN, this field is VAP.
+	    For \ref MXL862XX_LOGICAL_PORT_GPON, this field is GEM index.
+	    For \ref MXL862XX_LOGICAL_PORT_EPON, this field is stream index.
+	    For \ref MXL862XX_LOGICAL_PORT_GINT, this field is LLID.
+	    For others, this field is 0.
+	    If \ref MXL862XX_CTP_port_config_t::e_mask has
+	    \ref MXL862XX_Ctp_port_config_mask_t::MXL862XX_CTP_PORT_CONFIG_MASK_FORCE, this field
+	    is absolute index of CTP in hardware for debug purpose, bypassing
+	    any check. */
+	u16 n_sub_if_id_group;
+
+	/** Mask for updating/retrieving fields. */
+	mxl862xx_ctp_port_config_mask_t mask;
+
+	/** Ingress Bridge Port ID to which this CTP port is associated for ingress
+	    traffic. */
+	u16 bridge_port_id;
+
+	/** Default traffic class can not be overridden by other rules (except
+	    traffic flow table and special tag) in processing stages. */
+	bool forced_traffic_class;
+	/** Default traffic class associated with all ingress traffic from this CTP
+	    Port. */
+	u8 default_traffic_class;
+
+	/** Enable Extended VLAN processing for ingress non-IGMP traffic. */
+	bool ingress_extended_vlan_enable;
+	/** Extended VLAN block allocated for ingress non-IGMP traffic. It defines
+	    extended VLAN process for ingress non-IGMP traffic. Valid when
+	    b_ingress_extended_vlan_enable is TRUE. */
+	u16 ingress_extended_vlan_block_id;
+	/** Extended VLAN block size for ingress non-IGMP traffic. This is optional.
+	    If it is 0, the block size of n_ingress_extended_vlan_block_id will be used.
+	    Otherwise, this field will be used. */
+	u16 ingress_extended_vlan_block_size;
+	/** Enable extended VLAN processing for ingress IGMP traffic. */
+	bool ingress_extended_vlan_igmp_enable;
+	/** Extended VLAN block allocated for ingress IGMP traffic. It defines
+	    extended VLAN process for ingress IGMP traffic. Valid when
+	    b_ingress_extended_vlan_igmp_enable is TRUE. */
+	u16 ingress_extended_vlan_block_id_igmp;
+	/** Extended VLAN block size for ingress IGMP traffic. This is optional.
+	    If it is 0, the block size of n_ingress_extended_vlan_block_id_igmp will be
+	    used. Otherwise, this field will be used. */
+	u16 ingress_extended_vlan_block_size_igmp;
+
+	/** Enable extended VLAN processing for egress non-IGMP traffic. */
+	bool egress_extended_vlan_enable;
+	/** Extended VLAN block allocated for egress non-IGMP traffic. It defines
+	    extended VLAN process for egress non-IGMP traffic. Valid when
+	    b_egress_extended_vlan_enable is TRUE. */
+	u16 egress_extended_vlan_block_id;
+	/** Extended VLAN block size for egress non-IGMP traffic. This is optional.
+	    If it is 0, the block size of n_egress_extended_vlan_block_id will be used.
+	    Otherwise, this field will be used. */
+	u16 egress_extended_vlan_block_size;
+	/** Enable extended VLAN processing for egress IGMP traffic. */
+	bool egress_extended_vlan_igmp_enable;
+	/** Extended VLAN block allocated for egress IGMP traffic. It defines
+	    extended VLAN process for egress IGMP traffic. Valid when
+	    b_egress_extended_vlan_igmp_enable is TRUE. */
+	u16 egress_extended_vlan_block_id_igmp;
+	/** Extended VLAN block size for egress IGMP traffic. This is optional.
+	    If it is 0, the block size of n_egress_extended_vlan_block_id_igmp will be
+	    used. Otherwise, this field will be used. */
+	u16 egress_extended_vlan_block_size_igmp;
+
+	/** For WLAN type logical port, this should be FALSE. For other types, if
+	     enabled and ingress packet is VLAN tagged, outer VLAN ID is used for
+	    "n_sub_if_id" field in MAC table, otherwise, 0 is used for "n_sub_if_id". */
+	bool ingress_nto1vlan_enable;
+	/** For WLAN type logical port, this should be FALSE. For other types, if
+	     enabled and egress packet is known unicast, outer VLAN ID is from
+	     "n_sub_if_id" field in MAC table. */
+	bool egress_nto1vlan_enable;
+
+	/** Ingress color marking mode for ingress traffic. */
+	mxl862xx_color_marking_mode_t ingress_marking_mode;
+	/** Egress color marking mode for ingress traffic at egress priority queue
+	    color marking stage */
+	mxl862xx_color_marking_mode_t egress_marking_mode;
+	/** Egress color marking mode override color marking mode from last stage. */
+	bool egress_marking_override_enable;
+	/** Egress color marking mode for egress traffic. Valid only when
+	    b_egress_marking_override is TRUE. */
+	mxl862xx_color_marking_mode_t egress_marking_mode_override;
+
+	/** Color remarking for egress traffic. */
+	mxl862xx_color_remarking_mode_t egress_remarking_mode;
+
+	/** Traffic metering on ingress traffic applies. */
+	bool ingress_metering_enable;
+	/** Meter for ingress CTP process.
+
+	    \remarks
+	    Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before CTP
+	    port configuration. If this CTP port is re-set, the last used meter
+	    should be released. */
+	u16 ingress_traffic_meter_id;
+	/** Traffic metering on egress traffic applies. */
+	bool egress_metering_enable;
+	/** Meter for egress CTP process.
+
+	    \remarks
+	    Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before CTP
+	    port configuration. If this CTP port is re-set, the last used meter
+	    should be released. */
+	u16 egress_traffic_meter_id;
+
+	/** Ingress traffic bypass bridging/multicast processing. Following
+	    parameters are used to determine destination. Traffic flow table is not
+	    bypassed. */
+	bool bridging_bypass;
+	/** When b_bridging_bypass is TRUE, this field defines destination logical
+	    port. */
+	u8 dest_logical_port_id;
+	/** When b_bridging_bypass is TRUE, this field indicates whether to use
+	    \ref MXL862XX_CTP_port_config_t::n_dest_sub_if_id_group or
+	    \ref MXL862XX_CTP_port_config_t::e_pmapper_mapping_mode/
+	    \ref MXL862XX_CTP_port_config_t::s_pmapper. */
+	bool pmapper_enable;
+	/** When b_bridging_bypass is TRUE and b_pmapper_enable is FALSE, this field
+	    defines destination sub interface ID group. */
+	u16 dest_sub_if_id_group;
+	/** When b_bridging_bypass is TRUE and b_pmapper_enable is TRUE, this field
+	    selects either DSCP or PCP to derive sub interface ID. */
+	mxl862xx_pmapper_mapping_mode_t pmapper_mapping_mode;
+	/** When b_pmapper_enable is TRUE, P-mapper is used. This field determines
+	    whether s_pmapper.n_pmapper_id is valid. If this field is TRUE, the
+	    P-mapper is re-used and no allocation of new P-mapper or value
+	    change in the P-mapper. If this field is FALSE, allocation is
+	    taken care in the API implementation. */
+	bool pmapper_id_valid;
+	/** When b_bridging_bypass is TRUE and b_pmapper_enable is TRUE, P-mapper is
+	    used. If b_pmapper_id_valid is FALSE, API implementation need take care
+	    of P-mapper allocation, and maintain the reference counter of
+	    P-mapper used multiple times. If b_pmapper_id_valid is TRUE, only
+	    s_pmapper.n_pmapper_id is used to associate the P-mapper, and there is
+	    no allocation of new P-mapper or value change in the P-mapper. */
+	mxl862xx_pmapper_t pmapper;
+
+	/** First traffic flow table entry is associated to this CTP port. Ingress
+	    traffic from this CTP port will go through traffic flow table search
+	    starting from n_first_flow_entry_index. Should be times of 4. */
+	u16 first_flow_entry_index;
+	/** Number of traffic flow table entries are associated to this CTP port.
+	    Ingress traffic from this CTP port will go through PCE rules search
+	    ending at (n_first_flow_entry_index+n_number_of_flow_entries)-1. Should
+	    be times of 4. */
+	u16 number_of_flow_entries;
+
+	/** Ingress traffic from this CTP port will be redirected to ingress
+	    logical port of this CTP port with source sub interface ID used as
+	    destination sub interface ID. Following processing except traffic
+	    flow table search is bypassed if loopback enabled. */
+	bool ingress_loopback_enable;
+	/** Destination/Source MAC address of ingress traffic is swapped before
+	    transmitted (not swapped during PCE processing stages). If destination
+	    is multicast, there is no swap, but source MAC address is replaced
+	    with global configurable value. */
+	bool ingress_da_sa_swap_enable;
+	/** Egress traffic to this CTP port will be redirected to ingress logical
+	    port with same sub interface ID as ingress. */
+	bool egress_loopback_enable;
+	/** Destination/Source MAC address of egress traffic is swapped before
+	    transmitted. */
+	bool egress_da_sa_swap_enable;
+
+	/** If enabled, ingress traffic is mirrored to the monitoring port.
+	    \remarks
+	    This should be used exclusive with b_ingress_loopback_enable. */
+	bool ingress_mirror_enable;
+	/** If enabled, egress traffic is mirrored to the monitoring port.
+	    \remarks
+	    This should be used exclusive with b_egress_loopback_enable. */
+	bool egress_mirror_enable;
+} mxl862xx_ctp_port_config_t;
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+#endif /*_MXL862XX_CTP_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h
new file mode 100644
index 0000000..69fde14
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * drivers/net/dsa/host_api/mxl862xx_flow.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_FLOW_H_
+#define _MXL862XX_FLOW_H_
+
+#include "mxl862xx_types.h"
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+#define MPCE_RULES_INDEX 0
+#define MPCE_RULES_INDEX_LAST (MPCE_RULES_INDEX + 7)
+#define EAPOL_PCE_RULE_INDEX 8
+#define BPDU_PCE_RULE_INDEX 9
+#define PFC_PCE_RULE_INDEX 10
+
+/** \brief Register access parameter to directly modify internal registers.
+    Used by \ref GSW_REGISTER_MOD. */
+typedef struct {
+	/** Register Address Offset for modifiation. */
+	u16 reg_addr;
+	/** Value to write to 'reg_addr'. */
+	u16 data;
+	/** Mask of bits to be modified. 1 to modify, 0 to ignore. */
+	u16 mask;
+} mxl862xx_register_mod_t;
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+#endif /* _MXL862XX_FLOW_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c
new file mode 100644
index 0000000..1c91d2c
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c
@@ -0,0 +1,514 @@
+// spdx-license-identifier: gpl-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_host_api_impl.c - dsa driver for maxlinear mxl862xx switch chips family
+ *
+ * copyright (c) 2024 maxlinear inc.
+ *
+ * this program is free software; you can redistribute it and/or
+ * modify it under the terms of the gnu general public license
+ * as published by the free software foundation; either version 2
+ * of the license, or (at your option) any later version.
+ *
+ * this program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * merchantability or fitness for a particular purpose.  see the
+ * gnu general public license for more details.
+ *
+ * you should have received a copy of the gnu general public license
+ * along with this program; if not, write to the free software
+ * foundation, inc., 51 franklin street, fifth floor, boston, ma  02110-1301, usa.
+ *
+ */
+
+#include "mxl862xx_mmd_apis.h"
+
+#define CTRL_BUSY_MASK BIT(15)
+#define CTRL_CMD_MASK (BIT(15) - 1)
+
+#define MAX_BUSY_LOOP 1000 /* roughly 10ms */
+
+#define THR_RST_DATA 5
+
+#define ENABLE_GETSET_OPT 1
+
+//#define C22_MDIO
+
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+static struct {
+	uint16_t ctrl;
+	int16_t ret;
+	mmd_api_data_t data;
+} shadow = { .ctrl = ~0, .ret = -1, .data = { { 0 } } };
+#endif
+
+/* required for Clause 22 extended read/write access */
+#define MXL862XX_MMDDATA			0xE
+#define MXL862XX_MMDCTRL			0xD
+
+#define MXL862XX_ACTYPE_ADDRESS		(0 << 14)
+#define MXL862XX_ACTYPE_DATA			(1 << 14)
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#else
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0))
+#include <linux/delay.h>
+#endif
+
+#ifdef C22_MDIO
+/**
+ *  write access to MMD register of PHYs via Clause 22 extended access
+ */
+static int __mxl862xx_c22_ext_mmd_write(const mxl862xx_device_t *dev, struct mii_bus *bus, int sw_addr, int mmd,
+			    int reg, u16 val)
+{
+	int res;
+
+	/* Set the DevID for Write Command */
+	res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, mmd);
+	if (res < 0)
+		goto error;
+
+	/* Issue the write command */
+	res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, reg);
+	if (res < 0)
+		goto error;
+
+	/* Set the DevID for Write Command */
+	res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, MXL862XX_ACTYPE_DATA | mmd);
+	if (res < 0)
+		goto error;
+
+	/* Issue the write command */
+	res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, val);
+	if (res < 0)
+		goto error;
+
+error:
+	return res;
+}
+
+/**
+ *  read access to MMD register of PHYs via Clause 22 extended access
+ */
+static int __mxl862xx_c22_ext_mmd_read(const mxl862xx_device_t *dev, struct mii_bus *bus, int sw_addr, int mmd, int reg)
+{
+	int res;
+
+/* Set the DevID for Write Command */
+	res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, mmd);
+	if (res < 0)
+		goto error;
+
+	/* Issue the write command */
+	res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, reg);
+	if (res < 0)
+		goto error;
+
+	/* Set the DevID for Write Command */
+	res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, MXL862XX_ACTYPE_DATA | mmd);
+	if (res < 0)
+		goto error;
+
+	/* Read the data */
+	res = __mdiobus_read(bus, sw_addr, MXL862XX_MMDDATA);
+	if (res < 0)
+		goto error;
+
+error:
+	return res;
+}
+#endif
+
+int mxl862xx_read(const mxl862xx_device_t *dev, uint32_t regaddr)
+{
+	int mmd = MXL862XX_MMD_DEV;
+#ifdef C22_MDIO
+	int ret = __mxl862xx_c22_ext_mmd_read(dev, dev->bus, dev->sw_addr, mmd, regaddr);
+#else
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0))
+	u32 addr = MII_ADDR_C45 | (mmd << 16) | (regaddr & 0xffff);
+	int ret = __mdiobus_read(dev->bus, dev->sw_addr, addr);
+#else
+	int ret = __mdiobus_c45_read(dev->bus, dev->sw_addr, mmd, regaddr);
+#endif
+#endif
+	return ret;
+}
+
+int mxl862xx_write(const mxl862xx_device_t *dev, uint32_t regaddr, uint16_t data)
+{
+	int mmd = MXL862XX_MMD_DEV;
+#ifdef C22_MDIO
+	int ret = __mxl862xx_c22_ext_mmd_write(dev, dev->bus, dev->sw_addr, mmd, regaddr, data);
+#else
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0))
+	u32 addr = MII_ADDR_C45 | (mmd << 16) | (regaddr & 0xffff);
+	int ret = __mdiobus_write(dev->bus, dev->sw_addr, addr, data);
+#else
+	int ret = __mdiobus_c45_write(dev->bus, dev->sw_addr, mmd, regaddr, data);
+#endif
+#endif
+	return ret;
+}
+
+static int __wait_ctrl_busy(const mxl862xx_device_t *dev)
+{
+	int ret, i;
+
+	for (i = 0; i < MAX_BUSY_LOOP; i++) {
+		ret = mxl862xx_read(dev, MXL862XX_MMD_REG_CTRL);
+		if (ret < 0) {
+			goto busy_check_exit;
+		}
+
+		if (!(ret & CTRL_BUSY_MASK)) {
+			ret = 0;
+			goto busy_check_exit;
+		}
+
+		usleep_range(10, 15);
+	}
+	ret = -ETIMEDOUT;
+busy_check_exit:
+	return ret;
+}
+
+static int __mxl862xx_rst_data(const mxl862xx_device_t *dev)
+{
+	int ret;
+
+	ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL,
+			MMD_API_RST_DATA | CTRL_BUSY_MASK);
+	if (ret < 0)
+		return ret;
+
+	return __wait_ctrl_busy(dev);
+}
+
+static int __mxl862xx_set_data(const mxl862xx_device_t *dev, uint16_t words)
+{
+	int ret;
+	uint16_t cmd;
+
+	ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET,
+			MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(uint16_t));
+	if (ret < 0)
+		return ret;
+
+	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1;
+	if (!(cmd < 2))
+		return -EINVAL;
+
+	cmd += MMD_API_SET_DATA_0;
+	ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK);
+	if (ret < 0)
+		return ret;
+
+	return __wait_ctrl_busy(dev);
+}
+
+static int __mxl862xx_get_data(const mxl862xx_device_t *dev, uint16_t words)
+{
+	int ret;
+	uint16_t cmd;
+
+	ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET,
+			MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(uint16_t));
+	if (ret < 0)
+		return ret;
+
+	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE;
+	if (!(cmd > 0 && cmd < 3))
+		return -EINVAL;
+	cmd += MMD_API_GET_DATA_0;
+	ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK);
+	if (ret < 0)
+		return ret;
+
+	return __wait_ctrl_busy(dev);
+}
+
+static int __mxl862xx_send_cmd(const mxl862xx_device_t *dev, uint16_t cmd, uint16_t size,
+			  int16_t *presult)
+{
+	int ret;
+
+	ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET, size);
+	if (ret < 0) {
+		return ret;
+	}
+
+	ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK);
+	if (ret < 0) {
+		return ret;
+	}
+
+	ret = __wait_ctrl_busy(dev);
+	if (ret < 0) {
+		return ret;
+	}
+
+	ret = mxl862xx_read(dev, MXL862XX_MMD_REG_LEN_RET);
+	if (ret < 0) {
+		return ret;
+	}
+
+	*presult = ret;
+	return 0;
+}
+
+static bool __mxl862xx_cmd_r_valid(uint16_t cmd_r)
+{
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+	return (shadow.ctrl == cmd_r && shadow.ret >= 0) ? true : false;
+#else
+	return false;
+#endif
+}
+
+/* This is usually used to implement CFG_SET command.
+ * With previous CFG_GET command executed properly, the retrieved data
+ * are shadowed in local structure. WSP FW has a set of shadow too,
+ * so that only the difference to be sent over SMDIO.
+ */
+static int __mxl862xx_api_wrap_cmd_r(const mxl862xx_device_t *dev, uint16_t cmd,
+				void *pdata, uint16_t size, uint16_t r_size)
+{
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+	int ret;
+
+	uint16_t max, i;
+	uint16_t *data;
+	int16_t result = 0;
+
+	max = (size + 1) / 2;
+	data = pdata;
+
+	ret = __wait_ctrl_busy(dev);
+	if (ret < 0) {
+		return ret;
+	}
+
+	for (i = 0; i < max; i++) {
+		uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+		if (i && off == 0) {
+			/* Send command to set data when every
+			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written
+			 * and reload next batch of data from last CFG_GET.
+			 */
+			ret = __mxl862xx_set_data(dev, i);
+			if (ret < 0) {
+				return ret;
+			}
+		}
+
+		if (data[i] == shadow.data.data[i])
+			continue;
+
+		mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off,
+			  le16_to_cpu(data[i]));
+		//sys_le16_to_cpu(data[i]));
+	}
+
+	ret = __mxl862xx_send_cmd(dev, cmd, size, &result);
+	if (ret < 0) {
+		return ret;
+	}
+
+	if (result < 0) {
+		return result;
+	}
+
+	max = (r_size + 1) / 2;
+	for (i = 0; i < max; i++) {
+		uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+		if (i && off == 0) {
+			/* Send command to fetch next batch of data
+			 * when every MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs
+			 * are read.
+			 */
+			ret = __mxl862xx_get_data(dev, i);
+			if (ret < 0) {
+				return ret;
+			}
+		}
+
+		ret = mxl862xx_read(dev, MXL862XX_MMD_REG_DATA_FIRST + off);
+		if (ret < 0) {
+			return ret;
+		}
+
+		if ((i * 2 + 1) == r_size) {
+			/* Special handling for last BYTE
+			 * if it's not WORD aligned.
+			 */
+			*(uint8_t *)&data[i] = ret & 0xFF;
+		} else {
+			data[i] = cpu_to_le16((uint16_t)ret);
+		}
+	}
+
+	shadow.data.data[max] = 0;
+	memcpy(shadow.data.data, data, r_size);
+
+	return result;
+#else /* defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT */
+	ARG_UNUSED(dev);
+	ARG_UNUSED(cmd);
+	ARG_UNUSED(pdata);
+	ARG_UNUSED(size);
+	return -ENOTSUP;
+#endif /* defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT */
+}
+
+int mxl862xx_api_wrap(const mxl862xx_device_t *dev, uint16_t cmd, void *pdata,
+		 uint16_t size, uint16_t cmd_r, uint16_t r_size)
+{
+	int ret;
+	uint16_t max, i, cnt;
+	uint16_t *data;
+	int16_t result = 0;
+
+	if (!dev || (!pdata && size))
+		return -EINVAL;
+
+	if (!(size <= sizeof(mmd_api_data_t)) || !(r_size <= size))
+		return -EINVAL;
+
+	mutex_lock_nested(&dev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+	if (__mxl862xx_cmd_r_valid(cmd_r)) {
+		/* Special handling for GET and SET command pair. */
+		ret = __mxl862xx_api_wrap_cmd_r(dev, cmd, pdata, size, r_size);
+		goto EXIT;
+	}
+
+	max = (size + 1) / 2;
+	data = pdata;
+
+	/* Check whether it's worth to issue RST_DATA command. */
+	for (i = cnt = 0; i < max && cnt < THR_RST_DATA; i++) {
+		if (!data[i])
+			cnt++;
+	}
+
+	ret = __wait_ctrl_busy(dev);
+	if (ret < 0)
+		goto EXIT;
+
+	if (cnt >= THR_RST_DATA) {
+		/* Issue RST_DATA commdand. */
+		ret = __mxl862xx_rst_data(dev);
+		if (ret < 0)
+			goto EXIT;
+
+		for (i = 0, cnt = 0; i < max; i++) {
+			uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+			if (i && off == 0) {
+				uint16_t cnt_old = cnt;
+
+				cnt = 0;
+
+				/* No actual data was written. */
+				if (!cnt_old)
+					continue;
+
+				/* Send command to set data when every
+				 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written
+				 * and clear the MMD register space.
+				 */
+				ret = __mxl862xx_set_data(dev, i);
+				if (ret < 0)
+					goto EXIT;
+			}
+
+			/* Skip '0' data. */
+			if (!data[i])
+				continue;
+
+			mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off,
+				  le16_to_cpu(data[i]));
+			cnt++;
+		}
+	} else {
+		for (i = 0; i < max; i++) {
+			uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+			if (i && off == 0) {
+				/* Send command to set data when every
+				 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written.
+				 */
+				ret = __mxl862xx_set_data(dev, i);
+				if (ret < 0)
+					goto EXIT;
+			}
+
+			mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off,
+				  le16_to_cpu(data[i]));
+		}
+	}
+
+	ret = __mxl862xx_send_cmd(dev, cmd, size, &result);
+	if (ret < 0)
+		goto EXIT;
+
+	if (result < 0) {
+		ret = result;
+		goto EXIT;
+	}
+
+	max = (r_size + 1) / 2;
+	for (i = 0; i < max; i++) {
+		uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+		if (i && off == 0) {
+			/* Send command to fetch next batch of data
+			 * when every MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs
+			 * are read.
+			 */
+			ret = __mxl862xx_get_data(dev, i);
+			if (ret < 0)
+				goto EXIT;
+		}
+
+		ret = mxl862xx_read(dev, MXL862XX_MMD_REG_DATA_FIRST + off);
+		if (ret < 0)
+			goto EXIT;
+
+		if ((i * 2 + 1) == r_size) {
+			/* Special handling for last BYTE
+			 * if it's not WORD aligned.
+			 */
+			*(uint8_t *)&data[i] = ret & 0xFF;
+		} else {
+			data[i] = cpu_to_le16((uint16_t)ret);
+		}
+	}
+
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+	if ((cmd != 0x1801) && (cmd != 0x1802))
+		shadow.data.data[max] = 0;
+	memcpy(shadow.data.data, data, r_size);
+#endif
+
+	ret = result;
+
+EXIT:
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+	shadow.ctrl = cmd;
+	shadow.ret = ret;
+#endif
+	mutex_unlock(&dev->bus->mdio_lock);
+	return ret;
+}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h
new file mode 100644
index 0000000..707700c
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h
@@ -0,0 +1,31 @@
+// spdx-license-identifier: gpl-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_host_api_impl.h - dsa driver for maxlinear mxl862xx switch chips family
+ *
+ * copyright (c) 2024 maxlinear inc.
+ *
+ * this program is free software; you can redistribute it and/or
+ * modify it under the terms of the gnu general public license
+ * as published by the free software foundation; either version 2
+ * of the license, or (at your option) any later version.
+ *
+ * this program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * merchantability or fitness for a particular purpose.  see the
+ * gnu general public license for more details.
+ *
+ * you should have received a copy of the gnu general public license
+ * along with this program; if not, write to the free software
+ * foundation, inc., 51 franklin street, fifth floor, boston, ma  02110-1301, usa.
+ *
+ */
+
+#ifndef _MXL862XX_HOST_API_IMPL_H_
+#define _MXL862XX_HOST_API_IMPL_H_
+
+#include "mxl862xx_types.h"
+
+extern int mxl862xx_api_wrap(const mxl862xx_device_t *dev, uint16_t cmd, void *pdata,
+			uint16_t size, uint16_t cmd_r, uint16_t r_size);
+
+#endif /* _MXL862XX_HOST_API_IMPL_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c
new file mode 100644
index 0000000..03702d8
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_mdio_relay.c - dsa driver for Maxlinear mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include "mxl862xx_mdio_relay.h"
+#include "mxl862xx_host_api_impl.h"
+#include "mxl862xx_mmd_apis.h"
+
+int int_gphy_read(const mxl862xx_device_t *dev, struct mdio_relay_data *parm)
+{
+	return mxl862xx_api_wrap(dev, INT_GPHY_READ, parm, sizeof(*parm), 0,
+			    sizeof(parm->data));
+}
+
+int int_gphy_write(const mxl862xx_device_t *dev, struct mdio_relay_data *parm)
+{
+	return mxl862xx_api_wrap(dev, INT_GPHY_WRITE, parm, sizeof(*parm), 0, 0);
+}
+
+int int_gphy_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *parm)
+{
+	return mxl862xx_api_wrap(dev, INT_GPHY_MOD, parm, sizeof(*parm), 0, 0);
+}
+
+int ext_mdio_read(const mxl862xx_device_t *dev, struct mdio_relay_data *parm)
+{
+	return mxl862xx_api_wrap(dev, EXT_MDIO_READ, parm, sizeof(*parm), 0,
+			    sizeof(parm->data));
+}
+
+int ext_mdio_write(const mxl862xx_device_t *dev, struct mdio_relay_data *parm)
+{
+	return mxl862xx_api_wrap(dev, EXT_MDIO_WRITE, parm, sizeof(*parm), 0, 0);
+}
+
+int ext_mdio_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *parm)
+{
+	return mxl862xx_api_wrap(dev, EXT_MDIO_MOD, parm, sizeof(*parm), 0, 0);
+}
+
+EXPORT_SYMBOL(int_gphy_write);
+EXPORT_SYMBOL(int_gphy_read);
+EXPORT_SYMBOL(int_gphy_mod);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h
new file mode 100644
index 0000000..5e46947
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * drivers/net/dsa/host_api/mxl862xx_mdio_relay.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_MDIO_RELAY_H_
+#define _MXL862XX_MDIO_RELAY_H_
+
+#include <linux/types.h>
+#include "mxl862xx_types.h"
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+struct mdio_relay_data {
+	/* data to be read or written */
+	uint16_t data;
+	/* PHY index (0~7) for internal PHY
+	 * PHY address (0~31) for external PHY access via MDIO bus
+	 */
+	uint8_t phy;
+	/* MMD device (0~31) */
+	uint8_t mmd;
+	/* Register Index
+	 * 0~31 if mmd is 0 (CL22)
+	 * 0~65535 otherwise (CL45)
+	 */
+	uint16_t reg;
+};
+
+struct mdio_relay_mod_data {
+	/* data to be written with mask */
+	uint16_t data;
+	/* PHY index (0~7) for internal PHY
+	 * PHY address (0~31) for external PHY access via MDIO bus
+	 */
+	uint8_t phy;
+	/* MMD device (0~31) */
+	uint8_t mmd;
+	/* Register Index
+	 * 0~31 if mmd is 0 (CL22)
+	 * 0~65535 otherwise (CL45)
+	 */
+	uint16_t reg;
+	/* mask of bit fields to be updated
+	 * 1 to write the bit
+	 * 0 to ignore
+	 */
+	uint16_t mask;
+};
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+/* read internal GPHY MDIO/MMD registers */
+int int_gphy_read(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata);
+/* write internal GPHY MDIO/MMD registers */
+int int_gphy_write(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata);
+/* modify internal GPHY MDIO/MMD registers */
+int int_gphy_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *pdata);
+
+/* read external GPHY MDIO/MMD registers via MDIO bus */
+int ext_mdio_read(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata);
+/* write external GPHY MDIO/MMD registers via MDIO bus */
+int ext_mdio_write(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata);
+/* modify external GPHY MDIO/MMD registers via MDIO bus */
+int ext_mdio_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *pdata);
+
+#endif /*  _MXL862XX_MDIO_RELAY_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h
new file mode 100644
index 0000000..6f58974
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * drivers/net/dsa/host_api/mxl862xx_mmd_apis.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_MMD_APIS_H_
+#define _MXL862XX_MMD_APIS_H_
+
+#include "mxl862xx_api.h"
+#include "mxl862xx_mdio_relay.h"
+
+#define MXL862XX_MMD_DEV 30
+#define MXL862XX_MMD_REG_CTRL 0
+#define MXL862XX_MMD_REG_LEN_RET 1
+#define MXL862XX_MMD_REG_DATA_FIRST 2
+#define MXL862XX_MMD_REG_DATA_LAST 95
+#define MXL862XX_MMD_REG_DATA_MAX_SIZE \
+	(MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1)
+
+typedef union mmd_api_data {
+	uint16_t data[MXL862XX_MMD_REG_DATA_MAX_SIZE * 3]; //Maximum data size is GSW_PCE_rule_t (508)
+	mxl862xx_register_mod_t mxl862xx_register_mod_t_data;
+	mxl862xx_cpu_port_cfg_t mxl862xx_cpu_port_cfg_t_data;
+	mxl862xx_port_link_cfg_t mxl862xx_port_link_cfg_t_data;
+	mxl862xx_port_cfg_t mxl862xx_port_cfg_t_data;
+	mxl862xx_bridge_alloc_t mxl862xx_bridge_alloc_t_data;
+	mxl862xx_bridge_port_config_t mxl862xx_bridge_port_config_t_data;
+	mxl862xx_debug_rmon_port_cnt_t mxl862xx_debug_rmon_port_cnt_t_data;
+	mxl862xx_mac_table_clear_cond_t mxl862xx_mac_table_clear_cond_t_data;
+	mxl862xx_mac_table_read_t mxl862xx_mac_table_read_t_data;
+	mxl862xx_mac_table_add_t mxl862xx_mac_table_add_t_data;
+	mxl862xx_mac_table_remove_t mxl862xx_mac_table_remove_t_data;
+	mxl862xx_stp_port_cfg_t mxl862xx_stp_port_cfg_t_data;
+	mxl862xx_ss_sp_tag_t mxl862xx_ss_sp_tag_t_data;
+	mxl862xx_monitor_port_cfg_t mxl862xx_monitor_port_cfg_t_data;
+	mxl862xx_ctp_port_config_t mxl862xx_ctp_port_config_t_data;
+
+	struct mdio_relay_data mdio_relay_data;
+	struct mdio_relay_mod_data mdio_relay_mod_data;
+	struct sys_fw_image_version img_ver_data;
+#ifdef CONFIG_SENSOR_MXL
+	struct sys_sensor_value pvt_sensor_data;
+#endif
+} mmd_api_data_t;
+
+#define MXL862XX_COMMON_MAGIC 0x100
+#define MXL862XX_TFLOW_MAGIC 0x200
+#define MXL862XX_BRDG_MAGIC 0x300
+#define MXL862XX_BRDGPORT_MAGIC 0x400
+#define MXL862XX_CTP_MAGIC 0x500
+#define MXL862XX_QOS_MAGIC 0x600
+#define MXL862XX_RMON_MAGIC 0x700
+#define MXL862XX_DEBUG_MAGIC 0x800
+#define MXL862XX_PMAC_MAGIC 0x900
+#define MXL862XX_SWMAC_MAGIC 0xA00
+#define MXL862XX_EXTVLAN_MAGIC 0xB00
+#define MXL862XX_VLANFILTER_MAGIC 0xC00
+#define MXL862XX_MULTICAST_MAGIC 0xD00
+#define MXL862XX_TRUNKING_MAGIC 0xE00
+#define MXL862XX_STP_MAGIC 0xF00
+#define MXL862XX_PBB_MAGIC 0x1000
+#define MXL862XX_VLAN_RMON_MAGIC 0x1100
+#define MXL862XX_SS_MAGIC 0x1600
+
+#define GPY_GPY2XX_MAGIC 0x1800
+
+#define SYS_MISC_MAGIC 0x1900
+
+#ifdef MMD_API_TEST
+#define MMD_API_SIMPLE_TEST (0x0 + 0x1)
+#endif
+#define MMD_API_SET_DATA_0 (0x0 + 0x2)
+#define MMD_API_SET_DATA_1 (0x0 + 0x3)
+#define MMD_API_SET_DATA_2 (0x0 + 0x4)
+#define MMD_API_GET_DATA_0 (0x0 + 0x5)
+#define MMD_API_GET_DATA_1 (0x0 + 0x6)
+#define MMD_API_GET_DATA_2 (0x0 + 0x7)
+#define MMD_API_RST_DATA (0x0 + 0x8)
+
+#define MXL862XX_COMMON_REGISTERGET (MXL862XX_COMMON_MAGIC + 0x1)
+#define MXL862XX_COMMON_REGISTERSET (MXL862XX_COMMON_MAGIC + 0x2)
+#define MXL862XX_COMMON_CPU_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x3)
+#define MXL862XX_COMMON_CPU_PORTCFGSET (MXL862XX_COMMON_MAGIC + 0x4)
+#define MXL862XX_COMMON_PORTLINKCFGGET (MXL862XX_COMMON_MAGIC + 0x5)
+#define MXL862XX_COMMON_PORTLINKCFGSET (MXL862XX_COMMON_MAGIC + 0x6)
+#define MXL862XX_COMMON_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x7)
+#define MXL862XX_COMMON_PORTCFGSET (MXL862XX_COMMON_MAGIC + 0x8)
+#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9)
+#define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xA)
+#define MXL862XX_COMMON_MONITORPORTCFGGET (MXL862XX_COMMON_MAGIC + 0xD)
+#define MXL862XX_COMMON_MONITORPORTCFGSET (MXL862XX_COMMON_MAGIC + 0xE)
+#define MXL862XX_COMMON_FREEZE (MXL862XX_COMMON_MAGIC + 0xF)
+#define MXL862XX_COMMON_UNFREEZE (MXL862XX_COMMON_MAGIC + 0x10)
+#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)
+
+#define MXL862XX_TFLOW_PCERULEREAD (MXL862XX_TFLOW_MAGIC + 0x1)
+#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2)
+#define MXL862XX_TFLOW_PCERULEDELETE (MXL862XX_TFLOW_MAGIC + 0x3)
+#define MXL862XX_TFLOW_PCERULEALLOC (MXL862XX_TFLOW_MAGIC + 0x4)
+#define MXL862XX_TFLOW_PCERULEFREE (MXL862XX_TFLOW_MAGIC + 0x5)
+#define MXL862XX_TFLOW_PCERULEENABLE (MXL862XX_TFLOW_MAGIC + 0x6)
+#define MXL862XX_TFLOW_PCERULEDISABLE (MXL862XX_TFLOW_MAGIC + 0x7)
+
+#define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1)
+#define MXL862XX_BRIDGE_CONFIGSET (MXL862XX_BRDG_MAGIC + 0x2)
+#define MXL862XX_BRIDGE_CONFIGGET (MXL862XX_BRDG_MAGIC + 0x3)
+#define MXL862XX_BRIDGE_FREE (MXL862XX_BRDG_MAGIC + 0x4)
+
+#define MXL862XX_BRIDGEPORT_ALLOC (MXL862XX_BRDGPORT_MAGIC + 0x1)
+#define MXL862XX_BRIDGEPORT_CONFIGSET (MXL862XX_BRDGPORT_MAGIC + 0x2)
+#define MXL862XX_BRIDGEPORT_CONFIGGET (MXL862XX_BRDGPORT_MAGIC + 0x3)
+#define MXL862XX_BRIDGEPORT_FREE (MXL862XX_BRDGPORT_MAGIC + 0x4)
+
+#define MXL862XX_CTP_PORTASSIGNMENTALLOC (MXL862XX_CTP_MAGIC + 0x1)
+#define MXL862XX_CTP_PORTASSIGNMENTFREE (MXL862XX_CTP_MAGIC + 0x2)
+#define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3)
+#define MXL862XX_CTP_PORTASSIGNMENTGET (MXL862XX_CTP_MAGIC + 0x4)
+#define MXL862XX_CTP_PORTCONFIGSET (MXL862XX_CTP_MAGIC + 0x5)
+#define MXL862XX_CTP_PORTCONFIGGET (MXL862XX_CTP_MAGIC + 0x6)
+#define MXL862XX_CTP_PORTCONFIGRESET (MXL862XX_CTP_MAGIC + 0x7)
+
+#define MXL862XX_QOS_METERCFGGET (MXL862XX_QOS_MAGIC + 0x1)
+#define MXL862XX_QOS_METERCFGSET (MXL862XX_QOS_MAGIC + 0x2)
+#define MXL862XX_QOS_DSCP_CLASSGET (MXL862XX_QOS_MAGIC + 0x4)
+#define MXL862XX_QOS_DSCP_CLASSSET (MXL862XX_QOS_MAGIC + 0x5)
+#define MXL862XX_QOS_DSCP_DROPPRECEDENCECFGGET (MXL862XX_QOS_MAGIC + 0x6)
+#define MXL862XX_QOS_DSCP_DROPPRECEDENCECFGSET (MXL862XX_QOS_MAGIC + 0x7)
+#define MXL862XX_QOS_PORTREMARKINGCFGGET (MXL862XX_QOS_MAGIC + 0x8)
+#define MXL862XX_QOS_PORTREMARKINGCFGSET (MXL862XX_QOS_MAGIC + 0x9)
+#define MXL862XX_QOS_PCP_CLASSGET (MXL862XX_QOS_MAGIC + 0xA)
+#define MXL862XX_QOS_PCP_CLASSSET (MXL862XX_QOS_MAGIC + 0xB)
+#define MXL862XX_QOS_PORTCFGGET (MXL862XX_QOS_MAGIC + 0xC)
+#define MXL862XX_QOS_PORTCFGSET (MXL862XX_QOS_MAGIC + 0xD)
+#define MXL862XX_QOS_QUEUEPORTGET (MXL862XX_QOS_MAGIC + 0xE)
+#define MXL862XX_QOS_QUEUEPORTSET (MXL862XX_QOS_MAGIC + 0xF)
+#define MXL862XX_QOS_SCHEDULERCFGGET (MXL862XX_QOS_MAGIC + 0x10)
+#define MXL862XX_QOS_SCHEDULERCFGSET (MXL862XX_QOS_MAGIC + 0x11)
+#define MXL862XX_QOS_SHAPERCFGGET (MXL862XX_QOS_MAGIC + 0x12)
+#define MXL862XX_QOS_SHAPERCFGSET (MXL862XX_QOS_MAGIC + 0x13)
+#define MXL862XX_QOS_SHAPERQUEUEASSIGN (MXL862XX_QOS_MAGIC + 0x14)
+#define MXL862XX_QOS_SHAPERQUEUEDEASSIGN (MXL862XX_QOS_MAGIC + 0x15)
+#define MXL862XX_QOS_SHAPERQUEUEGET (MXL862XX_QOS_MAGIC + 0x16)
+#define MXL862XX_QOS_STORMCFGSET (MXL862XX_QOS_MAGIC + 0x17)
+#define MXL862XX_QOS_STORMCFGGET (MXL862XX_QOS_MAGIC + 0x18)
+#define MXL862XX_QOS_WREDCFGGET (MXL862XX_QOS_MAGIC + 0x19)
+#define MXL862XX_QOS_WREDCFGSET (MXL862XX_QOS_MAGIC + 0x1A)
+#define MXL862XX_QOS_WREDQUEUECFGGET (MXL862XX_QOS_MAGIC + 0x1B)
+#define MXL862XX_QOS_WREDQUEUECFGSET (MXL862XX_QOS_MAGIC + 0x1C)
+#define MXL862XX_QOS_WREDPORTCFGGET (MXL862XX_QOS_MAGIC + 0x1D)
+#define MXL862XX_QOS_WREDPORTCFGSET (MXL862XX_QOS_MAGIC + 0x1E)
+#define MXL862XX_QOS_FLOWCTRLCFGGET (MXL862XX_QOS_MAGIC + 0x1F)
+#define MXL862XX_QOS_FLOWCTRLCFGSET (MXL862XX_QOS_MAGIC + 0x20)
+#define MXL862XX_QOS_FLOWCTRLPORTCFGGET (MXL862XX_QOS_MAGIC + 0x21)
+#define MXL862XX_QOS_FLOWCTRLPORTCFGSET (MXL862XX_QOS_MAGIC + 0x22)
+#define MXL862XX_QOS_QUEUEBUFFERRESERVECFGGET (MXL862XX_QOS_MAGIC + 0x23)
+#define MXL862XX_QOS_QUEUEBUFFERRESERVECFGSET (MXL862XX_QOS_MAGIC + 0x24)
+#define MXL862XX_QOS_COLORMARKINGTABLEGET (MXL862XX_QOS_MAGIC + 0x26)
+#define MXL862XX_QOS_COLORMARKINGTABLESET (MXL862XX_QOS_MAGIC + 0x27)
+#define MXL862XX_QOS_COLORREMARKINGTABLESET (MXL862XX_QOS_MAGIC + 0x28)
+#define MXL862XX_QOS_COLORREMARKINGTABLEGET (MXL862XX_QOS_MAGIC + 0x29)
+#define MXL862XX_QOS_METERALLOC (MXL862XX_QOS_MAGIC + 0x2A)
+#define MXL862XX_QOS_METERFREE (MXL862XX_QOS_MAGIC + 0x2B)
+#define MXL862XX_QOS_DSCP2PCPTABLESET (MXL862XX_QOS_MAGIC + 0x2C)
+#define MXL862XX_QOS_DSCP2PCPTABLEGET (MXL862XX_QOS_MAGIC + 0x2D)
+#define MXL862XX_QOS_PMAPPERTABLESET (MXL862XX_QOS_MAGIC + 0x2E)
+#define MXL862XX_QOS_PMAPPERTABLEGET (MXL862XX_QOS_MAGIC + 0x2F)
+#define MXL862XX_QOS_SVLAN_PCP_CLASSGET (MXL862XX_QOS_MAGIC + 0x30)
+#define MXL862XX_QOS_SVLAN_PCP_CLASSSET (MXL862XX_QOS_MAGIC + 0x31)
+
+#define MXL862XX_RMON_PORT_GET (MXL862XX_RMON_MAGIC + 0x1)
+#define MXL862XX_RMON_MODE_SET (MXL862XX_RMON_MAGIC + 0x2)
+#define MXL862XX_RMON_METER_GET (MXL862XX_RMON_MAGIC + 0x3)
+#define MXL862XX_RMON_CLEAR (MXL862XX_RMON_MAGIC + 0x4)
+#define MXL862XX_RMON_TFLOWGET (MXL862XX_RMON_MAGIC + 0x5)
+#define MXL862XX_RMON_TFLOWCLEAR (MXL862XX_RMON_MAGIC + 0x6)
+#define MXL862XX_RMON_TFLOWCOUNTMODESET (MXL862XX_RMON_MAGIC + 0x7)
+#define MXL862XX_RMON_TFLOWCOUNTMODEGET (MXL862XX_RMON_MAGIC + 0x8)
+
+#define MXL862XX_DEBUG_RMON_PORT_GET (MXL862XX_DEBUG_MAGIC + 0x1)
+
+#define MXL862XX_PMAC_COUNTGET (MXL862XX_PMAC_MAGIC + 0x1)
+#define MXL862XX_PMAC_GBL_CFGSET (MXL862XX_PMAC_MAGIC + 0x2)
+#define MXL862XX_PMAC_GBL_CFGGET (MXL862XX_PMAC_MAGIC + 0x3)
+#define MXL862XX_PMAC_BM_CFGSET (MXL862XX_PMAC_MAGIC + 0x4)
+#define MXL862XX_PMAC_BM_CFGGET (MXL862XX_PMAC_MAGIC + 0x5)
+#define MXL862XX_PMAC_IG_CFGSET (MXL862XX_PMAC_MAGIC + 0x6)
+#define MXL862XX_PMAC_IG_CFGGET (MXL862XX_PMAC_MAGIC + 0x7)
+#define MXL862XX_PMAC_EG_CFGSET (MXL862XX_PMAC_MAGIC + 0x8)
+#define MXL862XX_PMAC_EG_CFGGET (MXL862XX_PMAC_MAGIC + 0x9)
+
+#define MXL862XX_MAC_TABLECLEAR (MXL862XX_SWMAC_MAGIC + 0x1)
+#define MXL862XX_MAC_TABLEENTRYADD (MXL862XX_SWMAC_MAGIC + 0x2)
+#define MXL862XX_MAC_TABLEENTRYREAD (MXL862XX_SWMAC_MAGIC + 0x3)
+#define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4)
+#define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5)
+#define MXL862XX_MAC_DEFAULTFILTERSET (MXL862XX_SWMAC_MAGIC + 0x6)
+#define MXL862XX_MAC_DEFAULTFILTERGET (MXL862XX_SWMAC_MAGIC + 0x7)
+#define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8)
+
+#define MXL862XX_EXTENDEDVLAN_ALLOC (MXL862XX_EXTVLAN_MAGIC + 0x1)
+#define MXL862XX_EXTENDEDVLAN_SET (MXL862XX_EXTVLAN_MAGIC + 0x2)
+#define MXL862XX_EXTENDEDVLAN_GET (MXL862XX_EXTVLAN_MAGIC + 0x3)
+#define MXL862XX_EXTENDEDVLAN_FREE (MXL862XX_EXTVLAN_MAGIC + 0x4)
+
+#define MXL862XX_VLANFILTER_ALLOC (MXL862XX_VLANFILTER_MAGIC + 0x1)
+#define MXL862XX_VLANFILTER_SET (MXL862XX_VLANFILTER_MAGIC + 0x2)
+#define MXL862XX_VLANFILTER_GET (MXL862XX_VLANFILTER_MAGIC + 0x3)
+#define MXL862XX_VLANFILTER_FREE (MXL862XX_VLANFILTER_MAGIC + 0x4)
+
+#define MXL862XX_VLAN_COUNTER_MAPPING_SET (MXL862XX_VLAN_RMON_MAGIC + 0x1)
+#define MXL862XX_VLAN_COUNTER_MAPPING_GET (MXL862XX_VLAN_RMON_MAGIC + 0x2)
+#define MXL862XX_VLAN_RMON_GET (MXL862XX_VLAN_RMON_MAGIC + 0x3)
+#define MXL862XX_VLAN_RMON_CLEAR (MXL862XX_VLAN_RMON_MAGIC + 0x4)
+#define MXL862XX_VLAN_RMON_CONTROL_SET (MXL862XX_VLAN_RMON_MAGIC + 0x5)
+#define MXL862XX_VLAN_RMON_CONTROL_GET (MXL862XX_VLAN_RMON_MAGIC + 0x6)
+
+#define MXL862XX_MULTICAST_ROUTERPORTADD (MXL862XX_MULTICAST_MAGIC + 0x1)
+#define MXL862XX_MULTICAST_ROUTERPORTREAD (MXL862XX_MULTICAST_MAGIC + 0x2)
+#define MXL862XX_MULTICAST_ROUTERPORTREMOVE (MXL862XX_MULTICAST_MAGIC + 0x3)
+#define MXL862XX_MULTICAST_SNOOPCFGGET (MXL862XX_MULTICAST_MAGIC + 0x4)
+#define MXL862XX_MULTICAST_SNOOPCFGSET (MXL862XX_MULTICAST_MAGIC + 0x5)
+#define MXL862XX_MULTICAST_TABLEENTRYADD (MXL862XX_MULTICAST_MAGIC + 0x6)
+#define MXL862XX_MULTICAST_TABLEENTRYREAD (MXL862XX_MULTICAST_MAGIC + 0x7)
+#define MXL862XX_MULTICAST_TABLEENTRYREMOVE (MXL862XX_MULTICAST_MAGIC + 0x8)
+
+#define MXL862XX_TRUNKING_CFGGET (MXL862XX_TRUNKING_MAGIC + 0x1)
+#define MXL862XX_TRUNKING_CFGSET (MXL862XX_TRUNKING_MAGIC + 0x2)
+#define MXL862XX_TRUNKING_PORTCFGGET (MXL862XX_TRUNKING_MAGIC + 0x3)
+#define MXL862XX_TRUNKING_PORTCFGSET (MXL862XX_TRUNKING_MAGIC + 0x4)
+
+#define MXL862XX_STP_PORTCFGGET (MXL862XX_STP_MAGIC + 0x1)
+#define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2)
+#define MXL862XX_STP_BPDU_RULEGET (MXL862XX_STP_MAGIC + 0x3)
+#define MXL862XX_STP_BPDU_RULESET (MXL862XX_STP_MAGIC + 0x4)
+
+#define MXL862XX_PBB_TEMPLATEALLOC (MXL862XX_PBB_MAGIC + 0x1)
+#define MXL862XX_PBB_TEMPLATEFREE (MXL862XX_PBB_MAGIC + 0x2)
+#define MXL862XX_PBB_TEMPLATESET (MXL862XX_PBB_MAGIC + 0x3)
+#define MXL862XX_PBB_TEMPLATEGET (MXL862XX_PBB_MAGIC + 0x4)
+
+#define MXL862XX_SS_SPTAG_GET (MXL862XX_SS_MAGIC + 0x01)
+#define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02)
+
+#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01)
+#define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02)
+#define INT_GPHY_MOD (GPY_GPY2XX_MAGIC + 0x03)
+#define EXT_MDIO_READ (GPY_GPY2XX_MAGIC + 0x11)
+#define EXT_MDIO_WRITE (GPY_GPY2XX_MAGIC + 0x12)
+#define EXT_MDIO_MOD (GPY_GPY2XX_MAGIC + 0x13)
+
+#define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x01)
+#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02)
+#define SYS_MISC_PVT_TEMP (SYS_MISC_MAGIC + 0x03)
+#define SYS_MISC_PVT_VOLTAGE (SYS_MISC_MAGIC + 0x04)
+
+#define MMD_API_MAXIMUM_ID 0x7FFF
+
+int mxl862xx_read(const mxl862xx_device_t *dev, uint32_t regaddr);
+int mxl862xx_write(const mxl862xx_device_t *dev, uint32_t regaddr, uint16_t data);
+
+
+#endif /* _MXL862XX_MMD_APIS_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h
new file mode 100644
index 0000000..c4d3a63
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_rmon.h - dsa driver for Maxlinear mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_RMON_H_
+#define _MXL862XX_RMON_H_
+
+#include "mxl862xx_types.h"
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+/** \brief Port Type - GSWIP-3.1 only.
+   Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+	/** Logical Port */
+	MXL862XX_LOGICAL_PORT = 0,
+	/** Physical Port
+	    Applicable only for GSWIP-3.1/3.2 */
+	MXL862XX_PHYSICAL_PORT = 1,
+	/** Connectivity Termination Port (CTP)
+	    Applicable only for GSWIP-3.1/3.2 */
+	MXL862XX_CTP_PORT = 2,
+	/** Bridge Port
+	    Applicable only for GSWIP-3.1/3.2 */
+	MXL862XX_BRIDGE_PORT = 3
+} mxl862xx_port_type_t;
+
+/**Defined as per RMON counter table structure
+  Applicable only for GSWIP 3.1*/
+typedef enum {
+	MXL862XX_RMON_CTP_PORT_RX = 0,
+	MXL862XX_RMON_CTP_PORT_TX = 1,
+	MXL862XX_RMON_BRIDGE_PORT_RX = 2,
+	MXL862XX_RMON_BRIDGE_PORT_TX = 3,
+	MXL862XX_RMON_CTP_PORT_PCE_BYPASS = 4,
+	MXL862XX_RMON_TFLOW_RX = 5,
+	MXL862XX_RMON_TFLOW_TX = 6,
+	MXL862XX_RMON_QMAP = 0x0E,
+	MXL862XX_RMON_METER = 0x19,
+	MXL862XX_RMON_PMAC = 0x1C,
+} mxl862xx_rmon_port_type_t;
+
+/** Defined as per RMON counter table structure
+ * Applicable only for GSWIP 3.0
+ */
+typedef enum {
+	MXL862XX_RMON_REDIRECTION = 0X18,
+	MXL862XX_RMON_IF = 0x1A,
+	MXL862XX_RMON_ROUTE = 0x1B,
+	MXL862XX_RMON_PMACIG = 0x1C,
+} mxl862xx_rmon_port_t;
+
+typedef enum {
+	/** Parser microcode table */
+	PMAC_BPMAP_INDEX = 0x00,
+	PMAC_IGCFG_INDEX = 0x01,
+	PMAC_EGCFG_INDEX = 0x02,
+} pm_tbl_cmds_t;
+/** \brief RMON Counters Type enumeration.
+    Used by \ref MXL862XX_RMON_clear_t and \ref MXL862XX_RMON_mode_t. */
+typedef enum {
+	/** All RMON Types Counters */
+	MXL862XX_RMON_ALL_TYPE = 0,
+	/** All PMAC RMON Counters */
+	MXL862XX_RMON_PMAC_TYPE = 1,
+	/** Port based RMON Counters */
+	MXL862XX_RMON_PORT_TYPE = 2,
+	/** Meter based RMON Counters */
+	MXL862XX_RMON_METER_TYPE = 3,
+	/** Interface based RMON Counters */
+	MXL862XX_RMON_IF_TYPE = 4,
+	/** Route based RMON Counters */
+	MXL862XX_RMON_ROUTE_TYPE = 5,
+	/** Redirected Traffic based RMON Counters */
+	MXL862XX_RMON_REDIRECT_TYPE = 6,
+	/** Bridge Port based RMON Counters */
+	MXL862XX_RMON_BRIDGE_TYPE = 7,
+	/** CTP Port based RMON Counters */
+	MXL862XX_RMON_CTP_TYPE = 8,
+} mxl862xx_rmon_type_t;
+
+/** \brief RMON Counters Mode Enumeration.
+    This enumeration defines Counters mode - Packets based or Bytes based counting.
+    Metering and Routing Sessions RMON counting support either Byte based or packets based only. */
+typedef enum {
+	/** Packet based RMON Counters */
+	MXL862XX_RMON_COUNT_PKTS = 0,
+	/** Bytes based RMON Counters */
+	MXL862XX_RMON_COUNT_BYTES = 1,
+	/**  number of dropped frames, supported only for interface cunters */
+	MXL862XX_RMON_DROP_COUNT = 2,
+} mxl862xx_rmon_count_mode_t;
+
+/** \brief Used for getting metering RMON counters.
+    Used by \ref MXL862XX_RMON_METER_GET */
+typedef enum {
+	/* Resereved */
+	MXL862XX_RMON_METER_COLOR_RES = 0,
+	/* Green color */
+	MXL862XX_RMON_METER_COLOR_GREEN = 1,
+	/* Yellow color */
+	MXL862XX_RMON_METER_COLOR_YELLOW = 2,
+	/* Red color */
+	MXL862XX_RMON_METER_COLOR_RED = 3,
+} mxl862xx_rmon_meter_color_t;
+
+/* TFLOW counter type */
+typedef enum {
+	/* Set all Rx/Tx/PCE-Bp-Tx registers to same value */
+	MXL862XX_TFLOW_COUNTER_ALL = 0, //Default for 'set' function.
+	/* SEt PCE Rx register config only */
+	MXL862XX_TFLOW_COUNTER_PCE_Rx = 1, //Default for 'get' function.
+	/* SEt PCE Tx register config only */
+	MXL862XX_TFLOW_COUNTER_PCE_Tx = 2,
+	/* SEt PCE-Bypass Tx register config only */
+	MXL862XX_TFLOW_COUNTER_PCE_BP_Tx = 3,
+} mxl862xx_tflow_count_conf_type_t;
+
+/* TFLOW counter mode type */
+typedef enum {
+	/* Global mode */
+	MXL862XX_TFLOW_CMODE_GLOBAL = 0,
+	/* Logical mode */
+	MXL862XX_TFLOW_CMODE_LOGICAL = 1,
+	/* CTP port mode */
+	MXL862XX_TFLOW_CMODE_CTP = 2,
+	/* Bridge port mode */
+	MXL862XX_TFLOW_CMODE_BRIDGE = 3,
+} mxl862xx_tflow_cmode_type_t;
+
+/* TFLOW CTP counter LSB bits */
+typedef enum {
+	/* Num of valid bits  */
+	MXL862XX_TCM_CTP_VAL_BITS_0 = 0,
+	MXL862XX_TCM_CTP_VAL_BITS_1 = 1,
+	MXL862XX_TCM_CTP_VAL_BITS_2 = 2,
+	MXL862XX_TCM_CTP_VAL_BITS_3 = 3,
+	MXL862XX_TCM_CTP_VAL_BITS_4 = 4,
+	MXL862XX_TCM_CTP_VAL_BITS_5 = 5,
+	MXL862XX_TCM_CTP_VAL_BITS_6 = 6,
+} mxl862xx_tflow_ctp_val_bits_t;
+
+/* TFLOW bridge port counter LSB bits */
+typedef enum {
+	/* Num of valid bits  */
+	MXL862XX_TCM_BRP_VAL_BITS_2 = 2,
+	MXL862XX_TCM_BRP_VAL_BITS_3 = 3,
+	MXL862XX_TCM_BRP_VAL_BITS_4 = 4,
+	MXL862XX_TCM_BRP_VAL_BITS_5 = 5,
+	MXL862XX_TCM_BRP_VAL_BITS_6 = 6,
+} mxl862xx_tflow_brp_val_bits_t;
+
+/**
+ \brief RMON Counters for individual Port.
+ This structure contains the RMON counters of an Ethernet Switch Port.
+    Used by \ref MXL862XX_RMON_PORT_GET. */
+typedef struct {
+	/** Port Type. This gives information which type of port to get RMON.
+	    port_id should be based on this field.
+	    This is new in GSWIP-3.1. For GSWIP-2.1/2.2/3.0, this field is always
+	    ZERO (MXL862XX_LOGICAL_PORT). */
+	mxl862xx_port_type_t port_type;
+	/** Ethernet Port number (zero-based counting). The valid range is hardware
+	    dependent. An error code is delivered if the selected port is not
+	    available. This parameter specifies for which MAC port the RMON
+	    counter is read. It has to be set by the application before
+	    calling \ref MXL862XX_RMON_PORT_GET. */
+	u16 port_id;
+	/** Sub interface ID group. The valid range is hardware/protocol dependent.
+
+	    \remarks
+	    This field is valid when \ref MXL862XX_RMON_Port_cnt_t::e_port_type is
+	    \ref MXL862XX_port_type_t::MXL862XX_CTP_PORT.
+	    Sub interface ID group is defined for each of \ref MXL862XX_Logical_port_mode_t.
+	    For both \ref MXL862XX_LOGICAL_PORT_8BIT_WLAN and
+	    \ref MXL862XX_LOGICAL_PORT_9BIT_WLAN, this field is VAP.
+	    For \ref MXL862XX_LOGICAL_PORT_GPON, this field is GEM index.
+	    For \ref MXL862XX_LOGICAL_PORT_EPON, this field is stream index.
+	    For \ref MXL862XX_LOGICAL_PORT_GINT, this field is LLID.
+	    For others, this field is 0. */
+	u16 sub_if_id_group;
+	/** Separate set of CTP Tx counters when PCE is bypassed. GSWIP-3.1 only.*/
+	bool pce_bypass;
+	/*Applicable only for GSWIP 3.1*/
+	/** Discarded at Extended VLAN Operation Packet Count. GSWIP-3.1 only. */
+	u32 rx_extended_vlan_discard_pkts;
+	/** Discarded MTU Exceeded Packet Count. GSWIP-3.1 only. */
+	u32 mtu_exceed_discard_pkts;
+	/** Tx Undersize (<64) Packet Count. GSWIP-3.1 only. */
+	u32 tx_under_size_good_pkts;
+	/** Tx Oversize (>1518) Packet Count. GSWIP-3.1 only. */
+	u32 tx_oversize_good_pkts;
+	/** Receive Packet Count (only packets that are accepted and not discarded). */
+	u32 rx_good_pkts;
+	/** Receive Unicast Packet Count. */
+	u32 rx_unicast_pkts;
+	/** Receive Broadcast Packet Count. */
+	u32 rx_broadcast_pkts;
+	/** Receive Multicast Packet Count. */
+	u32 rx_multicast_pkts;
+	/** Receive FCS Error Packet Count. */
+	u32 rx_fCSError_pkts;
+	/** Receive Undersize Good Packet Count. */
+	u32 rx_under_size_good_pkts;
+	/** Receive Oversize Good Packet Count. */
+	u32 rx_oversize_good_pkts;
+	/** Receive Undersize Error Packet Count. */
+	u32 rx_under_size_error_pkts;
+	/** Receive Good Pause Packet Count. */
+	u32 rx_good_pause_pkts;
+	/** Receive Oversize Error Packet Count. */
+	u32 rx_oversize_error_pkts;
+	/** Receive Align Error Packet Count. */
+	u32 rx_align_error_pkts;
+	/** Filtered Packet Count. */
+	u32 rx_filtered_pkts;
+	/** Receive Size 64 Bytes Packet Count. */
+	u32 rx64Byte_pkts;
+	/** Receive Size 65-127 Bytes Packet Count. */
+	u32 rx127Byte_pkts;
+	/** Receive Size 128-255 Bytes Packet Count. */
+	u32 rx255Byte_pkts;
+	/** Receive Size 256-511 Bytes Packet Count. */
+	u32 rx511Byte_pkts;
+	/** Receive Size 512-1023 Bytes Packet Count. */
+	u32 rx1023Byte_pkts;
+	/** Receive Size 1024-1522 Bytes (or more, if configured) Packet Count. */
+	u32 rx_max_byte_pkts;
+	/** Overall Transmit Good Packets Count. */
+	u32 tx_good_pkts;
+	/** Transmit Unicast Packet Count. */
+	u32 tx_unicast_pkts;
+	/** Transmit Broadcast Packet Count. */
+	u32 tx_broadcast_pkts;
+	/** Transmit Multicast Packet Count. */
+	u32 tx_multicast_pkts;
+	/** Transmit Single Collision Count. */
+	u32 tx_single_coll_count;
+	/** Transmit Multiple Collision Count. */
+	u32 tx_mult_coll_count;
+	/** Transmit Late Collision Count. */
+	u32 tx_late_coll_count;
+	/** Transmit Excessive Collision Count. */
+	u32 tx_excess_coll_count;
+	/** Transmit Collision Count. */
+	u32 tx_coll_count;
+	/** Transmit Pause Packet Count. */
+	u32 tx_pause_count;
+	/** Transmit Size 64 Bytes Packet Count. */
+	u32 tx64Byte_pkts;
+	/** Transmit Size 65-127 Bytes Packet Count. */
+	u32 tx127Byte_pkts;
+	/** Transmit Size 128-255 Bytes Packet Count. */
+	u32 tx255Byte_pkts;
+	/** Transmit Size 256-511 Bytes Packet Count. */
+	u32 tx511Byte_pkts;
+	/** Transmit Size 512-1023 Bytes Packet Count. */
+	u32 tx1023Byte_pkts;
+	/** Transmit Size 1024-1522 Bytes (or more, if configured) Packet Count. */
+	u32 tx_max_byte_pkts;
+	/** Transmit Drop Packet Count. */
+	u32 tx_dropped_pkts;
+	/** Transmit Dropped Packet Count, based on Congestion Management. */
+	u32 tx_acm_dropped_pkts;
+	/** Receive Dropped Packet Count. */
+	u32 rx_dropped_pkts;
+	/** Receive Good Byte Count (64 bit). */
+	u64 rx_good_bytes;
+	/** Receive Bad Byte Count (64 bit). */
+	u64 rx_bad_bytes;
+	/** Transmit Good Byte Count (64 bit). */
+	u64 tx_good_bytes;
+} mxl862xx_rmon_port_cnt_t;
+
+/** \brief RMON Counters Mode for different Elements.
+    This structure takes RMON Counter Element Name and mode config */
+typedef struct {
+	/** RMON Counters Type */
+	mxl862xx_rmon_type_t rmon_type;
+	/** RMON Counters Mode */
+	mxl862xx_rmon_count_mode_t count_mode;
+} mxl862xx_rmon_mode_t;
+
+/**
+ \brief RMON Counters for Meter - Type (GSWIP-3.0 only).
+ This structure contains the RMON counters of one Meter Instance.
+    Used by \ref MXL862XX_RMON_METER_GET. */
+typedef struct {
+	/** Meter Instance number (zero-based counting). The valid range is hardware
+	    dependent. An error code is delivered if the selected meter is not
+	    available. This parameter specifies for which Meter Id the RMON-1
+	    counter is read. It has to be set by the application before
+	    calling \ref MXL862XX_RMON_METER_GET. */
+	u8 meter_id;
+	/** Metered Green colored packets or bytes (depending upon mode) count. */
+	u32 green_count;
+	/** Metered Yellow colored packets or bytes (depending upon mode) count. */
+	u32 yellow_count;
+	/** Metered Red colored packets or bytes (depending upon mode) count. */
+	u32 red_count;
+	/** Metered Reserved (Future Use) packets or bytes (depending upon mode) count. */
+	u32 res_count;
+} mxl862xx_rmon_meter_cnt_t;
+
+/** \brief RMON Counters Data Structure for clearance of values.
+    Used by \ref MXL862XX_RMON_CLEAR. */
+typedef struct {
+	/** RMON Counters Type */
+	mxl862xx_rmon_type_t rmon_type;
+	/** RMON Counters Identifier - Meter, Port, If, Route, etc. */
+	u8 rmon_id;
+} mxl862xx_rmon_clear_t;
+
+/**
+   \brief Hardware platform TFLOW counter mode.
+   Supported modes include, Global (default), Logical, CTP, Bridge port mode.
+   The number of counters that can be assigned varies based these mode type.
+    Used by \ref MXL862XX_TFLOW_COUNT_MODE_SET and MXL862XX_TFLOW_COUNT_MODE_GET. */
+typedef struct {
+	//Counter type. PCE Rx/Tx/Bp-Tx.
+	mxl862xx_tflow_count_conf_type_t count_type;
+	//Counter mode. Global/Logical/CTP/br_p.
+	mxl862xx_tflow_cmode_type_t count_mode;
+	//The below params are valid only for CTP/br_p types.
+	//A group of ports matching MS (9-n) bits. n is n_ctp_lsb or n_brp_lsb.
+	u16 port_msb;
+	//Number of valid bits in CTP port counter mode.
+	mxl862xx_tflow_ctp_val_bits_t ctp_lsb;
+	//Number of valid bits in bridge port counter mode.
+	mxl862xx_tflow_brp_val_bits_t brp_lsb;
+} mxl862xx_tflow_cmode_conf_t;
+
+/**
+   \brief Hardware platform extended RMON Counters. GSWIP-3.1 only.
+   This structure contains additional RMON counters. These counters can be
+   used by the packet classification engine and can be freely assigned to
+   dedicated packet rules and flows.
+    Used by \ref MXL862XX_RMON_FLOW_GET. */
+typedef struct {
+	/** If TRUE, use \ref MXL862XX_RMON_flow_get_t::n_index to access the Flow Counter,
+	    otherwise, use \ref MXL862XX_TFLOW_COUNT_MODE_GET to determine mode and use
+	    \ref MXL862XX_RMON_flow_get_t::port_id and \ref MXL862XX_RMON_flow_get_t::flow_id
+	    to calculate index of the Flow Counter. */
+	bool use_index;
+	/** Absolute index of Flow Counter. */
+	u16 index;
+	/** Port ID. This could be Logical Port, CTP or Bridge Port. It depends
+	    on the mode set by \ref MXL862XX_TFLOW_COUNT_MODE_SET. */
+	u16 port_id;
+	/** \ref MXL862XX_PCE_action_t::flow_id. The range depends on the mode set
+	    by \ref MXL862XX_TFLOW_COUNT_MODE_SET. */
+	u16 flow_id;
+
+	/** Rx Packet Counter */
+	u32 rx_pkts;
+	/** Tx Packet Counter (non-PCE-Bypass) */
+	u32 tx_pkts;
+	/** Tx Packet Counter (PCE-Bypass) */
+	u32 tx_pce_bypass_pkts;
+} mxl862xx_rmon_flow_get_t;
+
+/** \brief For debugging Purpose only.
+    Used for GSWIP 3.1. */
+
+typedef struct {
+	/** Ethernet Port number (zero-based counting). The valid range is hardware
+	    dependent. An error code is delivered if the selected port is not
+	    available. This parameter specifies for which MAC port the RMON
+	    counter is read. It has to be set by the application before
+	    calling \ref MXL862XX_RMON_PORT_GET. */
+	u16 port_id;
+	/**Table address selection based on port type
+	  Applicable only for GSWIP 3.1
+	 \ref MXL862XX_RMON_port_type_t**/
+	mxl862xx_rmon_port_type_t port_type;
+	/*Applicable only for GSWIP 3.1*/
+	u32 rx_extended_vlan_discard_pkts;
+	/*Applicable only for GSWIP 3.1*/
+	u32 mtu_exceed_discard_pkts;
+	/*Applicable only for GSWIP 3.1*/
+	u32 tx_under_size_good_pkts;
+	/*Applicable only for GSWIP 3.1*/
+	u32 tx_oversize_good_pkts;
+	/** Receive Packet Count (only packets that are accepted and not discarded). */
+	u32 rx_good_pkts;
+	/** Receive Unicast Packet Count. */
+	u32 rx_unicast_pkts;
+	/** Receive Broadcast Packet Count. */
+	u32 rx_broadcast_pkts;
+	/** Receive Multicast Packet Count. */
+	u32 rx_multicast_pkts;
+	/** Receive FCS Error Packet Count. */
+	u32 rx_fcserror_pkts;
+	/** Receive Undersize Good Packet Count. */
+	u32 rx_under_size_good_pkts;
+	/** Receive Oversize Good Packet Count. */
+	u32 rx_oversize_good_pkts;
+	/** Receive Undersize Error Packet Count. */
+	u32 rx_under_size_error_pkts;
+	/** Receive Good Pause Packet Count. */
+	u32 rx_good_pause_pkts;
+	/** Receive Oversize Error Packet Count. */
+	u32 rx_oversize_error_pkts;
+	/** Receive Align Error Packet Count. */
+	u32 rx_align_error_pkts;
+	/** Filtered Packet Count. */
+	u32 rx_filtered_pkts;
+	/** Receive Size 64 Bytes Packet Count. */
+	u32 rx64byte_pkts;
+	/** Receive Size 65-127 Bytes Packet Count. */
+	u32 rx127byte_pkts;
+	/** Receive Size 128-255 Bytes Packet Count. */
+	u32 rx255byte_pkts;
+	/** Receive Size 256-511 Bytes Packet Count. */
+	u32 rx511byte_pkts;
+	/** Receive Size 512-1023 Bytes Packet Count. */
+	u32 rx1023byte_pkts;
+	/** Receive Size 1024-1522 Bytes (or more, if configured) Packet Count. */
+	u32 rx_max_byte_pkts;
+	/** Overall Transmit Good Packets Count. */
+	u32 tx_good_pkts;
+	/** Transmit Unicast Packet Count. */
+	u32 tx_unicast_pkts;
+	/** Transmit Broadcast Packet Count. */
+	u32 tx_broadcast_pkts;
+	/** Transmit Multicast Packet Count. */
+	u32 tx_multicast_pkts;
+	/** Transmit Single Collision Count. */
+	u32 tx_single_coll_count;
+	/** Transmit Multiple Collision Count. */
+	u32 tx_mult_coll_count;
+	/** Transmit Late Collision Count. */
+	u32 tx_late_coll_count;
+	/** Transmit Excessive Collision Count. */
+	u32 tx_excess_coll_count;
+	/** Transmit Collision Count. */
+	u32 tx_coll_count;
+	/** Transmit Pause Packet Count. */
+	u32 tx_pause_count;
+	/** Transmit Size 64 Bytes Packet Count. */
+	u32 tx64byte_pkts;
+	/** Transmit Size 65-127 Bytes Packet Count. */
+	u32 tx127byte_pkts;
+	/** Transmit Size 128-255 Bytes Packet Count. */
+	u32 tx255byte_pkts;
+	/** Transmit Size 256-511 Bytes Packet Count. */
+	u32 tx511byte_pkts;
+	/** Transmit Size 512-1023 Bytes Packet Count. */
+	u32 tx1023byte_pkts;
+	/** Transmit Size 1024-1522 Bytes (or more, if configured) Packet Count. */
+	u32 tx_max_byte_pkts;
+	/** Transmit Drop Packet Count. */
+	u32 tx_dropped_pkts;
+	/** Transmit Dropped Packet Count, based on Congestion Management. */
+	u32 tx_acm_dropped_pkts;
+	/** Receive Dropped Packet Count. */
+	u32 rx_dropped_pkts;
+	/** Receive Good Byte Count (64 bit). */
+	u64 rx_good_bytes;
+	/** Receive Bad Byte Count (64 bit). */
+	u64 rx_bad_bytes;
+	/** Transmit Good Byte Count (64 bit). */
+	u64 tx_good_bytes;
+	/**For GSWIP V32 only **/
+	/** Receive Unicast Packet Count for Yellow & Red packet. */
+	u32 rx_unicast_pkts_yellow_red;
+	/** Receive Broadcast Packet Count for Yellow & Red packet. */
+	u32 rx_broadcast_pkts_yellow_red;
+	/** Receive Multicast Packet Count for Yellow & Red packet. */
+	u32 rx_multicast_pkts_yellow_red;
+	/** Receive Good Byte Count (64 bit) for Yellow & Red packet. */
+	u64 rx_good_bytes_yellow_red;
+	/** Receive Packet Count for Yellow & Red packet.  */
+	u32 rx_good_pkts_yellow_red;
+	/** Transmit Unicast Packet Count for Yellow & Red packet. */
+	u32 tx_unicast_pkts_yellow_red;
+	/** Transmit Broadcast Packet Count for Yellow & Red packet. */
+	u32 tx_broadcast_pkts_yellow_red;
+	/** Transmit Multicast Packet Count for Yellow & Red packet. */
+	u32 tx_multicast_pkts_yellow_red;
+	/** Transmit Good Byte Count (64 bit) for Yellow & Red packet. */
+	u64 tx_good_bytes_yellow_red;
+	/** Transmit Packet Count for Yellow & Red packet.  */
+	u32 tx_good_pkts_yellow_red;
+} mxl862xx_debug_rmon_port_cnt_t;
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+#endif /* _MXL862XX_RMON_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h
new file mode 100644
index 0000000..e65906c
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_types.h - DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_TYPES_H_
+#define _MXL862XX_TYPES_H_
+
+#include <linux/phy.h>
+
+/** \brief This is the unsigned 64-bit datatype. */
+typedef uint64_t u64;
+/** \brief This is the unsigned 32-bit datatype. */
+typedef uint32_t u32;
+/** \brief This is the unsigned 16-bit datatype. */
+typedef uint16_t u16;
+/** \brief This is the unsigned 8-bit datatype. */
+typedef uint8_t u8;
+/** \brief This is the signed 64-bit datatype. */
+typedef int64_t i64;
+/** \brief This is the signed 32-bit datatype. */
+typedef int32_t i32;
+/** \brief This is the signed 16-bit datatype. */
+typedef int16_t i16;
+/** \brief This is the signed 8-bit datatype. */
+typedef int8_t i8;
+/** \brief This is the signed 8-bit datatype. */
+typedef int32_t s32;
+/** \brief This is the signed 8-bit datatype. */
+typedef int8_t s8;
+
+
+/** \brief This is a union to describe the IPv4 and IPv6 Address in numeric representation. Used by multiple Structures and APIs. The member selection would be based upon \ref GSW_IP_Select_t */
+typedef union {
+	/** Describe the IPv4 address.
+	    Only used if the IPv4 address should be read or configured.
+	    Cannot be used together with the IPv6 address fields. */
+	u32 nIPv4;
+	/** Describe the IPv6 address.
+	    Only used if the IPv6 address should be read or configured.
+	    Cannot be used together with the IPv4 address fields. */
+	u16 nIPv6[8];
+} mxl862xx_ip_t;
+
+/** \brief Selection to use IPv4 or IPv6.
+    Used  along with \ref GSW_IP_t to denote which union member to be accessed.
+*/
+typedef enum {
+	/** IPv4 Type */
+	MXL862XX_IP_SELECT_IPV4 = 0,
+	/** IPv6 Type */
+	MXL862XX_IP_SELECT_IPV6 = 1
+} mxl862xx_ip_select_t;
+
+typedef struct {
+	int sw_addr;
+	struct mii_bus *bus;
+} mxl862xx_device_t;
+#endif /* _MXL862XX_TYPES_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/mxl862xx.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/mxl862xx.c
new file mode 100755
index 0000000..5cab346
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -0,0 +1,4587 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/mxl862xx.c - DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_device.h>
+#include <linux/phy.h>
+#include <net/dsa.h>
+#include <linux/dsa/8021q.h>
+#include "host_api/mxl862xx_api.h"
+#include "host_api/mxl862xx_mmd_apis.h"
+#include "host_api/mxl862xx_mdio_relay.h"
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#else
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+
+#define MAX_BRIDGES 16
+#define MAX_PORTS 13
+#define MAX_VLAN_ENTRIES (1024-160)
+#define IDX_INVAL (-1)
+
+#define VID_RULES 2
+#define INGRESS_FINAL_RULES 5
+#define INGRESS_VID_RULES VID_RULES
+#define EGRESS_FINAL_RULES 3
+#define EGRESS_VID_RULES VID_RULES
+/* It's only the size of the array for storing VLAN info.
+ * The real number of simultaneous VLANS is lower
+ * and depends on the number of filtering rules and ports.
+ * It is calculated dynamically at runtime. */
+#define MAX_VLANS  100
+#define MAX_RULES_RECYCLED MAX_VLANS
+
+
+/* Index of the array is bridgeID */
+u16 mxl862xx_bridge_portmap[MAX_BRIDGES] = { 0 };
+
+struct mxl862xx_hw_info {
+	uint8_t max_ports;
+	uint8_t phy_ports;
+	uint8_t cpu_port;
+	const struct dsa_switch_ops *ops;
+};
+
+struct mxl862xx_filter_ids {
+	int16_t filters_idx[VID_RULES];
+	bool valid;
+};
+
+struct mxl862xx_vlan {
+	bool used;
+	uint16_t vid;
+	/* indexes of filtering rules(entries) used for this VLAN */
+	int16_t filters_idx[VID_RULES];
+	/* Indicates if tags are added for eggress direction. Makes sense only in egress block */
+	bool untagged;
+};
+
+struct mxl862xx_extended_vlan_block_info {
+	bool allocated;
+	/* current index of the VID related filtering rules  */
+	uint16_t vid_filters_idx;
+	/* current index of the final filtering rules;
+	 * counted backwards starting from the block end */
+	uint16_t final_filters_idx;
+	/* number of allocated  entries for filtering rules */
+	uint16_t filters_max;
+	/* number of entries per vlan */
+	uint16_t entries_per_vlan;
+	uint16_t block_id;
+	/* use this for storing indexes of vlan entries
+	 * for VLAN delete */
+	struct mxl862xx_vlan vlans[MAX_VLANS];
+	/* collect released filter entries (pairs) that can be reused */
+	struct mxl862xx_filter_ids filter_entries_recycled[MAX_VLANS];
+};
+
+struct mxl862xx_port_vlan_info {
+	uint16_t pvid;
+	bool filtering;
+	/* Allow one-time initial vlan_filtering port attribute configuration. */
+	bool filtering_mode_locked;
+	/* Only one block can be assigned per port and direction. Take care about releasing the previous one when
+	 * overwriting with the new one.*/
+	struct mxl862xx_extended_vlan_block_info ingress_vlan_block_info;
+	struct mxl862xx_extended_vlan_block_info egress_vlan_block_info;
+};
+
+struct mxl862xx_port_info {
+	bool port_enabled;
+	bool isolated;
+	uint16_t bridgeID;
+	uint16_t bridge_port_cpu;
+	struct net_device *bridge;
+	enum dsa_tag_protocol tag_protocol;
+	bool ingress_mirror_enabled;
+	bool egress_mirror_enabled;
+	struct mxl862xx_port_vlan_info vlan;
+};
+
+struct mxl862xx_priv {
+	struct mii_bus *bus;
+	struct device *dev;
+	int sw_addr;
+	const struct mxl862xx_hw_info *hw_info;
+	struct mxl862xx_port_info port_info[MAX_PORTS];
+	/* Number of simultaneously supported vlans (calculated in the runtime) */
+	uint16_t max_vlans;
+#if (KERNEL_VERSION(5, 16, 0) <= LINUX_VERSION_CODE)
+	/* pce_table_lock required for kernel 5.16 or later,
+	 * since rtnl_lock has been dropped from DSA.port_fdb_{add,del}
+	 * might cause dead-locks / hang in previous versions */
+	struct mutex pce_table_lock;
+#endif
+};
+
+/* The mxl862_8021q 4-byte tagging is not yet supported in
+ * kernels >= 5.16 due to differences in DSA 8021q tagging handlers.
+ * DSA tx/rx vid functions are not avaliable, so dummy
+ * functions are here to make the code compilable. */
+#if (KERNEL_VERSION(5, 16, 0) <= LINUX_VERSION_CODE)
+static u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port)
+{
+	return 0;
+}
+
+static u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port)
+{
+	return 0;
+}
+#endif
+
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
+				       struct net_device *bridge);
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
+				       struct net_device *bridge);
+#else
+static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
+				   struct dsa_bridge br,
+				   bool *tx_fwd_offload,
+				   struct netlink_ext_ack *extack);
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
+				       struct dsa_bridge br);
+#endif
+
+static int __update_bridge_conf_port(struct dsa_switch *ds, uint8_t port,
+				     struct net_device *bridge, int action);
+
+static int __deactivate_vlan_filter_entry(u16 block_id, u16 entry_idx);
+
+static int __prepare_vlan_ingress_filters_sp_tag_cpu(struct dsa_switch *ds,
+						uint8_t port, uint8_t cpu_port);
+static int __prepare_vlan_egress_filters_off_sp_tag_cpu(struct dsa_switch *ds,
+						uint8_t cpu_port);
+static int __prepare_vlan_ingress_filters_off_sp_tag_no_vid(struct dsa_switch *ds,
+						uint8_t port);
+static int __prepare_vlan_egress_filters_off_sp_tag_no_vid(struct dsa_switch *ds,
+						uint8_t port);
+
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+static void mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan);
+#else
+static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan,
+				  struct netlink_ext_ack *extack);
+#endif
+static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan);
+
+static mxl862xx_device_t mxl_dev;
+
+struct mxl862xx_rmon_cnt_desc {
+	unsigned int size;
+	unsigned int offset;
+	const char *name;
+};
+
+#define MIB_DESC(_name)       \
+	{                     \
+		.name = _name \
+	}
+
+static const struct mxl862xx_rmon_cnt_desc mxl862xx_rmon_cnt[] = {
+	MIB_DESC("RxGoodPkts"), //0
+	MIB_DESC("RxUnicastPkts"),
+	MIB_DESC("RxBroadcastPkts"),
+	MIB_DESC("RxMulticastPkts"),
+	MIB_DESC("RxFCSErrorPkts"),
+	MIB_DESC("RxUnderSizeGoodPkts"),
+	MIB_DESC("RxOversizeGoodPkts"),
+	MIB_DESC("RxUnderSizeErrorPkts"),
+	MIB_DESC("RxOversizeErrorPkts"),
+	MIB_DESC("RxFilteredPkts"),
+	MIB_DESC("Rx64BytePkts"), //10
+	MIB_DESC("Rx127BytePkts"),
+	MIB_DESC("Rx255BytePkts"),
+	MIB_DESC("Rx511BytePkts"),
+	MIB_DESC("Rx1023BytePkts"),
+	MIB_DESC("RxMaxBytePkts"),
+	MIB_DESC("RxDroppedPkts"),
+	MIB_DESC("RxExtendedVlanDiscardPkts"),
+	MIB_DESC("MtuExceedDiscardPkts"),
+	MIB_DESC("RxGoodBytes"),
+	MIB_DESC("RxBadBytes"), //20
+	MIB_DESC("RxUnicastPktsYellowRed"),
+	MIB_DESC("RxBroadcastPktsYellowRed"),
+	MIB_DESC("RxMulticastPktsYellowRed"),
+	MIB_DESC("RxGoodPktsYellowRed"),
+	MIB_DESC("RxGoodBytesYellowRed"),
+	MIB_DESC("RxGoodPausePkts"),
+	MIB_DESC("RxAlignErrorPkts"),
+	//-----------------------------
+	MIB_DESC("TxGoodPkts"),
+	MIB_DESC("TxUnicastPkts"),
+	MIB_DESC("TxBroadcastPkts"), //30
+	MIB_DESC("TxMulticastPkts"),
+	MIB_DESC("Tx64BytePkts"),
+	MIB_DESC("Tx127BytePkts"),
+	MIB_DESC("Tx255BytePkts"),
+	MIB_DESC("Tx511BytePkts"),
+	MIB_DESC("Tx1023BytePkts"),
+	MIB_DESC("TxMaxBytePkts"),
+	MIB_DESC("TxDroppedPkts"),
+	MIB_DESC("TxAcmDroppedPkts"),
+	MIB_DESC("TxGoodBytes"), //40
+	MIB_DESC("TxUnicastPktsYellowRed"),
+	MIB_DESC("TxBroadcastPktsYellowRed"),
+	MIB_DESC("TxMulticastPktsYellowRed"),
+	MIB_DESC("TxGoodPktsYellowRed"),
+	MIB_DESC("TxGoodBytesYellowRed"),
+	MIB_DESC("TxSingleCollCount"),
+	MIB_DESC("TxMultCollCount"),
+	MIB_DESC("TxLateCollCount"),
+	MIB_DESC("TxExcessCollCount"),
+	MIB_DESC("TxCollCount"), //50
+	MIB_DESC("TxPauseCount"),
+};
+
+static int mxl862xx_phy_read_mmd(struct dsa_switch *ds, int devnum, int port, int reg)
+{
+	int ret = MXL862XX_STATUS_ERR;
+	struct mdio_relay_data param = { 0 };
+
+	param.phy = port;
+	param.mmd = devnum;
+	param.reg = reg & 0xffff;
+
+	ret = int_gphy_read(&mxl_dev, &param);
+	if (ret == MXL862XX_STATUS_OK)
+		goto EXIT;
+	else {
+		pr_err("%s: reading port: %d failed with err: %d\n",
+			__func__, port, ret);
+	}
+
+	return ret;
+
+EXIT:
+	return param.data;
+}
+
+static int mxl862xx_phy_write_mmd(struct dsa_switch *ds, int devnum, int port, int reg,
+			      u16 data)
+{
+	int ret = MXL862XX_STATUS_ERR;
+	struct mdio_relay_data param = { 0 };
+
+	param.phy = port;
+	param.mmd = devnum;
+	param.reg = reg;
+	param.data = data;
+
+	ret = int_gphy_write(&mxl_dev, &param);
+	if (ret == MXL862XX_STATUS_OK)
+		goto EXIT;
+	else {
+		pr_err("%s: writing port: %d failed with err: %d\n",
+			__func__, port, ret);
+	}
+
+EXIT:
+	return ret;
+}
+
+static int mxl862xx_phy_read(struct dsa_switch *ds, int port, int reg)
+{
+	int ret = mxl862xx_phy_read_mmd(ds, 0, port, reg);
+	return ret;
+}
+
+static int mxl862xx_phy_write(struct dsa_switch *ds, int port, int reg,
+			      u16 data)
+{
+	int ret = mxl862xx_phy_write_mmd(ds, 0, port, reg, data);
+	return ret;
+}
+
+static int mxl862xx_mmd_write(const mxl862xx_device_t *dev, int reg, u16 data)
+{
+	int ret;
+	/* lock mdio bus */
+	mutex_lock_nested(&dev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+	ret = mxl862xx_write(dev, reg, data);
+	/* unlock mdio bus */
+	mutex_unlock(&dev->bus->mdio_lock);
+
+	return ret;
+}
+
+static int __config_mxl862_tag_proto(struct dsa_switch *ds, uint8_t port, bool enable)
+{
+
+	int ret = MXL862XX_STATUS_ERR;
+	uint8_t pid = port + 1;
+	mxl862xx_ss_sp_tag_t param = { 0 };
+	mxl862xx_ctp_port_assignment_t ctp_port_assign = { 0 };
+
+	param.pid = pid;
+	param.mask = BIT(0) | BIT(1);
+	if (enable) {
+		param.rx = 2;
+		param.tx = 2;
+	} else {
+		param.rx = 1;
+		param.tx = 3;
+	}
+
+	ret = mxl862xx_ss_sp_tag_set(&mxl_dev, &param);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+		"%s: Error %d while configuring DSA frame tagging for port:%d\n",
+		__func__, ret, port);
+		goto EXIT;
+	}
+
+	if (enable)
+		ctp_port_assign.number_of_ctp_port = 32 - pid;
+	else
+		ctp_port_assign.number_of_ctp_port = 1;
+
+	ctp_port_assign.logical_port_id = pid;
+	ctp_port_assign.first_ctp_port_id = pid;
+	ctp_port_assign.mode = MXL862XX_LOGICAL_PORT_GPON;
+
+	ret = mxl862xx_ctp_port_assignment_set(&mxl_dev, &ctp_port_assign);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Error %d while configuring assignment of port %d for DSA frame tagging.\n",
+			__func__, ret, port);
+		goto EXIT;
+	}
+
+EXIT:
+	return ret;
+}
+
+#if (KERNEL_VERSION(5, 3, 0) > LINUX_VERSION_CODE)
+static void mxl862xx_port_disable(struct dsa_switch *ds, int port, struct phy_device *phy)
+#else
+static void mxl862xx_port_disable(struct dsa_switch *ds, int port)
+#endif
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	mxl862xx_register_mod_t register_mod = { 0 };
+	int ret = 0;
+
+	if (!dsa_is_user_port(ds, port))
+		return;
+
+	/* Disable datapath */
+	register_mod.reg_addr = MxL862XX_SDMA_PCTRLp(port + 1);
+	register_mod.data = 0;
+	register_mod.mask = MXL862XX_SDMA_PCTRL_EN;
+	ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: Disable Datapath SDMA failed:%d\n",
+			__func__, port);
+		return;
+	}
+	register_mod.reg_addr = MxL862XX_FDMA_PCTRLp(port + 1);
+	register_mod.data = 0;
+	register_mod.mask = MXL862XX_FDMA_PCTRL_EN;
+	ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: Disable Datapath FDMA failed:%d\n",
+			__func__, port);
+		return;
+	}
+
+	priv->port_info[port].port_enabled = false;
+}
+
+static int mxl862xx_port_enable(struct dsa_switch *ds, int port,
+				struct phy_device *phydev)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	mxl862xx_register_mod_t register_mod = { 0 };
+	int ret;
+
+	if (!dsa_is_user_port(ds, port))
+		return 0;
+
+	if (!dsa_is_cpu_port(ds, port)) {
+		/* Enable datapath */
+		register_mod.reg_addr = MxL862XX_SDMA_PCTRLp(port + 1);
+		register_mod.data = MXL862XX_SDMA_PCTRL_EN;
+		register_mod.mask = MXL862XX_SDMA_PCTRL_EN;
+		ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: Enable Datapath SDMA failed:%d\n",
+				__func__, port);
+			return ret;
+		}
+		register_mod.reg_addr = MxL862XX_FDMA_PCTRLp(port + 1);
+		register_mod.data = MXL862XX_FDMA_PCTRL_EN;
+		register_mod.mask = MXL862XX_FDMA_PCTRL_EN;
+		ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: Enable Datapath FDMA failed:%d\n",
+				__func__, port);
+			return ret;
+		}
+	}
+
+	priv->port_info[port].port_enabled = true;
+
+	return 0;
+}
+
+static int __isolate_port(struct dsa_switch *ds, uint8_t port)
+{
+	mxl862xx_bridge_alloc_t br_alloc = { 0 };
+	struct mxl862xx_priv *priv = ds->priv;
+	int ret = -EINVAL;
+	uint8_t cpu_port = priv->hw_info->cpu_port;
+	bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+
+	/* Exit if port is already isolated */
+	if (priv->port_info[port].isolated)
+		return ret;
+
+	ret = mxl862xx_bridge_alloc(&mxl_dev, &br_alloc);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: bridge alloc failed for port %d\n, ret:%d",
+			__func__, port, ret);
+		return ret;
+	}
+
+	priv->port_info[port].bridgeID = br_alloc.bridge_id;
+	priv->port_info[port].bridge = NULL;
+
+	ret = __update_bridge_conf_port(ds, port, NULL, 1);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: bridge port adding failed for port %d, ret %d\n",
+			__func__, port, ret);
+	}
+
+	/* for VLAN special tagging mode add port to vlan 1 to apply also
+	 * the special tag handling filters */
+	if (vlan_sp_tag) {
+		/* set port vlan 1 untagged */
+		struct switchdev_obj_port_vlan vlan;
+		uint16_t vid = 1;
+		bool filtering_prev = priv->port_info[port].vlan.filtering;
+
+		priv->port_info[port].vlan.filtering = true;
+		vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED|BRIDGE_VLAN_INFO_PVID;
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+		vlan.vid_begin = vid;
+		mxl862xx_port_vlan_add(ds, port, &vlan);
+#else
+		vlan.vid = vid;
+		ret = mxl862xx_port_vlan_add(ds, port, &vlan, NULL);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: adding port %d, to vlan:%d failed with ret %d\n",
+				__func__, port, vlan.vid, ret);
+		}
+
+#endif
+		priv->port_info[port].vlan.filtering = filtering_prev;
+	}
+
+	priv->port_info[port].isolated = true;
+	return ret;
+}
+
+static void __deisolate_port(struct dsa_switch *ds, uint8_t port)
+{
+	mxl862xx_bridge_alloc_t br_alloc = { 0 };
+	struct mxl862xx_priv *priv = ds->priv;
+	uint8_t cpu_port = priv->hw_info->cpu_port;
+	bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+	int ret = -EINVAL;
+
+	/* Check if port is isolated. The isolation with standalone bridges was
+	 * done skipping the linux bridge layer so bridge should be NULL */
+	if (!priv->port_info[port].isolated)
+		return;
+
+	ret = __update_bridge_conf_port(ds, port, NULL, 0);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: bridge port removal failed for port %d, ret %d\n",
+			__func__, port, ret);
+		return;
+	}
+
+	br_alloc.bridge_id = priv->port_info[port].bridgeID;
+	ret = mxl862xx_bridge_free(&mxl_dev, &br_alloc);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: bridge free failed for port:%d, BridgeID: %d, ret: %d\n",
+			__func__, port, br_alloc.bridge_id, ret);
+		return;
+	}
+
+	/* For VLAN special tagging mode isolated port is assigned to VLAN 1
+	 * to apply also the special tag handling filters. Now for deisolation
+	 * VLAN 1 must be unassigned */
+	if (vlan_sp_tag) {
+		struct switchdev_obj_port_vlan vlan;
+		uint16_t vid = 1;
+		uint16_t i;
+
+		vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED|BRIDGE_VLAN_INFO_PVID;
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+		vlan.vid_begin = vid;
+#else
+		vlan.vid = vid;
+#endif
+		/* Removes vid dependant 'dynamic' rules */
+		ret = mxl862xx_port_vlan_del(ds, port, &vlan);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: deleting port %d, from vlan:%d failed with ret %d\n",
+				__func__, port, vid, ret);
+		}
+
+		/* Clear/deactivate 'static' set of filtering rules, placed at the end of the block */
+		for (i = 0 ; i < 2 ; i++) {
+			uint16_t j, start_idx, stop_idx, block_id;
+			struct mxl862xx_extended_vlan_block_info *block_info = (i == 0)
+				? &priv->port_info[port].vlan.ingress_vlan_block_info
+				: &priv->port_info[port].vlan.egress_vlan_block_info;
+
+			block_id = block_info->block_id;
+			stop_idx = block_info->filters_max;
+			start_idx = block_info->final_filters_idx;
+
+			for (j = start_idx ; j < stop_idx ; j++) {
+				ret = __deactivate_vlan_filter_entry(block_id, j);
+				if (ret != MXL862XX_STATUS_OK)
+					return;
+			}
+			/* Entries cleared, so point out to the end */
+			block_info->final_filters_idx = j;
+		}
+	}
+
+	priv->port_info[port].isolated = false;
+
+	return;
+}
+
+static int __update_bridge_conf_port(struct dsa_switch *ds, uint8_t port,
+				     struct net_device *bridge, int action)
+{
+	int ret = -EINVAL;
+	uint8_t i;
+	struct mxl862xx_priv *priv = ds->priv;
+	uint8_t phy_ports = priv->hw_info->phy_ports;
+	uint8_t cpu_port = priv->hw_info->cpu_port;
+	bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+
+	/* Update local bridge port map */
+	for (i = 0; i < phy_ports; i++) {
+		int bridgeID = priv->port_info[i].bridgeID;
+
+		if (!((struct dsa_port *)dsa_to_port(ds, i)))
+			continue;
+
+		/* CPU port is assigned to all bridges and cannot be modified  */
+		if ((dsa_is_cpu_port(ds, i)))
+			continue;
+
+		/* Skip if bridge does not match, except the self port assignment  */
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+		if ((dsa_to_port(ds, i)->bridge_dev != bridge) && (i != port))
+#else
+		if ((dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != bridge) && (i != port))
+#endif
+			continue;
+
+		/* Case for standalone bridges assigned only to single user and CPU ports.
+		 * Used only for initial ports isolation */
+		if ((bridge == NULL) && (i != port))
+			continue;
+
+		if (action) {
+			/* Add port to bridge portmap */
+			mxl862xx_bridge_portmap[bridgeID] |= BIT(port + 1);
+		} else {
+			/* Remove port from the bitmap */
+			mxl862xx_bridge_portmap[bridgeID] &= ~BIT(port + 1);
+		}
+	}
+
+	/* Update switch according to local bridge port map */
+	/* Add this port to the port maps of other ports skiping it's own map */
+	for (i = 0; i < phy_ports; i++) {
+		mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+		int bridgeID = priv->port_info[i].bridgeID;
+
+		if (!(dsa_is_user_port(ds, i)))
+			continue;
+
+		/* Case for standalone bridges assigned only to single user and CPU ports.
+		 * Used only for initial ports isolation */
+		if ((bridge == NULL) && (i != port))
+			continue;
+
+		/* Do not reconfigure any standalone bridge if this is bridge join scenario */
+		if ((bridge != NULL) && (priv->port_info[i].bridge == NULL))
+			continue;
+
+		br_port_cfg.bridge_port_id = i + 1;
+		br_port_cfg.mask |=
+			MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
+			MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID;
+
+		ret = mxl862xx_bridge_port_config_get(&mxl_dev, &br_port_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: Bridge port configuration for port %d failed with %d\n",
+				__func__, port, ret);
+			goto EXIT;
+		}
+
+		/* Skip port map update if for the existing bridge the port
+		 * is not assigned there. */
+		if (i != port && (br_port_cfg.bridge_id != bridgeID ||
+				  br_port_cfg.bridge_id == 0))
+			continue;
+
+		br_port_cfg.mask |=
+			MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
+			MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
+			MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+
+		/* Skip the port itself in it's own portmap */
+		br_port_cfg.bridge_port_map[0] =
+			mxl862xx_bridge_portmap[bridgeID] & ~(BIT(i + 1));
+
+		if (action) {
+			/* If bridge is null then this is port isolation scenario. Disable MAC learning. */
+			br_port_cfg.src_mac_learning_disable =
+				(bridge == NULL) ? true : false;
+			br_port_cfg.bridge_id = bridgeID;
+		}
+		/* When port is removed from the bridge, assign it back to the default
+		 * bridge 0 */
+		else {
+			br_port_cfg.src_mac_learning_disable = true;
+			/* Cleanup the port own map leaving only the CPU port mapping. */
+			if (i == port) {
+				br_port_cfg.bridge_port_map[0] =
+					BIT(cpu_port + 1);
+				br_port_cfg.bridge_id = 0;
+			}
+		}
+
+		ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: Bridge port configuration for port %d failed with ret=%d\n",
+				__func__, port, ret);
+			goto EXIT;
+		}
+	}
+
+	/* Configure additional bridge port for VLAN based tagging */
+	if (vlan_sp_tag) {
+		int bridgeID = priv->port_info[port].bridgeID;
+		uint16_t bridge_port_cpu = port + 1 + 16;
+		mxl862xx_bridge_port_alloc_t bpa_param = { 0 };
+		mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+
+		bpa_param.bridge_port_id = bridge_port_cpu;
+
+		if (action) {
+			ret = mxl862xx_bridge_port_alloc(&mxl_dev, &bpa_param);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to prepare associated bridge port\n",
+					__func__, port);
+				goto EXIT;
+			}
+
+			br_port_cfg.mask |=
+				MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
+				MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
+				MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING |
+				MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+			br_port_cfg.bridge_id = bridgeID;
+			br_port_cfg.bridge_port_id = bridge_port_cpu;
+			br_port_cfg.bridge_port_map[0] =	BIT(port + 1);
+			br_port_cfg.dest_logical_port_id = cpu_port + 1;
+			br_port_cfg.src_mac_learning_disable = true;
+
+			ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+				"%s: Configuration of cpu bridge port:%d  for port:%d on bridge:%d failed with ret=%d\n",
+				__func__, bridge_port_cpu, port, bridgeID, ret);
+				goto EXIT;
+			}
+
+			/* Add bridge cpu port to portmap */
+			mxl862xx_bridge_portmap[bridgeID] |= BIT(bridge_port_cpu);
+			priv->port_info[port].bridge_port_cpu = bridge_port_cpu;
+		}
+		/* remove */
+		else {
+			ret = mxl862xx_bridge_port_free(&mxl_dev, &bpa_param);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to free associated bridge port\n",
+					__func__, port);
+				goto EXIT;
+			}
+			/* Remove bridge cpu port from portmap */
+			mxl862xx_bridge_portmap[bridgeID] &= ~BIT(bridge_port_cpu);
+			priv->port_info[port].bridge_port_cpu = 0;
+		}
+
+	}
+
+EXIT:
+	return ret;
+}
+
+/* Returns bridgeID, or 0 if bridge not found */
+static int __find_bridgeID(struct dsa_switch *ds, struct net_device *bridge)
+{
+	uint8_t i;
+	struct mxl862xx_priv *priv = ds->priv;
+	uint8_t phy_ports = priv->hw_info->phy_ports;
+	int bridgeID = 0;
+
+	/* The only use case where bridge is NULL is the isolation
+	 * with individual port bridges configured in the setup function. */
+	if (bridge == NULL)
+		return bridgeID;
+
+	for (i = 0; i < phy_ports; i++) {
+		if (priv->port_info[i].bridge == bridge) {
+			bridgeID = priv->port_info[i].bridgeID;
+			break;
+		}
+	}
+	return bridgeID;
+}
+
+static void __set_vlan_filters_limits(struct dsa_switch *ds)
+{
+	uint8_t i;
+	uint16_t cpu_ingress_entries;
+	uint16_t cpu_egress_entries;
+	uint16_t user_ingress_entries;
+	uint16_t user_egress_entries;
+	struct mxl862xx_priv *priv = ds->priv;
+	uint8_t cpu_port = priv->hw_info->cpu_port;
+
+	/* Set limits and indexes required for processing VLAN rules for CPU port */
+
+	/* The calculation of the max number of simultaneously supported VLANS (priv->max_vlans)
+	 * comes from the equation:
+	 *
+	 * MAX_VLAN_ENTRIES = phy_ports * (EGRESS_FINAL_RULES + EGRESS_VID_RULES * priv->max_vlans)
+	 *  + phy_ports * (INGRESS_FINAL_RULES + INGRESS_VID_RULES * priv-> max_vlans)
+	 *  + cpu_ingress_entries + cpu_egress_entries  */
+
+	if (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q) {
+
+		priv->max_vlans = (MAX_VLAN_ENTRIES - priv->hw_info->phy_ports *
+				(EGRESS_FINAL_RULES + INGRESS_FINAL_RULES + 2) - 3) /
+			(priv->hw_info->phy_ports * (EGRESS_VID_RULES + INGRESS_VID_RULES) + 2);
+		/* 2 entries per port and 1 entry for fixed rule */
+		cpu_ingress_entries = priv->hw_info->phy_ports * 2 + 1;
+		/* 2 entries per each vlan and 2 entries for fixed rules */
+		cpu_egress_entries = priv->max_vlans * 2 + 2;
+
+		//priv->port_info[cpu_port].vlan.ingress_vlan_block_info.entries_per_vlan = 2;
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.entries_per_vlan = 0;
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.filters_max = cpu_ingress_entries;
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.entries_per_vlan = 2;
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.filters_max = cpu_egress_entries;
+
+		user_ingress_entries = INGRESS_FINAL_RULES + INGRESS_VID_RULES * priv->max_vlans;
+		user_egress_entries = EGRESS_FINAL_RULES + EGRESS_VID_RULES * priv->max_vlans;
+	} else {
+		priv->max_vlans = (MAX_VLAN_ENTRIES - priv->hw_info->phy_ports *
+				(EGRESS_FINAL_RULES + INGRESS_FINAL_RULES) - 1) /
+			(priv->hw_info->phy_ports * (EGRESS_VID_RULES + INGRESS_VID_RULES) + 2);
+		/* 1 entry for fixed rule */
+		cpu_ingress_entries =  1;
+		/* 2 entries per each vlan  */
+		cpu_egress_entries = priv->max_vlans * 2;
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.entries_per_vlan = 0;
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.filters_max = cpu_ingress_entries;
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.entries_per_vlan = 2;
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.filters_max = cpu_egress_entries;
+
+		user_ingress_entries = INGRESS_FINAL_RULES + INGRESS_VID_RULES * priv->max_vlans;
+		user_egress_entries = EGRESS_FINAL_RULES + EGRESS_VID_RULES * priv->max_vlans;
+	}
+
+	/* This index is counted backwards */
+	priv->port_info[cpu_port].vlan.ingress_vlan_block_info.final_filters_idx =
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.filters_max - 1;
+	priv->port_info[cpu_port].vlan.egress_vlan_block_info.final_filters_idx =
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.filters_max - 1;
+
+	/* Set limits and indexes required for processing VLAN rules for user ports */
+	for (i = 0; i < priv->hw_info->phy_ports; i++) {
+		priv->port_info[i].vlan.ingress_vlan_block_info.entries_per_vlan = INGRESS_VID_RULES;
+		priv->port_info[i].vlan.ingress_vlan_block_info.filters_max = user_ingress_entries;
+		priv->port_info[i].vlan.egress_vlan_block_info.entries_per_vlan = EGRESS_VID_RULES;
+		priv->port_info[i].vlan.egress_vlan_block_info.filters_max = user_egress_entries;
+		/* This index is counted backwards */
+		priv->port_info[i].vlan.ingress_vlan_block_info.final_filters_idx =
+			priv->port_info[i].vlan.ingress_vlan_block_info.filters_max - 1;
+		priv->port_info[i].vlan.egress_vlan_block_info.final_filters_idx =
+			priv->port_info[i].vlan.egress_vlan_block_info.filters_max - 1;
+	}
+	dev_info(ds->dev, "%s: phy_ports:%d, priv->max_vlans: %d, cpu_egress_entries: %d, user_ingress_entries: %d, INGRESS_VID_RULES: %d\n",
+			__func__, priv->hw_info->phy_ports, priv->max_vlans,
+			cpu_egress_entries, user_ingress_entries, INGRESS_VID_RULES);
+}
+
+static enum dsa_tag_protocol __dt_parse_tag_proto(struct dsa_switch *ds, uint8_t port)
+{
+	/* Default value if no dt entry found */
+	enum dsa_tag_protocol tag_proto = DSA_TAG_PROTO_MXL862;
+	struct dsa_port *dp = (struct dsa_port *)dsa_to_port(ds, port);
+	const char *user_protocol = NULL;
+
+	if (dp != NULL)
+		user_protocol = of_get_property(dp->dn, "dsa-tag-protocol", NULL);
+	if (user_protocol != NULL) {
+		if (strcmp("mxl862", user_protocol) == 0)
+			tag_proto = DSA_TAG_PROTO_MXL862;
+		else if (strcmp("mxl862_8021q", user_protocol) == 0)
+			tag_proto = DSA_TAG_PROTO_MXL862_8021Q;
+	}
+
+	return tag_proto;
+}
+
+static int mxl862xx_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+	int ret = -EINVAL;
+	mxl862xx_cfg_t cfg = { 0 };
+
+	ret =  mxl862xx_cfg_get(&mxl_dev, &cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: failed to read switch config\n", __func__);
+		goto EXIT;
+	}
+
+	cfg.mac_table_age_timer = MXL862XX_AGETIMER_CUSTOM;
+	cfg.age_timer = msecs / 1000;
+
+	ret =  mxl862xx_cfg_set(&mxl_dev, &cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: failed to configure global MAC ageing value: %ds\n",
+			__func__, cfg.age_timer);
+	}
+
+EXIT:
+	return ret;
+}
+
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
+					struct net_device *bridge)
+#else
+static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
+					struct dsa_bridge br, bool *tx_fwd_offload,
+					struct netlink_ext_ack *extack)
+#endif
+
+{
+#if (KERNEL_VERSION(5, 17, 0) <= LINUX_VERSION_CODE)
+	struct net_device *bridge = br.dev;
+#endif
+	struct mxl862xx_priv *priv = ds->priv;
+	int bridgeID = 0;
+	int ret = -EINVAL;
+	uint8_t cpu_port = priv->hw_info->cpu_port;
+	bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+
+	if (port < 0 || port >= MAX_PORTS) {
+		dev_err(priv->dev, "invalid port: %d\n", port);
+		return ret;
+	}
+
+	__deisolate_port(ds, port);
+
+	bridgeID = __find_bridgeID(ds, bridge);
+
+	/* no bridge found -> create new bridge */
+	if (bridgeID == 0) {
+		mxl862xx_bridge_alloc_t br_alloc = { 0 };
+
+		ret = mxl862xx_bridge_alloc(&mxl_dev, &br_alloc);
+		if (ret != MXL862XX_STATUS_OK) {
+#if (KERNEL_VERSION(5, 17, 0) <= LINUX_VERSION_CODE)
+			NL_SET_ERR_MSG_MOD(extack,
+				   "MxL862xx: bridge alloc failed");
+#endif
+			dev_err(ds->dev,
+				"%s: bridge alloc failed for port %d\n, ret:%d",
+				__func__, port, ret);
+			goto EXIT;
+		}
+		priv->port_info[port].bridgeID = br_alloc.bridge_id;
+		priv->port_info[port].bridge = bridge;
+		/* bridge found  */
+	} else {
+		priv->port_info[port].bridgeID = bridgeID;
+		priv->port_info[port].bridge = bridge;
+	}
+
+	ret = __update_bridge_conf_port(ds, port, bridge, 1);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: bridge port adding failed for port %d, ret %d\n",
+			__func__, port, ret);
+		goto EXIT;
+	}
+	/* For some kernel versions for VLAN unaware bridges Linux calls .port_vlan_add,
+	 * for others not. To support VLAN unaware bridge in mxl862_8021q tagging mode,
+	 * the required vlan filtering rules (adding sp tag, forwarding to cpu port)
+	 * are added here.*/
+	if (vlan_sp_tag) {
+		mxl862xx_ctp_port_config_t ctp_param = { 0 };
+		mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+
+		ret = __prepare_vlan_ingress_filters_sp_tag_cpu(ds, port, cpu_port);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+		ret = __prepare_vlan_egress_filters_off_sp_tag_cpu(ds, cpu_port);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+		ret = __prepare_vlan_ingress_filters_off_sp_tag_no_vid(ds, port);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+		ret = __prepare_vlan_egress_filters_off_sp_tag_no_vid(ds, port);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		/* update cpu port */
+		ctp_param.logical_port_id = cpu_port + 1;
+		ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN |
+				     MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN;
+		ctp_param.egress_extended_vlan_enable = true;
+		ctp_param.egress_extended_vlan_block_id =
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.block_id;
+		ctp_param.ingress_extended_vlan_enable = true;
+		ctp_param.ingress_extended_vlan_block_id =
+			priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id;
+
+		ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: CTP port %d config failed on port config set with %d\n",
+				__func__, cpu_port, ret);
+#if (KERNEL_VERSION(5, 17, 0) <= LINUX_VERSION_CODE)
+			NL_SET_ERR_MSG_MOD(extack, "Failed to configure VLAN for cpu port");
+#endif
+			goto EXIT;
+		}
+
+		/* Update bridge port */
+		br_port_cfg.bridge_port_id = port + 1;
+		br_port_cfg.mask |= MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN |
+			     MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN;
+		br_port_cfg.egress_extended_vlan_enable = true;
+		br_port_cfg.egress_extended_vlan_block_id =
+			priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+		br_port_cfg.ingress_extended_vlan_enable = true;
+		br_port_cfg.ingress_extended_vlan_block_id =
+			priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+
+		ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: Bridge port configuration for port %d failed with %d\n",
+				__func__, port, ret);
+#if (KERNEL_VERSION(5, 17, 0) <= LINUX_VERSION_CODE)
+			NL_SET_ERR_MSG_MOD(extack, "Bridge port configuration for VLAN failed");
+#endif
+		}
+	}
+
+EXIT:
+	return ret;
+}
+
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
+				       struct net_device *bridge)
+#else
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
+				       struct dsa_bridge br)
+#endif
+{
+#if (KERNEL_VERSION(5, 17, 0) <= LINUX_VERSION_CODE)
+	struct net_device *bridge = br.dev;
+#endif
+	struct mxl862xx_priv *priv = ds->priv;
+	mxl862xx_bridge_alloc_t br_alloc = { 0 };
+	unsigned int cpu_port = priv->hw_info->cpu_port;
+	int bridgeID = 0;
+	int ret;
+
+	bridgeID = __find_bridgeID(ds, bridge);
+	ret = __update_bridge_conf_port(ds, port, bridge, 0);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: bridge port removal failed for port %d, ret %d\n",
+			__func__, port, ret);
+		goto EXIT;
+	}
+
+	/* If only CPU port mapping found, the bridge should be deleted */
+	if (mxl862xx_bridge_portmap[bridgeID] == BIT(cpu_port + 1)) {
+		br_alloc.bridge_id = priv->port_info[port].bridgeID;
+		ret = mxl862xx_bridge_free(&mxl_dev, &br_alloc);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: bridge free failed for port:%d, BridgeID: %d, ret: %d\n",
+				__func__, port, br_alloc.bridge_id, ret);
+			goto EXIT;
+		}
+	}
+
+	ret = __isolate_port(ds, port);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: Port%d isolation failed with ret:%d\n",
+			__func__, port, ret);
+	}
+
+	priv->port_info[port].vlan.filtering_mode_locked = false;
+
+EXIT:
+	return;
+}
+
+
+static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int addr, int regnum)
+{
+	int ret = mxl862xx_phy_read_mmd(NULL, 0, addr, regnum);
+	return ret;
+}
+
+
+static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int addr, int regnum, u16 val)
+{
+	int ret = mxl862xx_phy_write_mmd(NULL, 0, addr, regnum, val);
+	return ret;
+}
+
+#if (KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE)
+static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int devnum, int addr, int regnum)
+{
+	int ret = mxl862xx_phy_read_mmd(NULL, devnum, addr, regnum);
+	return ret;
+}
+
+
+static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int devnum, int addr, int regnum, u16 val)
+{
+	int ret = mxl862xx_phy_write_mmd(NULL, devnum, addr, regnum, val);
+	return ret;
+}
+#endif
+
+static int
+mxl862xx_setup_mdio(struct dsa_switch *ds)
+{
+	struct device *dev = ds->dev;
+	struct mii_bus *bus;
+	static int idx;
+	int ret;
+
+	bus = devm_mdiobus_alloc(dev);
+	if (!bus)
+		return -ENOMEM;
+
+#if (KERNEL_VERSION(6, 7, 0) > LINUX_VERSION_CODE)
+	ds->slave_mii_bus = bus;
+#else
+	ds->user_mii_bus = bus;
+#endif
+	bus->name = KBUILD_MODNAME "-mii";
+	snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++);
+#if (KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE)
+	bus->read_c45 = mxl862xx_phy_read_c45_mii_bus;
+	bus->write_c45 = mxl862xx_phy_write_c45_mii_bus;
+#endif
+	bus->read = mxl862xx_phy_read_mii_bus;
+	bus->write = mxl862xx_phy_write_mii_bus;
+	bus->parent = dev;
+	bus->phy_mask = ~ds->phys_mii_mask;
+
+#if (KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE)
+	ret = devm_mdiobus_register(dev, bus);
+#else
+	ret = mdiobus_register(bus);
+#endif
+	if (ret)
+		dev_err(dev, "failed to register MDIO bus: %d\n", ret);
+
+	return ret;
+}
+
+static int mxl862xx_setup(struct dsa_switch *ds)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	unsigned int cpu_port = priv->hw_info->cpu_port;
+	int ret = 0;
+	uint8_t i;
+
+	ret = mxl862xx_setup_mdio(ds);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: mdio setup failed with %d\n", __func__,
+			ret);
+		goto EXIT;
+	}
+
+	/* Trigger ETHSW_SWRES to re-initiate all previous settings */
+	ret = mxl862xx_mmd_write(&mxl_dev, 1, 0);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: mmd write failed with %d\n", __func__,
+			ret);
+		goto EXIT;
+	}
+
+	ret = mxl862xx_mmd_write(&mxl_dev, 0, 0x9907);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: mmd write failed with %d\n", __func__,
+			ret);
+		goto EXIT;
+	}
+
+	/* The reset duration could be improved by implementing
+	 * register polling at offset 0, 1, 2 for specific values
+	 * 0x20, 0x21, 0x22, indicating that booting is completed. */
+	usleep_range(4000000, 6000000);
+
+	priv->port_info[priv->hw_info->cpu_port].tag_protocol =
+		__dt_parse_tag_proto(ds, priv->hw_info->cpu_port);
+
+	if (priv->port_info[priv->hw_info->cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862) {
+		ret = __config_mxl862_tag_proto(ds, cpu_port, true);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev, "%s: DSA tagging protocol setting failed with  %d\n", __func__,
+				ret);
+			goto EXIT;
+		}
+	}
+
+	/* For CPU port MAC learning setting depends on the kernel version. */
+	{
+		bool lrn_dis;
+		mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+
+		br_port_cfg.bridge_port_id = cpu_port + 1;
+		br_port_cfg.mask = MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+		lrn_dis = false;
+#else
+		lrn_dis = true;
+#endif
+		br_port_cfg.src_mac_learning_disable = lrn_dis;
+
+		ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: %s MAC learning for port %d failed with ret=%d\n",
+				__func__, lrn_dis ? "Disabled" : "Enabled", cpu_port, ret);
+			goto EXIT;
+		}
+		dev_info(ds->dev, "%s: %s MAC learning for port:%d\n",
+			 __func__, lrn_dis ? "Disabled" : "Enabled", cpu_port);
+	}
+
+	/* Store bridge portmap in the driver cache.
+	 * Add CPU port for each bridge. */
+	for (i = 0; i < MAX_BRIDGES; i++)
+		mxl862xx_bridge_portmap[i] = BIT(cpu_port + 1);
+
+	__set_vlan_filters_limits(ds);
+	/* by default set all vlans on cpu port in untagged mode */
+	for (i = 0; i < MAX_VLANS; i++)
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.vlans[i].untagged = true;
+
+	for (i = 0; i < priv->hw_info->phy_ports; i++) {
+		mxl862xx_register_mod_t register_mod = { 0 };
+
+		/* unblock vlan_filtering change */
+		priv->port_info[i].vlan.filtering_mode_locked = false;
+		priv->port_info[i].isolated = false;
+
+		if (dsa_is_cpu_port(ds, i)) {
+			dev_info(ds->dev, "%s: cpu port with index :%d\n",
+				 __func__, i);
+			continue;
+		}
+
+		/* disable datapath */
+		register_mod.reg_addr = MxL862XX_SDMA_PCTRLp(i + 1);
+		register_mod.data = 0;
+		register_mod.mask = MXL862XX_SDMA_PCTRL_EN;
+		ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev, "%s: Disable Datapath failed:%d\n",
+				__func__, i);
+		}
+
+		ret = __isolate_port(ds, i);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: Port%d isolation failed with ret:%d\n",
+				__func__, i, ret);
+			goto EXIT;
+		}
+	}
+
+	/* Clear MAC address table */
+	ret = mxl862xx_mac_table_clear(&mxl_dev);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: MAC table clear failed\n", __func__);
+		goto EXIT;
+	}
+
+EXIT:
+	return ret;
+}
+
+static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port,
+					u8 state)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	mxl862xx_stp_port_cfg_t stp_portCfg;
+	int ret;
+
+	if (port < 0 || port >= MAX_PORTS) {
+		dev_err(priv->dev, "invalid port: %d\n", port);
+		return;
+	}
+
+	stp_portCfg.port_id = port + 1;
+
+	switch (state) {
+	case BR_STATE_DISABLED:
+		stp_portCfg.port_state = MXL862XX_STP_PORT_STATE_DISABLE;
+		return;
+	case BR_STATE_BLOCKING:
+	case BR_STATE_LISTENING:
+		stp_portCfg.port_state = MXL862XX_STP_PORT_STATE_BLOCKING;
+		break;
+	case BR_STATE_LEARNING:
+		stp_portCfg.port_state = MXL862XX_STP_PORT_STATE_LEARNING;
+		break;
+	case BR_STATE_FORWARDING:
+		stp_portCfg.port_state = MXL862XX_STP_PORT_STATE_FORWARD;
+		break;
+	default:
+		dev_err(priv->dev, "invalid STP state: %d\n", state);
+		return;
+	}
+
+	ret = mxl862xx_stp_port_cfg_set(&mxl_dev, &stp_portCfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: stp configuration failed, port: %d, state: %d\n",
+			__func__, port, state);
+	}
+
+	/* Since MxL862xx firmware enables MAC learning in some STP states,
+	 * we have to disable it for isolated user ports. For CPU port,
+	 * this setting depends on the kernel version. */
+	if ((priv->port_info[port].bridge == NULL) || (dsa_is_cpu_port(ds, port))) {
+		mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+		bool lrn_dis;
+
+		if ((dsa_is_cpu_port(ds, port)))
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+			lrn_dis = false;
+#else
+			lrn_dis = true;
+#endif
+		else
+			lrn_dis = true;
+
+		br_port_cfg.mask =
+			MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+		br_port_cfg.bridge_port_id = port + 1;
+		br_port_cfg.src_mac_learning_disable = lrn_dis;
+		ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: %s MAC learning for port %d failed with ret=%d\n",
+				__func__, lrn_dis ? "Disabled" : "Enabled", port, ret);
+			return;
+		}
+	}
+}
+
+#if (KERNEL_VERSION(4, 18, 0) <= LINUX_VERSION_CODE)
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+static void mxl862xx_phylink_set_capab(unsigned long *supported,
+				       struct phylink_link_state *state)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = {
+		0,
+	};
+
+	/* Allow all the expected bits */
+	phylink_set(mask, Autoneg);
+	phylink_set_port_modes(mask);
+	phylink_set(mask, Pause);
+	phylink_set(mask, Asym_Pause);
+
+	phylink_set(mask, 2500baseT_Full);
+	phylink_set(mask, 1000baseT_Full);
+	phylink_set(mask, 1000baseT_Half);
+	phylink_set(mask, 100baseT_Half);
+	phylink_set(mask, 100baseT_Full);
+	phylink_set(mask, 10baseT_Half);
+	phylink_set(mask, 10baseT_Full);
+
+	bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+	bitmap_and(state->advertising, state->advertising, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+#endif
+
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+static void mxl862xx_phylink_validate(struct dsa_switch *ds, int port,
+				      unsigned long *supported,
+				      struct phylink_link_state *state)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+
+	if (port >= 0 && port < priv->hw_info->phy_ports) {
+		if (state->interface != PHY_INTERFACE_MODE_INTERNAL)
+			goto unsupported;
+	} else if (port == 8 || port == 9) {
+#if (KERNEL_VERSION(5, 3, 0) > LINUX_VERSION_CODE)
+		if (state->interface != PHY_INTERFACE_MODE_10GKR)
+			goto unsupported;
+#else
+		if (state->interface != PHY_INTERFACE_MODE_USXGMII)
+			goto unsupported;
+#endif
+	} else {
+		bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+		dev_err(ds->dev, "Unsupported port: %i\n", port);
+		return;
+	}
+
+	mxl862xx_phylink_set_capab(supported, state);
+	return;
+
+unsupported:
+	bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+	dev_err(ds->dev, "Unsupported interface '%s' for port %d\n",
+		phy_modes(state->interface), port);
+}
+
+#else
+static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
+				      struct phylink_config *config)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+
+	if (port >= 0 && port < priv->hw_info->phy_ports) {
+		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
+			  config->supported_interfaces);
+	} else if (port == 8 || port == 9) {
+		__set_bit(PHY_INTERFACE_MODE_USXGMII,
+			  config->supported_interfaces);
+	} else if (port > 9) {
+		__set_bit(PHY_INTERFACE_MODE_NA, config->supported_interfaces);
+	}
+
+	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
+				   MAC_100 | MAC_1000 |
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+					MAC2500FD;
+#else
+					MAC_2500FD;
+#endif
+
+	return;
+}
+#endif
+
+static void mxl862xx_phylink_mac_config(struct dsa_switch *ds, int port,
+					unsigned int mode,
+					const struct phylink_link_state *state)
+{
+	switch (state->interface) {
+	case PHY_INTERFACE_MODE_INTERNAL:
+		return;
+	case PHY_INTERFACE_MODE_SGMII:
+		return;
+#if (KERNEL_VERSION(5, 3, 0) > LINUX_VERSION_CODE)
+	case PHY_INTERFACE_MODE_10GKR:
+		/* Configure the USXGMII */
+		break;
+#else
+	case PHY_INTERFACE_MODE_USXGMII:
+		/* Configure the USXGMII */
+		break;
+#endif
+	default:
+		dev_err(ds->dev, "Unsupported interface: %d\n",
+			state->interface);
+		return;
+	}
+}
+
+static void mxl862xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
+					   unsigned int mode,
+					   phy_interface_t interface)
+{
+	mxl862xx_port_link_cfg_t port_link_cfg = { 0 };
+	int ret;
+
+	if (dsa_is_cpu_port(ds, port))
+		return;
+
+	port_link_cfg.port_id = port + 1;
+
+	port_link_cfg.link_force = true;
+	port_link_cfg.link = MXL862XX_PORT_LINK_DOWN;
+
+	ret = mxl862xx_port_link_cfg_set(&mxl_dev, &port_link_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: MAC link port configuration for port %d failed with %d\n",
+			__func__, port, ret);
+	}
+}
+
+#if (KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE)
+static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+					 unsigned int mode,
+					 phy_interface_t interface,
+					 struct phy_device *phydev, int speed,
+					 int duplex, bool tx_pause,
+					 bool rx_pause)
+{
+	mxl862xx_port_link_cfg_t port_link_cfg = { 0 };
+	mxl862xx_port_cfg_t port_cfg = { 0 };
+	int ret;
+
+	if (dsa_is_cpu_port(ds, port))
+		return;
+
+	port_link_cfg.port_id = port + 1;
+
+	port_link_cfg.link_force = true;
+	port_link_cfg.link = MXL862XX_PORT_LINK_UP;
+
+	port_link_cfg.speed_force = true;
+	switch (speed) {
+	case SPEED_10:
+		port_link_cfg.speed = MXL862XX_PORT_SPEED_10;
+		break;
+	case SPEED_100:
+		port_link_cfg.speed = MXL862XX_PORT_SPEED_100;
+		break;
+	case SPEED_1000:
+		port_link_cfg.speed = MXL862XX_PORT_SPEED_1000;
+		break;
+	case SPEED_2500:
+		port_link_cfg.speed = MXL862XX_PORT_SPEED_2500;
+		break;
+	case SPEED_5000:
+		port_link_cfg.speed = MXL862XX_PORT_SPEED_5000;
+		break;
+	case SPEED_10000:
+		port_link_cfg.speed = MXL862XX_PORT_SPEED_10000;
+		break;
+	default:
+		dev_err(ds->dev,
+			"%s: Unsupported  MAC link speed %d Mbps on port:%d\n",
+			__func__, speed, port);
+		return;
+	}
+
+	port_link_cfg.duplex_force = true;
+	switch (duplex) {
+	case DUPLEX_HALF:
+		port_link_cfg.duplex = MXL862XX_DUPLEX_HALF;
+		break;
+	case DUPLEX_FULL:
+		port_link_cfg.duplex = MXL862XX_DUPLEX_FULL;
+		break;
+	default:
+		port_link_cfg.duplex = MXL862XX_DUPLEX_AUTO;
+	}
+
+	ret = mxl862xx_port_link_cfg_set(&mxl_dev, &port_link_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Port link configuration for port %d failed with %d\n",
+			__func__, port, ret);
+		return;
+	}
+
+	port_cfg.port_id = port + 1;
+	ret = mxl862xx_port_cfg_get(&mxl_dev, &port_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Port configuration read for port %d failed with %d\n",
+			__func__, port, ret);
+		return;
+	}
+
+	if (tx_pause && rx_pause)
+		port_cfg.flow_ctrl = MXL862XX_FLOW_RXTX;
+	else if (tx_pause)
+		port_cfg.flow_ctrl = MXL862XX_FLOW_TX;
+	else if (rx_pause)
+		port_cfg.flow_ctrl = MXL862XX_FLOW_RX;
+	else
+		port_cfg.flow_ctrl = MXL862XX_FLOW_OFF;
+
+	ret = mxl862xx_port_cfg_set(&mxl_dev, &port_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Port configuration for port %d failed with %d\n",
+			__func__, port, ret);
+	}
+
+	return;
+}
+
+#else
+static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+					 unsigned int mode,
+					 phy_interface_t interface,
+					 struct phy_device *phydev)
+{
+	mxl862xx_port_link_cfg_t port_link_cfg = { 0 };
+	int ret;
+
+	if (dsa_is_cpu_port(ds, port))
+		return;
+
+	port_link_cfg.port_id = port + 1;
+
+	port_link_cfg.link_force = true;
+	port_link_cfg.link = MXL862XX_PORT_LINK_UP;
+
+	ret = mxl862xx_port_link_cfg_set(&mxl_dev, &port_link_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Port link configuration for port %d failed with %d\n",
+			__func__, port, ret);
+		return;
+	}
+}
+#endif
+#endif
+
+static void mxl862xx_get_ethtool_stats(struct dsa_switch *ds, int port,
+				       uint64_t *data)
+{
+	int ret = -EINVAL;
+	uint8_t i = 0;
+	mxl862xx_debug_rmon_port_cnt_t dbg_rmon_port_cnt = { 0 };
+
+	/* RX */
+	dbg_rmon_port_cnt.port_id = port + 1;
+	dbg_rmon_port_cnt.port_type = MXL862XX_RMON_CTP_PORT_RX;
+	ret = mxl862xx_debug_rmon_port_get(&mxl_dev, &dbg_rmon_port_cnt);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Reading RMON RX statistics for port %d failed with %d\n",
+			__func__, port, ret);
+		return;
+	}
+	data[i++] = dbg_rmon_port_cnt.rx_good_pkts; //0
+	data[i++] = dbg_rmon_port_cnt.rx_unicast_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_broadcast_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_multicast_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_fcserror_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_under_size_good_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_oversize_good_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_under_size_error_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_oversize_error_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_filtered_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx64byte_pkts; //10
+	data[i++] = dbg_rmon_port_cnt.rx127byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx255byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx511byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx1023byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_max_byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_dropped_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_extended_vlan_discard_pkts;
+	data[i++] = dbg_rmon_port_cnt.mtu_exceed_discard_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_good_bytes;
+	data[i++] = dbg_rmon_port_cnt.rx_bad_bytes; //20
+	data[i++] = dbg_rmon_port_cnt.rx_unicast_pkts_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.rx_broadcast_pkts_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.rx_multicast_pkts_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.rx_good_pkts_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.rx_good_bytes_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.rx_good_pause_pkts;
+	data[i++] = dbg_rmon_port_cnt.rx_align_error_pkts;
+
+	/* TX */
+	memset(&dbg_rmon_port_cnt, 0, sizeof(dbg_rmon_port_cnt));
+	dbg_rmon_port_cnt.port_id = port + 1;
+	dbg_rmon_port_cnt.port_type = MXL862XX_RMON_CTP_PORT_TX;
+	ret = mxl862xx_debug_rmon_port_get(&mxl_dev, &dbg_rmon_port_cnt);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Reading RMON TX statistics for port %d failed with %d\n",
+			__func__, port, ret);
+		return;
+	}
+	data[i++] = dbg_rmon_port_cnt.tx_good_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx_unicast_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx_broadcast_pkts; //30
+	data[i++] = dbg_rmon_port_cnt.tx_multicast_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx64byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx127byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx255byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx511byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx1023byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx_max_byte_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx_dropped_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx_acm_dropped_pkts;
+	data[i++] = dbg_rmon_port_cnt.tx_good_bytes; //40
+	data[i++] = dbg_rmon_port_cnt.tx_unicast_pkts_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.tx_broadcast_pkts_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.tx_multicast_pkts_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.tx_good_pkts_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.tx_good_bytes_yellow_red;
+	data[i++] = dbg_rmon_port_cnt.tx_single_coll_count;
+	data[i++] = dbg_rmon_port_cnt.tx_mult_coll_count;
+	data[i++] = dbg_rmon_port_cnt.tx_late_coll_count;
+	data[i++] = dbg_rmon_port_cnt.tx_excess_coll_count;
+	data[i++] = dbg_rmon_port_cnt.tx_coll_count; //50
+	data[i++] = dbg_rmon_port_cnt.tx_pause_count;
+
+	return;
+}
+
+static void mxl862xx_get_strings(struct dsa_switch *ds, int port,
+				 uint32_t stringset, uint8_t *data)
+{
+	uint8_t i;
+
+	if (stringset != ETH_SS_STATS)
+		return;
+	for (i = 0; i < ARRAY_SIZE(mxl862xx_rmon_cnt); i++)
+		strscpy(data + i * ETH_GSTRING_LEN, mxl862xx_rmon_cnt[i].name,
+			ETH_GSTRING_LEN);
+}
+
+static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+	if (sset != ETH_SS_STATS)
+		return 0;
+
+	return ARRAY_SIZE(mxl862xx_rmon_cnt);
+}
+
+static void mxl862xx_port_fast_age(struct dsa_switch *ds, int port)
+{
+	int ret = -EINVAL;
+	mxl862xx_mac_table_clear_cond_t param = { 0 };
+
+	param.type = MXL862XX_MAC_CLEAR_PHY_PORT;
+	param.port_id = port + 1;
+
+	ret = mxl862xx_mac_table_clear_cond(&mxl_dev, &param);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Clearing MAC table for port %d failed with %d\n",
+			__func__, port, ret);
+	}
+}
+
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+static int mxl862xx_port_mirror_add(struct dsa_switch *ds, int port,
+				    struct dsa_mall_mirror_tc_entry *mirror,
+				    bool ingress)
+#else
+static int mxl862xx_port_mirror_add(struct dsa_switch *ds, int port,
+				    struct dsa_mall_mirror_tc_entry *mirror,
+				    bool ingress, struct netlink_ext_ack *extack)
+#endif
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	int ret = -EINVAL;
+	mxl862xx_ctp_port_config_t ctp_param = { 0 };
+	mxl862xx_monitor_port_cfg_t mon_param = { 0 };
+
+	if (port < 0 || port >= MAX_PORTS) {
+		dev_err(priv->dev, "invalid port: %d\n", port);
+		goto EXIT;
+	}
+
+	/* first read, then change */
+	ctp_param.logical_port_id = port + 1;
+	ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_MASK_ALL;
+	ret = mxl862xx_ctp_port_config_get(&mxl_dev, &ctp_param);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Enabling monitoring of port %d failed on port config get with %d\n",
+			__func__, port, ret);
+		goto EXIT;
+	}
+
+	ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_LOOPBACK_AND_MIRROR;
+	if (ingress) {
+		priv->port_info[port].ingress_mirror_enabled = true;
+		ctp_param.ingress_mirror_enable = true;
+	} else {
+		priv->port_info[port].egress_mirror_enabled = true;
+		ctp_param.egress_mirror_enable = true;
+	}
+
+	ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Enabling monitoring of port %d failed on port config set with %d\n",
+			__func__, port, ret);
+		goto EXIT;
+	}
+
+	mon_param.port_id = mirror->to_local_port + 1;
+	ret = mxl862xx_monitor_port_cfg_set(&mxl_dev, &mon_param);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev, "%s: Setting monitor port %d failed with %d\n",
+			__func__, mon_param.port_id, ret);
+		goto EXIT;
+	}
+
+EXIT:
+	return ret;
+}
+
+static void mxl862xx_port_mirror_del(struct dsa_switch *ds, int port,
+				     struct dsa_mall_mirror_tc_entry *mirror)
+{
+	int ret = -EINVAL;
+	uint8_t i;
+	struct mxl862xx_priv *priv = ds->priv;
+	uint8_t phy_ports = priv->hw_info->phy_ports;
+	mxl862xx_ctp_port_config_t ctp_param = { 0 };
+
+	if (port < 0 || port >= MAX_PORTS) {
+		dev_err(priv->dev, "invalid port: %d\n", port);
+		return;
+	}
+
+	ctp_param.logical_port_id = port + 1;
+	ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_LOOPBACK_AND_MIRROR;
+	if (mirror->ingress) {
+		priv->port_info[port].ingress_mirror_enabled = false;
+		ctp_param.ingress_mirror_enable = false;
+	} else {
+		priv->port_info[port].egress_mirror_enabled = false;
+		ctp_param.egress_mirror_enable = false;
+	}
+
+	ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Disabling monitoring of port %d failed with %d\n",
+			__func__, port, ret);
+		goto EXIT;
+	}
+
+	for (i = 0; i < phy_ports; i++) {
+		/* some ports are still mirrored, keep the monitor port configured */
+		if (priv->port_info[i].egress_mirror_enabled ||
+		    priv->port_info[i].egress_mirror_enabled)
+			break;
+		/* checked all and no port is being mirrored - release the monitor port */
+		if (i == phy_ports - 1) {
+			mxl862xx_monitor_port_cfg_t mon_param = { 0 };
+
+			ret = mxl862xx_monitor_port_cfg_set(&mxl_dev,
+							    &mon_param);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Releasing monitor port %d failed with %d\n",
+					__func__, mon_param.port_id, ret);
+				goto EXIT;
+			}
+		}
+	}
+
+EXIT:
+	return;
+}
+
+static int
+__get_vlan_vid_filters_idx(struct mxl862xx_priv *priv, uint8_t port, bool ingress,
+		uint16_t vid, int *f_0, int *f_1, uint16_t *vlan_idx)
+{
+	int ret = -EINVAL;
+	int x, i = 0;
+	/* negative values if not found */
+	int filter_0 = -1;
+	int filter_1 = -1;
+	struct mxl862xx_extended_vlan_block_info *block_info;
+
+	if (ingress)
+		block_info = &(priv->port_info[port].vlan.ingress_vlan_block_info);
+	else
+		block_info = &(priv->port_info[port].vlan.egress_vlan_block_info);
+
+	/* Check if there's active entry for the requested VLAN. If found, overwrite it. */
+	if (filter_0 < 0 && filter_1 < 0) {
+		for (i = 0; i < MAX_VLANS; i++) {
+			if (block_info->vlans[i].vid == vid) {
+				filter_0 = block_info->vlans[i].filters_idx[0];
+				filter_1 = block_info->vlans[i].filters_idx[1];
+				ret = 0;
+				break;
+			}
+		}
+	}
+
+	/* If there are no matching active VLAN entries, check in recycled */
+	if (filter_0 < 0 && filter_1 < 0) {
+	/* check if there are recycled filter entries for use */
+		for (x = 0; x < MAX_VLANS; x++) {
+			if (block_info->filter_entries_recycled[x].valid) {
+				filter_0 = block_info->filter_entries_recycled[x].filters_idx[0];
+				filter_1 = block_info->filter_entries_recycled[x].filters_idx[1];
+				/* remove filter entries from recycled inventory */
+				block_info->filter_entries_recycled[x].valid = false;
+				ret = 0;
+				break;
+			}
+		}
+
+		/* find empty slot for storing ID's of vlan filtering rules */
+		for (i = 0; i < MAX_VLANS; i++) {
+			if (!(block_info->vlans[i].used)) {
+				ret = 0;
+				break;
+			}
+			if (i == priv->max_vlans - 1) {
+				ret = -ENOSPC;
+				dev_err(priv->dev,
+					"%s: Port:%d reached max number of defined VLAN's: %d\n",
+					__func__, port, priv->max_vlans);
+				goto EXIT;
+			}
+		}
+	}
+
+	if (f_0 != NULL)
+		*f_0 = filter_0;
+	if (f_1 != NULL)
+		*f_1 = filter_1;
+	if (vlan_idx != NULL)
+		*vlan_idx = i;
+
+EXIT:
+	return ret;
+}
+
+
+static int
+__prepare_vlan_egress_filters_off_sp_tag_no_vid(struct dsa_switch *ds, uint8_t port)
+{
+	int ret = -EINVAL;
+	struct mxl862xx_priv *priv = ds->priv;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	/* Allocate new block if needed */
+	if (!(priv->port_info[port].vlan.egress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[port]
+				.vlan.egress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		priv->port_info[port].vlan.egress_vlan_block_info.allocated =
+			true;
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	// Static entry :  Outer and iner tag.
+	// Remove outer tag  one as it must be sp_tag. Transparent for inner tag.
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 2;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	/* remove  sp tag */
+	vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+	// Last entry :  Only outer tag. Remove it as it must be sp_tag
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 1;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	/* remove  sp tag */
+	vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+EXIT:
+	return ret;
+}
+
+
+static int
+__prepare_vlan_egress_filters_off_sp_tag(struct dsa_switch *ds, uint8_t port, uint16_t vid, bool untagged)
+{
+	int ret = -EINVAL;
+	uint16_t idx = 0;
+	/* negative values if not found */
+	int filter_0 = -1;
+	int filter_1 = -1;
+	struct mxl862xx_priv *priv = ds->priv;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	/* Allocate new block if needed */
+	if (!(priv->port_info[port].vlan.egress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[port]
+				.vlan.egress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		priv->port_info[port].vlan.egress_vlan_block_info.allocated =
+			true;
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	/* VID specific entries must be processed before the final entries,
+	 * so putting them at the beginnig of the block */
+
+	ret = __get_vlan_vid_filters_idx(priv, port, false, vid, &filter_0, &filter_1, &idx);
+	dev_dbg(priv->dev, "%s: Port:%d  vid:%d f_0:%d f_1:%d idx:%d\n", __func__, port, vid, filter_0, filter_1, idx);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	// Entry 0 :  Outer and Inner tags are present. Inner tag matching vid.
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_0 >= 0 ?
+			filter_0 :
+			priv->port_info[port]
+				.vlan.egress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.vid_enable = true;
+	vlan_cfg.filter.inner_vlan.vid_val = vid;
+
+	if (untagged) {
+		/* remove both sp_tag(outer) and vid (inner) */
+		vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG;
+	} else {
+		/* remove only sp tag */
+		vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+	}
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[port]
+		.vlan.egress_vlan_block_info.vlans[idx]
+		.filters_idx[0] = vlan_cfg.entry_index;
+
+	priv->port_info[port]
+		.vlan.egress_vlan_block_info.vlans[idx]
+		.filters_idx[1] = IDX_INVAL;
+
+	// Static entry :  Outer and iner tag, not matching vid. Remove outer tag  one as it must be sp_tag
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 2;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	/* remove  sp tag */
+	vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+	// Last entry :  Only outer tag. Remove it as it must be sp_tag
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 2;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	/* remove  sp tag */
+	vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+	// Last entry :  Only outer tag. Remove it as it must be sp_tag
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 1;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	/* remove  sp tag */
+	vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index ;
+
+
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].vid = vid;
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].used = true;
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].untagged = untagged;
+
+EXIT:
+	return ret;
+}
+
+static int
+__prepare_vlan_egress_filters_off(struct mxl862xx_priv *priv, uint8_t port, uint16_t vid, bool untagged)
+{
+	int ret = -EINVAL;
+	uint16_t idx = 0;
+	/* negative values if not found */
+	int filter_0 = -1;
+	int filter_1 = -1;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	/* Allocate new block if needed */
+	if (!(priv->port_info[port].vlan.egress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[port]
+				.vlan.egress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		priv->port_info[port].vlan.egress_vlan_block_info.allocated =
+			true;
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	/* VID specific entries must be processed before the final entries,
+	 * so putting them at the beginnig of the block */
+
+	ret = __get_vlan_vid_filters_idx(priv, port, false, vid, &filter_0, &filter_1, &idx);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	// Entry 0 : ACCEPT VLAN tags that are matching  VID. Outer and Inner tags are present
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_0 >= 0 ?
+			filter_0 :
+			priv->port_info[port]
+				.vlan.egress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.vid_enable = true;
+	vlan_cfg.filter.inner_vlan.vid_val = vid;
+	if (untagged)
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+	else
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[port]
+		.vlan.egress_vlan_block_info.vlans[idx]
+		.filters_idx[0] = vlan_cfg.entry_index;
+
+	//	 Entry 1 : ACCEPT VLAN tags that are matching PVID or port VID. Only the outer tags are present
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_1 >= 0 ?
+			filter_1 :
+			priv->port_info[port]
+				.vlan.egress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.filter.inner_vlan.vid_enable = true;
+	vlan_cfg.filter.inner_vlan.vid_val = vid;
+	if (untagged)
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+	else
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[port]
+		.vlan.egress_vlan_block_info.vlans[idx]
+		.filters_idx[1] = vlan_cfg.entry_index;
+
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].vid = vid;
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].used = true;
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].untagged = untagged;
+
+
+EXIT:
+	return ret;
+}
+
+
+static int
+__prepare_vlan_ingress_filters_off_sp_tag_no_vid(struct dsa_switch *ds, uint8_t port)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	int ret = -EINVAL;
+	struct mxl862xx_extended_vlan_block_info *block_info =
+		&priv->port_info[port].vlan.ingress_vlan_block_info;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	/* Allocate new block if needed */
+	if (!(block_info->allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries = block_info->filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		block_info->allocated =	true;
+		block_info->block_id = vlan_alloc.extended_vlan_block_id;
+	}
+
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+
+	//Static rules. No tags, add SP tag
+	vlan_cfg.entry_index = block_info->filters_max - 3;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.add_outer_vlan = true;
+	vlan_cfg.treatment.outer_vlan.vid_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+	vlan_cfg.treatment.outer_vlan.tpid =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+	vlan_cfg.treatment.outer_vlan.priority_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.outer_vlan.priority_val = 0;
+	vlan_cfg.treatment.outer_vlan.dei =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+	vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	block_info->final_filters_idx = vlan_cfg.entry_index;
+
+	// Static rules
+	// Single tag. Use transparent mode. Add sp tag
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	vlan_cfg.entry_index = block_info->filters_max - 2;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.add_outer_vlan = true;
+	vlan_cfg.treatment.outer_vlan.vid_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+	vlan_cfg.treatment.outer_vlan.tpid =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+	vlan_cfg.treatment.outer_vlan.priority_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.outer_vlan.priority_val = 0;
+	vlan_cfg.treatment.outer_vlan.dei =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+	vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	block_info->final_filters_idx = vlan_cfg.entry_index;
+
+	// Two tags. Use transparent mode. Do not apply vid as this is tagged pkt
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	vlan_cfg.entry_index = block_info->filters_max - 1;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.treatment.add_outer_vlan = true;
+	vlan_cfg.treatment.outer_vlan.vid_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+	vlan_cfg.treatment.outer_vlan.tpid =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+	vlan_cfg.treatment.outer_vlan.priority_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.outer_vlan.priority_val = 0;
+	vlan_cfg.treatment.outer_vlan.dei =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+	vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	block_info->final_filters_idx = vlan_cfg.entry_index;
+
+EXIT:
+	return ret;
+}
+
+static int
+__prepare_vlan_ingress_filters_off_sp_tag(struct dsa_switch *ds, uint8_t port, uint16_t vid)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	int ret = -EINVAL;
+	//there's possible only one rule for single pvid, so it always uses idx 0
+	uint16_t idx = 0;
+	struct mxl862xx_extended_vlan_block_info *block_info =
+		&priv->port_info[port].vlan.ingress_vlan_block_info;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	/* Allocate new block if needed */
+	if (!(block_info->allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries = block_info->filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		block_info->allocated =	true;
+		block_info->block_id = vlan_alloc.extended_vlan_block_id;
+	}
+
+	/*  If port has pvid then add vid dependand dynamic rule.
+	 *  It's done that way because it's required for proper handling of
+	 *  vlan delete scenario. If no pvid configured, create 'static' rule */
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+
+	// vid dynamic rule
+	if (priv->port_info[port].vlan.pvid) {
+		/* As there's only one  pvid  per port possible, always overwrite the rule at position 0 */
+		vlan_cfg.entry_index = 0;
+		vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+		vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+		vlan_cfg.treatment.add_outer_vlan = true;
+		vlan_cfg.treatment.outer_vlan.vid_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+		vlan_cfg.treatment.outer_vlan.tpid =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+		vlan_cfg.treatment.outer_vlan.priority_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+		vlan_cfg.treatment.outer_vlan.priority_val = 0;
+		vlan_cfg.treatment.outer_vlan.dei =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+		vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+		vlan_cfg.treatment.add_inner_vlan = true;
+		vlan_cfg.treatment.inner_vlan.vid_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+		vlan_cfg.treatment.inner_vlan.vid_val =
+			priv->port_info[port].vlan.pvid;
+		vlan_cfg.treatment.inner_vlan.tpid =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+		vlan_cfg.treatment.inner_vlan.priority_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+		vlan_cfg.treatment.inner_vlan.priority_val = 0;
+		vlan_cfg.treatment.inner_vlan.dei =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+
+		ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(priv->dev,
+				"%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+				__func__, port, vlan_cfg.entry_index,
+				vlan_cfg.extended_vlan_block_id);
+			goto EXIT;
+		}
+
+		block_info->vlans[idx].filters_idx[0] = vlan_cfg.entry_index;
+		block_info->vlans[idx].filters_idx[1] = IDX_INVAL;
+
+		block_info->vlans[idx].vid = vid;
+		block_info->vlans[idx].used = true;
+
+	}
+	// no pvid, static rule
+	else {
+		// deactivate possible dynamic rule if there's no pvid
+		if (block_info->vlans[idx].vid) {
+			ret = __deactivate_vlan_filter_entry(block_info->block_id, block_info->vlans[idx].filters_idx[0]);
+			if (ret != MXL862XX_STATUS_OK)
+				goto EXIT;
+			block_info->vlans[idx].vid = 0;
+			block_info->vlans[idx].used = false;
+		}
+
+		vlan_cfg.entry_index = block_info->filters_max - 3;
+		vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+		vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+		vlan_cfg.treatment.add_outer_vlan = true;
+		vlan_cfg.treatment.outer_vlan.vid_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+		vlan_cfg.treatment.outer_vlan.tpid =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+		vlan_cfg.treatment.outer_vlan.priority_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+		vlan_cfg.treatment.outer_vlan.priority_val = 0;
+		vlan_cfg.treatment.outer_vlan.dei =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+		vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+		ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(priv->dev,
+				"%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+				__func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+			goto EXIT;
+		}
+
+		block_info->final_filters_idx = vlan_cfg.entry_index;
+	}
+
+	// Static rules
+	// Single tag. Use transparent mode. Do not apply PVID as this is the tagged traffic
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	vlan_cfg.entry_index = block_info->filters_max - 2;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.add_outer_vlan = true;
+	vlan_cfg.treatment.outer_vlan.vid_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+	vlan_cfg.treatment.outer_vlan.tpid =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+	vlan_cfg.treatment.outer_vlan.priority_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.outer_vlan.priority_val = 0;
+	vlan_cfg.treatment.outer_vlan.dei =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+	vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	block_info->final_filters_idx = vlan_cfg.entry_index;
+
+	// Two tags. Use transparent mode. Do not apply vid as this is tagged pkt
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	vlan_cfg.entry_index = block_info->filters_max - 1;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.treatment.add_outer_vlan = true;
+	vlan_cfg.treatment.outer_vlan.vid_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+	vlan_cfg.treatment.outer_vlan.tpid =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+	vlan_cfg.treatment.outer_vlan.priority_mode =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.outer_vlan.priority_val = 0;
+	vlan_cfg.treatment.outer_vlan.dei =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+	vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	block_info->final_filters_idx = vlan_cfg.entry_index;
+
+EXIT:
+	return ret;
+}
+
+static int
+__prepare_vlan_ingress_filters_off(struct mxl862xx_priv *priv, uint8_t port, uint16_t vid)
+{
+	int ret = -EINVAL;
+	uint16_t idx = 0;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	/* Allocate new block if needed */
+	if (!(priv->port_info[port].vlan.ingress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[port]
+				.vlan.ingress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		priv->port_info[port].vlan.ingress_vlan_block_info.allocated =
+			true;
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	// Entry 4  untagged pkts. If there's PVID accept and add PVID tag
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	/* for cpu port this entry is fixed and always put at the end of the block */
+	if (port == priv->hw_info->cpu_port)
+		vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 1;
+	else {
+		vlan_cfg.entry_index =
+			priv->port_info[port]
+				.vlan.ingress_vlan_block_info.final_filters_idx--;
+	}
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+
+	if (priv->port_info[port].vlan.pvid) {
+		vlan_cfg.treatment.add_outer_vlan = true;
+		vlan_cfg.treatment.outer_vlan.vid_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+		vlan_cfg.treatment.outer_vlan.vid_val =
+			priv->port_info[port].vlan.pvid;
+		vlan_cfg.treatment.outer_vlan.tpid =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+		vlan_cfg.treatment.outer_vlan.priority_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+		vlan_cfg.treatment.outer_vlan.priority_val = 0;
+		vlan_cfg.treatment.outer_vlan.dei =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+	}
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	ret = __get_vlan_vid_filters_idx(priv, port, true, vid, NULL, NULL, &idx);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d couldn't get idx for VID:%d and  block ID:%d\n",
+			__func__, port, vid, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].vid = vid;
+	priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+	return ret;
+}
+
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+static int mxl862xx_port_vlan_prepare(struct dsa_switch *ds, int port,
+				    const struct switchdev_obj_port_vlan *vlan)
+{
+	/* function needed to make VLAN working for legacy Linux kernels */
+	return 0;
+}
+#endif
+
+#if (KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE)
+static int mxl862xx_port_vlan_filtering(struct dsa_switch *ds, int port,
+					bool vlan_filtering)
+#else
+static int mxl862xx_port_vlan_filtering(struct dsa_switch *ds, int port,
+					bool vlan_filtering,
+					struct netlink_ext_ack *extack)
+#endif
+{
+	int ret = 0;
+	struct mxl862xx_priv *priv = ds->priv;
+	struct dsa_port *dsa_port = dsa_to_port(ds, port);
+#if (KERNEL_VERSION(5, 17, 0) <= LINUX_VERSION_CODE)
+	struct net_device *bridge = dsa_port_bridge_dev_get(dsa_port);
+#else
+	struct net_device *bridge;
+
+	if (dsa_port)
+		bridge = dsa_port->bridge_dev;
+	else
+		return -EINVAL;
+#endif
+
+	/* Prevent dynamic setting of the vlan_filtering. */
+	if (bridge && priv->port_info[port].vlan.filtering_mode_locked) {
+		ret = -ENOTSUPP;
+		dev_err(ds->dev, "%s: Change of vlan_filtering mode is not allowed while port:%d is joined to a bridge\n",
+				__func__, port);
+#if (KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Change of vlan_filtering mode is not allowedwhile port is joind to a bridge.");
+#endif
+
+	} else {
+		priv->port_info[port].vlan.filtering = vlan_filtering;
+		/* Do not lock if port is isolated. */
+		if (!priv->port_info[port].isolated)
+			priv->port_info[port].vlan.filtering_mode_locked = true;
+	}
+
+	return ret;
+}
+
+static int
+__prepare_vlan_egress_filters(struct dsa_switch *ds, uint8_t port, uint16_t vid, bool untagged)
+{
+	int ret = -EINVAL;
+	uint16_t idx = 0;
+	/* negative values if not found */
+	int filter_0 = -1;
+	int filter_1 = -1;
+	struct mxl862xx_priv *priv = ds->priv;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	struct mxl862xx_extended_vlan_block_info *block_info =
+		&priv->port_info[port].vlan.egress_vlan_block_info;
+
+	/* Allocate new block if needed */
+	if (!(block_info->allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries = block_info->filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		block_info->allocated = true;
+		block_info->block_id = vlan_alloc.extended_vlan_block_id;
+	}
+
+	/* First populate the block with set of rules which should be executed finally after
+	 * VID specific filtering. The final rules (not related to VID) are placed on the end of the block. The number of
+	 * rules is fixed  per port. Order of execution  is important. To avoid static reservations they are
+	 * stored in reversed order starting from the end of the block */
+
+	//Entry 4: no outer/inner tag, no PVID  DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	vlan_cfg.entry_index = block_info->final_filters_idx--;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	//Entry 3: Only Outer tag present. Discard if VID is not matching the previous rules
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	vlan_cfg.entry_index = block_info->final_filters_idx--;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	//Entry 2: Outer and Inner tags are present. Discard if VID is not matching the previous rules
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	vlan_cfg.entry_index = block_info->final_filters_idx--;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	/* VID specific entries must be processed before the final entries,
+	 * so putting them at the beginnig of the block */
+	ret = __get_vlan_vid_filters_idx(priv, port, false, vid, &filter_0, &filter_1, &idx);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	// Entry 0 : ACCEPT VLAN tags that are matching  VID. Outer and Inner tags are present
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_0 >= 0 ? filter_0 : block_info->vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = vid;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+
+	if (untagged)
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+	else
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	block_info->vlans[idx].filters_idx[0] = vlan_cfg.entry_index;
+
+	//	 Entry 1 : ACCEPT VLAN tags that are matching PVID or port VID. Only the outer tags are present
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_1 >= 0 ? filter_1 : block_info->vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = vid;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	if (untagged)
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+	else
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	block_info->vlans[idx].filters_idx[1] = vlan_cfg.entry_index;
+
+	block_info->vlans[idx].vid = vid;
+	block_info->vlans[idx].used = true;
+	block_info->vlans[idx].untagged = untagged;
+
+EXIT:
+	return ret;
+}
+
+static int
+__prepare_vlan_egress_filters_sp_tag(struct dsa_switch *ds, uint8_t port, uint16_t vid, bool untagged)
+{
+	int ret = -EINVAL;
+	uint16_t idx = 0;
+	/* negative values if not found */
+	int filter_0 = IDX_INVAL;
+	int filter_1 = IDX_INVAL;
+	struct mxl862xx_priv *priv = ds->priv;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+	/* Allocate new block if needed */
+	if (!(priv->port_info[port].vlan.egress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[port]
+				.vlan.egress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(priv->dev,
+				"%s: Extended VLAN allocation for port%d egress filtering failed with %d\n",
+				__func__, port, ret);
+			goto EXIT;
+		}
+
+		priv->port_info[port].vlan.egress_vlan_block_info.allocated =
+			true;
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	/* First populate the fixed block of rules which should be executed finally after
+	 * VID specific filtering. The final rules (not related to VID)
+	 * are placed at the end of the block. */
+
+	//Entry 4: only outer tag (SP tag), no PVID  DISCARD
+	if (untagged) {
+		memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+		vlan_cfg.extended_vlan_block_id =
+			priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	   vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 1;
+		vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+		vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+		ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(priv->dev,
+				"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+				__func__, port, vlan_cfg.entry_index,
+				vlan_cfg.extended_vlan_block_id);
+			goto EXIT;
+		}
+
+		priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx =
+			vlan_cfg.entry_index;
+	}
+
+	//Entry 3: there is any other inner tag -> discard upstream traffic
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 2;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.remove_tag =	MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx =
+		vlan_cfg.entry_index;
+
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 3;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+	/* VID specific entries must be processed before the final entries,
+	 * so putting them at the beginnig of the block */
+	ret = __get_vlan_vid_filters_idx(priv, port, false, vid, &filter_0, &filter_1, &idx);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d couldn't get idx for VID specific filters for VID:%d and  block ID:%d\n",
+			__func__, port, vid, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	/* Set of dynamic rules that depend on VID.
+	 * The number of rules depends on the number of handled vlans */
+
+	// Entry 0 : ACCEPT VLAN tags that are matching  VID. Outer and Inner tags are present
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_0 >= 0 ?
+			filter_0 :
+			priv->port_info[port]
+				.vlan.egress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.vid_enable = true;
+	vlan_cfg.filter.inner_vlan.vid_val = vid;
+
+	if (untagged) {
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG;
+	} else {
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+	}
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[port]
+		.vlan.egress_vlan_block_info.vlans[idx]
+		.filters_idx[0] = vlan_cfg.entry_index;
+
+   /* mark as unused */
+	priv->port_info[port]
+		.vlan.egress_vlan_block_info.vlans[idx]
+		.filters_idx[1] = IDX_INVAL;
+
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].vid = vid;
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].used = true;
+	priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].untagged = untagged;
+
+EXIT:
+	return ret;
+}
+
+
+static int
+__prepare_vlan_egress_filters_off_sp_tag_cpu(struct dsa_switch *ds, uint8_t cpu_port)
+{
+	int ret = -EINVAL;
+	struct mxl862xx_priv *priv = ds->priv;
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	struct mxl862xx_extended_vlan_block_info *block_info =
+		&priv->port_info[cpu_port].vlan.egress_vlan_block_info;
+
+	/* Allocate new block if needed */
+	if (!(block_info->allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries = block_info->filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(priv->dev,
+				"%s: Extended VLAN allocation for port%d egress filtering failed with %d\n",
+				__func__, cpu_port, ret);
+			goto EXIT;
+		}
+
+		block_info->allocated =	true;
+		block_info->block_id = vlan_alloc.extended_vlan_block_id;
+	}
+
+	// Entry last - 1  : Outer and Inner tags are present.
+	// Transparent mode, no tag modifications
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+
+	vlan_cfg.entry_index = block_info->filters_max - 2;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, cpu_port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	block_info->final_filters_idx = vlan_cfg.entry_index;
+
+	// Entry last : Outer tag is present.
+	// Transparent mode, no tag modifications
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id = block_info->block_id;
+
+	vlan_cfg.entry_index = block_info->filters_max - 1;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, cpu_port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	block_info->final_filters_idx = vlan_cfg.entry_index;
+
+EXIT:
+	return ret;
+
+}
+
+
+static int
+__prepare_vlan_egress_filters_sp_tag_cpu(struct dsa_switch *ds, uint8_t cpu_port, uint16_t vid, bool untagged)
+{
+	int ret = -EINVAL;
+	struct mxl862xx_priv *priv = ds->priv;
+	int filter_0 = -1;
+	int filter_1 = -1;
+	uint16_t idx = 0;
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+	/* Allocate new block if needed */
+	if (!(priv->port_info[cpu_port].vlan.egress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[cpu_port]
+				.vlan.egress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(priv->dev,
+				"%s: Extended VLAN allocation for port%d egress filtering failed with %d\n",
+				__func__, cpu_port, ret);
+			goto EXIT;
+		}
+
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.allocated =
+			true;
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	/* Populate the  block of rules related to VID */
+
+	/* VID specific entries must be processed before the final entries,
+	 * so putting them at the beginnig of the block */
+	ret = __get_vlan_vid_filters_idx(priv, cpu_port, false, vid, &filter_0, &filter_1, &idx);
+
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	// Entry 0 : Outer and Inner tags are present. If user port is untagged
+	// remove inner tag if the outer tag is matching the user port
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[cpu_port].vlan.egress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_0 >= 0 ?
+			filter_0 :
+			priv->port_info[cpu_port]
+				.vlan.egress_vlan_block_info.vid_filters_idx++;
+
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.vid_enable = true;
+	vlan_cfg.filter.inner_vlan.vid_val = vid;
+
+	if (untagged) {
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG;
+		vlan_cfg.treatment.add_outer_vlan = true;
+		vlan_cfg.treatment.outer_vlan.vid_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID;
+		vlan_cfg.treatment.outer_vlan.tpid = MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+		vlan_cfg.treatment.outer_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY;
+		vlan_cfg.treatment.outer_vlan.dei = MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI;
+	}	else {
+		vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+	}
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+			__func__, cpu_port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[cpu_port]
+		.vlan.egress_vlan_block_info.vlans[idx]
+		.filters_idx[0] = vlan_cfg.entry_index;
+
+   /* mark as unused */
+	priv->port_info[cpu_port]
+		.vlan.egress_vlan_block_info.vlans[idx]
+		.filters_idx[1] = IDX_INVAL;
+
+	priv->port_info[cpu_port].vlan.egress_vlan_block_info.vlans[idx].vid = vid;
+	priv->port_info[cpu_port].vlan.egress_vlan_block_info.vlans[idx].untagged = untagged;
+	priv->port_info[cpu_port].vlan.egress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+	return ret;
+
+}
+
+
+static int
+__prepare_vlan_ingress_filters_sp_tag(struct dsa_switch *ds, uint8_t port, uint16_t vid)
+{
+	int ret = -EINVAL;
+	uint16_t idx = 0;
+	/* negative values if not found */
+	int filter_0 = -1;
+	int filter_1 = -1;
+	struct mxl862xx_priv *priv = ds->priv;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+	/* Allocate new block if needed */
+	if (!(priv->port_info[port].vlan.ingress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[port]
+				.vlan.ingress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+
+		if (ret != MXL862XX_STATUS_OK)
+			goto EXIT;
+
+		priv->port_info[port].vlan.ingress_vlan_block_info.allocated =
+			true;
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	/* First populate the fixed block of rules which should be executed finally after
+	 * VID specific filtering. The final rules (not related to VID)
+	 * are placed at the end of the block. */
+
+	//Entry 6 no other rule applies Outer tag default Inner tag  not present DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 1;
+	vlan_cfg.filter.outer_vlan.type =
+		MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+	//Entry 5 no other rule applies Outer tag default Inner tag  present DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 2;
+	vlan_cfg.filter.outer_vlan.type =
+		MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+	// Entry 4  untagged pkts. If there's PVID accept and add PVID tag, otherwise reject
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 3;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	if (!priv->port_info[port].vlan.pvid) {
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+	} else {
+		vlan_cfg.treatment.add_outer_vlan = true;
+		vlan_cfg.treatment.outer_vlan.vid_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+		vlan_cfg.treatment.outer_vlan.tpid =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+		vlan_cfg.treatment.outer_vlan.priority_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+		vlan_cfg.treatment.outer_vlan.priority_val = 0;
+		vlan_cfg.treatment.outer_vlan.dei =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+		vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+		vlan_cfg.treatment.add_inner_vlan = true;
+		vlan_cfg.treatment.inner_vlan.vid_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+		vlan_cfg.treatment.inner_vlan.vid_val =
+			priv->port_info[port].vlan.pvid;
+		vlan_cfg.treatment.inner_vlan.tpid =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+		vlan_cfg.treatment.inner_vlan.priority_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+		vlan_cfg.treatment.inner_vlan.priority_val = 0;
+		vlan_cfg.treatment.inner_vlan.dei =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+	}
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add PVID entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx =
+		vlan_cfg.entry_index;
+
+	// Entry 3 : Only Outer tag present : not matching  DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 4;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add DISCARD entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx =
+		vlan_cfg.entry_index;
+
+	// Entry 2 : Outer and Inner VLAN tag present : not matching  DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 5;
+
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add DISCARD entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+	/* VID specific filtering rules which should be executed first before final ones.
+	 * Storing starts at the beginning of the block. */
+
+	ret = __get_vlan_vid_filters_idx(priv, port, true, vid, &filter_0, &filter_1, &idx);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d couldn't get idx for VID specific filters for VID:%d and  block ID:%d\n",
+			__func__, port, vid, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	// Entry 0 : Outer and Inner VLAN tag present :  matching  ACCEPT
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_0 >= 0 ?
+			filter_0 :
+			priv->port_info[port]
+				.vlan.ingress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = vid;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.add_outer_vlan = true;
+	vlan_cfg.treatment.outer_vlan.vid_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+	vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+	vlan_cfg.treatment.outer_vlan.tpid = MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+	vlan_cfg.treatment.outer_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.outer_vlan.priority_val = 0;
+	vlan_cfg.treatment.outer_vlan.dei = MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for block_id:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[port]
+		.vlan.ingress_vlan_block_info.vlans[idx]
+		.filters_idx[0] = vlan_cfg.entry_index;
+
+	// Entry 1 : Only Outer tags is present : matching  ACCEPT
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_1 >= 0 ?
+			filter_1 :
+			priv->port_info[port]
+				.vlan.ingress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = vid;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.add_outer_vlan = true;
+	vlan_cfg.treatment.outer_vlan.vid_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+	vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+	vlan_cfg.treatment.outer_vlan.tpid = MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+	vlan_cfg.treatment.outer_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.outer_vlan.priority_val = 0;
+	vlan_cfg.treatment.outer_vlan.dei = MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add entry:%d for block_id:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[port]
+		.vlan.ingress_vlan_block_info.vlans[idx]
+		.filters_idx[1] = vlan_cfg.entry_index;
+
+	priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].vid = vid;
+	priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+	return ret;
+}
+
+
+static int
+__prepare_vlan_ingress_filters_sp_tag_cpu(struct dsa_switch *ds, uint8_t port, uint8_t cpu_port)
+{
+	int ret = -EINVAL;
+	uint16_t idx = 0;
+	/* negative values if not found */
+	int filter_0 = -1;
+	int filter_1 = -1;
+	struct mxl862xx_priv *priv = ds->priv;
+	uint16_t bridge_port_cpu = priv->port_info[port].bridge_port_cpu;
+	uint16_t vid = dsa_8021q_tx_vid(ds, port);
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+	/* Allocate new block if needed */
+	if (!(priv->port_info[cpu_port].vlan.ingress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[cpu_port]
+				.vlan.ingress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(priv->dev,
+				"%s: Extended VLAN allocation for cpu_port%d ingress filtering failed with %d\n",
+				__func__, cpu_port, ret);
+			goto EXIT;
+		}
+
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.allocated =
+			true;
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	/* VID specific filtering rules which should be executed first before final ones.
+	 * Storing starts at the beginning of the block. */
+	ret = __get_vlan_vid_filters_idx(priv, cpu_port, true, vid, &filter_0, &filter_1, &idx);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d couldn't get idx for VID specific filters for VID:%d and  block ID:%d\n",
+			__func__, cpu_port, vid, vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	// Entry 0 : Outer and Inner VLAN tag present
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_0 >= 0 ?
+			filter_0 :
+			priv->port_info[cpu_port]
+				.vlan.ingress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = vid;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.reassign_bridge_port = true;
+	vlan_cfg.treatment.new_bridge_port_id = bridge_port_cpu;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: cpu_port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, cpu_port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[cpu_port]
+		.vlan.ingress_vlan_block_info.vlans[idx]
+		.filters_idx[0] = vlan_cfg.entry_index;
+
+	// Entry 1 : Only Outer tags is present
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_1 >= 0 ?
+			filter_1 :
+			priv->port_info[cpu_port]
+				.vlan.ingress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = vid;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.reassign_bridge_port = true;
+	vlan_cfg.treatment.new_bridge_port_id = bridge_port_cpu;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: cpu_port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, cpu_port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[cpu_port]
+		.vlan.ingress_vlan_block_info.vlans[idx]
+		.filters_idx[1] = vlan_cfg.entry_index;
+
+	priv->port_info[cpu_port].vlan.ingress_vlan_block_info.vlans[idx].vid = vid;
+	priv->port_info[cpu_port].vlan.ingress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+	return ret;
+}
+
+static int
+__prepare_vlan_ingress_filters(struct dsa_switch *ds, uint8_t port, uint16_t vid)
+{
+	int ret = -EINVAL;
+	uint16_t idx = 0;
+	/* negative values if not found */
+	int filter_0 = -1;
+	int filter_1 = -1;
+	struct mxl862xx_priv *priv = ds->priv;
+
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+	/* Allocate new block if needed */
+	if (!(priv->port_info[port].vlan.ingress_vlan_block_info.allocated)) {
+		mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+		/* Reserve fixed number of entries per port and direction */
+		vlan_alloc.number_of_entries =
+			priv->port_info[port]
+				.vlan.ingress_vlan_block_info.filters_max;
+		ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(priv->dev,
+				"%s: Extended VLAN allocation for port%d ingress filtering failed with %d\n",
+				__func__, port, ret);
+			goto EXIT;
+		}
+
+		priv->port_info[port].vlan.ingress_vlan_block_info.allocated =
+			true;
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id =
+			vlan_alloc.extended_vlan_block_id;
+	}
+
+	/* First populate the block with set of rules which should be executed finally after
+	 * VID specific filtering. The final rules (not related to VID) are placed on the end of the block. The number of
+	 * rules is fixed  per port. Order of execution  is important. To avoid static reservations they are
+	 * stored in reversed order starting from the end of the block */
+
+	//Entry 6 no other rule applies Outer tag default Inner tag  not present DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index =
+		priv->port_info[port]
+			.vlan.ingress_vlan_block_info.final_filters_idx--;
+	vlan_cfg.filter.outer_vlan.type =
+		MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add default DISCARD entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	//Entry 5 no other rule applies Outer tag default Inner tag  present DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index =
+		priv->port_info[port]
+			.vlan.ingress_vlan_block_info.final_filters_idx--;
+	vlan_cfg.filter.outer_vlan.type =
+		MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(priv->dev,
+			"%s: Port:%d failed to add default DISCARD entry:%d for ingress extended VLAN block ID:%d\n",
+			__func__, port, vlan_cfg.entry_index,
+			vlan_cfg.extended_vlan_block_id);
+		goto EXIT;
+	}
+
+	// Entry 4  untagged pkts. If there's PVID accept and add PVID tag, otherwise reject
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index =
+		priv->port_info[port]
+			.vlan.ingress_vlan_block_info.final_filters_idx--;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	if (!priv->port_info[port].vlan.pvid) {
+		vlan_cfg.treatment.remove_tag =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+	} else {
+		vlan_cfg.treatment.add_outer_vlan = true;
+		vlan_cfg.treatment.outer_vlan.vid_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+		vlan_cfg.treatment.outer_vlan.tpid =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+		vlan_cfg.treatment.outer_vlan.priority_mode =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+		vlan_cfg.treatment.outer_vlan.priority_val = 0;
+		vlan_cfg.treatment.outer_vlan.dei =
+			MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+		vlan_cfg.treatment.outer_vlan.vid_val =
+			priv->port_info[port].vlan.pvid;
+	}
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	// Entry 3 : Only Outer tag present : not matching  DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index =
+		priv->port_info[port]
+			.vlan.ingress_vlan_block_info.final_filters_idx--;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	// Entry 2 : Outer and Inner VLAN tag present : not matching  DISCARD
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	vlan_cfg.entry_index =
+		priv->port_info[port]
+			.vlan.ingress_vlan_block_info.final_filters_idx--;
+
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	/* VID specific filtering rules which should be executed first before final ones.
+	 * Storing starts at the beginning of the block. */
+
+	ret = __get_vlan_vid_filters_idx(priv, port, true, vid, &filter_0, &filter_1, &idx);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	// Entry 0 : Outer and Inner VLAN tag present :  matching  ACCEPT
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_0 >= 0 ?
+			filter_0 :
+			priv->port_info[port]
+				.vlan.ingress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = vid;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[port]
+		.vlan.ingress_vlan_block_info.vlans[idx]
+		.filters_idx[0] = vlan_cfg.entry_index;
+
+	// Entry 1 : Only Outer tag is present : matching  ACCEPT
+	memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+	vlan_cfg.extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+	/* if found recycled entry reuse it, otherwise create new one */
+	vlan_cfg.entry_index =
+		filter_1 >= 0 ?
+			filter_1 :
+			priv->port_info[port]
+				.vlan.ingress_vlan_block_info.vid_filters_idx++;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = vid;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+	vlan_cfg.treatment.remove_tag =
+		MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK)
+		goto EXIT;
+
+	/* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+	priv->port_info[port]
+		.vlan.ingress_vlan_block_info.vlans[idx]
+		.filters_idx[1] = vlan_cfg.entry_index;
+
+	priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].vid = vid;
+	priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+	return ret;
+}
+
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+static void mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan)
+#else
+static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan,
+				  struct netlink_ext_ack *extack)
+#endif
+{
+	int ret = -EINVAL;
+	struct mxl862xx_priv *priv = ds->priv;
+	mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+	uint8_t cpu_port = priv->hw_info->cpu_port;
+	bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+	bool standalone_port = false;
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+	uint16_t vid = vlan->vid_begin;
+#else
+	uint16_t vid = vlan->vid;
+#endif
+
+	if (port < 0 || port >= MAX_PORTS) {
+		dev_err(priv->dev, "invalid port: %d\n", port);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+		NL_SET_ERR_MSG_MOD(extack, "Port out of range");
+#endif
+		goto EXIT;
+	}
+
+	if (!((struct dsa_port *)dsa_to_port(ds, port))) {
+		dev_err(ds->dev, "%s:  port:%d is out of DSA domain\n", __func__, port);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+		NL_SET_ERR_MSG_MOD(extack, "Port out of DSA domain");
+#endif
+		goto EXIT;
+	}
+
+	/* standalone port */
+	if ((priv->port_info[port].bridge == NULL) && (!dsa_is_cpu_port(ds, port)))
+		standalone_port = true;
+
+	if (vid == 0)
+		goto EXIT;
+
+	/* If this is request to set pvid, just overwrite it as there may be
+	 * only one pid per port */
+	if (pvid)
+		priv->port_info[port].vlan.pvid = vid;
+	/* If this is pvid disable request, check if there's already matching vid
+	 * and only then disable it. If vid doesn't match active pvid, don't touch it */
+	else {
+		if (priv->port_info[port].vlan.pvid == vid)
+			priv->port_info[port].vlan.pvid = 0;
+	}
+
+	/* Check if there's enough room for ingress and egress rules */
+	if ((priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx -
+			priv->port_info[port].vlan.ingress_vlan_block_info.vid_filters_idx) <
+			(priv->port_info[port].vlan.ingress_vlan_block_info.entries_per_vlan)) {
+
+		dev_err(ds->dev,
+			"%s: Port:%d vlan:%d. Number of avaliable ingress entries too low. Required:%d  ff_idx:%d vf_idx:%d .\n",
+			__func__, port, vid,
+			priv->port_info[port].vlan.ingress_vlan_block_info.entries_per_vlan,
+			priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx,
+			priv->port_info[port].vlan.ingress_vlan_block_info.vid_filters_idx);
+
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+		ret = -ENOSPC;
+		NL_SET_ERR_MSG_MOD(extack, "Reached max number of VLAN ingress filter entries per port");
+#endif
+		goto EXIT;
+	}
+
+	if ((priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx -
+			priv->port_info[port].vlan.egress_vlan_block_info.vid_filters_idx) <
+			(priv->port_info[port].vlan.egress_vlan_block_info.entries_per_vlan)) {
+
+		dev_err(ds->dev,
+			"%s: Port:%d vlan:%d. Number of avaliable egress entries too low. Required:%d  ff_idx:%d vf_idx:%d .\n",
+			__func__, port, vid,
+			priv->port_info[port].vlan.egress_vlan_block_info.entries_per_vlan,
+			priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx,
+			priv->port_info[port].vlan.egress_vlan_block_info.vid_filters_idx);
+
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+		ret = -ENOSPC;
+		NL_SET_ERR_MSG_MOD(extack, "Reached max number of VLAN egress filter entries per port");
+#endif
+		goto EXIT;
+	}
+
+	/* Although 4-byte vlan special tagging handling is similar to 8 byte MxL tagging,
+	 * keep VLAN rules separate for better readibility */
+	if (vlan_sp_tag) {
+		if (!dsa_is_cpu_port(ds, port)) {
+		/* Special rules for CPU port based on user port id */
+			ret = __prepare_vlan_ingress_filters_sp_tag_cpu(ds, port, cpu_port);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to prepare ingress filters for VLAN:%d with vlan_filtering disabled\n",
+					__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters with vlan_filtering disabled");
+#endif
+				goto EXIT;
+			}
+			ret = __prepare_vlan_egress_filters_sp_tag_cpu(ds, cpu_port, vid, untagged);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to prepare egress filters for VLAN:%d with vlan_filtering disabled\n",
+					__func__, cpu_port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters with vlan_filtering disabled");
+#endif
+				goto EXIT;
+			}
+			/* vlan_filtering disabled */
+			/* skiping this configuration for vlan_sp_tag/cpu port as it requires special rules defined above */
+			if (!priv->port_info[port].vlan.filtering) {
+				dev_info(ds->dev,
+					"%s: port:%d setting VLAN:%d with vlan_filtering disabled\n",
+					__func__, port, vid);
+				ret = __prepare_vlan_ingress_filters_off_sp_tag(ds, port, vid);
+				if (ret != MXL862XX_STATUS_OK) {
+					dev_err(ds->dev,
+						"%s: Port:%d failed to prepare ingress filters for VLAN:%d with vlan_filtering disabled\n",
+						__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+					NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters with vlan_filtering disabled");
+#endif
+					goto EXIT;
+				}
+
+				ret = __prepare_vlan_egress_filters_off_sp_tag(ds, port, vid, untagged);
+				if (ret != MXL862XX_STATUS_OK) {
+					dev_err(ds->dev,
+						"%s: Port:%d failed to prepare egress filters for VLAN:%d with vlan_filtering disabled\n",
+						__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+					NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters with vlan_filtering disabled");
+#endif
+					goto EXIT;
+				}
+			}
+			/* vlan_filtering enabled */
+			else {
+				/* special rules for the CPU port are already defined,
+				 * so define only the rules for user ports */
+				ret = __prepare_vlan_ingress_filters_sp_tag(ds, port, vid);
+				if (ret != MXL862XX_STATUS_OK) {
+					dev_err(ds->dev,
+						"%s: Port:%d failed to prepare ingress filters for VLAN:%d\n",
+						__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+					NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters for VLAN");
+#endif
+					goto EXIT;
+				}
+
+				ret = __prepare_vlan_egress_filters_sp_tag(ds, port, vid, untagged);
+				if (ret != MXL862XX_STATUS_OK) {
+					dev_err(ds->dev,
+						"%s: Port:%d failed to prepare egress filters for VLAN:%d\n",
+						__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+					NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters for VLAN");
+#endif
+					goto EXIT;
+				}
+			}
+		} else {
+			/* CPU port. This else block handles explicit request for adding
+			 * VLAN to CPU port. Only egress rule requires reconfiguration.*/
+			ret = __prepare_vlan_egress_filters_sp_tag_cpu(ds, cpu_port, vid, untagged);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to prepare egress filters for VLAN:%d with vlan_filtering disabled\n",
+					__func__, cpu_port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters with vlan_filtering disabled");
+#endif
+				goto EXIT;
+			}
+		}
+
+		/* CPU port is explicitely added by the DSA framework to the new vlans.
+		   Apply block_id with filtering rules defined while processing user ports
+			For 8021q special tag mode cpu port rules may change because of new ports added,
+			so they need to be reloaded */
+		{
+			mxl862xx_ctp_port_config_t ctp_param = { 0 };
+
+			ctp_param.logical_port_id = cpu_port + 1;
+			ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN |
+					     MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN;
+			ctp_param.egress_extended_vlan_enable = true;
+			ctp_param.egress_extended_vlan_block_id =
+			priv->port_info[cpu_port].vlan.egress_vlan_block_info.block_id;
+			ctp_param.ingress_extended_vlan_enable = true;
+			ctp_param.ingress_extended_vlan_block_id =
+				priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id;
+
+			ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: CTP port %d config failed on port config set with %d\n",
+					__func__, cpu_port, ret);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to configure VLAN for cpu port");
+#endif
+				goto EXIT;
+			}
+		}
+	}
+
+	/* VLAN rules for 8 byte MxL tagging*/
+	else {
+		/* vlan_filtering disabled */
+		/* skiping this configuration for vlan_sp_tag/cpu port as it requires special rules defined above */
+		if (!priv->port_info[port].vlan.filtering) {
+			ret = __prepare_vlan_ingress_filters_off(priv, port, vid);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to prepare ingress filters for VLAN:%d with vlan_filtering disabled\n",
+					__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters with vlan_filtering disabled");
+#endif
+				goto EXIT;
+			}
+
+			ret = __prepare_vlan_egress_filters_off(priv, port, vid, untagged);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to prepare egress filters for VLAN:%d with vlan_filtering disabled\n",
+					__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters with vlan_filtering disabled");
+#endif
+				goto EXIT;
+			}
+		}
+		/* vlan_filtering enabled */
+		else {
+			ret = __prepare_vlan_ingress_filters(ds, port, vid);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to prepare ingress filters for VLAN:%d\n",
+					__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters for VLAN");
+#endif
+				goto EXIT;
+			}
+			ret = __prepare_vlan_egress_filters(ds, port, vid, untagged);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: Port:%d failed to prepare egress filters for VLAN:%d\n",
+					__func__, port, vid);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters for VLAN");
+#endif
+				goto EXIT;
+			}
+		}
+
+		/* CPU port is explicitely added by the DSA framework to new vlans */
+		if (dsa_is_cpu_port(ds, port)) {
+			mxl862xx_ctp_port_config_t ctp_param = { 0 };
+
+			ctp_param.logical_port_id = port + 1;
+			ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN |
+					     MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN;
+			ctp_param.egress_extended_vlan_enable = true;
+			ctp_param.egress_extended_vlan_block_id =
+				priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+			ctp_param.ingress_extended_vlan_enable = true;
+			ctp_param.ingress_extended_vlan_block_id =
+				priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+
+			ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+			if (ret != MXL862XX_STATUS_OK) {
+				dev_err(ds->dev,
+					"%s: CTP port %d config failed on port config set with %d\n",
+					__func__, port, ret);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+				NL_SET_ERR_MSG_MOD(extack, "Failed to configure VLAN for cpu port");
+#endif
+				goto EXIT;
+			}
+
+			goto EXIT;
+		}
+	}
+
+	/* Update bridge port */
+	br_port_cfg.bridge_port_id = port + 1;
+	br_port_cfg.mask |= MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN |
+			     MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN |
+				  MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+	br_port_cfg.egress_extended_vlan_enable = true;
+	br_port_cfg.egress_extended_vlan_block_id =
+		priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+	br_port_cfg.ingress_extended_vlan_enable = true;
+	br_port_cfg.ingress_extended_vlan_block_id =
+		priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+
+	/* Disable MAC learning for standalone ports. */
+	br_port_cfg.src_mac_learning_disable =
+				(standalone_port) ? true : false;
+
+	ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		dev_err(ds->dev,
+			"%s: Bridge port configuration for port %d failed with %d\n",
+			__func__, port, ret);
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+		NL_SET_ERR_MSG_MOD(extack, "Bridge port configuration for VLAN failed");
+#endif
+		goto EXIT;
+	}
+
+EXIT:
+#if (KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE)
+	return ret;
+#else
+	return;
+#endif
+}
+
+static int __deactivate_vlan_filter_entry(u16 block_id, u16 entry_idx)
+{
+	int ret = -EINVAL;
+	mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+	/* Set default reset values as it makes the rule transparent */
+	vlan_cfg.extended_vlan_block_id = block_id;
+	vlan_cfg.entry_index = entry_idx;
+	vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.outer_vlan.priority_enable = true;
+	vlan_cfg.filter.outer_vlan.priority_val = 0;
+	vlan_cfg.filter.outer_vlan.vid_enable = true;
+	vlan_cfg.filter.outer_vlan.vid_val = 0;
+	vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+	vlan_cfg.filter.inner_vlan.priority_enable = true;
+	vlan_cfg.filter.inner_vlan.priority_val = 0;
+	vlan_cfg.filter.inner_vlan.vid_enable = true;
+	vlan_cfg.filter.inner_vlan.vid_val = 0;
+	vlan_cfg.treatment.add_outer_vlan = true;
+	vlan_cfg.treatment.outer_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.outer_vlan.priority_val = 0;
+	vlan_cfg.treatment.add_inner_vlan = true;
+	vlan_cfg.treatment.inner_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+	vlan_cfg.treatment.inner_vlan.priority_val = 0;
+
+	ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+	if (ret != MXL862XX_STATUS_OK) {
+		pr_err("%s: failed to deactivate entry:%d for VLAN block ID:%d\n",
+			__func__, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+		goto exit;
+	}
+
+exit:
+	return ret;
+}
+
+static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan)
+{
+	int ret = -EINVAL;
+	int dir;
+	struct mxl862xx_priv *priv = ds->priv;
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+	uint16_t vid = vlan->vid_begin;
+#else
+	uint16_t vid = vlan->vid;
+#endif
+
+	for (dir = 0 ; dir < 2 ; dir++) {
+		struct mxl862xx_extended_vlan_block_info *block_info = (dir == 0)
+			? &priv->port_info[port].vlan.ingress_vlan_block_info
+			: &priv->port_info[port].vlan.egress_vlan_block_info;
+		char *dir_txt = (dir == 0)	? "ingress" : "egress";
+		int16_t entry_idx;
+		int vlan_idx, x;
+		u16 block_id = block_info->block_id;
+		/* Indicator of the last dynamic vid related entry being processed.
+		 * Required for cleanup of static rules at the end of the block. */
+		bool last_vlan = false;
+		bool vlan_found = false;
+
+		/* check if vlan is present */
+		for (vlan_idx = 0; vlan_idx < MAX_VLANS; vlan_idx++) {
+			if ((block_info->vlans[vlan_idx].vid == vid)
+					&& block_info->vlans[vlan_idx].used)
+				vlan_found = true;
+
+			if (vlan_idx == MAX_VLANS - 1)
+				last_vlan = true;
+
+			if (vlan_found)
+				break;
+		}
+
+		if (!vlan_found) {
+			dev_err(ds->dev, "%s: Port:%d VLAN:%d not found (%s)\n", __func__, port, vid, dir_txt);
+			goto static_rules_cleanup;
+		}
+
+		/* cleanup */
+		for (x = 0; x < VID_RULES ; x++) {
+			entry_idx = block_info->vlans[vlan_idx].filters_idx[x];
+			if (entry_idx != IDX_INVAL) {
+				ret = __deactivate_vlan_filter_entry(block_id, entry_idx);
+				if (ret != MXL862XX_STATUS_OK)
+					goto EXIT;
+			}
+		}
+
+		/* cleanup of the vlan record in the port vlan inventory */
+		block_info->vlans[vlan_idx].vid = 0;
+		block_info->vlans[vlan_idx].used = false;
+
+		/* find the first free slot for storing recycled filter entries */
+		for (x = 0; x < MAX_VLANS; x++) {
+			if (!(block_info->filter_entries_recycled[x].valid)) {
+				block_info->filter_entries_recycled[x].filters_idx[0] = block_info->vlans[vlan_idx].filters_idx[0];
+				block_info->filter_entries_recycled[x].filters_idx[1] = block_info->vlans[vlan_idx].filters_idx[1];
+				block_info->filter_entries_recycled[x].valid = true;
+				block_info->vlans[vlan_idx].filters_idx[0] = IDX_INVAL;
+				block_info->vlans[vlan_idx].filters_idx[1] = IDX_INVAL;
+				break;
+			}
+
+			if (x == MAX_VLANS - 1) {
+				ret = -ENOSPC;
+				dev_err(ds->dev,
+					"%s: Port:%d no free slots for recycled %s filter entries\n",
+					__func__, port, dir_txt);
+				goto EXIT;
+			}
+		}
+
+static_rules_cleanup:
+		/* If this is the last vlan entry or no entries left,
+		 * remove static entries (placed at the end of the block) */
+		if (last_vlan) {
+			for (entry_idx = block_info->final_filters_idx; entry_idx < block_info->filters_max ; entry_idx++) {
+				ret = __deactivate_vlan_filter_entry(block_id, entry_idx);
+				if (ret != MXL862XX_STATUS_OK)
+					goto EXIT;
+			}
+			/* Entries cleared, so point out to the end */
+			block_info->final_filters_idx = entry_idx;
+		}
+	}
+
+/*  The block release is not needed as the blocks/entries are distributed
+ *  evenly for all ports so it's static assignment. */
+
+EXIT:
+	return ret;
+}
+
+#if (KERNEL_VERSION(5, 18, 0) > LINUX_VERSION_CODE)
+static int mxl862xx_port_fdb_add(struct dsa_switch *ds, int port,
+				 const unsigned char *addr, u16 vid)
+#else
+static int mxl862xx_port_fdb_add(struct dsa_switch *ds, int port,
+				 const unsigned char *addr, u16 vid, struct dsa_db db)
+#endif
+{
+	int ret = -EINVAL;
+	struct mxl862xx_priv *priv = ds->priv;
+	mxl862xx_mac_table_add_t mac_table_add = { 0 };
+	uint8_t i = 0;
+
+	memcpy(mac_table_add.mac, addr, ETH_ALEN);
+
+	mac_table_add.port_id = port + 1;
+	mac_table_add.tci = (vid & 0xFFF);
+	mac_table_add.static_entry = true;
+
+	/* For CPU port add entries corresponding to all FIDs */
+	for (i = 0; i < priv->hw_info->phy_ports; i++) {
+
+		if (!(dsa_is_cpu_port(ds, port)))
+			i = port;
+		/* Bypass entry add for the isolated port as it may turn back
+		 * the traffic originated on the host to the cpu port */
+		if (priv->port_info[i].isolated)
+			continue;
+
+		mac_table_add.fid = priv->port_info[i].bridgeID;
+		ret = mxl862xx_mac_table_entry_add(&mxl_dev, &mac_table_add);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev, "%s: Port:%d failed to add MAC table entry for FID:%d\n",
+				__func__, port, mac_table_add.fid);
+			goto EXIT;
+		}
+
+	if (!(dsa_is_cpu_port(ds, port)))
+		break;
+	}
+
+EXIT:
+	return ret;
+}
+
+#if (KERNEL_VERSION(5, 18, 0) > LINUX_VERSION_CODE)
+static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port,
+				 const unsigned char *addr, u16 vid)
+#else
+static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port,
+				 const unsigned char *addr, u16 vid, struct dsa_db db)
+#endif
+{
+	int ret = -EINVAL;
+	struct mxl862xx_priv *priv = ds->priv;
+	mxl862xx_mac_table_remove_t mac_table_remove = { 0 };
+	uint8_t i = 0;
+
+	memcpy(mac_table_remove.mac, addr, ETH_ALEN);
+	mac_table_remove.tci = (vid & 0xFFF);
+
+	/* For CPU port remove entries corresponding to all FIDs */
+	for (i = 0; i < priv->hw_info->phy_ports; i++) {
+		if (!(dsa_is_cpu_port(ds, port)))
+			i = port;
+		mac_table_remove.fid = priv->port_info[i].bridgeID;
+		ret = mxl862xx_mac_table_entry_remove(&mxl_dev, &mac_table_remove);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev, "%s: Port:%d failed to remove MAC table entry for FID:%d\n",
+				__func__, port, mac_table_remove.fid);
+			goto EXIT;
+		}
+
+		if (!(dsa_is_cpu_port(ds, port)))
+			break;
+	}
+
+EXIT:
+	return ret;
+}
+
+static int mxl862xx_port_fdb_dump(struct dsa_switch *ds, int port,
+				  dsa_fdb_dump_cb_t *cb, void *data)
+{
+	int ret = -EINVAL;
+	mxl862xx_mac_table_read_t mac_table_read = { 0 };
+
+	mac_table_read.initial = 1;
+
+	for (;;) {
+		ret = mxl862xx_mac_table_entry_read(&mxl_dev, &mac_table_read);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: Port:%d failed to read MAC table entry\n",
+				__func__, port);
+			goto EXIT;
+		}
+
+		if (mac_table_read.last == 1)
+			break;
+
+		if (mac_table_read.port_id == port + 1)
+			cb(mac_table_read.mac, mac_table_read.tci & 0x0FFF,
+			   mac_table_read.static_entry, data);
+
+		memset(&mac_table_read, 0, sizeof(mac_table_read));
+	}
+
+EXIT:
+	return ret;
+}
+
+#if (KERNEL_VERSION(5, 11, 0) < LINUX_VERSION_CODE)
+static int mxl862xx_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+	struct switchdev_brport_flags flags, struct netlink_ext_ack *extack)
+{
+	int ret = 0;
+
+	if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING)) {
+		dev_err(ds->dev, "%s: Port:%d unsupported bridge flags:0x%lx\n",
+				__func__, port, flags.mask);
+		if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING)) {
+			ret = -EINVAL;
+			NL_SET_ERR_MSG_MOD(extack, "Unsupported bridge flags:0x%lx");
+		}
+	}
+
+	return ret;
+}
+
+
+static int mxl862xx_port_bridge_flags(struct dsa_switch *ds, int port,
+	struct switchdev_brport_flags flags, struct netlink_ext_ack *extack)
+{
+	int ret = 0;
+	uint16_t bridge_id;
+	struct mxl862xx_priv *priv = ds->priv;
+	bool bridge_ctx = true;
+
+	if (!dsa_is_user_port(ds, port))
+		return 0;
+
+	/* .port_pre_bridge_flags is called after this function,
+	 *  so the supported flags check is needed also here */
+	if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING)) {
+		dev_err(ds->dev, "%s: Port:%d unsupported bridge flags:0x%lx\n",
+				__func__, port, flags.mask);
+		if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING)) {
+			ret = -EINVAL;
+			goto EXIT;
+		}
+	}
+
+	bridge_id = priv->port_info[port].bridgeID;
+	if ((bridge_id == 0) || (priv->port_info[port].bridge == NULL))
+		bridge_ctx = false;
+
+	/* Handle flooding flags (bridge context) */
+	if (bridge_ctx && (flags.mask & (BR_FLOOD|BR_MCAST_FLOOD|BR_BCAST_FLOOD))) {
+		mxl862xx_bridge_config_t bridge_config = { 0 };
+
+		bridge_config.mask = MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE;
+		bridge_config.bridge_id = bridge_id;
+
+		if (flags.mask & BR_FLOOD)
+			bridge_config.forward_unknown_unicast = (flags.val & BR_FLOOD) ?
+				MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD;
+		if (flags.mask & BR_MCAST_FLOOD) {
+			bridge_config.forward_unknown_multicast_ip = (flags.val & BR_MCAST_FLOOD) ?
+				MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD;
+			bridge_config.forward_unknown_multicast_non_ip = bridge_config.forward_unknown_multicast_ip;
+		}
+		if (flags.mask & BR_BCAST_FLOOD)
+			bridge_config.forward_broadcast = (flags.val & BR_BCAST_FLOOD) ?
+			MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD;
+
+		ret = mxl862xx_bridge_config_set(&mxl_dev, &bridge_config);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev, "%s: Port:%d bridge:%d configuration  failed\n",
+				__func__, port, bridge_config.bridge_id);
+			NL_SET_ERR_MSG_MOD(extack, "Configuration of bridge flooding flags failed");
+			goto EXIT;
+		}
+	}
+	/* Handle learning flag (bridge port context) */
+	if (flags.mask & BR_LEARNING) {
+		mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+
+		br_port_cfg.mask =	MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+		br_port_cfg.bridge_port_id = port + 1;
+		br_port_cfg.src_mac_learning_disable = (flags.val & BR_LEARNING) ? false : true;
+		ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+		if (ret != MXL862XX_STATUS_OK) {
+			dev_err(ds->dev,
+				"%s: MAC learning disable for port %d failed with ret=%d\n",
+				__func__, port, ret);
+			NL_SET_ERR_MSG_MOD(extack, "Configuration of bridge port learning flags failed");
+			goto EXIT;
+		}
+	}
+
+EXIT:
+	return ret;
+}
+#endif
+
+#if (KERNEL_VERSION(5, 11, 0) < LINUX_VERSION_CODE)
+#if (KERNEL_VERSION(5, 19, 0) > LINUX_VERSION_CODE)
+static int mxl862xx_change_tag_protocol(struct dsa_switch *ds, int port,
+				     enum dsa_tag_protocol proto)
+#else
+static int mxl862xx_change_tag_protocol(struct dsa_switch *ds,
+				     enum dsa_tag_protocol proto)
+#endif
+{
+	int ret = MXL862XX_STATUS_OK;
+
+	dev_info(ds->dev, "%s: DSA tag protocol change not supported\n",  __func__);
+	return ret;
+}
+#endif
+
+#if (KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE)
+static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
+						       int port)
+#else
+static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
+						       int port, enum dsa_tag_protocol m)
+#endif
+{
+	enum dsa_tag_protocol tag_proto;
+
+	tag_proto = __dt_parse_tag_proto(ds, port);
+
+	return tag_proto;
+}
+
+static const struct dsa_switch_ops mxl862xx_switch_ops = {
+	.get_ethtool_stats = mxl862xx_get_ethtool_stats,
+	.get_strings = mxl862xx_get_strings,
+	.get_sset_count = mxl862xx_get_sset_count,
+#if (KERNEL_VERSION(5, 11, 0) < LINUX_VERSION_CODE)
+	.change_tag_protocol	= mxl862xx_change_tag_protocol,
+#endif
+	.get_tag_protocol = mxl862xx_get_tag_protocol,
+	.phy_read = mxl862xx_phy_read,
+	.phy_write = mxl862xx_phy_write,
+#if (KERNEL_VERSION(4, 18, 0) <= LINUX_VERSION_CODE)
+	.phylink_mac_config = mxl862xx_phylink_mac_config,
+	.phylink_mac_link_down = mxl862xx_phylink_mac_link_down,
+	.phylink_mac_link_up = mxl862xx_phylink_mac_link_up,
+#if (KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE)
+	.phylink_validate = mxl862xx_phylink_validate,
+#else
+	.phylink_get_caps = mxl862xx_phylink_get_caps,
+#endif
+#endif
+	.set_ageing_time = mxl862xx_set_ageing_time,
+	.port_bridge_join = mxl862xx_port_bridge_join,
+	.port_bridge_leave = mxl862xx_port_bridge_leave,
+	.port_disable = mxl862xx_port_disable,
+	.port_enable = mxl862xx_port_enable,
+	.port_fast_age = mxl862xx_port_fast_age,
+	.port_stp_state_set = mxl862xx_port_stp_state_set,
+	.port_mirror_add = mxl862xx_port_mirror_add,
+	.port_mirror_del = mxl862xx_port_mirror_del,
+#if (KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE)
+	.port_vlan_prepare	= mxl862xx_port_vlan_prepare,
+#endif
+	.port_vlan_filtering = mxl862xx_port_vlan_filtering,
+	.port_vlan_add = mxl862xx_port_vlan_add,
+	.port_vlan_del = mxl862xx_port_vlan_del,
+	.port_fdb_add = mxl862xx_port_fdb_add,
+	.port_fdb_del = mxl862xx_port_fdb_del,
+	.port_fdb_dump = mxl862xx_port_fdb_dump,
+#if (KERNEL_VERSION(5, 11, 0) < LINUX_VERSION_CODE)
+	.port_pre_bridge_flags = mxl862xx_port_pre_bridge_flags,
+	.port_bridge_flags = mxl862xx_port_bridge_flags,
+#endif
+	.setup = mxl862xx_setup,
+};
+
+static int mxl862xx_probe(struct mdio_device *mdiodev)
+{
+	struct device *dev = &mdiodev->dev;
+	struct mxl862xx_priv *priv;
+	struct dsa_switch *ds;
+	int ret;
+	struct sys_fw_image_version sys_img_ver = { 0 };
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(dev, "%s: Error allocating mxl862xx switch\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	priv->dev = dev;
+	priv->bus = mdiodev->bus;
+	priv->sw_addr = mdiodev->addr;
+	priv->hw_info = of_device_get_match_data(dev);
+	if (!priv->hw_info)
+		return -EINVAL;
+
+	mxl_dev.bus = mdiodev->bus;
+	mxl_dev.sw_addr = mdiodev->addr;
+
+#if (KERNEL_VERSION(5, 16, 0) <= LINUX_VERSION_CODE)
+	mutex_init(&priv->pce_table_lock);
+#endif
+
+	ret = sys_misc_fw_version(&mxl_dev, &sys_img_ver);
+	if (ret < 0)
+		dev_err(dev, "\t%40s:\t0x%x\n",
+			"fapi_GSW_FW_Version failed with ret code", ret);
+	else {
+		dev_info(dev, "\t%40s:\t%x\n", "Iv Major",
+			 sys_img_ver.iv_major);
+		dev_info(dev, "\t%40s:\t%x\n", "Iv Minor",
+			 sys_img_ver.iv_minor);
+		dev_info(dev, "\t%40s:\t%u\n", "Revision",
+			 sys_img_ver.iv_revision);
+		dev_info(dev, "\t%40s:\t%u\n", "Build Num",
+			 sys_img_ver.iv_build_num);
+	}
+
+#if (KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE)
+	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
+	if (!ds) {
+		dev_err(dev, "%s: Error allocating DSA switch\n", __func__);
+		return -ENOMEM;
+	}
+
+	ds->dev = dev;
+	ds->num_ports = priv->hw_info->max_ports;
+#else
+	ds = dsa_switch_alloc(&mdiodev->dev, priv->hw_info->max_ports);
+#endif
+	ds->priv = priv;
+	ds->ops = priv->hw_info->ops;
+#if (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)
+	ds->assisted_learning_on_cpu_port = true;
+#endif
+
+	dev_set_drvdata(dev, ds);
+
+	ret = dsa_register_switch(ds);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "%s: Error %d register DSA switch\n",
+				__func__, ret);
+		return ret;
+	}
+
+	if (!dsa_is_cpu_port(ds, priv->hw_info->cpu_port)) {
+		dev_err(dev,
+			"wrong CPU port defined, HW only supports port: %i",
+			priv->hw_info->cpu_port);
+		ret = -EINVAL;
+		dsa_unregister_switch(ds);
+	}
+
+	return ret;
+}
+
+static void mxl862xx_remove(struct mdio_device *mdiodev)
+{
+	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
+
+	dsa_unregister_switch(ds);
+
+}
+
+static const struct mxl862xx_hw_info mxl86282_data = {
+	.max_ports = 9,
+	.phy_ports = 8,
+	.cpu_port = 8,
+	.ops = &mxl862xx_switch_ops,
+};
+
+static const struct mxl862xx_hw_info mxl86252_data = {
+	.max_ports = 9,
+	.phy_ports = 5,
+	.cpu_port = 8,
+	.ops = &mxl862xx_switch_ops,
+};
+
+static const struct of_device_id mxl862xx_of_match[] = {
+	{ .compatible = "mxl,86282", .data = &mxl86282_data },
+	{ .compatible = "mxl,86252", .data = &mxl86252_data },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, mxl862xx_of_match);
+
+static struct mdio_driver mxl862xx_driver = {
+	.probe  = mxl862xx_probe,
+	.remove = mxl862xx_remove,
+	.mdiodrv.driver = {
+		.name = "mxl862xx",
+		.of_match_table = mxl862xx_of_match,
+	},
+};
+
+mdio_module_driver(mxl862xx_driver);
+
+MODULE_DESCRIPTION("Driver for MaxLinear MxL862xx switch family");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mxl862xx");
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 1916266..045fc2b 100644
--- 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
@@ -30,6 +30,13 @@
 #include "mtk_eth_dbg.h"
 #include "mtk_eth_reset.h"
 
+enum mt753x_presence {
+	MT753X_ABSENT = 0,
+	MT753X_PRESENT = 1,
+	MT753X_UNKNOWN = 0xffff,
+};
+
+enum mt753x_presence mt753x_presence = MT753X_UNKNOWN;
 u32 hw_lro_agg_num_cnt[MTK_HW_LRO_RING_NUM][MTK_HW_LRO_MAX_AGG_CNT + 1];
 u32 hw_lro_agg_size_cnt[MTK_HW_LRO_RING_NUM][16];
 u32 hw_lro_tot_agg_cnt[MTK_HW_LRO_RING_NUM];
@@ -42,6 +49,7 @@
 u32 mtk_hwlro_stats_ebl;
 u32 dbg_show_level;
 u32 cur_rss_num;
+int eth_debug_level;
 
 static struct proc_dir_entry *proc_hw_lro_stats, *proc_hw_lro_auto_tlb,
 			     *proc_rss_ctrl;
@@ -122,6 +130,35 @@
 	return (high << 16) | (low & 0xffff);
 }
 
+static enum mt753x_presence mt753x_sw_detect(struct mtk_eth *eth)
+{
+	struct device_node *np;
+	u32 sw_id;
+	u32 rev;
+
+	/* mt7988 with built-in 7531 */
+	np = of_find_compatible_node(NULL, NULL, "mediatek,mt7988-switch");
+	if (np) {
+		of_node_put(np);
+		return MT753X_PRESENT;
+	}
+	/* external 753x */
+	rev = mt7530_mdio_r32(eth, 0x781c);
+	sw_id = (rev & 0xffff0000) >> 16;
+	if (sw_id == 0x7530 || sw_id == 0x7531)
+		return MT753X_PRESENT;
+
+	return MT753X_ABSENT;
+}
+
+static enum mt753x_presence mt7530_exist(struct mtk_eth *eth)
+{
+	if (mt753x_presence == MT753X_UNKNOWN)
+		mt753x_presence = mt753x_sw_detect(eth);
+
+	return mt753x_presence;
+}
+
 void mt753x_set_port_link_state(bool up)
 {
 	struct mtk_eth *eth = g_eth;
@@ -174,7 +211,7 @@
 				   mac->phy_dev->addr, j, d);
 			j++;
 		}
-#endif		
+#endif
 	}
 	return 0;
 }
@@ -259,6 +296,7 @@
 		return 0;
 	}
 
+	mt798x_iomap();
 	for (i = 0 ; i < ARRAY_SIZE(ranges) ; i++) {
 		for (offset = ranges[i].start;
 		     offset <= ranges[i].end; offset += 4) {
@@ -267,6 +305,7 @@
 				   offset, data);
 		}
 	}
+	mt798x_iounmap();
 
 	return 0;
 }
@@ -325,11 +364,13 @@
 	if (kstrtoul(token, 16, (unsigned long *)&value))
 		return -EINVAL;
 
+	mt798x_iomap();
 	pr_info("%s:phy=%d, reg=0x%lx, val=0x%lx\n", __func__,
 		0x1f, reg, value);
 	mt7530_mdio_w32(eth, reg, value);
 	pr_info("%s:phy=%d, reg=0x%lx, val=0x%x confirm..\n", __func__,
 		0x1f, reg, mt7530_mdio_r32(eth, reg));
+	mt798x_iounmap();
 
 	return len;
 }
@@ -406,9 +447,10 @@
 			atomic_set(&force, 0);
 			break;
 		case 1:
-			if (atomic_read(&force) == 1)
+			if (atomic_read(&force) == 1) {
+				eth->reset.event = MTK_FE_START_RESET;
 				schedule_work(&eth->pending_work);
-			else
+			} else
 				pr_info(" stat:disable\n");
 			break;
 		case 2:
@@ -416,7 +458,7 @@
 			break;
 		case 3:
 			if (atomic_read(&force) == 1) {
-				mtk_reset_flag = MTK_FE_STOP_TRAFFIC;
+				eth->reset.event = MTK_FE_STOP_TRAFFIC;
 				schedule_work(&eth->pending_work);
 			} else
 				pr_info(" device resetting !!!\n");
@@ -477,6 +519,49 @@
 	return len;
 }
 
+static int eth_debug_level_read(struct seq_file *m, void *private)
+{
+	pr_info("eth debug level=%d!\n", eth_debug_level);
+
+	return 0;
+}
+
+static int eth_debug_level_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, eth_debug_level_read, inode->i_private);
+}
+
+static ssize_t eth_debug_level_write(struct file *file, const char __user *ptr,
+				 size_t len, loff_t *off)
+{
+	char *p_delimiter = " \t";
+	char *p_token = NULL;
+	char buf[8] = {0};
+	long arg0 = 0;
+	char *p_buf;
+	int ret;
+
+	if ((len > 8) || copy_from_user(buf, ptr, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	p_buf = buf;
+	p_token = strsep(&p_buf, p_delimiter);
+	if (!p_token)
+		arg0 = 0;
+	else
+		ret = kstrtol(p_token, 10, &arg0);
+
+	if (ret)
+		arg0 = 0;
+
+	eth_debug_level = arg0;
+	pr_info("Set eth debug level=%d!\n", eth_debug_level);
+
+	return len;
+}
+
 int pse_info_usage(struct seq_file *m, void *private)
 {
 	pr_info("====================Advanced Settings====================\n");
@@ -699,6 +784,15 @@
 	.llseek = noop_llseek,
 };
 
+static const struct file_operations fops_eth_debug = {
+	.owner = THIS_MODULE,
+	.open = eth_debug_level_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.write = eth_debug_level_write,
+	.release = single_release,
+};
+
 void mtketh_debugfs_exit(struct mtk_eth *eth)
 {
 	debugfs_remove_recursive(eth_debug.root);
@@ -724,6 +818,8 @@
 			    eth_debug.root, eth,  &fops_reg_w);
 	debugfs_create_file("reset", S_IFREG | S_IWUSR,
 			    eth_debug.root, eth,  &fops_eth_reset);
+	debugfs_create_file("eth_debug_level", 0444,
+			    eth_debug.root, eth, &fops_eth_debug);
 	if (mt7530_exist(eth)) {
 		debugfs_create_file("mt7530sw_regs", S_IRUGO,
 				    eth_debug.root, eth,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
index 5fba9b5..9887835 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
@@ -68,6 +68,11 @@
 	(((x) == 1) ? (mtk_r32(eth, MTK_PSE_OQ_STA(4)) & 0x0FFF0000) :	\
 	(mtk_r32(eth, MTK_PSE_OQ_STA(6)) & 0x0FFF0000)))
 
+#define MTK_FE_GDM_IQ(x)		\
+	(((x) == 2) ? mtk_r32(eth, MTK_PSE_IQ_STA(7)) & 0x0fff0000 :	\
+	((x) == 1) ? mtk_r32(eth, MTK_PSE_IQ_STA(1)) & 0x00000fff :	\
+	mtk_r32(eth, MTK_PSE_IQ_STA(0)) & 0x0fff0000)
+
 #define MTK_FE_GDM_OQ(x)		\
 	(((x) == 2) ? mtk_r32(eth, MTK_PSE_OQ_STA(7)) & 0x0fff0000 :	\
 	((x) == 1) ? mtk_r32(eth, MTK_PSE_OQ_STA(1)) & 0x00000fff :	\
@@ -403,23 +408,12 @@
 	unsigned int val_out;
 };
 
-#if defined(CONFIG_NET_DSA_MT7530) || defined(CONFIG_MT753X_GSW)
-static inline bool mt7530_exist(struct mtk_eth *eth)
-{
-	return true;
-}
-#else
-static inline bool mt7530_exist(struct mtk_eth *eth)
-{
-	return false;
-}
-#endif
-
 extern u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg);
 extern u32 _mtk_mdio_write(struct mtk_eth *eth, int phy_addr,
 		    int phy_reg, u16 write_data);
 
 extern atomic_t force;
+extern int eth_debug_level;
 
 int debug_proc_init(struct mtk_eth *eth);
 void debug_proc_exit(void);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c
index cc4b1d5..e61390a 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c
@@ -31,8 +31,8 @@
 		return "gmac2_rgmii";
 	case MTK_ETH_PATH_GMAC2_SGMII:
 		return "gmac2_sgmii";
-	case MTK_ETH_PATH_GMAC2_XGMII:
-		return "gmac2_xgmii";
+	case MTK_ETH_PATH_GMAC2_2P5GPHY:
+		return "gmac2_2p5gphy";
 	case MTK_ETH_PATH_GMAC2_GEPHY:
 		return "gmac2_gephy";
 	case MTK_ETH_PATH_GMAC3_SGMII:
@@ -135,7 +135,7 @@
 	return 0;
 }
 
-static int set_mux_gmac2_to_xgmii(struct mtk_eth *eth, u64 path)
+static int set_mux_gmac2_to_2p5gphy(struct mtk_eth *eth, u64 path)
 {
 	unsigned int val = 0;
 	bool updated = true;
@@ -149,7 +149,7 @@
 	regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
 
 	switch (path) {
-	case MTK_ETH_PATH_GMAC2_XGMII:
+	case MTK_ETH_PATH_GMAC2_2P5GPHY:
 		val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2;
 		mac_id = MTK_GMAC2_ID;
 		break;
@@ -315,9 +315,9 @@
 		.cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY,
 		.set_path = set_mux_u3_gmac2_to_qphy,
 	}, {
-		.name = "mux_gmac2_to_xgmii",
-		.cap_bit = MTK_ETH_MUX_GMAC2_TO_XGMII,
-		.set_path = set_mux_gmac2_to_xgmii,
+		.name = "mux_gmac2_to_2p5gphy",
+		.cap_bit = MTK_ETH_MUX_GMAC2_TO_2P5GPHY,
+		.set_path = set_mux_gmac2_to_2p5gphy,
 	}, {
 		.name = "mux_gmac1_gmac2_to_sgmii_rgmii",
 		.cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII,
@@ -403,13 +403,13 @@
 	return 0;
 }
 
-int mtk_gmac_xgmii_path_setup(struct mtk_eth *eth, int mac_id)
+int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id)
 {
 	int err;
 	u64 path = 0;
 
-	if (mac_id == 1)
-		path = MTK_ETH_PATH_GMAC2_XGMII;
+	if (mac_id == MTK_GMAC2_ID)
+		path = MTK_ETH_PATH_GMAC2_2P5GPHY;
 
 	if (!path)
 		return -EINVAL;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
index 19cd4e3..5ebeb97 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
@@ -29,9 +29,8 @@
 };
 
 static struct mtk_qdma_cfg mtk_qdma_cfg_backup;
-static int mtk_wifi_num = 0;
 static int mtk_rest_cnt = 0;
-u32 mtk_reset_flag = MTK_FE_START_RESET;
+int mtk_wifi_num;
 bool mtk_stop_fail;
 
 typedef u32 (*mtk_monitor_xdma_func) (struct mtk_eth *eth);
@@ -90,7 +89,7 @@
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
 		reset_bits |= RSTCTRL_PPE2;
-	if (mtk_reset_flag == MTK_FE_START_RESET)
+	if (eth->reset.event == MTK_FE_START_RESET)
 		reset_bits |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
 #endif
 	ethsys_reset(eth, reset_bits);
@@ -130,7 +129,7 @@
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
 			reset_bits |= RSTCTRL_PPE2;
-		if (mtk_reset_flag == MTK_FE_START_RESET)
+		if (eth->reset.event == MTK_FE_START_RESET)
 			reset_bits |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
 #endif
 
@@ -275,17 +274,20 @@
 		mtk_dump_reg(eth, "XGMAC0", 0x12000, 0x300);
 		mtk_dump_reg(eth, "XGMAC1", 0x13000, 0x300);
 		mtk_dump_regmap(eth->usxgmii->pcs[0].regmap,
-				"USXGMII0", 0, 0x1000);
+				"USXGMII0", 0x800, 0x500);
 		mtk_dump_regmap(eth->usxgmii->pcs[1].regmap,
-				"USXGMII1", 0, 0x1000);
+				"USXGMII1", 0x800, 0x500);
 	}
 }
 
-void mtk_check_pse_oq_sta(struct mtk_eth *eth, u32 port, u32 *pre_opq, u32 *err_opq)
+void mtk_check_pse_oq_sta(struct mtk_eth *eth, u8 port, u16 *pre_opq, u8 *err_opq)
 {
-	u32 mask = port % 2 ? 0x0FFF0000 : 0x00000FFF;
-	u32 id = port / 2;
-	u32 cur_opq;
+	u32 mask = (port % 2) ? 0x0FFF0000 : 0x00000FFF;
+	u8 id = port / 2;
+	u16 cur_opq;
+
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1))
+		return;
 
 	cur_opq = (mtk_r32(eth, MTK_PSE_OQ_STA(id)) & mask);
 	if ((cur_opq != 0) && (cur_opq == *pre_opq))
@@ -298,23 +300,26 @@
 
 u32 mtk_monitor_wdma_tx(struct mtk_eth *eth)
 {
-	static u32 pre_dtx[MTK_WDMA_CNT];
-	static u32 err_cnt[MTK_WDMA_CNT];
-	static u32 err_opq1, err_opq2, err_opq8;
-	static u32 err_opq9, err_opq13, err_opq15;
-	static u32 opq1, opq2, opq8, opq9, opq13, opq15;
+	struct wdma_tx_monitor *wdma_tx = &eth->reset.wdma_monitor.tx;
 	u32 cur_dtx, tx_busy, fsm_ts;
 	u32 i, err_opq = 0, err_flag = 0;
 
-	mtk_check_pse_oq_sta(eth, 1, &opq1, &err_opq1);
-	mtk_check_pse_oq_sta(eth, 2, &opq2, &err_opq2);
-	mtk_check_pse_oq_sta(eth, 8, &opq8, &err_opq8);
-	mtk_check_pse_oq_sta(eth, 9, &opq9, &err_opq9);
-	mtk_check_pse_oq_sta(eth, 13, &opq13, &err_opq13);
-	mtk_check_pse_oq_sta(eth, 15, &opq15, &err_opq15);
+	mtk_check_pse_oq_sta(eth, PSE_GDM1_PORT, &wdma_tx->pre_opq_gdm[0],
+			     &wdma_tx->err_opq_gdm[0]);
+	mtk_check_pse_oq_sta(eth, PSE_GDM2_PORT, &wdma_tx->pre_opq_gdm[1],
+			     &wdma_tx->err_opq_gdm[1]);
+	mtk_check_pse_oq_sta(eth, PSE_GDM3_PORT, &wdma_tx->pre_opq_gdm[2],
+			     &wdma_tx->err_opq_gdm[2]);
+	mtk_check_pse_oq_sta(eth, PSE_WDMA0_PORT, &wdma_tx->pre_opq_wdma[0],
+			     &wdma_tx->err_opq_wdma[0]);
+	mtk_check_pse_oq_sta(eth, PSE_WDMA1_PORT, &wdma_tx->pre_opq_wdma[1],
+			     &wdma_tx->err_opq_wdma[1]);
+	mtk_check_pse_oq_sta(eth, PSE_WDMA2_PORT, &wdma_tx->pre_opq_wdma[2],
+			     &wdma_tx->err_opq_wdma[2]);
 
-	if ((err_opq1 >= 3) || (err_opq2 >= 3) || (err_opq8 >= 3) ||
-	    (err_opq9 >= 3) || (err_opq13 >= 3) || (err_opq15 >= 3))
+	if ((wdma_tx->err_opq_gdm[0] >= 3) || (wdma_tx->err_opq_gdm[1] >= 3) ||
+	    (wdma_tx->err_opq_gdm[2] >= 3) || (wdma_tx->err_opq_wdma[0] >= 3) ||
+	    (wdma_tx->err_opq_wdma[1] >= 3) || (wdma_tx->err_opq_wdma[2] >= 3))
 		err_opq = 1;
 
 	for (i = 0; i < MTK_WDMA_CNT; i++) {
@@ -322,14 +327,14 @@
 		tx_busy = mtk_r32(eth, MTK_WDMA_GLO_CFG(i)) & MTK_TX_DMA_BUSY;
 		fsm_ts = mtk_r32(eth, MTK_FE_CDM_FSM(i)) &
 			(MTK_CDM_TS_FSM_MASK | MTK_CDM_TS_PARSER_FSM_MASK);
-		/*dtx unchange && tx busy && cdm-ts-fsm && ouput*/
-		if (cur_dtx == pre_dtx[i] && tx_busy && fsm_ts && err_opq) {
-			err_cnt[i]++;
-			if (err_cnt[i] >= 3) {
+		/* dtx unchange && tx busy && cdm-ts-fsm && ouput */
+		if (cur_dtx == wdma_tx->pre_dtx[i] && tx_busy && fsm_ts && err_opq) {
+			wdma_tx->hang_count[i]++;
+			if (wdma_tx->hang_count[i] >= 5) {
 				pr_info("WDMA %d Tx Info\n", i);
-				pr_info("err_cnt = %d", err_cnt[i]);
+				pr_info("hang count = %d\n", wdma_tx->hang_count[i]);
 				pr_info("prev_dtx = 0x%x	| cur_dtx = 0x%x\n",
-					pre_dtx[i], cur_dtx);
+					wdma_tx->pre_dtx[i], cur_dtx);
 				pr_info("WDMA_CTX_PTR = 0x%x\n",
 					mtk_r32(eth, MTK_WDMA_CTX_PTR(i)));
 				pr_info("WDMA_DTX_PTR = 0x%x\n",
@@ -352,8 +357,9 @@
 				err_flag = 1;
 			}
 		} else
-			err_cnt[i] = 0;
-		pre_dtx[i] = cur_dtx;
+			wdma_tx->hang_count[i] = 0;
+
+		wdma_tx->pre_dtx[i] = cur_dtx;
 	}
 
 	if (err_flag)
@@ -364,42 +370,54 @@
 
 u32 mtk_monitor_wdma_rx(struct mtk_eth *eth)
 {
-	static u32 pre_drx[MTK_WDMA_CNT];
-	static u32 pre_crx[MTK_WDMA_CNT];
-	static u32 pre_opq[MTK_WDMA_CNT];
-	static u32 err_cnt[MTK_WDMA_CNT];
+	struct wdma_rx_monitor *wdma_rx = &eth->reset.wdma_monitor.rx;
 	bool connsys_busy, netsys_busy;
-	u32 cur_crx = 0, cur_drx = 0, cur_opq = 0, fsm_fs, max_cnt;
-	u32 i, err_flag = 0;
+	u32 cur_crx, cur_drx, cur_opq, fsm_fs, max_cnt;
+	u32 i, j, err_flag = 0;
+	bool rx_en, crx_unchanged, drx_unchanged;
 	int rx_cnt;
 
 	for (i = 0; i < MTK_WDMA_CNT; i++) {
-		connsys_busy = netsys_busy = false;
+		rx_en = !!(mtk_r32(eth, MTK_WDMA_GLO_CFG(i)) & MTK_RX_DMA_EN);
 		max_cnt = mtk_r32(eth, MTK_WDMA_RX_MAX_CNT(i));
-		cur_crx = mtk_r32(eth, MTK_WDMA_CRX_PTR(i));
-		cur_drx = mtk_r32(eth, MTK_WDMA_DRX_PTR(i));
+		if (!rx_en || max_cnt == 0)
+			continue;
+
+		connsys_busy = netsys_busy = false;
+		crx_unchanged = drx_unchanged = true;
+		for (j = 0; j < 3; j++) {
+			cur_crx = mtk_r32(eth, MTK_WDMA_CRX_PTR(i));
+			if (cur_crx != wdma_rx->pre_crx[i])
+				crx_unchanged = false;
+
+			cur_drx = mtk_r32(eth, MTK_WDMA_DRX_PTR(i));
+			if (cur_drx != wdma_rx->pre_drx[i])
+				drx_unchanged = false;
+
+			msleep(50);
+		}
 		rx_cnt = (cur_drx > cur_crx) ? (cur_drx - 1 - cur_crx) :
 					       (cur_drx - 1 - cur_crx + max_cnt);
 		cur_opq = MTK_FE_WDMA_OQ(i);
 		fsm_fs = mtk_r32(eth, MTK_FE_CDM_FSM(i)) &
 			(MTK_CDM_FS_FSM_MASK | MTK_CDM_FS_PARSER_FSM_MASK);
 		/* drx and crx remain unchanged && rx_cnt is not zero */
-		if ((cur_drx == pre_drx[i]) && (cur_crx == pre_crx[i]) && (rx_cnt > 0))
+		if (drx_unchanged && crx_unchanged && (rx_cnt > 0))
 			connsys_busy = true;
 		/* drx and crx remain unchanged && pse_opq is not empty */
-		else if ((cur_drx == pre_drx[i]) && (cur_crx == pre_crx[i]) &&
-			 (cur_opq != 0 && cur_opq == pre_opq[i]) && fsm_fs)
+		else if (drx_unchanged && crx_unchanged &&
+			 (cur_opq != 0 && cur_opq == wdma_rx->pre_opq[i]) && fsm_fs)
 			netsys_busy = true;
 		if (connsys_busy || netsys_busy) {
-			err_cnt[i]++;
-			if (err_cnt[i] >= 3) {
+			wdma_rx->hang_count[i]++;
+			if (wdma_rx->hang_count[i] >= 5) {
 				pr_info("WDMA %d Rx Info (%s)\n", i,
 					connsys_busy ? "CONNSYS busy" : "NETSYS busy");
-				pr_info("err_cnt = %d", err_cnt[i]);
+				pr_info("hang count = %d", wdma_rx->hang_count[i]);
 				pr_info("prev_drx = 0x%x	| cur_drx = 0x%x\n",
-					pre_drx[i], cur_drx);
+					wdma_rx->pre_drx[i], cur_drx);
 				pr_info("prev_crx = 0x%x	| cur_crx = 0x%x\n",
-					pre_crx[i], cur_crx);
+					wdma_rx->pre_crx[i], cur_crx);
 				pr_info("rx cnt = %d\n", rx_cnt);
 				pr_info("WDMA_CRX_PTR = 0x%x\n",
 					mtk_r32(eth, MTK_WDMA_CRX_PTR(i)));
@@ -413,10 +431,11 @@
 				err_flag = 1;
 			}
 		} else
-			err_cnt[i] = 0;
-		pre_crx[i] = cur_crx;
-		pre_drx[i] = cur_drx;
-		pre_opq[i] = cur_opq;
+			wdma_rx->hang_count[i] = 0;
+
+		wdma_rx->pre_crx[i] = cur_crx;
+		wdma_rx->pre_drx[i] = cur_drx;
+		wdma_rx->pre_opq[i] = cur_opq;
 	}
 
 	if (err_flag)
@@ -425,42 +444,26 @@
 		return 0;
 }
 
-u32 mtk_monitor_rx_fc(struct mtk_eth *eth)
-{
-	struct mtk_hw_stats *hw_stats;
-	static u32 gdm_rx_fc[MTK_MAX_DEVS];
-	u32 i = 0, is_rx_fc = 0;
-
-	for (i = 0; i < MTK_MAC_COUNT; i++) {
-		if (!eth->mac[i] || !netif_running(eth->netdev[i]))
-			continue;
-
-		hw_stats = eth->mac[i]->hw_stats;
-		if (hw_stats->rx_flow_control_packets - gdm_rx_fc[i])
-			is_rx_fc = 1;
-
-		gdm_rx_fc[i] = hw_stats->rx_flow_control_packets;
-	}
-	return is_rx_fc;
-}
-
 u32 mtk_monitor_qdma_tx(struct mtk_eth *eth)
 {
-	static u32 err_cnt_qtx;
+	struct qdma_tx_monitor *qdma_tx = &eth->reset.qdma_monitor.tx;
+	bool pse_fc, qfsm_hang, qfwd_hang;
 	u32 err_flag = 0;
-	u32 is_rx_fc = 0;
 
-	u32 is_qfsm_hang = (mtk_r32(eth, MTK_QDMA_FSM) & 0xF00) != 0;
-	u32 is_qfwd_hang = mtk_r32(eth, MTK_QDMA_FWD_CNT) == 0;
+	pse_fc = !!(mtk_r32(eth, MTK_FE_INT_STATUS) & BIT(PSE_QDMA_TX_PORT));
+	if (pse_fc)
+		mtk_w32(eth, BIT(PSE_QDMA_TX_PORT), MTK_FE_INT_STATUS);
 
-	is_rx_fc = mtk_monitor_rx_fc(eth);
-	if (is_qfsm_hang && is_qfwd_hang && !is_rx_fc) {
-		err_cnt_qtx++;
-		if (err_cnt_qtx >= 3) {
+	qfsm_hang = !!(mtk_r32(eth, MTK_QDMA_FSM) & 0xF00);
+	qfwd_hang = !mtk_r32(eth, MTK_QDMA_FWD_CNT);
+
+	if (!pse_fc && qfsm_hang && qfwd_hang) {
+		qdma_tx->hang_count++;
+		if (qdma_tx->hang_count >= 5) {
 			pr_info("QDMA Tx Info\n");
-			pr_info("err_cnt = %d", err_cnt_qtx);
-			pr_info("is_qfsm_hang = %d\n", is_qfsm_hang);
-			pr_info("is_qfwd_hang = %d\n", is_qfwd_hang);
+			pr_info("hang count = %d\n", qdma_tx->hang_count);
+			pr_info("qfsm_hang = %d\n", qfsm_hang);
+			pr_info("qfwd_hang = %d\n", qfwd_hang);
 			pr_info("-- -- -- -- -- -- --\n");
 			pr_info("MTK_QDMA_FSM = 0x%x\n",
 				mtk_r32(eth, MTK_QDMA_FSM));
@@ -472,7 +475,7 @@
 			err_flag = 1;
 		}
 	} else
-		err_cnt_qtx = 0;
+		qdma_tx->hang_count = 0;
 
 	if (err_flag)
 		return MTK_FE_STOP_TRAFFIC;
@@ -482,21 +485,22 @@
 
 u32 mtk_monitor_qdma_rx(struct mtk_eth *eth)
 {
-	static u32 err_cnt_qrx;
-	static u32 pre_fq_head, pre_fq_tail;
+	struct qdma_rx_monitor *qdma_rx = &eth->reset.qdma_monitor.rx;
+	u32 cur_fq_head, cur_fq_tail;
 	u32 err_flag = 0;
+	bool qrx_fsm;
 
-	u32 qrx_fsm = (mtk_r32(eth, MTK_QDMA_FSM) & 0x1F) == 9;
-	u32 fq_head = mtk_r32(eth, MTK_QDMA_FQ_HEAD);
-	u32 fq_tail = mtk_r32(eth, MTK_QDMA_FQ_TAIL);
+	qrx_fsm = (mtk_r32(eth, MTK_QDMA_FSM) & 0x1F) == 0x9;
+	cur_fq_head = mtk_r32(eth, MTK_QDMA_FQ_HEAD);
+	cur_fq_tail = mtk_r32(eth, MTK_QDMA_FQ_TAIL);
 
-	if (qrx_fsm && fq_head == pre_fq_head &&
-			fq_tail == pre_fq_tail) {
-		err_cnt_qrx++;
-		if (err_cnt_qrx >= 3) {
+	if (qrx_fsm && (cur_fq_head == qdma_rx->pre_fq_head) &&
+		       (cur_fq_tail == qdma_rx->pre_fq_tail)) {
+		qdma_rx->hang_count++;
+		if (qdma_rx->hang_count >= 5) {
 			pr_info("QDMA Rx Info\n");
-			pr_info("err_cnt = %d", err_cnt_qrx);
-			pr_info("MTK_QDMA_FSM = %d\n",
+			pr_info("hang count = %d\n", qdma_rx->hang_count);
+			pr_info("MTK_QDMA_FSM = 0x%x\n",
 				mtk_r32(eth, MTK_QDMA_FSM));
 			pr_info("FQ_HEAD = 0x%x\n",
 				mtk_r32(eth, MTK_QDMA_FQ_HEAD));
@@ -504,10 +508,11 @@
 				mtk_r32(eth, MTK_QDMA_FQ_TAIL));
 			err_flag = 1;
 		} else
-			err_cnt_qrx = 0;
+			qdma_rx->hang_count = 0;
 	}
-	pre_fq_head = fq_head;
-	pre_fq_tail = fq_tail;
+
+	qdma_rx->pre_fq_head = cur_fq_head;
+	qdma_rx->pre_fq_tail = cur_fq_tail;
 
 	if (err_flag)
 		return MTK_FE_STOP_TRAFFIC;
@@ -518,22 +523,31 @@
 
 u32 mtk_monitor_adma_rx(struct mtk_eth *eth)
 {
-	static u32 err_cnt_arx, pre_drx, pre_opq;
-	u32 err_flag = 0;
+	struct adma_rx_monitor *adma_rx = &eth->reset.adma_monitor.rx;
+	u32 i, cur_opq, cur_fsm, cur_drx[4], err_flag = 0;
+	bool drx_hang = true;
 
-	u32 opq0 = (mtk_r32(eth, MTK_PSE_OQ_STA(0)) & 0xFFF);
-	u32 fsm_fs = (mtk_r32(eth, MTK_FE_CDM1_FSM) & 0x0F0F0000) != 0;
-	u32 cur_crx = mtk_r32(eth, MTK_ADMA_CRX_PTR);
-	u32 cur_drx = mtk_r32(eth, MTK_ADMA_DRX_PTR);
+	cur_opq = (mtk_r32(eth, MTK_PSE_OQ_STA(0)) & 0xFFF);
+	cur_fsm = (mtk_r32(eth, MTK_FE_CDM1_FSM) & 0x0F0F0000) != 0;
+	cur_drx[0] = mtk_r32(eth, MTK_ADMA_DRX_PTR);
+	if (cur_drx[0] != adma_rx->pre_drx[0])
+		drx_hang = false;
 
-	/*drx don't move && ring not full && output queue && fs_fsm*/
-	if ((cur_drx == pre_drx) && (cur_crx != cur_drx) &&
-	    (opq0 != 0 && opq0 == pre_opq) && fsm_fs) {
-		err_cnt_arx++;
-		if (err_cnt_arx >= 3) {
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) {
+		for (i = 1; i < eth->soc->rss_num; i++) {
+			cur_drx[i] = mtk_r32(eth, MTK_PRX_DRX_IDX_CFG(i));
+			if (cur_drx[i] != adma_rx->pre_drx[i])
+				drx_hang = false;
+		}
+	}
+
+	/* drx remain unchanged && output queue is not zero && fs_fsm busy */
+	if (drx_hang && (cur_opq != 0 && cur_opq == adma_rx->pre_opq) && cur_fsm) {
+		adma_rx->hang_count++;
+		if (adma_rx->hang_count >= 5) {
 			pr_info("ADMA Rx Info\n");
-			pr_info("err_cnt = %d", err_cnt_arx);
-			pr_info("CDM1_FSM = %d\n",
+			pr_info("hang count = %d\n", adma_rx->hang_count);
+			pr_info("CDM1_FSM = 0x%x\n",
 				mtk_r32(eth, MTK_FE_CDM1_FSM));
 			pr_info("MTK_PSE_OQ_STA1 = 0x%x\n",
 				mtk_r32(eth, MTK_PSE_OQ_STA(0)));
@@ -545,14 +559,27 @@
 				mtk_r32(eth, MTK_ADMA_CRX_PTR));
 			pr_info("MTK_ADMA_DRX_PTR = 0x%x\n",
 				mtk_r32(eth, MTK_ADMA_DRX_PTR));
+			if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) {
+				for (i = 1; i < eth->soc->rss_num; i++) {
+					pr_info("MTK_ADMA_CRX_PTR%d = 0x%x\n", i,
+						mtk_r32(eth, MTK_PRX_CRX_IDX_CFG(i)));
+					pr_info("MTK_ADMA_DRX_PTR%d = 0x%x\n", i,
+						mtk_r32(eth, MTK_PRX_DRX_IDX_CFG(i)));
+				}
+			}
 			pr_info("==============================\n");
 			err_flag = 1;
 		}
 	} else
-		err_cnt_arx = 0;
+		adma_rx->hang_count = 0;
+
+	adma_rx->pre_opq = cur_opq;
+	adma_rx->pre_drx[0] = cur_drx[0];
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) {
+		for (i = 1; i < eth->soc->rss_num; i++)
+			adma_rx->pre_drx[i] = cur_drx[i];
+	}
 
-	pre_drx = cur_drx;
-	pre_opq = opq0;
 	if (err_flag)
 		return MTK_FE_STOP_TRAFFIC;
 	else
@@ -561,40 +588,35 @@
 
 u32 mtk_monitor_tdma_tx(struct mtk_eth *eth)
 {
-	static u32 err_cnt_ttx;
-	static u32 pre_ipq10;
-	static u32 pre_fsm;
-	u32 err_flag = 0;
-	u32 cur_fsm = 0;
-	u32 tx_busy = 0;
-	u32 ipq10;
+	struct tdma_tx_monitor *tdma_tx = &eth->reset.tdma_monitor.tx;
+	u32 cur_ipq10, cur_fsm, tx_busy, err_flag = 0;
 
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
-		ipq10 = mtk_r32(eth, MTK_PSE_IQ_STA(6)) & 0xFFF;
+		cur_ipq10 = mtk_r32(eth, MTK_PSE_IQ_STA(6)) & 0xFFF;
 		cur_fsm = (mtk_r32(eth, MTK_FE_CDM6_FSM) & 0x1FFF) != 0;
 		tx_busy = ((mtk_r32(eth, MTK_TDMA_GLO_CFG) & 0x2) != 0);
 
-		if (ipq10 && cur_fsm && tx_busy
-		    && cur_fsm == pre_fsm
-		    && ipq10 == pre_ipq10) {
-			err_cnt_ttx++;
-			if (err_cnt_ttx >= 3) {
+		if (cur_ipq10 && cur_fsm && tx_busy &&
+		    (cur_fsm == tdma_tx->pre_fsm) &&
+		    (cur_ipq10 == tdma_tx->pre_ipq10)) {
+			tdma_tx->hang_count++;
+			if (tdma_tx->hang_count >= 5) {
 				pr_info("TDMA Tx Info\n");
-				pr_info("err_cnt = %d", err_cnt_ttx);
+				pr_info("hang count = %d\n", tdma_tx->hang_count);
 				pr_info("CDM6_FSM = 0x%x, PRE_CDM6_FSM = 0x%x\n",
-					mtk_r32(eth, MTK_FE_CDM6_FSM), pre_fsm);
+					mtk_r32(eth, MTK_FE_CDM6_FSM), tdma_tx->pre_fsm);
 				pr_info("PSE_IQ_P10 = 0x%x, PRE_PSE_IQ_P10 = 0x%x\n",
-					mtk_r32(eth, MTK_PSE_IQ_STA(6)), pre_ipq10);
+					mtk_r32(eth, MTK_PSE_IQ_STA(6)), tdma_tx->pre_ipq10);
 				pr_info("DMA CFG = 0x%x\n",
 					mtk_r32(eth, MTK_TDMA_GLO_CFG));
 				pr_info("==============================\n");
 				err_flag = 1;
 			}
 		} else
-			err_cnt_ttx = 0;
+			tdma_tx->hang_count = 0;
 
-		pre_fsm = cur_fsm;
-		pre_ipq10 = ipq10;
+		tdma_tx->pre_fsm = cur_fsm;
+		tdma_tx->pre_ipq10 = cur_ipq10;
 	}
 
 	if (err_flag)
@@ -605,21 +627,18 @@
 
 u32 mtk_monitor_tdma_rx(struct mtk_eth *eth)
 {
-	static u32 err_cnt_trx;
-	static u32 pre_fsm;
-	u32 err_flag = 0;
-	u32 cur_fsm = 0;
-	u32 rx_busy = 0;
+	struct tdma_rx_monitor *tdma_rx = &eth->reset.tdma_monitor.rx;
+	u32 cur_fsm, rx_busy, err_flag = 0;
 
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
 		cur_fsm = (mtk_r32(eth, MTK_FE_CDM6_FSM) & 0xFFF0000) != 0;
 		rx_busy = ((mtk_r32(eth, MTK_TDMA_GLO_CFG) & 0x8) != 0);
 
-		if (cur_fsm == pre_fsm && cur_fsm != 0 && rx_busy) {
-			err_cnt_trx++;
-			if (err_cnt_trx >= 3) {
+		if ((cur_fsm == tdma_rx->pre_fsm) && (cur_fsm != 0) && rx_busy) {
+			tdma_rx->hang_count++;
+			if (tdma_rx->hang_count >= 5) {
 				pr_info("TDMA Rx Info\n");
-				pr_info("err_cnt = %d", err_cnt_trx);
+				pr_info("hang count = %d", tdma_rx->hang_count);
 				pr_info("CDM6_FSM = %d\n",
 					mtk_r32(eth, MTK_FE_CDM6_FSM));
 				pr_info("DMA CFG = 0x%x\n",
@@ -628,9 +647,9 @@
 				err_flag = 1;
 			}
 		} else
-			err_cnt_trx = 0;
+			tdma_rx->hang_count = 0;
 
-		pre_fsm = cur_fsm;
+		tdma_rx->pre_fsm = cur_fsm;
 	}
 
 	if (err_flag)
@@ -641,69 +660,56 @@
 
 u32 mtk_monitor_gdm_rx(struct mtk_eth *eth)
 {
-	static u32 gmac_cnt[MTK_MAX_DEVS];
-	static u32 gdm_cnt[MTK_MAX_DEVS];
-	static u32 pre_fsm[MTK_MAX_DEVS];
-	static u32 pre_ipq[MTK_MAX_DEVS];
-	static u32 gmac_rxcnt[MTK_MAX_DEVS];
-	u32 is_gmac_rx[MTK_MAX_DEVS];
-	u32 cur_fsm, pse_ipq, err_flag = 0, i;
+	struct gdm_rx_monitor *gdm_rx = &eth->reset.gdm_monitor.rx;
+	struct mtk_hw_stats *hw_stats;
+	u32 i, cur_rx_cnt, cur_fsm_gdm, cur_ipq_gdm, err_flag = 0;
+	bool gmac_rx;
 
 	for (i = 0; i < MTK_MAX_DEVS; i++) {
 		if (!eth->mac[i] || !netif_running(eth->netdev[i]))
 			continue;
 
-		struct mtk_hw_stats *hw_stats = eth->mac[i]->hw_stats;
-
-		is_gmac_rx[i] = (mtk_r32(eth, MTK_MAC_FSM(i)) & 0xFF0000) != 0x10000;
-		if (is_gmac_rx[i] && gmac_rxcnt[i] == hw_stats->rx_packets)
-			gmac_cnt[i]++;
-		if (gmac_cnt[i] > 4) {
-			pr_info("GMAC%d Rx Info\n", i+1);
-			pr_info("err_cnt = %d", gmac_cnt[i]);
-			pr_info("GMAC_FSM = 0x%x\n",
-				mtk_r32(eth, MTK_MAC_FSM(i)));
-			err_flag = 1;
-		} else
-			gmac_cnt[i] = 0;
-
-		gmac_rxcnt[i] = hw_stats->rx_packets;
-	}
-
-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
-		for (i = 0; i < MTK_MAX_DEVS; i++) {
-			if (!eth->mac[i] || !netif_running(eth->netdev[i]))
-				continue;
-
-			if (i == 0) {
-				pse_ipq = (mtk_r32(eth, MTK_PSE_IQ_STA(0)) >> 16) & 0xFFF;
-				cur_fsm = mtk_r32(eth, MTK_FE_GDM1_FSM) & 0xFF;
-			} else if (i == 1) {
-				pse_ipq = mtk_r32(eth, MTK_PSE_IQ_STA(1)) & 0xFFF;
-				cur_fsm = mtk_r32(eth, MTK_FE_GDM2_FSM) & 0xFF;
-			} else {
-				pse_ipq = (mtk_r32(eth, MTK_PSE_IQ_STA(7)) >> 16) & 0xFFF;
-				cur_fsm = mtk_r32(eth, MTK_FE_GDM3_FSM) & 0xFF;
-			}
+		hw_stats = eth->mac[i]->hw_stats;
+		cur_rx_cnt = hw_stats->rx_packets;
 
-			if (((cur_fsm == pre_fsm[i] && cur_fsm == 0x23) ||
-				(cur_fsm == pre_fsm[i] && cur_fsm == 0x24)) &&
-				(pse_ipq == pre_ipq[i] && pse_ipq != 0x00)) {
-				gdm_cnt[i]++;
-				if (gdm_cnt[i] >= 3) {
+		if (eth->mac[i]->type == MTK_GDM_TYPE) {
+			gmac_rx = (mtk_r32(eth, MTK_MAC_FSM(i)) & 0xFF0000) != 0x10000;
+			if (gmac_rx && (cur_rx_cnt == gdm_rx->pre_rx_cnt[i]))
+				gdm_rx->hang_count_gmac[i]++;
+			if (gdm_rx->hang_count_gmac[i] >= 5) {
+				pr_info("GMAC%d Rx Info\n", i + 1);
+				pr_info("hang count = %d\n",
+					gdm_rx->hang_count_gmac[i]);
+				pr_info("GMAC_FSM = 0x%x\n",
+					mtk_r32(eth, MTK_MAC_FSM(i)));
+				err_flag = 1;
+			} else
+				gdm_rx->hang_count_gmac[i] = 0;
+		} else if (eth->mac[i]->type == MTK_XGDM_TYPE && eth->mac[i]->id != MTK_GMAC1_ID) {
+			cur_ipq_gdm = MTK_FE_GDM_IQ(i);
+			cur_fsm_gdm = mtk_r32(eth, MTK_FE_GDM_FSM(i)) & 0xFF;
+			if (((cur_fsm_gdm == gdm_rx->pre_fsm_gdm[i] && cur_fsm_gdm == 0x23) ||
+			     (cur_fsm_gdm == gdm_rx->pre_fsm_gdm[i] && cur_fsm_gdm == 0x24)) &&
+			    (cur_ipq_gdm == gdm_rx->pre_ipq_gdm[i] && cur_ipq_gdm != 0x00) &&
+			    (cur_rx_cnt == gdm_rx->pre_rx_cnt[i])) {
+				gdm_rx->hang_count_gdm[i]++;
+				if (gdm_rx->hang_count_gdm[i] >= 5) {
 					pr_info("GDM%d Rx Info\n", i + 1);
-					pr_info("err_cnt = %d", gdm_cnt[i]);
-					pr_info("GDM%d_FSM = %x\n", i + 1,
+					pr_info("hang count = %d\n",
+						gdm_rx->hang_count_gdm[i]);
+					pr_info("GDM_FSM = 0x%x\n",
 						mtk_r32(eth, MTK_FE_GDM_FSM(i)));
 					pr_info("==============================\n");
 					err_flag = 1;
 				}
 			} else
-				gdm_cnt[i] = 0;
+				gdm_rx->hang_count_gdm[i] = 0;
 
-			pre_fsm[i] = cur_fsm;
-			pre_ipq[i] = pse_ipq;
+			gdm_rx->pre_fsm_gdm[i] = cur_fsm_gdm;
+			gdm_rx->pre_ipq_gdm[i] = cur_ipq_gdm;
 		}
+
+		gdm_rx->pre_rx_cnt[i] = cur_rx_cnt;
 	}
 
 	if (err_flag)
@@ -714,62 +720,58 @@
 
 u32 mtk_monitor_gdm_tx(struct mtk_eth *eth)
 {
-	static u32 err_cnt[MTK_MAX_DEVS];
-	static u32 pre_gdm[MTK_MAX_DEVS];
-	static u32 pre_opq[MTK_MAX_DEVS];
-	static u32 gdm_err_cnt[MTK_MAX_DEVS];
-	static u32 gmac_txcnt[MTK_MAX_DEVS];
-	u32 is_gmac_tx[MTK_MAX_DEVS];
-	u32 gdm_fsm = 0;
-	u32 err_flag = 0, i, pse_opq;
+	struct gdm_tx_monitor *gdm_tx = &eth->reset.gdm_monitor.tx;
+	struct mtk_hw_stats *hw_stats;
+	u32 i, cur_tx_cnt, cur_fsm_gdm, cur_opq_gdm, err_flag = 0;
+	bool gmac_tx;
 
 	for (i = 0; i < MTK_MAX_DEVS; i++) {
-		if (!eth->mac[i] || !netif_running(eth->netdev[i]))
+		if (!eth->mac[i] || !netif_running(eth->netdev[i]) ||
+		    eth->mac[i]->type != MTK_GDM_TYPE)
 			continue;
 
-		struct mtk_hw_stats *hw_stats = eth->mac[i]->hw_stats;
-
-		if (i == 0)
-			pse_opq = (mtk_r32(eth, MTK_PSE_OQ_STA(0)) >> 16) & 0xFFF;
-		else if (i == 1)
-			pse_opq = mtk_r32(eth, MTK_PSE_OQ_STA(1)) & 0xFFF;
-		else
-			pse_opq = (mtk_r32(eth, MTK_PSE_OQ_STA(7)) >> 16) & 0xFFF;
+		hw_stats = eth->mac[i]->hw_stats;
+		cur_tx_cnt = hw_stats->tx_packets;
 
-		is_gmac_tx[i] = (mtk_r32(eth, MTK_MAC_FSM(i)) & 0xFF000000) != 0x1000000;
-		if (is_gmac_tx[i] && gmac_txcnt[i] == hw_stats->tx_packets && pse_opq > 0)
-			err_cnt[i]++;
-		if (err_cnt[i] > 4) {
-			pr_info("GMAC%d Tx Info\n", i+1);
-			pr_info("err_cnt = %d", err_cnt[i]);
+		cur_opq_gdm = MTK_FE_GDM_OQ(i);
+		gmac_tx = (mtk_r32(eth, MTK_MAC_FSM(i)) & 0xFF000000) != 0x1000000;
+		if (gmac_tx && (cur_tx_cnt == gdm_tx->pre_tx_cnt[i]) &&
+		    (cur_opq_gdm > 0))
+			gdm_tx->hang_count_gmac[i]++;
+		if (gdm_tx->hang_count_gmac[i] >= 5) {
+			pr_info("GMAC%d Tx Info\n", i + 1);
+			pr_info("hang count = %d\n",
+				gdm_tx->hang_count_gmac[i]);
 			pr_info("GMAC_FSM = 0x%x\n",
 				mtk_r32(eth, MTK_MAC_FSM(i)));
 			err_flag = 1;
 		} else
-			err_cnt[i] = 0;
+			gdm_tx->hang_count_gmac[i] = 0;
 
-		gmac_txcnt[i] = hw_stats->tx_packets;
+		gdm_tx->pre_tx_cnt[i] = cur_tx_cnt;
 	}
 
 	for (i = 0; i < MTK_MAX_DEVS; i++) {
 		if (!eth->mac[i] || !netif_running(eth->netdev[i]))
 			continue;
 
-		gdm_fsm = mtk_r32(eth, MTK_FE_GDM_FSM(i)) & 0x1FFF0000;
-		pse_opq = MTK_FE_GDM_OQ(i);
-		if ((pre_gdm[i] == gdm_fsm) && (gdm_fsm == 0x10330000) &&
-		    (pre_opq[i] == pse_opq) && (pse_opq > 0))
-			gdm_err_cnt[i]++;
-		if (gdm_err_cnt[i] > 4) {
-			pr_info("GDM%d Tx Info\n", i+1);
-			pr_info("err_cnt = %d", err_cnt[i]);
+		cur_fsm_gdm = mtk_r32(eth, MTK_FE_GDM_FSM(i)) & 0x1FFF0000;
+		cur_opq_gdm = MTK_FE_GDM_OQ(i);
+		if ((cur_fsm_gdm == gdm_tx->pre_fsm_gdm[i]) && (cur_fsm_gdm == 0x10330000) &&
+		    (cur_opq_gdm == gdm_tx->pre_opq_gdm[i]) && (cur_opq_gdm > 0))
+			gdm_tx->hang_count_gdm[i]++;
+		if (gdm_tx->hang_count_gdm[i] >= 5) {
+			pr_info("GDM%d Tx Info\n", i + 1);
+			pr_info("hang count = %d\n",
+				gdm_tx->hang_count_gdm[i]);
 			pr_info("GDM_FSM = 0x%x\n",
 				mtk_r32(eth, MTK_FE_GDM_FSM(i)));
 			err_flag = 1;
 		} else
-			gdm_err_cnt[i] = 0;
-		pre_gdm[i] = gdm_fsm;
-		pre_opq[i] = pse_opq;
+			gdm_tx->hang_count_gdm[i] = 0;
+
+		gdm_tx->pre_fsm_gdm[i] = cur_fsm_gdm;
+		gdm_tx->pre_opq_gdm[i] = cur_opq_gdm;
 	}
 
 	if (err_flag)
@@ -790,25 +792,22 @@
 	[8] = mtk_monitor_gdm_rx,
 };
 
-void mtk_dma_monitor(struct timer_list *t)
+void mtk_hw_reset_monitor(struct mtk_eth *eth)
 {
-	struct mtk_eth *eth = from_timer(eth, t, mtk_dma_monitor_timer);
 	u32 i = 0, ret = 0;
 
 	for (i = 0; i < ARRAY_SIZE(mtk_reset_monitor_func); i++) {
 		ret = (*mtk_reset_monitor_func[i]) (eth);
 		if ((ret == MTK_FE_START_RESET) ||
-			(ret == MTK_FE_STOP_TRAFFIC)) {
+		    (ret == MTK_FE_STOP_TRAFFIC)) {
 			if ((atomic_read(&reset_lock) == 0) &&
-				(atomic_read(&force) == 1)) {
-				mtk_reset_flag = ret;
+			    (atomic_read(&force) == 1)) {
+				eth->reset.event = ret;
 				schedule_work(&eth->pending_work);
 			}
 			break;
 		}
 	}
-
-	mod_timer(&eth->mtk_dma_monitor_timer, jiffies + 1 * HZ);
 }
 
 void mtk_save_qdma_cfg(struct mtk_eth *eth)
@@ -989,11 +988,14 @@
 	struct mtk_eth *eth = container_of(n, struct mtk_eth, netdevice_notifier);
 
 	switch (event) {
+	case MTK_TOPS_DUMP_DONE:
+		complete(&wait_tops_done);
+		break;
 	case MTK_WIFI_RESET_DONE:
 	case MTK_FE_STOP_TRAFFIC_DONE:
 		pr_info("%s rcv done event:%lx\n", __func__, event);
 		mtk_rest_cnt--;
-		if(!mtk_rest_cnt) {
+		if (!mtk_rest_cnt) {
 			complete(&wait_ser_done);
 			mtk_rest_cnt = mtk_wifi_num;
 		}
@@ -1008,7 +1010,7 @@
 		break;
 	case MTK_FE_STOP_TRAFFIC_DONE_FAIL:
 		mtk_stop_fail = true;
-		mtk_reset_flag = MTK_FE_START_RESET;
+		eth->reset.event = MTK_FE_START_RESET;
 		pr_info("%s rcv done event:%lx\n", __func__, event);
 		complete(&wait_ser_done);
 		mtk_rest_cnt = mtk_wifi_num;
@@ -1017,7 +1019,7 @@
 		pr_info("%s rcv fe start reset init event:%lx\n", __func__, event);
 		if ((atomic_read(&reset_lock) == 0) &&
 		    (atomic_read(&force) == 1)) {
-			mtk_reset_flag = MTK_FE_START_RESET;
+			eth->reset.event = MTK_FE_START_RESET;
 			schedule_work(&eth->pending_work);
 		}
 	default:
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h
index facb645..e26c57c 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h
@@ -13,6 +13,7 @@
 #define MTK_WIFI_RESET_DONE	0x2002
 #define MTK_WIFI_CHIP_ONLINE 	0x2003
 #define MTK_WIFI_CHIP_OFFLINE 	0x2004
+#define MTK_TOPS_DUMP_DONE	0x3001
 #define MTK_FE_RESET_NAT_DONE	0x4001
 
 #define MTK_FE_STOP_TRAFFIC	(0x2005)
@@ -43,12 +44,10 @@
 #define MTK_PPE_BUSY		BIT(31)
 
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
-#define MTK_WDMA_CNT	(0x3)
 #define MTK_GDM_RX_BASE	(0x8)
 #define MTK_GDM_CNT_OFFSET	(0x80)
 #define MTK_GDM_TX_BASE	(0x48)
 #else
-#define MTK_WDMA_CNT	(0x2)
 #define MTK_GDM_RX_BASE	(0x8)
 #define MTK_GDM_CNT_OFFSET	(0x40)
 #define MTK_GDM_TX_BASE	(0x38)
@@ -74,10 +73,11 @@
 
 int mtk_eth_netdevice_event(struct notifier_block *n, unsigned long event, void *ptr);
 extern struct completion wait_ser_done;
+extern struct completion wait_tops_done;
 extern char* mtk_reset_event_name[32];
 extern atomic_t reset_lock;
 extern struct completion wait_nat_done;
-extern u32 mtk_reset_flag;
+extern int mtk_wifi_num;
 extern bool mtk_stop_fail;
 
 irqreturn_t mtk_handle_fe_irq(int irq, void *_eth);
@@ -86,7 +86,7 @@
 int mtk_eth_warm_reset(struct mtk_eth *eth);
 void mtk_reset_event_update(struct mtk_eth *eth, u32 id);
 void mtk_dump_netsys_info(void *_eth);
-void mtk_dma_monitor(struct timer_list *t);
+void mtk_hw_reset_monitor(struct mtk_eth *eth);
 void mtk_save_qdma_cfg(struct mtk_eth *eth);
 void mtk_restore_qdma_cfg(struct mtk_eth *eth);
 void mtk_prepare_reset_fe(struct mtk_eth *eth);
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 82f87b5..1b9eabc 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
@@ -39,6 +39,7 @@
 module_param_named(msg_level, mtk_msg_level, int, 0);
 MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
 DECLARE_COMPLETION(wait_ser_done);
+DECLARE_COMPLETION(wait_tops_done);
 
 #define MTK_ETHTOOL_STAT(x) { #x, \
 			      offsetof(struct mtk_hw_stats, x) / sizeof(u64) }
@@ -290,6 +291,13 @@
 	"top_netsys_warp_sel", "top_macsec_sel",
 };
 
+u32 (*mtk_get_tnl_netsys_params)(struct sk_buff *skb) = NULL;
+EXPORT_SYMBOL(mtk_get_tnl_netsys_params);
+struct net_device *(*mtk_get_tnl_dev)(u8 tops_crsn) = NULL;
+EXPORT_SYMBOL(mtk_get_tnl_dev);
+void (*mtk_set_tops_crsn)(struct sk_buff *skb, u8 tops_crsn) = NULL;
+EXPORT_SYMBOL(mtk_set_tops_crsn);
+
 void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
 {
 	__raw_writel(val, eth->base + reg);
@@ -590,6 +598,45 @@
 	return 0;
 }
 
+static void mtk_set_mcr_max_rx(struct mtk_mac *mac, u32 val)
+{
+	struct mtk_eth *eth = mac->hw;
+	u32 mcr_cur, mcr_new;
+
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+		return;
+
+	if (mac->type == MTK_GDM_TYPE) {
+		mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+		mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
+
+		if (val <= 1518)
+			mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518);
+		else if (val <= 1536)
+			mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536);
+		else if (val <= 1552)
+			mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552);
+		else {
+			mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048);
+			mcr_new |= MAC_MCR_MAX_RX_JUMBO;
+		}
+
+		if (mcr_new != mcr_cur)
+			mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
+	} else if (mac->type == MTK_XGDM_TYPE && mac->id != MTK_GMAC1_ID) {
+		mcr_cur = mtk_r32(mac->hw, MTK_XMAC_RX_CFG2(mac->id));
+		mcr_new = mcr_cur & ~MTK_XMAC_MAX_RX_MASK;
+
+		if (val < MTK_MAX_RX_LENGTH_9K)
+			mcr_new |= val;
+		else
+			mcr_new |= MTK_MAX_RX_LENGTH_9K;
+
+		if (mcr_new != mcr_cur)
+			mtk_w32(mac->hw, mcr_new, MTK_XMAC_RX_CFG2(mac->id));
+	}
+}
+
 static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
 					      phy_interface_t interface)
 {
@@ -598,15 +645,15 @@
 	struct mtk_eth *eth = mac->hw;
 	unsigned int sid;
 
-	if (interface == PHY_INTERFACE_MODE_SGMII ||
-	    phy_interface_mode_is_8023z(interface)) {
+	if ((interface == PHY_INTERFACE_MODE_SGMII ||
+	     phy_interface_mode_is_8023z(interface)) && eth->sgmii) {
 		sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
 		       0 : mtk_mac2xgmii_id(eth, mac->id);
 
 		return mtk_sgmii_select_pcs(eth->sgmii, sid);
-	} else if (interface == PHY_INTERFACE_MODE_USXGMII ||
-		   interface == PHY_INTERFACE_MODE_10GKR ||
-		   interface == PHY_INTERFACE_MODE_5GBASER) {
+	} else if ((interface == PHY_INTERFACE_MODE_USXGMII ||
+		    interface == PHY_INTERFACE_MODE_10GKR ||
+		    interface == PHY_INTERFACE_MODE_5GBASER) && eth->usxgmii) {
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
 		    mac->id != MTK_GMAC1_ID) {
 			sid = mtk_mac2xgmii_id(eth, mac->id);
@@ -648,6 +695,7 @@
 	struct mtk_mac *mac = container_of(config, struct mtk_mac,
 					   phylink_config);
 	struct mtk_eth *eth = mac->hw;
+	struct net_device *dev = eth->netdev[mac->id];
 	u32 i;
 	int val = 0, ge_mode, err = 0;
 	unsigned int mac_type = mac->type;
@@ -696,14 +744,6 @@
 					goto init_err;
 			}
 			break;
-		case PHY_INTERFACE_MODE_XGMII:
-			mac->type = MTK_XGDM_TYPE;
-			if (MTK_HAS_CAPS(eth->soc->caps, MTK_XGMII)) {
-				err = mtk_gmac_xgmii_path_setup(eth, mac->id);
-				if (err)
-					goto init_err;
-			}
-			break;
 		case PHY_INTERFACE_MODE_USXGMII:
 		case PHY_INTERFACE_MODE_10GKR:
 		case PHY_INTERFACE_MODE_5GBASER:
@@ -714,6 +754,15 @@
 					goto init_err;
 			}
 			break;
+		case PHY_INTERFACE_MODE_INTERNAL:
+			if (mac->id == MTK_GMAC2_ID &&
+			    MTK_HAS_CAPS(eth->soc->caps, MTK_2P5GPHY)) {
+				mac->type = MTK_XGDM_TYPE;
+				err = mtk_gmac_2p5gphy_path_setup(eth, mac->id);
+				if (err)
+					goto init_err;
+			}
+			break;
 		default:
 			goto err_phy;
 		}
@@ -801,6 +850,7 @@
 	}
 
 	/* Setup gmac */
+	mtk_set_mcr_max_rx(mac, dev->mtu + MTK_RX_ETH_HLEN);
 	if (mac->type == MTK_XGDM_TYPE) {
 		mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id));
 		mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id));
@@ -975,9 +1025,12 @@
 static void mtk_pse_set_mac_port_link(struct mtk_mac *mac, bool up,
 				      phy_interface_t interface)
 {
+	struct mtk_eth *eth = mac->hw;
 	u32 port = 0;
 
-	if (!up && interface == PHY_INTERFACE_MODE_XGMII) {
+	if (!up && mac->id == MTK_GMAC2_ID &&
+	    interface == PHY_INTERFACE_MODE_INTERNAL &&
+	    MTK_HAS_CAPS(eth->soc->caps, MTK_2P5GPHY)) {
 		void __iomem *base;
 
 		base = ioremap(0x0F0CFB00, SZ_4K);
@@ -1017,8 +1070,6 @@
 {
 	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_set_mac_port_link(mac, false, interface);
@@ -1027,8 +1078,6 @@
 		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) {
-		struct mtk_usxgmii_pcs *mpcs;
-
 		mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
 		mcr &= 0xfffffff0;
 		mcr |= XMAC_MCR_TRX_DISABLE;
@@ -1037,9 +1086,6 @@
 		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];
 	}
 }
 
@@ -1134,6 +1180,8 @@
 	u32 mcr, mcr_cur, sts;
 
 	mac->speed = speed;
+	if (phy)
+		mac->phy_speed = phy->speed;
 
 	if (mac->type == MTK_GDM_TYPE) {
 		mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
@@ -1141,8 +1189,9 @@
 		mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 |
 			 MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC |
 			 MAC_MCR_FORCE_RX_FC | MAC_MCR_PRMBL_LMT_EN);
-		mcr |= MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
-		       MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK;
+		mcr |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
+		       MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN |
+		       MAC_MCR_FORCE_LINK;
 
 		/* Configure speed */
 		switch (speed) {
@@ -1224,8 +1273,8 @@
 	    !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII) &&
 	      (state->interface == PHY_INTERFACE_MODE_SGMII ||
 	       phy_interface_mode_is_8023z(state->interface))) &&
-	    !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_XGMII) &&
-	      (state->interface == PHY_INTERFACE_MODE_XGMII)) &&
+	    !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) &&
+	      (state->interface == PHY_INTERFACE_MODE_INTERNAL)) &&
 	    !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII) &&
 	      (state->interface == PHY_INTERFACE_MODE_USXGMII)) &&
 	    !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII) &&
@@ -1258,7 +1307,7 @@
 	case PHY_INTERFACE_MODE_TRGMII:
 		phylink_set(mask, 1000baseT_Full);
 		break;
-	case PHY_INTERFACE_MODE_XGMII:
+	case PHY_INTERFACE_MODE_INTERNAL:
 		/* fall through */
 	case PHY_INTERFACE_MODE_1000BASEX:
 		phylink_set(mask, 1000baseX_Full);
@@ -1334,11 +1383,6 @@
 
 	linkmode_and(supported, supported, mask);
 	linkmode_and(state->advertising, state->advertising, mask);
-
-	/* We can only operate at 2500BaseX or 1000BaseX. If requested
-	 * to advertise both, only report advertising at 2500BaseX.
-	 */
-	phylink_helper_basex_speed(state);
 }
 
 static const struct phylink_mac_ops mtk_phylink_ops = {
@@ -1918,8 +1962,9 @@
 		data |= 0x4 << TX_DMA_FPORT_SHIFT;
 	}
 
-	trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
-		     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
+	if (eth_debug_level >= 7)
+		trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
+			     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
 #endif
 	WRITE_ONCE(desc->txd4, data);
 }
@@ -1948,8 +1993,9 @@
 		data |= 0x4 << TX_DMA_FPORT_SHIFT_V2;
 	}
 
-	trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
-		     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
+	if (eth_debug_level >= 7)
+		trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
+			     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
 #endif
 	WRITE_ONCE(desc->txd4, data);
 
@@ -1979,6 +2025,10 @@
 	struct mtk_eth *eth = mac->hw;
 	struct mtk_tx_dma_v2 *desc = txd;
 	u32 data = 0;
+	u32 params;
+	u8 tops_entry  = 0;
+	u8 tport = 0;
+	u8 cdrt = 0;
 
 	WRITE_ONCE(desc->txd1, info->addr);
 
@@ -2000,8 +2050,40 @@
 		data |= 0x4 << TX_DMA_FPORT_SHIFT_V2;
 	}
 
+	if (eth_debug_level >= 7)
+		trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
+			     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
+
+#endif
+
+#if IS_ENABLED(CONFIG_MEDIATEK_NETSYS_V3)
+	if (mtk_get_tnl_netsys_params && skb && !(skb->inner_protocol == IPPROTO_ESP)) {
+		params = mtk_get_tnl_netsys_params(skb);
+		tops_entry = params & 0x000000FF;
+		tport = (params & 0x0000FF00) >> 8;
+		cdrt = (params & 0x00FF0000) >> 16;
+	}
+
+	/* forward to eip197 if this packet is going to encrypt */
+#if IS_ENABLED(CONFIG_NET_MEDIATEK_HNAT) || IS_ENABLED(CONFIG_NET_MEDIATEK_HNAT_MODULE)
+	else if (unlikely(skb->inner_protocol == IPPROTO_ESP &&
+		 skb_hnat_cdrt(skb) && is_magic_tag_valid(skb))) {
+		/* carry cdrt index for encryption */
+		cdrt = skb_hnat_cdrt(skb);
+		skb_hnat_magic_tag(skb) = 0;
+#else
+	else if (unlikely(skb->inner_protocol == IPPROTO_ESP &&
+		 skb_tnl_cdrt(skb) && is_tnl_tag_valid(skb))) {
+		cdrt = skb_tnl_cdrt(skb);
+		skb_tnl_magic_tag(skb) = 0;
+#endif
+		tport = EIP197_QDMA_TPORT;
+	}
+
-	trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
-		     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
+	if (tport) {
+		data &= ~(TX_DMA_TPORT_MASK << TX_DMA_TPORT_SHIFT);
+		data |= (tport & TX_DMA_TPORT_MASK) << TX_DMA_TPORT_SHIFT;
+	}
 #endif
 	WRITE_ONCE(desc->txd4, data);
 
@@ -2024,7 +2106,20 @@
 	WRITE_ONCE(desc->txd6, data);
 
 	WRITE_ONCE(desc->txd7, 0);
-	WRITE_ONCE(desc->txd8, 0);
+
+	data = 0;
+
+	if (tops_entry) {
+		data &= ~(TX_DMA_TOPS_ENTRY_MASK << TX_DMA_TOPS_ENTRY_SHIFT);
+		data |= (tops_entry & TX_DMA_TOPS_ENTRY_MASK) << TX_DMA_TOPS_ENTRY_SHIFT;
+	}
+
+	if (cdrt) {
+		data &= ~(TX_DMA_CDRT_MASK << TX_DMA_CDRT_SHIFT);
+		data |= (cdrt & TX_DMA_CDRT_MASK) << TX_DMA_CDRT_SHIFT;
+	}
+
+	WRITE_ONCE(desc->txd8, data);
 }
 
 static void mtk_tx_set_pdma_desc(struct sk_buff *skb, struct net_device *dev, void *txd,
@@ -2396,6 +2491,7 @@
 	struct mtk_rx_ring *ring = rx_napi->rx_ring;
 	int idx;
 	struct sk_buff *skb;
+	u8 tops_crsn = 0;
 	u8 *data, *new_data;
 	struct mtk_rx_dma_v2 *rxd, trxd;
 	int done = 0;
@@ -2436,11 +2532,20 @@
 				      0 : RX_DMA_GET_SPORT(trxd.rxd4) - 1;
 		}
 
-		if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
-			     !eth->netdev[mac]))
-			goto release_desc;
+		tops_crsn = RX_DMA_GET_TOPS_CRSN(trxd.rxd6);
+		if (mtk_get_tnl_dev && tops_crsn) {
+			netdev = mtk_get_tnl_dev(tops_crsn);
+			if (IS_ERR(netdev))
+				netdev = NULL;
+		}
+
+		if (!netdev) {
+			if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
+				     !eth->netdev[mac]))
+				goto release_desc;
 
-		netdev = eth->netdev[mac];
+			netdev = eth->netdev[mac];
+		}
 
 		if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
 			goto release_desc;
@@ -2527,18 +2632,27 @@
 
 		skb_hnat_alg(skb) = 0;
 		skb_hnat_filled(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",
-				     __func__, skb_hnat_reason(skb));
+			if (eth_debug_level >= 7)
+				trace_printk("[%s] reason=0x%x(force to CPU) from WAN to Ext\n",
+					     __func__, skb_hnat_reason(skb));
 			skb->pkt_type = PACKET_HOST;
 		}
 
-		trace_printk("[%s] rxd:(entry=%x,sport=%x,reason=%x,alg=%x\n",
-			     __func__, skb_hnat_entry(skb), skb_hnat_sport(skb),
-			     skb_hnat_reason(skb), skb_hnat_alg(skb));
+		if (eth_debug_level >= 7)
+			trace_printk("[%s] rxd:(entry=%x,sport=%x,reason=%x,alg=%x\n",
+				     __func__, skb_hnat_entry(skb), skb_hnat_sport(skb),
+				     skb_hnat_reason(skb), skb_hnat_alg(skb));
 #endif
+		if (mtk_set_tops_crsn && skb && tops_crsn)
+			mtk_set_tops_crsn(skb, tops_crsn);
+
 		if (mtk_hwlro_stats_ebl &&
 		    IS_HW_LRO_RING(ring->ring_no) && eth->hwlro) {
 			hw_lro_stats_update(ring->ring_no, &trxd);
@@ -4230,7 +4344,7 @@
 	phylink_start(mac->phylink);
 	netif_tx_start_all_queues(dev);
 	phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0);
-	if (!phy_node && eth->sgmii->pcs[id].regmap)
+	if (!phy_node && eth->sgmii && eth->sgmii->pcs[id].regmap)
 		regmap_write(eth->sgmii->pcs[id].regmap,
 			     SGMSYS_QPHY_PWR_STATE_CTRL, 0);
 
@@ -4275,7 +4389,7 @@
 	netif_tx_disable(dev);
 
 	phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0);
-	if (!phy_node && eth->sgmii->pcs[id].regmap) {
+	if (!phy_node && eth->sgmii && eth->sgmii->pcs[id].regmap) {
 		regmap_read(eth->sgmii->pcs[id].regmap,
 			    SGMSYS_QPHY_PWR_STATE_CTRL, &val);
 		val |= SGMII_PHYA_PWD;
@@ -4418,6 +4532,23 @@
 	return 0;
 }
 
+static void mtk_hw_reset_monitor_work(struct work_struct *work)
+{
+	struct delayed_work *del_work = to_delayed_work(work);
+	struct mtk_eth *eth = container_of(del_work, struct mtk_eth,
+					   reset.monitor_work);
+
+	if (test_bit(MTK_RESETTING, &eth->state))
+		goto out;
+
+	/* DMA stuck checks */
+	mtk_hw_reset_monitor(eth);
+
+out:
+	schedule_delayed_work(&eth->reset.monitor_work,
+			      MTK_DMA_MONITOR_TIMEOUT);
+}
+
 static int mtk_hw_init(struct mtk_eth *eth, u32 type)
 {
 	u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
@@ -4543,8 +4674,8 @@
 				PSE_DUMMY_WORK_GDM(2) |	PSE_DUMMY_WORK_GDM(3) |
 				DUMMY_PAGE_THR, PSE_DUMY_REQ);
 
-		/* PSE should not drop port1, port8 and port9 packets */
-		mtk_w32(eth, 0x00000302, PSE_NO_DROP_CFG);
+		/* PSE should not drop port8 and port9 packets */
+		mtk_w32(eth, 0x00000300, PSE_NO_DROP_CFG);
 
 		/* PSE should drop p8 and p9 packets when WDMA Rx ring full*/
 		mtk_w32(eth, 0x00000300, PSE_PPE0_DROP);
@@ -4553,7 +4684,6 @@
 		mtk_w32(eth, 0x00600009, PSE_IQ_REV(8));
 
 		/* GDM and CDM Threshold */
-		mtk_w32(eth, 0x00000004, MTK_CDM2_THRES);
 		mtk_w32(eth, 0x08000707, MTK_CDMW0_THRES);
 		mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES);
 
@@ -4662,6 +4792,17 @@
 	mtk_rx_irq_disable(eth, ~0);
 }
 
+static int mtk_change_mtu(struct net_device *dev, int new_mtu)
+{
+	int length = new_mtu + MTK_RX_ETH_HLEN;
+	struct mtk_mac *mac = netdev_priv(dev);
+
+	mtk_set_mcr_max_rx(mac, length);
+	dev->mtu = new_mtu;
+
+	return 0;
+}
+
 static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
 	struct mtk_mac *mac = netdev_priv(dev);
@@ -4718,9 +4859,9 @@
 static void mtk_pending_work(struct work_struct *work)
 {
 	struct mtk_eth *eth = container_of(work, struct mtk_eth, pending_work);
-	int err, i = 0;
 	unsigned long restart = 0;
-	u32 val = 0;
+	u32 val;
+	int i;
 
 	atomic_inc(&reset_lock);
 	val = mtk_r32(eth, MTK_FE_INT_STATUS);
@@ -4756,44 +4897,57 @@
 	for (i = 0; i < MTK_MAC_COUNT; i++) {
 		if (!eth->netdev[i])
 			continue;
-		if (mtk_reset_flag == MTK_FE_STOP_TRAFFIC) {
-			pr_info("send MTK_FE_STOP_TRAFFIC event\n");
+
+		if (eth->reset.event == MTK_FE_STOP_TRAFFIC) {
+			pr_info("send MTK_FE_STOP_TRAFFIC event !\n");
 			call_netdevice_notifiers(MTK_FE_STOP_TRAFFIC,
-				eth->netdev[i]);
+						 eth->netdev[i]);
 		} else {
-			pr_info("send MTK_FE_START_RESET event\n");
+			pr_info("send MTK_FE_START_RESET event !\n");
 			call_netdevice_notifiers(MTK_FE_START_RESET,
-				eth->netdev[i]);
+						 eth->netdev[i]);
 		}
 		rtnl_unlock();
-		if (wait_for_completion_timeout(&wait_ser_done, msecs_to_jiffies(3000))) {
-			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
-			    (mtk_stop_fail)) {
-				pr_info("send MTK_FE_START_RESET stop\n");
-				rtnl_lock();
-				call_netdevice_notifiers(MTK_FE_START_RESET,
-							 eth->netdev[i]);
-				rtnl_unlock();
-				if (!wait_for_completion_timeout(&wait_ser_done,
-								 msecs_to_jiffies(3000)))
-					pr_warn("wait for MTK_FE_START_RESET\n");
-				mtk_stop_fail = 0;
+		if (mtk_wifi_num > 0) {
+			if (wait_for_completion_timeout(&wait_ser_done,
+							msecs_to_jiffies(10000))) {
+				pr_info("received %s event from WiFi !\n",
+					!mtk_stop_fail ? "MTK_FE_STOP_TRAFFIC_DONE" :
+							 "MTK_FE_STOP_TRAFFIC_DONE_FAIL");
+				if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
+				    mtk_stop_fail) {
+					pr_info("send MTK_FE_START_RESET event "
+						"(WiFi stop traffic fail) !\n");
+					rtnl_lock();
+					call_netdevice_notifiers(MTK_FE_START_RESET,
+								 eth->netdev[i]);
+					rtnl_unlock();
+					if (!wait_for_completion_timeout(&wait_ser_done,
+									 msecs_to_jiffies(10000)))
+						pr_warn("wait for WiFi response timeout "
+							"(WiFi stop traffic fail) !\n");
+					mtk_stop_fail = 0;
+				}
+			} else {
+				pr_warn("wait for WiFi response timeout !\n");
 			}
-			pr_warn("wait for MTK_FE_START_RESET\n");
 		}
+		if (!try_wait_for_completion(&wait_tops_done))
+			pr_warn("wait for TOPS response timeout !\n");
 		rtnl_lock();
 		break;
 	}
 
-	del_timer_sync(&eth->mtk_dma_monitor_timer);
 	pr_info("[%s] mtk_stop starts !\n", __func__);
 	/* stop all devices to make sure that dma is properly shut down */
 	for (i = 0; i < MTK_MAC_COUNT; i++) {
 		if (!eth->netdev[i] || !netif_running(eth->netdev[i]))
 			continue;
+
 		mtk_stop(eth->netdev[i]);
 		__set_bit(i, &restart);
 	}
+
 	pr_info("[%s] mtk_stop ends !\n", __func__);
 	mdelay(15);
 
@@ -4809,10 +4963,10 @@
 	for (i = 0; i < MTK_MAC_COUNT; i++) {
 		if (!test_bit(i, &restart) || !eth->netdev[i])
 			continue;
-		err = mtk_open(eth->netdev[i]);
-		if (err) {
+
+		if (mtk_open(eth->netdev[i])) {
 			netif_alert(eth, ifup, eth->netdev[i],
-			      "Driver up/down cycle failed, closing device.\n");
+				    "Driver up/down cycle failed, closing device.\n");
 			dev_close(eth->netdev[i]);
 		}
 	}
@@ -4820,17 +4974,18 @@
 	for (i = 0; i < MTK_MAC_COUNT; i++) {
 		if (!eth->netdev[i])
 			continue;
-		if (mtk_reset_flag == MTK_FE_STOP_TRAFFIC) {
-			pr_info("send MTK_FE_START_TRAFFIC event\n");
+
+		if (eth->reset.event == MTK_FE_STOP_TRAFFIC) {
+			pr_info("send MTK_FE_START_TRAFFIC event !\n");
 			call_netdevice_notifiers(MTK_FE_START_TRAFFIC,
-				eth->netdev[i]);
+						 eth->netdev[i]);
 		} else {
-			pr_info("send MTK_FE_RESET_DONE event\n");
+			pr_info("send MTK_FE_RESET_DONE event !\n");
 			call_netdevice_notifiers(MTK_FE_RESET_DONE,
-				eth->netdev[i]);
+						 eth->netdev[i]);
 		}
 		call_netdevice_notifiers(MTK_FE_RESET_NAT_DONE,
-			eth->netdev[i]);
+					 eth->netdev[i]);
 		break;
 	}
 
@@ -4840,13 +4995,9 @@
 
 	atomic_dec(&reset_lock);
 
-	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);
-
 	mt753x_set_port_link_state(1);
 	mtk_phy_config(eth, 1);
-	mtk_reset_flag = 0;
+	eth->reset.event = 0;
 	clear_bit_unlock(MTK_RESETTING, &eth->state);
 
 	rtnl_unlock();
@@ -4887,6 +5038,7 @@
 	mtk_unreg_dev(eth);
 	mtk_free_dev(eth);
 	cancel_work_sync(&eth->pending_work);
+	cancel_delayed_work_sync(&eth->reset.monitor_work);
 
 	return 0;
 }
@@ -5259,6 +5411,7 @@
 	.ndo_select_queue       = mtk_select_queue,
 	.ndo_set_mac_address	= mtk_set_mac_address,
 	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_change_mtu		= mtk_change_mtu,
 	.ndo_do_ioctl		= mtk_do_ioctl,
 	.ndo_tx_timeout		= mtk_tx_timeout,
 	.ndo_get_stats64        = mtk_get_stats64,
@@ -5577,6 +5730,7 @@
 
 	eth->netdev[id]->irq = eth->irq_fe[0];
 	eth->netdev[id]->dev.of_node = np;
+	eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
 
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
 		mac->device_notifier.notifier_call = mtk_device_event;
@@ -5678,6 +5832,8 @@
 	spin_lock_init(&eth->txrx_irq_lock);
 	spin_lock_init(&eth->syscfg0_lock);
 
+	INIT_DELAYED_WORK(&eth->reset.monitor_work, mtk_hw_reset_monitor_work);
+
 	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
 		eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 							      "mediatek,ethsys");
@@ -5958,9 +6114,8 @@
 	eth->netdevice_notifier.notifier_call = mtk_eth_netdevice_event;
 	register_netdevice_notifier(&eth->netdevice_notifier);
 #if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
-	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);
+	schedule_delayed_work(&eth->reset.monitor_work,
+			      MTK_DMA_MONITOR_TIMEOUT);
 #endif
 
 	return 0;
@@ -6008,7 +6163,6 @@
 	mtk_cleanup(eth);
 	mtk_mdio_cleanup(eth);
 	unregister_netdevice_notifier(&eth->netdevice_notifier);
-	del_timer_sync(&eth->mtk_dma_monitor_timer);
 
 	return 0;
 }
@@ -6130,7 +6284,7 @@
 		.tx_dma_size = MTK_DMA_SIZE(4K),
 		.rx_dma_size = MTK_DMA_SIZE(1K),
 		.fq_dma_size = MTK_DMA_SIZE(2K),
-		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
+		.rx_dma_l4_valid = RX_DMA_L4_VALID,
 		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
 	},
@@ -6151,7 +6305,7 @@
 		.tx_dma_size = MTK_DMA_SIZE(4K),
 		.rx_dma_size = MTK_DMA_SIZE(1K),
 		.fq_dma_size = MTK_DMA_SIZE(2K),
-		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
+		.rx_dma_l4_valid = RX_DMA_L4_VALID,
 		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
 		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
 	},
@@ -6160,7 +6314,7 @@
 static const struct mtk_soc_data mt7988_data = {
 	.reg_map = &mt7988_reg_map,
 	.ana_rgc3 = 0x128,
-	.caps = MT7988_CAPS | MTK_HWLRO,
+	.caps = MT7988_CAPS,
 	.hw_features = MTK_HW_FEATURES,
 	.required_clks = MT7988_CLKS_BITMAP,
 	.required_pctl = false,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 08f5988..7805edf 100644
--- 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
@@ -18,6 +18,8 @@
 
 #define MTK_QDMA_PAGE_SIZE	2048
 #define	MTK_MAX_RX_LENGTH	1536
+#define MTK_MAX_RX_LENGTH_2K	2048
+#define MTK_MAX_RX_LENGTH_9K	9216
 #define MTK_MIN_TX_LENGTH	60
 #define MTK_DMA_SIZE(x)		(SZ_##x)
 #define MTK_FQ_DMA_HEAD		32
@@ -26,8 +28,10 @@
 
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 #define MTK_MAC_COUNT		3
+#define MTK_WDMA_CNT		3
 #else
 #define MTK_MAC_COUNT		2
+#define MTK_WDMA_CNT		2
 #endif
 
 #define MTK_RX_ETH_HLEN		(VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
@@ -576,6 +580,12 @@
 #define MTK_QDMA_GMAC2_QID	8
 #define MTK_QDMA_GMAC3_QID	6
 
+/* QDMA V2 descriptor txd8 */
+#define TX_DMA_CDRT_SHIFT          0
+#define TX_DMA_CDRT_MASK           0xff
+#define TX_DMA_TOPS_ENTRY_SHIFT    8
+#define TX_DMA_TOPS_ENTRY_MASK     0x3f
+
 /* QDMA V2 descriptor txd6 */
 #define TX_DMA_INS_VLAN_V2         BIT(16)
 
@@ -585,6 +595,9 @@
 #define TX_DMA_SPTAG_V3            BIT(27)
 
 /* QDMA V2 descriptor txd4 */
+#define EIP197_QDMA_TPORT          3
+#define TX_DMA_TPORT_SHIFT         0
+#define TX_DMA_TPORT_MASK          0xf
 #define TX_DMA_FPORT_SHIFT_V2      8
 #define TX_DMA_FPORT_MASK_V2       0xf
 #define TX_DMA_SWC_V2              BIT(30)
@@ -698,6 +711,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)
@@ -746,7 +762,13 @@
 
 /* Mac control registers */
 #define MTK_MAC_MCR(x)		(0x10100 + (x * 0x100))
-#define MAC_MCR_MAX_RX_1536	BIT(24)
+#define MAC_MCR_MAX_RX_JUMBO	FIELD_PREP(GENMASK(31, 28), 2)
+#define MAC_MCR_MAX_RX_MASK	GENMASK(25, 24)
+#define MAC_MCR_MAX_RX(_x)	(MAC_MCR_MAX_RX_MASK & ((_x) << 24))
+#define MAC_MCR_MAX_RX_1518	0x0
+#define MAC_MCR_MAX_RX_1536	0x1
+#define MAC_MCR_MAX_RX_1552	0x2
+#define MAC_MCR_MAX_RX_2048	0x3
 #define MAC_MCR_IPG_CFG		(BIT(18) | BIT(16) | BIT(12))
 #define MAC_MCR_FORCE_MODE	BIT(15)
 #define MAC_MCR_TX_EN		BIT(14)
@@ -771,6 +793,10 @@
 #define XMAC_MCR_FORCE_TX_FC	BIT(5)
 #define XMAC_MCR_FORCE_RX_FC	BIT(4)
 
+/* XFI Mac Rx configuration registers */
+#define MTK_XMAC_RX_CFG2(x)	(MTK_XMAC_MCR(x) + 0xd0)
+#define MTK_XMAC_MAX_RX_MASK	GENMASK(13, 0)
+
 /* XFI Mac logic reset registers */
 #define MTK_XMAC_LOGIC_RST(x)	(MTK_XMAC_BASE(x) + 0x10)
 #define XMAC_LOGIC_RST		BIT(0)
@@ -1076,6 +1102,48 @@
 #define MT7628_SDM_MAC_ADRL	(MT7628_SDM_OFFSET + 0x0c)
 #define MT7628_SDM_MAC_ADRH	(MT7628_SDM_OFFSET + 0x10)
 
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+#if !defined(CONFIG_NET_MEDIATEK_HNAT) && !defined(CONFIG_NET_MEDIATEK_HNAT_MODULE)
+struct tnl_desc {
+	u32 entry : 15;
+	u32 filled : 3;
+	u32 crsn : 5;
+	u32 resv1 : 3;
+	u32 sport : 4;
+	u32 resv2 : 1;
+	u32 alg : 1;
+	u32 iface : 8;
+	u32 wdmaid : 2;
+	u32 rxid : 2;
+	u32 wcid : 16;
+	u32 bssid : 8;
+	u32 usr_info : 16;
+	u32 tid : 4;
+	u32 is_fixedrate : 1;
+	u32 is_prior : 1;
+	u32 is_sp : 1;
+	u32 hf : 1;
+	u32 amsdu : 1;
+	u32 tops : 6;
+	u32 is_decap : 1;
+	u32 cdrt : 8;
+	u32 resv3 : 4;
+	u32 magic_tag_protect : 16;
+} __packed;
+
+#define TNL_MAGIC_TAG 0x6789
+#define skb_tnl_cdrt(skb) (((struct tnl_desc *)((skb)->head))->cdrt)
+#define skb_tnl_set_cdrt(skb, cdrt) ((skb_tnl_cdrt(skb)) = (cdrt))
+#define skb_tnl_magic_tag(skb) (((struct tnl_desc *)((skb)->head))->magic_tag_protect)
+#define is_tnl_tag_valid(skb) (skb_tnl_magic_tag(skb) == TNL_MAGIC_TAG)
+#else /* defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) */
+#define skb_tnl_cdrt(skb) (0)
+#define skb_tnl_set_cdrt(skb, cdrt) (0)
+#define skb_tnl_magic_tag(skb) (0)
+#define is_tnl_tag_valid(skb) (0)
+#endif /* !defined(CONFIG_NET_MEDIATEK_HNAT) && !defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) */
+#endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */
+
 struct mtk_rx_dma {
 	unsigned int rxd1;
 	unsigned int rxd2;
@@ -1459,8 +1527,8 @@
 	MTK_RGMII_BIT = 0,
 	MTK_TRGMII_BIT,
 	MTK_SGMII_BIT,
-	MTK_XGMII_BIT,
 	MTK_USXGMII_BIT,
+	MTK_2P5GPHY_BIT,
 	MTK_ESW_BIT,
 	MTK_GEPHY_BIT,
 	MTK_MUX_BIT,
@@ -1486,7 +1554,7 @@
 	MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
 	MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT,
 	MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT,
-	MTK_ETH_MUX_GMAC2_TO_XGMII_BIT,
+	MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT,
 	MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT,
 	MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT,
 	MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT,
@@ -1498,7 +1566,7 @@
 	MTK_ETH_PATH_GMAC1_SGMII_BIT,
 	MTK_ETH_PATH_GMAC2_RGMII_BIT,
 	MTK_ETH_PATH_GMAC2_SGMII_BIT,
-	MTK_ETH_PATH_GMAC2_XGMII_BIT,
+	MTK_ETH_PATH_GMAC2_2P5GPHY_BIT,
 	MTK_ETH_PATH_GMAC2_GEPHY_BIT,
 	MTK_ETH_PATH_GMAC3_SGMII_BIT,
 	MTK_ETH_PATH_GDM1_ESW_BIT,
@@ -1511,8 +1579,8 @@
 #define MTK_RGMII		BIT_ULL(MTK_RGMII_BIT)
 #define MTK_TRGMII		BIT_ULL(MTK_TRGMII_BIT)
 #define MTK_SGMII		BIT_ULL(MTK_SGMII_BIT)
-#define MTK_XGMII		BIT_ULL(MTK_XGMII_BIT)
 #define MTK_USXGMII		BIT_ULL(MTK_USXGMII_BIT)
+#define MTK_2P5GPHY		BIT_ULL(MTK_2P5GPHY_BIT)
 #define MTK_ESW			BIT_ULL(MTK_ESW_BIT)
 #define MTK_GEPHY		BIT_ULL(MTK_GEPHY_BIT)
 #define MTK_MUX			BIT_ULL(MTK_MUX_BIT)
@@ -1540,8 +1608,8 @@
 	BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT)
 #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY		\
 	BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT)
-#define MTK_ETH_MUX_GMAC2_TO_XGMII		\
-	BIT_ULL(MTK_ETH_MUX_GMAC2_TO_XGMII_BIT)
+#define MTK_ETH_MUX_GMAC2_TO_2P5GPHY		\
+	BIT_ULL(MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT)
 #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII	\
 	BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT)
 #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII	\
@@ -1557,7 +1625,7 @@
 #define MTK_ETH_PATH_GMAC1_SGMII	BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT)
 #define MTK_ETH_PATH_GMAC2_RGMII	BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT)
 #define MTK_ETH_PATH_GMAC2_SGMII	BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT)
-#define MTK_ETH_PATH_GMAC2_XGMII	BIT_ULL(MTK_ETH_PATH_GMAC2_XGMII_BIT)
+#define MTK_ETH_PATH_GMAC2_2P5GPHY	BIT_ULL(MTK_ETH_PATH_GMAC2_2P5GPHY_BIT)
 #define MTK_ETH_PATH_GMAC2_GEPHY	BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT)
 #define MTK_ETH_PATH_GMAC3_SGMII	BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT)
 #define MTK_ETH_PATH_GDM1_ESW		BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT)
@@ -1570,7 +1638,7 @@
 #define MTK_GMAC1_SGMII		(MTK_ETH_PATH_GMAC1_SGMII | MTK_SGMII)
 #define MTK_GMAC2_RGMII		(MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII)
 #define MTK_GMAC2_SGMII		(MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII)
-#define MTK_GMAC2_XGMII		(MTK_ETH_PATH_GMAC2_XGMII | MTK_XGMII)
+#define MTK_GMAC2_2P5GPHY	(MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY)
 #define MTK_GMAC2_GEPHY		(MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY)
 #define MTK_GMAC3_SGMII		(MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII)
 #define MTK_GDM1_ESW		(MTK_ETH_PATH_GDM1_ESW | MTK_ESW)
@@ -1596,8 +1664,8 @@
 	MTK_SHARED_SGMII)
 
 /* 2: GMAC2 -> XGMII */
-#define MTK_MUX_GMAC2_TO_XGMII      \
-	(MTK_ETH_MUX_GMAC2_TO_XGMII | MTK_MUX | MTK_INFRA)
+#define MTK_MUX_GMAC2_TO_2P5GPHY      \
+	(MTK_ETH_MUX_GMAC2_TO_2P5GPHY | MTK_MUX | MTK_INFRA)
 
 /* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */
 #define MTK_MUX_GMAC12_TO_GEPHY_SGMII   \
@@ -1645,8 +1713,8 @@
 		       MTK_NETSYS_V3 | MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | \
 		       MTK_GMAC1_USXGMII | MTK_GMAC2_USXGMII | \
 		       MTK_GMAC3_USXGMII | MTK_MUX_GMAC123_TO_USXGMII | \
-		       MTK_GMAC2_XGMII | MTK_MUX_GMAC2_TO_XGMII | MTK_RSS | \
-		       MTK_NETSYS_RX_V2 | MTK_36BIT_DMA)
+		       MTK_GMAC2_2P5GPHY | MTK_MUX_GMAC2_TO_2P5GPHY | MTK_RSS | \
+		       MTK_HWLRO | MTK_NETSYS_RX_V2 | MTK_36BIT_DMA)
 
 struct mtk_tx_dma_desc_info {
 	dma_addr_t	addr;
@@ -1758,6 +1826,8 @@
 	} txrx;
 };
 
+#define MTK_DMA_MONITOR_TIMEOUT		msecs_to_jiffies(1000)
+
 /* currently no SoC has more than 3 macs */
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 #define MTK_MAX_DEVS		3
@@ -1873,6 +1943,71 @@
 	int			link;
 };
 
+struct adma_monitor {
+	struct adma_rx_monitor {
+		u32		pre_drx[4];
+		u16		pre_opq;
+		u8		hang_count;
+	} rx;
+};
+
+struct qdma_monitor {
+	struct qdma_tx_monitor {
+		u8		hang_count;
+	} tx;
+	struct qdma_rx_monitor {
+		u32		pre_fq_head;
+		u32		pre_fq_tail;
+		u8		hang_count;
+	} rx;
+};
+
+struct tdma_monitor {
+	struct tdma_tx_monitor {
+		u32		pre_ipq10;
+		u32		pre_fsm;
+		u8		hang_count;
+	} tx;
+	struct tdma_rx_monitor {
+		u32		pre_fsm;
+		u8		hang_count;
+	} rx;
+};
+
+struct wdma_monitor {
+	struct wdma_tx_monitor {
+		u32		pre_dtx[MTK_WDMA_CNT];
+		u16		pre_opq_gdm[MTK_MAX_DEVS];
+		u16		pre_opq_wdma[MTK_WDMA_CNT];
+		u8		err_opq_gdm[MTK_MAX_DEVS];
+		u8		err_opq_wdma[MTK_WDMA_CNT];
+		u8		hang_count[MTK_WDMA_CNT];
+	} tx;
+	struct wdma_rx_monitor {
+		u32		pre_crx[MTK_WDMA_CNT];
+		u32		pre_drx[MTK_WDMA_CNT];
+		u32		pre_opq[MTK_WDMA_CNT];
+		u8		hang_count[MTK_WDMA_CNT];
+	} rx;
+};
+
+struct gdm_monitor {
+	struct gdm_tx_monitor {
+		u64		pre_tx_cnt[MTK_MAX_DEVS];
+		u32		pre_fsm_gdm[MTK_MAX_DEVS];
+		u32		pre_opq_gdm[MTK_MAX_DEVS];
+		u8		hang_count_gdm[MTK_MAX_DEVS];
+		u8		hang_count_gmac[MTK_MAX_DEVS];
+	} tx;
+	struct gdm_rx_monitor {
+		u64		pre_rx_cnt[MTK_MAX_DEVS];
+		u32		pre_fsm_gdm[MTK_MAX_DEVS];
+		u16		pre_ipq_gdm[MTK_MAX_DEVS];
+		u8		hang_count_gdm[MTK_MAX_DEVS];
+		u8		hang_count_gmac[MTK_MAX_DEVS];
+	} rx;
+};
+
 /* struct mtk_eth -	This is the main datasructure for holding the state
  *			of the driver
  * @dev:		The device pointer
@@ -1954,11 +2089,20 @@
 
 	const struct mtk_soc_data	*soc;
 
+	struct {
+		struct delayed_work	monitor_work;
+		struct adma_monitor	adma_monitor;
+		struct qdma_monitor	qdma_monitor;
+		struct tdma_monitor	tdma_monitor;
+		struct wdma_monitor	wdma_monitor;
+		struct gdm_monitor	gdm_monitor;
+		u32			event;
+	} reset;
+
 	u32				rx_dma_l4_valid;
 	int				ip_align;
 	spinlock_t			syscfg0_lock;
 	struct notifier_block		netdevice_notifier;
-	struct timer_list		mtk_dma_monitor_timer;
 };
 
 /* struct mtk_mac -	the structure that holds the info about the MACs of the
@@ -1975,6 +2119,7 @@
 	unsigned int			mode;
 	unsigned int			type;
 	int				speed;
+	int				phy_speed;
 	struct device_node		*of_node;
 	struct phylink			*phylink;
 	struct phylink_config		phylink_config;
@@ -2014,6 +2159,11 @@
 extern u32 mtk_hwlro_stats_ebl;
 extern u32 dbg_show_level;
 
+/* tunnel offload related */
+extern u32 (*mtk_get_tnl_netsys_params)(struct sk_buff *skb);
+extern struct net_device *(*mtk_get_tnl_dev)(u8 tops_crsn);
+extern void (*mtk_set_tops_crsn)(struct sk_buff *skb, u8 tops_crsn);
+
 /* read the hardware status register */
 void mtk_stats_update_mac(struct mtk_mac *mac);
 
@@ -2026,7 +2176,7 @@
 		   u32 ana_rgc3);
 
 int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
-int mtk_gmac_xgmii_path_setup(struct mtk_eth *eth, int mac_id);
+int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id);
 int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
 int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
 int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
index d4f74c1..e9508eb 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
@@ -52,6 +52,14 @@
 EXPORT_SYMBOL(ppe_dev_register_hook);
 void (*ppe_dev_unregister_hook)(struct net_device *dev) = NULL;
 EXPORT_SYMBOL(ppe_dev_unregister_hook);
+int (*mtk_tnl_encap_offload)(struct sk_buff *skb, struct ethhdr *eth) = NULL;
+EXPORT_SYMBOL(mtk_tnl_encap_offload);
+int (*mtk_tnl_decap_offload)(struct sk_buff *skb) = NULL;
+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);
 
 int (*hnat_set_wdma_pse_port_state)(u32 wdma_idx, bool up) = NULL;
 EXPORT_SYMBOL(hnat_set_wdma_pse_port_state);
@@ -65,6 +73,16 @@
 			     SMA, SMA_FWD_CPU_BUILD_ENTRY);
 }
 
+struct foe_entry *hnat_get_foe_entry(u32 ppe_id, u32 index)
+{
+	if (index == 0x7fff || index >= hnat_priv->foe_etry_num ||
+	    ppe_id >= CFG_PPE_NUM)
+		return ERR_PTR(-EINVAL);
+
+	return &hnat_priv->foe_table_cpu[ppe_id][index];
+}
+EXPORT_SYMBOL(hnat_get_foe_entry);
+
 void hnat_cache_ebl(int enable)
 {
 	int i;
@@ -75,6 +93,7 @@
 		cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_EN, enable);
 	}
 }
+EXPORT_SYMBOL(hnat_cache_ebl);
 
 static void hnat_reset_timestamp(struct timer_list *t)
 {
@@ -414,6 +433,16 @@
 				    entry->ipv4_hnapt.winfo.wcid != wcid ||
 				    entry->ipv4_hnapt.iblk2.dp != port)
 					continue;
+			} else if (IS_IPV4_MAPE(entry) || IS_IPV4_MAPT(entry)) {
+				if (entry->ipv4_mape.winfo.bssid != bssid ||
+				    entry->ipv4_mape.winfo.wcid != wcid ||
+				    entry->ipv4_mape.iblk2.dp != port)
+					continue;
+			} else if (IS_IPV6_HNAPT(entry) || IS_IPV6_HNAT(entry)) {
+				if (entry->ipv6_hnapt.winfo.bssid != bssid ||
+				    entry->ipv6_hnapt.winfo.wcid != wcid ||
+				    entry->ipv6_hnapt.iblk2.dp != port)
+					continue;
 			} else {
 				if (entry->ipv6_5t_route.winfo.bssid != bssid ||
 				    entry->ipv6_5t_route.winfo.wcid != wcid ||
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
index c59e97e..534fc03 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
@@ -1155,15 +1155,25 @@
 #define NR_WDMA2_PORT 13
 #define NR_GMAC3_PORT 15
 #define NR_QDMA_TPORT 1
+#define NR_EIP197_TPORT 2
+#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 WAN_DEV_NAME hnat_priv->wan
 #define LAN_DEV_NAME hnat_priv->lan
 #define LAN2_DEV_NAME hnat_priv->lan2
-#define IS_WAN(dev)                                                            \
-	(!strncmp((dev)->name, hnat_priv->wan, strlen(hnat_priv->wan)))
+#define IS_WAN(dev) (!strncmp((dev)->name, WAN_DEV_NAME, strlen(WAN_DEV_NAME)))
 #define IS_LAN_GRP(dev) (IS_LAN(dev) | IS_LAN2(dev))
-#define IS_LAN(dev) (!strncmp(dev->name, LAN_DEV_NAME, strlen(LAN_DEV_NAME)))
-#define IS_LAN2(dev) (!strncmp(dev->name, LAN2_DEV_NAME,			\
-		      strlen(LAN2_DEV_NAME)))
+#define IS_LAN(dev)								\
+	(!strncmp(dev->name, LAN_DEV_NAME, strlen(LAN_DEV_NAME)) ||		\
+	 IS_BOND(dev))
+#define IS_LAN2(dev)								\
+	(!strncmp(dev->name, LAN2_DEV_NAME, strlen(LAN2_DEV_NAME)) ||		\
+	 IS_BOND(dev))
 #define IS_BR(dev) (!strncmp(dev->name, "br", 2))
+#define IS_BOND(dev) (!strncmp(dev->name, "bond", 4))
 #define IS_WHNAT(dev)								\
 	((hnat_priv->data->whnat &&						\
 	 (get_wifi_hook_if_index_from_dev(dev) != 0)) ? 1 : 0)
@@ -1184,7 +1194,6 @@
 	(IS_IPV6_3T_ROUTE(x) | IS_IPV6_5T_ROUTE(x) | IS_IPV6_6RD(x) |          \
 	 IS_IPV4_DSLITE(x) | IS_IPV4_MAPE(x) | IS_IPV4_MAPT(x) |	       \
 	 IS_IPV6_HNAPT(x) | IS_IPV6_HNAT(x))
-#define IS_BOND_MODE (!strncmp(LAN_DEV_NAME, "bond", 4))
 #define IS_GMAC1_MODE ((hnat_priv->gmac_num == 1) ? 1 : 0)
 #define IS_HQOS_MODE (qos_toggle == 1)
 #define IS_PPPQ_MODE (qos_toggle == 2)		/* Per Port Per Queue */
@@ -1221,6 +1230,8 @@
 #define IS_DSA_1G_LAN(dev) (!strncmp(dev->name, "lan", 3) &&		       \
 			    strcmp(dev->name, "lan5"))
 #define IS_DSA_WAN(dev) (!strncmp(dev->name, "wan", 3))
+#define IS_DSA_TAG_PROTO_MXL862_8021Q(dp)				       \
+	(dp->cpu_dp->tag_ops->proto == DSA_TAG_PROTO_MXL862_8021Q)
 #define NONE_DSA_PORT 0xff
 #define MAX_CRSN_NUM 32
 #define IPV6_HDR_LEN 40
@@ -1302,6 +1313,8 @@
 #endif
 }
 
+struct foe_entry *hnat_get_foe_entry(u32 ppe_id, u32 index);
+
 void hnat_deinit_debugfs(struct mtk_hnat *h);
 int hnat_init_debugfs(struct mtk_hnat *h);
 int hnat_register_nf_hooks(void);
@@ -1318,7 +1331,14 @@
 extern int hook_toggle;
 extern int mape_toggle;
 extern int qos_toggle;
-
+extern int tnl_toggle;
+extern int (*mtk_tnl_encap_offload)(struct sk_buff *skb, struct ethhdr *eth);
+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);
+extern int hnat_bind_crypto_entry(struct sk_buff *skb,
+				  const struct net_device *dev,
+				  int fill_inner_info);
 int ext_if_add(struct extdev_entry *ext_entry);
 int ext_if_del(struct extdev_entry *ext_entry);
 void cr_set_field(void __iomem *reg, u32 field, u32 val);
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 53f4d10..29ea69b 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
@@ -36,6 +36,7 @@
 int qos_toggle;
 int qos_dl_toggle = 1;
 int qos_ul_toggle = 1;
+int tnl_toggle;
 int xlat_toggle;
 struct hnat_desc headroom[DEF_ETRY_NUM];
 unsigned int dbg_cpu_reason_cnt[MAX_CRSN_NUM];
@@ -2687,6 +2688,47 @@
 	.release = single_release,
 };
 
+static int hnat_tnl_toggle_read(struct seq_file *m, void *private)
+{
+	pr_info("value=%d, tnl is %s now!\n",
+		tnl_toggle, (tnl_toggle) ? "enabled" : "disabled");
+
+	return 0;
+}
+
+static int hnat_tnl_toggle_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, hnat_tnl_toggle_read, file->private_data);
+}
+
+static ssize_t hnat_tnl_toggle_write(struct file *file,
+				     const char __user *buffer,
+				     size_t count, loff_t *data)
+{
+	char buf[8] = {0};
+	int len = count;
+
+	if ((len > 8) || copy_from_user(buf, buffer, len))
+		return -EFAULT;
+
+	if (buf[0] == '1' && !tnl_toggle) {
+		pr_info("tnl is going to be enabled !\n");
+		tnl_toggle = 1;
+	} else if (buf[0] == '0' && tnl_toggle) {
+		pr_info("tnl is going to be disabled !\n");
+		tnl_toggle = 0;
+	}
+
+	return len;
+}
+
+static const struct file_operations hnat_tnl_toggle_fops = {
+	.open = hnat_tnl_toggle_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.write = hnat_tnl_toggle_write,
+	.release = single_release,
+};
 static int hnat_xlat_toggle_read(struct seq_file *m, void *private)
 {
 	pr_info("value=%d, xlat is %s now!\n",
@@ -3523,6 +3565,8 @@
 			    &hnat_ppd_if_fops);
 	debugfs_create_file("static_entry", 0444, root, h,
 			    &hnat_static_fops);
+	debugfs_create_file("tnl_toggle", 0444, root, h,
+			    &hnat_tnl_toggle_fops);
 	debugfs_create_file("xlat_toggle", 0444, root, h,
 			    &hnat_xlat_toggle_fops);
 	debugfs_create_file("xlat_cfg", 0444, root, h,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c
index edf17cb..2c9c3fd 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c
@@ -56,8 +56,9 @@
 		*mac_hi = swab32(entry->addr.u.ip6.s6_addr32[3]);
 		break;
 	}
-	trace_printk("%s:group mac_h=0x%08x, mac_l=0x%04x\n",
-		     __func__, *mac_hi, *mac_lo);
+	if (debug_level >= 7)
+		trace_printk("%s:group mac_h=0x%08x, mac_l=0x%04x\n",
+			     __func__, *mac_hi, *mac_lo);
 }
 
 /*set_hnat_mtbl - set ppe multicast register*/
@@ -83,8 +84,9 @@
 	mcast_h.u.info.mc_px_en = mc_port;
 	mcast_l.addr = mac_hi;
 	mcast_h.u.info.valid = group->valid;
-	trace_printk("%s:index=%d,group info=0x%x,addr=0x%x\n",
-		     __func__, index, mcast_h.u.value, mcast_l.addr);
+	if (debug_level >= 7)
+		trace_printk("%s:index=%d,group info=0x%x,addr=0x%x\n",
+			     __func__, index, mcast_h.u.value, mcast_l.addr);
 	if (index < 0x10) {
 		reg = hnat_priv->ppe_base[ppe_id] + PPE_MCAST_H_0 + ((index) * 8);
 		writel(mcast_h.u.value, reg);
@@ -152,8 +154,9 @@
 			}
 		break;
 	}
-	trace_printk("%s:devname=%s,eif=%d,oif=%d\n", __func__,
-		     dev->name, group->eif, group->oif);
+	if (debug_level >= 7)
+		trace_printk("%s:devname=%s,eif=%d,oif=%d\n", __func__,
+			     dev->name, group->eif, group->oif);
 	if (group->valid) {
 		if (group->oif && group->eif)
 			/*eth&wifi both in group,forward to cpu&GDMA1*/
@@ -209,12 +212,14 @@
 			}
 
 			entry = (struct br_mdb_entry *)nla_data(info);
-			trace_printk("%s:cmd=0x%2x,ifindex=0x%x,state=0x%x",
-				     __func__, nlh->nlmsg_type,
-				     entry->ifindex, entry->state);
-			trace_printk("vid=0x%x,ip=0x%x,proto=0x%x\n",
-				     entry->vid, entry->addr.u.ip4,
-				     entry->addr.proto);
+			if (debug_level >= 7) {
+				trace_printk("%s:cmd=0x%2x,ifindex=0x%x,state=0x%x",
+					     __func__, nlh->nlmsg_type,
+					     entry->ifindex, entry->state);
+				trace_printk("vid=0x%x,ip=0x%x,proto=0x%x\n",
+					     entry->vid, entry->addr.u.ip4,
+					     entry->addr.proto);
+			}
 			hnat_mcast_table_update(nlh->nlmsg_type, entry);
 		}
 		kfree_skb(skb);
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 6960462..17f5e4e 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
@@ -184,6 +184,72 @@
 	return i;
 }
 
+static void foe_clear_ethdev_bind_entries(struct net_device *dev)
+{
+	struct net_device *master_dev = dev;
+	const struct dsa_port *dp;
+	struct foe_entry *entry;
+	struct mtk_mac *mac;
+	bool match_dev = false;
+	int port_id, gmac;
+	u32 i, hash_index;
+	u32 dsa_tag;
+	u32 total = 0;
+
+	/* Get the master device if the device is slave device */
+	port_id = hnat_dsa_get_port(&master_dev);
+	mac = netdev_priv(master_dev);
+	gmac = HNAT_GMAC_FP(mac->id);
+
+	if (gmac < 0)
+		return;
+
+	if (port_id >= 0) {
+		dp = dsa_port_from_netdev(dev);
+		if (IS_ERR(dp))
+			return;
+
+		if (IS_DSA_TAG_PROTO_MXL862_8021Q(dp))
+			dsa_tag = port_id + BIT(11);
+		else
+			dsa_tag = BIT(port_id);
+	}
+
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
+			entry = hnat_priv->foe_table_cpu[i] + hash_index;
+			if (!entry_hnat_is_bound(entry))
+				continue;
+
+			match_dev = (IS_IPV4_GRP(entry)) ? entry->ipv4_hnapt.iblk2.dp == gmac :
+							   entry->ipv6_5t_route.iblk2.dp == gmac;
+
+			if (match_dev && port_id >= 0) {
+				if (IS_DSA_TAG_PROTO_MXL862_8021Q(dp)) {
+					match_dev = (IS_IPV4_GRP(entry)) ?
+						entry->ipv4_hnapt.vlan1 == dsa_tag :
+						entry->ipv6_5t_route.vlan1 == dsa_tag;
+				} else {
+					match_dev = (IS_IPV4_GRP(entry)) ?
+						!!(entry->ipv4_hnapt.etype & dsa_tag) :
+						!!(entry->ipv6_5t_route.etype & dsa_tag);
+				}
+			}
+
+			if (match_dev) {
+				entry->bfib1.state = INVALID;
+				entry->bfib1.time_stamp =
+					readl((hnat_priv->fe_base + 0x0010)) & 0xFF;
+				total++;
+			}
+		}
+	}
+
+	/* clear HWNAT cache */
+	if (total > 0)
+		hnat_cache_ebl(1);
+}
+
 void foe_clear_all_bind_entries(void)
 {
 	int i, hash_index;
@@ -235,6 +301,17 @@
 		extif_set_dev(dev);
 
 		break;
+	case NETDEV_CHANGE:
+		/* Clear PPE entries if the slave of bond device physical link down */
+		if (!netif_is_bond_slave(dev) ||
+		    (!IS_LAN_GRP(dev) && !IS_WAN(dev)))
+			break;
+
+		if (netif_carrier_ok(dev))
+			break;
+
+		foe_clear_ethdev_bind_entries(dev);
+		break;
 	case NETDEV_GOING_DOWN:
 		if (!get_wifi_hook_if_index_from_dev(dev))
 			extif_put_dev(dev);
@@ -403,17 +480,19 @@
 		skb->vlan_proto = htons(ETH_P_8021Q);
 		skb->vlan_tci =
 			(VLAN_CFI_MASK | (in->ifindex & VLAN_VID_MASK));
-		trace_printk(
-			"%s: vlan_prot=0x%x, vlan_tci=%x, in->name=%s, skb->dev->name=%s\n",
-			__func__, ntohs(skb->vlan_proto), skb->vlan_tci,
-			in->name, hnat_priv->g_ppdev->name);
 		skb->dev = hnat_priv->g_ppdev;
 		dev_queue_xmit(skb);
-		trace_printk("%s: called from %s successfully\n", __func__, func);
+		if (debug_level >= 7) {
+			trace_printk("%s: vlan_prot=0x%x, vlan_tci=%x, in->name=%s, skb->dev->name=%s\n",
+				     __func__, ntohs(skb->vlan_proto), skb->vlan_tci,
+				     in->name, hnat_priv->g_ppdev->name);
+			trace_printk("%s: called from %s successfully\n", __func__, func);
+		}
 		return 0;
 	}
 
-	trace_printk("%s: called from %s fail\n", __func__, func);
+	if (debug_level >= 7)
+		trace_printk("%s: called from %s fail\n", __func__, func);
 	return -1;
 }
 
@@ -423,8 +502,9 @@
 	struct net_device *dev;
 	struct foe_entry *entry;
 
-	trace_printk("%s: vlan_prot=0x%x, vlan_tci=%x\n", __func__,
-		     ntohs(skb->vlan_proto), skb->vlan_tci);
+	if (debug_level >= 7)
+		trace_printk("%s: vlan_prot=0x%x, vlan_tci=%x\n", __func__,
+			     ntohs(skb->vlan_proto), skb->vlan_tci);
 
 	if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num ||
 	    skb_hnat_ppe(skb) >= CFG_PPE_NUM)
@@ -444,7 +524,7 @@
 				return -1;
 		}
 
-		if (IS_BOND_MODE &&
+		if (IS_BOND(dev) &&
 		    (((hnat_priv->data->version == MTK_HNAT_V2 ||
 		       hnat_priv->data->version == MTK_HNAT_V3) &&
 				(skb_hnat_entry(skb) != 0x7fff)) ||
@@ -456,8 +536,9 @@
 		set_from_extge(skb);
 		fix_skb_packet_type(skb, skb->dev, eth);
 		netif_rx(skb);
-		trace_printk("%s: called from %s successfully\n", __func__,
-			     func);
+		if (debug_level >= 7)
+			trace_printk("%s: called from %s successfully\n", __func__,
+				     func);
 		return 0;
 	} else {
 		/* MapE WAN --> LAN/WLAN PingPong. */
@@ -476,7 +557,8 @@
 				return 0;
 			}
 		}
-		trace_printk("%s: called from %s fail\n", __func__, func);
+		if (debug_level >= 7)
+			trace_printk("%s: called from %s fail\n", __func__, func);
 		return -1;
 	}
 }
@@ -501,8 +583,9 @@
 
 	dev = get_dev_from_index(index);
 	if (!dev) {
-		trace_printk("%s: called from %s. Get wifi interface fail\n",
-			     __func__, func);
+		if (debug_level >= 7)
+			trace_printk("%s: called from %s. Get wifi interface fail\n",
+				     __func__, func);
 		return 0;
 	}
 
@@ -526,8 +609,9 @@
 		skb_set_network_header(skb, 0);
 		skb_push(skb, ETH_HLEN);
 		dev_queue_xmit(skb);
-		trace_printk("%s: called from %s successfully\n", __func__,
-			     func);
+		if (debug_level >= 7)
+			trace_printk("%s: called from %s successfully\n", __func__,
+				     func);
 		return 0;
 	} else {
 		if (mape_toggle) {
@@ -542,8 +626,9 @@
 					dev_queue_xmit(skb);
 					return 0;
 				}
-				trace_printk("%s: called from %s fail[MapE]\n", __func__,
-					     func);
+				if (debug_level >= 7)
+					trace_printk("%s: called from %s fail[MapE]\n", __func__,
+						     func);
 				return -1;
 			}
 		}
@@ -559,31 +644,34 @@
 		/* clear HWNAT cache */
 		hnat_cache_ebl(1);
 	}
-	trace_printk("%s: called from %s fail, index=%x\n", __func__,
-		     func, index);
+	if (debug_level >= 7)
+		trace_printk("%s: called from %s fail, index=%x\n", __func__,
+			     func, index);
 	return -1;
 }
 
 static void pre_routing_print(struct sk_buff *skb, const struct net_device *in,
 			      const struct net_device *out, const char *func)
 {
-	trace_printk(
-		"[%s]: %s(iif=0x%x CB2=0x%x)-->%s (ppe_hash=0x%x) sport=0x%x reason=0x%x alg=0x%x from %s\n",
-		__func__, in->name, skb_hnat_iface(skb),
-		HNAT_SKB_CB2(skb)->magic, out->name, skb_hnat_entry(skb),
-		skb_hnat_sport(skb), skb_hnat_reason(skb), skb_hnat_alg(skb),
-		func);
+	if (debug_level >= 7)
+		trace_printk(
+			"[%s]: %s(iif=0x%x CB2=0x%x)-->%s (ppe_hash=0x%x) sport=0x%x reason=0x%x alg=0x%x from %s\n",
+			__func__, in->name, skb_hnat_iface(skb),
+			HNAT_SKB_CB2(skb)->magic, out->name, skb_hnat_entry(skb),
+			skb_hnat_sport(skb), skb_hnat_reason(skb), skb_hnat_alg(skb),
+			func);
 }
 
 static void post_routing_print(struct sk_buff *skb, const struct net_device *in,
 			       const struct net_device *out, const char *func)
 {
-	trace_printk(
-		"[%s]: %s(iif=0x%x, CB2=0x%x)-->%s (ppe_hash=0x%x) sport=0x%x reason=0x%x alg=0x%x from %s\n",
-		__func__, in->name, skb_hnat_iface(skb),
-		HNAT_SKB_CB2(skb)->magic, out->name, skb_hnat_entry(skb),
-		skb_hnat_sport(skb), skb_hnat_reason(skb), skb_hnat_alg(skb),
-		func);
+	if (debug_level >= 7)
+		trace_printk(
+			"[%s]: %s(iif=0x%x, CB2=0x%x)-->%s (ppe_hash=0x%x) sport=0x%x reason=0x%x alg=0x%x from %s\n",
+			__func__, in->name, skb_hnat_iface(skb),
+			HNAT_SKB_CB2(skb)->magic, out->name, skb_hnat_entry(skb),
+			skb_hnat_sport(skb), skb_hnat_reason(skb), skb_hnat_alg(skb),
+			func);
 }
 
 static inline void hnat_set_iif(const struct nf_hook_state *state,
@@ -739,10 +827,14 @@
 	case ETH_P_IP:
 		iph = ip_hdr(skb);
 
-		/* do not accelerate non tcp/udp traffic */
-		if ((iph->protocol == IPPROTO_TCP) ||
-		    (iph->protocol == IPPROTO_UDP) ||
-		    (iph->protocol == IPPROTO_IPV6)) {
+		if (mtk_tnl_decap_offloadable && mtk_tnl_decap_offloadable(skb)) {
+			/* tunnel protocol is offloadable */
+			skb_hnat_set_is_decap(skb, 1);
+			return 1;
+		} else if ((iph->protocol == IPPROTO_TCP) ||
+			   (iph->protocol == IPPROTO_UDP) ||
+			   (iph->protocol == IPPROTO_IPV6)) {
+			/* do not accelerate non tcp/udp traffic */
 			return 1;
 		}
 
@@ -827,18 +919,55 @@
 
 	return NF_ACCEPT;
 drop:
-	if (skb)
+	if (skb && (debug_level >= 7))
 		printk_ratelimited(KERN_WARNING
-			"%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n"
-			"sport=0x%x, reason=0x%x, alg=0x%x)\n",
-			__func__, state->in->name, skb_hnat_iface(skb),
-			HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
-			skb_hnat_sport(skb), skb_hnat_reason(skb),
-			skb_hnat_alg(skb));
+				   "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n"
+				   "sport=0x%x, reason=0x%x, alg=0x%x)\n",
+				   __func__, state->in->name, skb_hnat_iface(skb),
+				   HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
+				   skb_hnat_sport(skb), skb_hnat_reason(skb),
+				   skb_hnat_alg(skb));
 
 	return NF_DROP;
 }
 
+static inline void qos_rate_limit_set(u32 id, const struct net_device *dev)
+{
+	const struct mtk_mac *mac;
+	u32 max_man = SPEED_10000 / SPEED_100;
+	u32 max_exp = 5;
+	u32 cfg;
+
+	if (id > MTK_QDMA_TX_NUM)
+		return;
+
+	if (!dev)
+		goto setup_rate_limit;
+
+	mac = netdev_priv(dev);
+
+	switch (mac->phy_speed) {
+	case SPEED_100:
+	case SPEED_1000:
+	case SPEED_2500:
+	case SPEED_5000:
+	case SPEED_10000:
+		max_man = mac->phy_speed / SPEED_100;
+		break;
+	default:
+		return;
+	}
+
+setup_rate_limit:
+	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 unsigned int
 mtk_hnat_ipv4_nf_pre_routing(void *priv, struct sk_buff *skb,
 			     const struct nf_hook_state *state)
@@ -861,13 +990,28 @@
 	hw_path.dev = skb->dev;
 	hw_path.virt_dev = skb->dev;
 
+	if (skb_hnat_tops(skb) && skb_hnat_is_decap(skb) &&
+	    is_magic_tag_valid(skb) &&
+	    skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL &&
+	    mtk_tnl_decap_offload && !mtk_tnl_decap_offload(skb)) {
+		if (skb_hnat_cdrt(skb) && skb_hnat_is_decrypt(skb))
+			/*
+			 * inbound flow of offload engines use QID 13
+			 * set its rate limit to maximum
+			 */
+			qos_rate_limit_set(13, NULL);
+
+		return NF_ACCEPT;
+	}
+
 	/*
 	 * Avoid mistakenly binding of outer IP, ports in SW L2TP decap flow.
 	 * In pre-routing, if dev is virtual iface, TOPS module is not loaded,
 	 * and it's L2TP flow, then do not bind.
 	 */
-	if (skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL
-	    && skb->dev->netdev_ops->ndo_flow_offload_check) {
+	if (skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL &&
+	    skb->dev->netdev_ops->ndo_flow_offload_check &&
+	    !mtk_tnl_decap_offload) {
 		skb->dev->netdev_ops->ndo_flow_offload_check(&hw_path);
 
 		if (hw_path.flags & FLOW_OFFLOAD_PATH_TNL)
@@ -896,14 +1040,14 @@
 
 	return NF_ACCEPT;
 drop:
-	if (skb)
+	if (skb && (debug_level >= 7))
 		printk_ratelimited(KERN_WARNING
-			"%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n"
-			"sport=0x%x, reason=0x%x, alg=0x%x)\n",
-			__func__, state->in->name, skb_hnat_iface(skb),
-			HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
-			skb_hnat_sport(skb), skb_hnat_reason(skb),
-			skb_hnat_alg(skb));
+				   "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n"
+				   "sport=0x%x, reason=0x%x, alg=0x%x)\n",
+				   __func__, state->in->name, skb_hnat_iface(skb),
+				   HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
+				   skb_hnat_sport(skb), skb_hnat_reason(skb),
+				   skb_hnat_alg(skb));
 
 	return NF_DROP;
 }
@@ -936,6 +1080,20 @@
 
 	hnat_set_head_frags(state, skb, -1, hnat_set_iif);
 
+	if (skb_hnat_tops(skb) && skb_hnat_is_decap(skb) &&
+	    is_magic_tag_valid(skb) &&
+	    skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL &&
+	    mtk_tnl_decap_offload && !mtk_tnl_decap_offload(skb)) {
+		if (skb_hnat_cdrt(skb) && skb_hnat_is_decrypt(skb))
+			/*
+			 * inbound flow of offload engines use QID 13
+			 * set its rate limit to maximum
+			 */
+			qos_rate_limit_set(13, NULL);
+
+		return NF_ACCEPT;
+	}
+
 	pre_routing_print(skb, state->in, state->out, __func__);
 
 	if (unlikely(debug_level >= 7)) {
@@ -986,14 +1144,14 @@
 #endif
 	return NF_ACCEPT;
 drop:
-	if (skb)
+	if (skb && (debug_level >= 7))
 		printk_ratelimited(KERN_WARNING
-			"%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n"
-			"sport=0x%x, reason=0x%x, alg=0x%x)\n",
-			__func__, state->in->name, skb_hnat_iface(skb),
-			HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
-			skb_hnat_sport(skb), skb_hnat_reason(skb),
-			skb_hnat_alg(skb));
+				   "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n"
+				   "sport=0x%x, reason=0x%x, alg=0x%x)\n",
+				   __func__, state->in->name, skb_hnat_iface(skb),
+				   HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
+				   skb_hnat_sport(skb), skb_hnat_reason(skb),
+				   skb_hnat_alg(skb));
 
 	return NF_DROP;
 }
@@ -1005,23 +1163,9 @@
 	const struct in6_addr *ipv6_nexthop;
 	struct neighbour *neigh = NULL;
 	struct dst_entry *dst = skb_dst(skb);
-	struct ethhdr *eth;
-	u16 eth_pppoe_hlen = ETH_HLEN + PPPOE_SES_HLEN;
 
-	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) {
-		if (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPIP) {
-			eth = (struct ethhdr *)(skb->data - eth_pppoe_hlen);
-			eth->h_proto = skb->protocol;
-			ether_addr_copy(eth->h_dest, hw_path->eth_dest);
-			ether_addr_copy(eth->h_source,  hw_path->eth_src);
-		} else {
-			eth = eth_hdr(skb);
-			memcpy(eth->h_source, hw_path->eth_src, ETH_ALEN);
-			memcpy(eth->h_dest, hw_path->eth_dest, ETH_ALEN);
-		}
-
+	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE)
 		return 0;
-	}
 
 	rcu_read_lock_bh();
 	ipv6_nexthop =
@@ -1040,16 +1184,8 @@
 		return -1;
 	}
 
-	if (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPIP) {
-		/*copy ether type for DS-Lite and MapE */
-		eth = (struct ethhdr *)(skb->data - ETH_HLEN);
-		eth->h_proto = skb->protocol;
-	} else {
-		eth = eth_hdr(skb);
-	}
-
-	ether_addr_copy(eth->h_dest, neigh->ha);
-	ether_addr_copy(eth->h_source, out->dev_addr);
+	ether_addr_copy(hw_path->eth_dest, neigh->ha);
+	ether_addr_copy(hw_path->eth_src, out->dev_addr);
 
 	rcu_read_unlock_bh();
 
@@ -1065,15 +1201,12 @@
 	struct dst_entry *dst = skb_dst(skb);
 	struct rtable *rt = (struct rtable *)dst;
 	struct net_device *dev = (__force struct net_device *)out;
-	struct ethhdr *eth;
 
-	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) {
-		rcu_read_lock_bh();
-		memcpy(eth_hdr(skb)->h_source, hw_path->eth_src, ETH_ALEN);
-		memcpy(eth_hdr(skb)->h_dest, hw_path->eth_dest, ETH_ALEN);
-		rcu_read_unlock_bh();
+	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE)
 		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);
@@ -1091,15 +1224,8 @@
 		return -1;
 	}
 
-	if (ip_hdr(skb)->protocol == IPPROTO_IPV6)
-		/* 6RD LAN->WAN(6to4) */
-		eth = (struct ethhdr *)(skb->data - ETH_HLEN);
-	else
-		eth = eth_hdr(skb);
-
-	memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
-	memcpy(eth->h_source, out->dev_addr, ETH_ALEN);
-	eth->h_proto = htons(ETH_P_IP);
+	memcpy(hw_path->eth_dest, neigh->ha, ETH_ALEN);
+	memcpy(hw_path->eth_src, out->dev_addr, ETH_ALEN);
 
 	rcu_read_unlock_bh();
 
@@ -1126,16 +1252,16 @@
 	return chksum_base;
 }
 
-struct foe_entry ppe_fill_L2_info(struct ethhdr *eth, struct foe_entry entry,
+struct foe_entry ppe_fill_L2_info(struct foe_entry entry,
 				  struct flow_offload_hw_path *hw_path)
 {
 	switch ((int)entry.bfib1.pkt_type) {
 	case IPV4_HNAPT:
 	case IPV4_HNAT:
-		entry.ipv4_hnapt.dmac_hi = swab32(*((u32 *)eth->h_dest));
-		entry.ipv4_hnapt.dmac_lo = swab16(*((u16 *)&eth->h_dest[4]));
-		entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source));
-		entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)&eth->h_source[4]));
+		entry.ipv4_hnapt.dmac_hi = swab32(*((u32 *)hw_path->eth_dest));
+		entry.ipv4_hnapt.dmac_lo = swab16(*((u16 *)&hw_path->eth_dest[4]));
+		entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)hw_path->eth_src));
+		entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)&hw_path->eth_src[4]));
 		entry.ipv4_hnapt.pppoe_id = hw_path->pppoe_sid;
 		break;
 	case IPV4_DSLITE:
@@ -1145,18 +1271,17 @@
 	case IPV6_3T_ROUTE:
 	case IPV6_HNAPT:
 	case IPV6_HNAT:
-		entry.ipv6_5t_route.dmac_hi = swab32(*((u32 *)eth->h_dest));
-		entry.ipv6_5t_route.dmac_lo = swab16(*((u16 *)&eth->h_dest[4]));
-		entry.ipv6_5t_route.smac_hi = swab32(*((u32 *)eth->h_source));
-		entry.ipv6_5t_route.smac_lo =
-			swab16(*((u16 *)&eth->h_source[4]));
+		entry.ipv6_5t_route.dmac_hi = swab32(*((u32 *)hw_path->eth_dest));
+		entry.ipv6_5t_route.dmac_lo = swab16(*((u16 *)&hw_path->eth_dest[4]));
+		entry.ipv6_5t_route.smac_hi = swab32(*((u32 *)hw_path->eth_src));
+		entry.ipv6_5t_route.smac_lo = swab16(*((u16 *)&hw_path->eth_src[4]));
 		entry.ipv6_5t_route.pppoe_id = hw_path->pppoe_sid;
 		break;
 	}
 	return entry;
 }
 
-struct foe_entry ppe_fill_info_blk(struct ethhdr *eth, struct foe_entry entry,
+struct foe_entry ppe_fill_info_blk(struct foe_entry entry,
 				   struct flow_offload_hw_path *hw_path)
 {
 	entry.bfib1.psn = (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) ? 1 : 0;
@@ -1172,7 +1297,7 @@
 	case IPV4_HNAPT:
 	case IPV4_HNAT:
 		if (hnat_priv->data->mcast &&
-		    is_multicast_ether_addr(&eth->h_dest[0])) {
+		    is_multicast_ether_addr(&hw_path->eth_dest[0])) {
 			entry.ipv4_hnapt.iblk2.mcast = 1;
 			if (hnat_priv->data->version == MTK_HNAT_V1_3) {
 				entry.bfib1.sta = 1;
@@ -1195,7 +1320,7 @@
 	case IPV6_HNAPT:
 	case IPV6_HNAT:
 		if (hnat_priv->data->mcast &&
-		    is_multicast_ether_addr(&eth->h_dest[0])) {
+		    is_multicast_ether_addr(&hw_path->eth_dest[0])) {
 			entry.ipv6_5t_route.iblk2.mcast = 1;
 			if (hnat_priv->data->version == MTK_HNAT_V1_3) {
 				entry.bfib1.sta = 1;
@@ -1215,56 +1340,293 @@
 	return entry;
 }
 
-static struct ethhdr *get_ipv6_ipip_ethhdr(struct sk_buff *skb,
+static inline void hnat_get_filled_unbind_entry(struct sk_buff *skb,
+						struct foe_entry *entry)
+{
+	if (unlikely(!skb || !entry))
+		return;
+
+	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;
+}
+
+/*
+ * 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 ethhdr *eth;
-	u16 eth_pppoe_hlen = ETH_HLEN + PPPOE_SES_HLEN;
+	struct dst_entry *dst = skb_dst(skb);
 
-	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE)
-		eth = (struct ethhdr *)(skb->data - eth_pppoe_hlen);
-	else
-		eth = (struct ethhdr *)(skb->data - ETH_HLEN);
+	if ((skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL))) {
+		if (!tnl_toggle)
+			return -1;
+
+		/* 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;
+	}
+
+	/* no need for tunnel encapsulation or crypto encryption */
+	return 0;
+}
+
+static inline void hnat_fill_offload_engine_entry(struct sk_buff *skb,
+						  struct foe_entry *entry,
+						  const struct net_device *dev)
+{
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+	if (!tnl_toggle)
+		return;
+
+	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) {
+		/*
+		 * if skb_hnat_tops(skb) is setup for encapsulation,
+		 * we fill in hnat tport and tops_entry for tunnel encapsulation
+		 * offloading
+		 */
+		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);
 
-	return eth;
+	} 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 {
+		return;
+	}
+
+	/*
+	 * outbound flow of offload engines use QID 12
+	 * set its rate limit to line rate
+	 */
+	entry->ipv4_hnapt.iblk2.qid = 12;
+	qos_rate_limit_set(12, dev);
+#endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */
 }
 
+int hnat_bind_crypto_entry(struct sk_buff *skb, const struct net_device *dev, int fill_inner_info)
+{
+	struct foe_entry *foe;
+	struct foe_entry entry = { 0 };
+	struct ethhdr *eth = eth_hdr(skb);
+	struct iphdr *iph;
+	struct tcpudphdr _ports;
+	const struct tcpudphdr *pptr;
+	u32 gmac = NR_DISCARD;
+	int udp = 0;
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct flow_offload_hw_path hw_path = { .dev = (struct net_device *) dev,
+						.virt_dev = (struct net_device *) dev };
+
+	if (!tnl_toggle) {
+		pr_notice("tnl_toggle is disable now!|\n");
+		return -1;
+	}
+
+	if (!skb_hnat_is_hashed(skb) || skb_hnat_ppe(skb) >= CFG_PPE_NUM)
+		return 0;
+
+	foe = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
+
+	if (entry_hnat_is_bound(foe))
+		return 0;
+
+	if (eth->h_proto != htons(ETH_P_IP))
+		return 0;
+
+	hnat_get_filled_unbind_entry(skb, &entry);
+
+	if (dev->netdev_ops->ndo_flow_offload_check)
+		dev->netdev_ops->ndo_flow_offload_check(&hw_path);
+
+	if (skb_hnat_tops(skb) && mtk_tnl_encap_offload)
+		mtk_tnl_encap_offload(skb, eth);
+
+	/* For packets pass through VTI (route-based IPSec),
+	 * We need to fill the inner packet info into hnat entry.
+	 * Since the skb->mac_header is not pointed to correct position
+	 * in skb_to_hnat_info().
+	 */
+	if (fill_inner_info) {
+		iph = ip_hdr(skb);
+		switch (iph->protocol) {
+		case IPPROTO_UDP:
+			udp = 1;
+			/* fallthrough */
+		case IPPROTO_TCP:
+			entry.ipv4_hnapt.etype = htons(ETH_P_IP);
+			if (IS_IPV4_GRP(&entry)) {
+				entry.ipv4_hnapt.iblk2.dscp = iph->tos;
+				if (hnat_priv->data->per_flow_accounting)
+					entry.ipv4_hnapt.iblk2.mibf = 1;
+
+				entry.ipv4_hnapt.vlan1 = hw_path.vlan_id;
+
+				if (skb_vlan_tagged(skb)) {
+					entry.bfib1.vlan_layer += 1;
+
+					if (entry.ipv4_hnapt.vlan1)
+						entry.ipv4_hnapt.vlan2 =
+							skb->vlan_tci;
+					else
+						entry.ipv4_hnapt.vlan1 =
+							skb->vlan_tci;
+				}
+
+				entry.ipv4_hnapt.sip = foe->ipv4_hnapt.sip;
+				entry.ipv4_hnapt.dip = foe->ipv4_hnapt.dip;
+				entry.ipv4_hnapt.sport = foe->ipv4_hnapt.sport;
+				entry.ipv4_hnapt.dport = foe->ipv4_hnapt.dport;
+
+				entry.ipv4_hnapt.new_sip = ntohl(iph->saddr);
+				entry.ipv4_hnapt.new_dip = ntohl(iph->daddr);
+
+				if (IS_IPV4_HNAPT(&entry)) {
+					pptr = skb_header_pointer(skb, iph->ihl * 4 + ETH_HLEN,
+								  sizeof(_ports),
+								  &_ports);
+					if (unlikely(!pptr))
+						return -1;
+
+					entry.ipv4_hnapt.new_sport = ntohs(pptr->src);
+					entry.ipv4_hnapt.new_dport = ntohs(pptr->dst);
+				}
+			} else
+				return 0;
+
+			entry.ipv4_hnapt.bfib1.udp = udp;
+
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+				entry.ipv4_hnapt.eg_keep_ecn = 1;
+				entry.ipv4_hnapt.eg_keep_dscp = 1;
+#endif
+			break;
+
+		default:
+			return -1;
+		}
+	}
+
+	entry = ppe_fill_info_blk(entry, &hw_path);
+
+	if (IS_LAN(dev)) {
+		if (IS_BOND(dev))
+			gmac = ((skb_hnat_entry(skb) >> 1) % hnat_priv->gmac_num) ?
+				 NR_GMAC2_PORT : NR_GMAC1_PORT;
+		else
+			gmac = NR_GMAC1_PORT;
+	} else if (IS_LAN2(dev)) {
+		gmac = (mac->id == MTK_GMAC2_ID) ? NR_GMAC2_PORT : NR_GMAC3_PORT;
+	} else if (IS_WAN(dev)) {
+		if (IS_GMAC1_MODE)
+			gmac = NR_GMAC1_PORT;
+		else
+			gmac = (mac->id == MTK_GMAC2_ID) ? NR_GMAC2_PORT : NR_GMAC3_PORT;
+	} else {
+		pr_notice("Unknown case of dp, iif=%x --> %s\n", skb_hnat_iface(skb), dev->name);
+		return -1;
+	}
+
+	entry.ipv4_hnapt.iblk2.mibf = 1;
+	entry.ipv4_hnapt.iblk2.dp = gmac;
+	entry.ipv4_hnapt.iblk2.port_mg =
+		(hnat_priv->data->version == MTK_HNAT_V1_1) ? 0x3f : 0;
+	entry.bfib1.ttl = 1;
+	entry.bfib1.state = BIND;
+
+	hnat_fill_offload_engine_entry(skb, &entry, dev);
+
+	if (!skb_hnat_tops(skb)) {
+		entry.ipv4_hnapt.dmac_hi = swab32(*((u32 *)eth->h_dest));
+		entry.ipv4_hnapt.dmac_lo = swab16(*((u16 *)&eth->h_dest[4]));
+		entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source));
+		entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)&eth->h_source[4]));
+	}
+
+	/* wait for entry written done */
+	wmb();
+
+	if (entry_hnat_is_bound(foe))
+		return 0;
+
+	spin_lock(&hnat_priv->entry_lock);
+	memcpy(foe, &entry, sizeof(entry));
+	spin_unlock(&hnat_priv->entry_lock);
+
+	if (hnat_priv->data->per_flow_accounting &&
+	    skb_hnat_entry(skb) < hnat_priv->foe_etry_num &&
+	    skb_hnat_ppe(skb) < CFG_PPE_NUM)
+		memset(&hnat_priv->acct[skb_hnat_ppe(skb)][skb_hnat_entry(skb)],
+		       0, sizeof(struct mib_entry));
+
+	return 0;
+}
+EXPORT_SYMBOL(hnat_bind_crypto_entry);
+
 static unsigned int skb_to_hnat_info(struct sk_buff *skb,
 				     const struct net_device *dev,
 				     struct foe_entry *foe,
 				     struct flow_offload_hw_path *hw_path)
 {
 	struct net_device *master_dev = (struct net_device *)dev;
+	struct net_device *slave_dev[10];
+	struct list_head *iter;
 	struct foe_entry entry = { 0 };
-	int whnat = IS_WHNAT(dev);
 	struct mtk_mac *mac;
-	struct ethhdr *eth;
 	struct iphdr *iph;
 	struct ipv6hdr *ip6h;
 	struct tcpudphdr _ports;
 	const struct tcpudphdr *pptr;
 	struct nf_conn *ct;
 	enum ip_conntrack_info ctinfo;
+	int whnat = IS_WHNAT(dev);
 	int gmac = NR_DISCARD;
 	int udp = 0;
 	int port_id = 0;
 	u32 qid = 0;
 	u32 payload_len = 0;
 	int mape = 0;
-
-	if (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPIP)
-		/* point to ethernet header for DS-Lite and MapE */
-		eth = get_ipv6_ipip_ethhdr(skb, hw_path);
-	else if (ip_hdr(skb)->protocol == IPPROTO_IPV6)
-		/* 6RD LAN->WAN(6to4) */
-		eth = (struct ethhdr *)(skb->data - ETH_HLEN);
-	else
-		eth = eth_hdr(skb);
+	int ret;
+	int i = 0;
+	u16 h_proto = 0;
 
 	/*do not bind multicast if PPE mcast not enable*/
-	if (!hnat_priv->data->mcast && is_multicast_ether_addr(eth->h_dest))
+	if (!hnat_priv->data->mcast && is_multicast_ether_addr(hw_path->eth_dest))
 		return 0;
 
+	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*/
 	entry.bfib1.state = foe->udib1.state;
 
@@ -1275,7 +1637,14 @@
 	entry.bfib1.sp = foe->udib1.sp;
 #endif
 
-	switch (ntohs(eth->h_proto)) {
+	if (ip_hdr(skb)->version == IPVERSION_V4)
+		h_proto = ETH_P_IP;
+	else if (ip_hdr(skb)->version == IPVERSION_V6)
+		h_proto = ETH_P_IPV6;
+	else
+		return 0;
+
+	switch (h_proto) {
 	case ETH_P_IP:
 		iph = ip_hdr(skb);
 		/* Do not bind if pkt is fragmented */
@@ -1436,10 +1805,11 @@
 		default:
 			return -1;
 		}
-		trace_printk(
-			"[%s]skb->head=%p, skb->data=%p,ip_hdr=%p, skb->len=%d, skb->data_len=%d\n",
-			__func__, skb->head, skb->data, iph, skb->len,
-			skb->data_len);
+		if (debug_level >= 7)
+			trace_printk(
+				"[%s]skb->head=%p, skb->data=%p,ip_hdr=%p, skb->len=%d, skb->data_len=%d\n",
+				__func__, skb->head, skb->data, iph, skb->len,
+				skb->data_len);
 		break;
 
 	case ETH_P_IPV6:
@@ -1684,11 +2054,11 @@
 		default:
 			return -1;
 		}
-
-		trace_printk(
-			"[%s]skb->head=%p, skb->data=%p,ipv6_hdr=%p, skb->len=%d, skb->data_len=%d\n",
-			__func__, skb->head, skb->data, ip6h, skb->len,
-			skb->data_len);
+		if (debug_level >= 7)
+			trace_printk(
+				"[%s]skb->head=%p, skb->data=%p,ipv6_hdr=%p, skb->len=%d, skb->data_len=%d\n",
+				__func__, skb->head, skb->data, ip6h, skb->len,
+				skb->data_len);
 		break;
 
 	default:
@@ -1696,26 +2066,49 @@
 	}
 
 	/* Fill Layer2 Info.*/
-	entry = ppe_fill_L2_info(eth, entry, hw_path);
+	entry = ppe_fill_L2_info(entry, hw_path);
 
+	if ((skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL) ||
+	    (!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:
 	/* Fill Info Blk*/
-	entry = ppe_fill_info_blk(eth, entry, hw_path);
+	entry = ppe_fill_info_blk(entry, hw_path);
 
 	if (IS_LAN_GRP(dev) || IS_WAN(dev)) { /* Forward to GMAC Ports */
+		if (IS_BOND(dev)) {
+			/* Retrieve subordinate devices that are connected to the Bond device */
+			netdev_for_each_lower_dev(master_dev, slave_dev[i], iter) {
+				/* Check the link status of the slave device */
+				if (!(slave_dev[i]->flags & IFF_UP) ||
+				    !netif_carrier_ok(slave_dev[i]))
+					continue;
+				i++;
+				if (i >= ARRAY_SIZE(slave_dev))
+					break;
+			}
+			if (i > 0) {
+				i = (skb_hnat_entry(skb) >> 1) % i;
+				if (i >= 0 && i < ARRAY_SIZE(slave_dev)) {
+					/* Choose a subordinate device by hash index */
+					dev = slave_dev[i];
+					master_dev = slave_dev[i];
+				}
+			}
+		}
 		port_id = hnat_dsa_get_port(&master_dev);
 		if (port_id >= 0) {
 			if (hnat_dsa_fill_stag(dev, &entry, hw_path,
-					       ntohs(eth->h_proto), mape) < 0)
+					       h_proto, mape) < 0)
 				return 0;
 		}
 
 		mac = netdev_priv(master_dev);
 		gmac = HNAT_GMAC_FP(mac->id);
 
-		if (IS_LAN(dev) && IS_BOND_MODE) {
-			gmac = ((skb_hnat_entry(skb) >> 1) % hnat_priv->gmac_num) ?
-				 NR_GMAC2_PORT : NR_GMAC1_PORT;
-		} else if (IS_WAN(dev) && mape_toggle && mape == 1) {
+		if (IS_WAN(dev) && mape_toggle && mape == 1) {
 			gmac = NR_PDMA_PORT;
 			/* Set act_dp = wan_dev */
 			entry.ipv4_hnapt.act_dp &= ~UDF_PINGPONG_IFIDX;
@@ -1733,8 +2126,9 @@
 				entry.ipv4_hnapt.vlan1 = 2;
 		}
 
-		trace_printk("learn of lan or wan(iif=%x) --> %s(ext)\n",
-			     skb_hnat_iface(skb), dev->name);
+		if (debug_level >= 7)
+			trace_printk("learn of lan or wan(iif=%x) --> %s(ext)\n",
+				     skb_hnat_iface(skb), dev->name);
 		/* To CPU then stolen by pre-routing hant hook of LAN/WAN
 		 * Current setting is PDMA RX.
 		 */
@@ -1750,7 +2144,7 @@
 		gmac = -EINVAL;
 	}
 
-	if (gmac < 0) {
+	if ((gmac < 0) && (debug_level >= 7)) {
 		printk_ratelimited(KERN_WARNING
 				   "Unknown case of dp, iif=%x --> %s\n",
 				   skb_hnat_iface(skb), dev->name);
@@ -1765,7 +2159,7 @@
 		qid = 0;
 
 	if (IS_PPPQ_MODE && IS_PPPQ_PATH(dev, skb)) {
-		if (ntohs(eth->h_proto) == ETH_P_IP) {
+		if (h_proto == ETH_P_IP) {
 			iph = ip_hdr(skb);
 			if (iph->protocol == IPPROTO_TCP) {
 				skb_set_transport_header(skb, sizeof(struct iphdr));
@@ -1775,7 +2169,7 @@
 				if (payload_len == 0)
 					qid += 6;
 			}
-		} else if (ntohs(eth->h_proto) == ETH_P_IPV6) {
+		} else if (h_proto == ETH_P_IPV6) {
 			ip6h = ipv6_hdr(skb);
 			if (ip6h->nexthdr == NEXTHDR_TCP) {
 				skb_set_transport_header(skb, sizeof(struct ipv6hdr));
@@ -1885,6 +2279,10 @@
 		}
 	}
 
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+	hnat_fill_offload_engine_entry(skb, &entry, dev);
+#endif
+
 	/* The INFO2.port_mg and 2nd VLAN ID fields of PPE entry are redefined
 	 * by Wi-Fi whnat engine. These data and INFO2.dp will be updated and
 	 * the entry is set to BIND state in mtk_sw_nat_hook_tx().
@@ -1922,6 +2320,7 @@
 		return 0;
 	}
 
+hnat_entry_skip_bind:
 	if (spin_trylock(&hnat_priv->entry_lock)) {
 		/* Final check if the entry is not in UNBIND state,
 		 * we should not modify it right now.
@@ -1989,11 +2388,12 @@
 	    !is_magic_tag_valid(skb) || !IS_SPACE_AVAILABLE_HEAD(skb))
 		goto check_release_entry_lock;
 
-	trace_printk(
-		"[%s]entry=%x reason=%x gmac_no=%x wdmaid=%x rxid=%x wcid=%x bssid=%x\n",
-		__func__, skb_hnat_entry(skb), skb_hnat_reason(skb), gmac_no,
-		skb_hnat_wdma_id(skb), skb_hnat_bss_id(skb),
-		skb_hnat_wc_id(skb), skb_hnat_rx_id(skb));
+	if (debug_level >= 7)
+		trace_printk(
+			"[%s]entry=%x reason=%x gmac_no=%x wdmaid=%x rxid=%x wcid=%x bssid=%x\n",
+			__func__, skb_hnat_entry(skb), skb_hnat_reason(skb), gmac_no,
+			skb_hnat_wdma_id(skb), skb_hnat_bss_id(skb),
+			skb_hnat_wc_id(skb), skb_hnat_rx_id(skb));
 
 	if ((gmac_no != NR_WDMA0_PORT) && (gmac_no != NR_WDMA1_PORT) &&
 	    (gmac_no != NR_WDMA2_PORT) && (gmac_no != NR_WHNAT_WDMA_PORT))
@@ -2040,6 +2440,12 @@
 	switch ((int)entry.bfib1.pkt_type) {
 	case IPV4_HNAPT:
 	case IPV4_HNAT:
+		/*
+		 * skip if packet is an encap tnl packet or it may
+		 * screw up inner mac header
+		 */
+		if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
+			break;
 		entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source));
 		entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)&eth->h_source[4]));
 		break;
@@ -2231,6 +2637,10 @@
 		entry.ipv6_5t_route.iblk2.dp = gmac_no & 0xf;
 	}
 
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+	hnat_fill_offload_engine_entry(skb, &entry, NULL);
+#endif
+
 	entry.bfib1.ttl = 1;
 	entry.bfib1.state = BIND;
 
@@ -2335,6 +2745,9 @@
 
 	skb_hnat_alg(skb) = 0;
 	skb_hnat_filled(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)
@@ -2421,7 +2834,8 @@
 	 * 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);
@@ -2762,10 +3176,12 @@
 		  struct flow_offload_hw_path *),
 	const char *func)
 {
+	struct ethhdr eth = {0};
 	struct foe_entry *entry;
 	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;
+	bool is_virt_dev = false;
 
 	if (xlat_toggle && !mtk_464xlat_post_process(skb, out))
 		return 0;
@@ -2785,14 +3201,42 @@
 
 	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) {
+			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;
+			}
+		}
 	}
 
+	/* 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;
+
+	if (is_virt_dev
+	    && !(skb_hnat_tops(skb) && skb_hnat_is_encap(skb)
+		 && (hw_path.flags & FLOW_OFFLOAD_PATH_TNL)))
 		return 0;
 
-	trace_printk("[%s] case hit, %x-->%s, reason=%x\n", __func__,
-		     skb_hnat_iface(skb), out->name, skb_hnat_reason(skb));
+	if (debug_level >= 7)
+		trace_printk("[%s] case hit, %x-->%s, reason=%x\n", __func__,
+			     skb_hnat_iface(skb), out->name, skb_hnat_reason(skb));
 
 	if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num ||
 	    skb_hnat_ppe(skb) >= CFG_PPE_NUM)
@@ -2808,8 +3252,29 @@
 		if (fn && !mtk_hnat_accel_type(skb))
 			break;
 
-		if (fn && fn(skb, arp_dev, &hw_path))
-			break;
+		if (!is_virt_dev) {
+			if (!fn) {
+				memcpy(hw_path.eth_dest, eth_hdr(skb)->h_dest, ETH_ALEN);
+				memcpy(hw_path.eth_src, eth_hdr(skb)->h_source, ETH_ALEN);
+			} else if (fn(skb, arp_dev, &hw_path)) {
+				break;
+			}
+		}
+		/* skb_hnat_tops(skb) is updated in mtk_tnl_offload() */
+		if (skb_hnat_tops(skb)) {
+			memcpy(eth.h_dest, hw_path.eth_dest, ETH_ALEN);
+			memcpy(eth.h_source, hw_path.eth_src, ETH_ALEN);
+			if (ip_hdr(skb)->version == IPVERSION_V4)
+				eth.h_proto = htons(ETH_P_IP);
+			else if (ip_hdr(skb)->version == IPVERSION_V6)
+				eth.h_proto = htons(ETH_P_IPV6);
+
+			if (skb_hnat_is_encap(skb) && !is_virt_dev &&
+			    mtk_tnl_encap_offload && mtk_tnl_encap_offload(skb, &eth))
+				break;
+			if (skb_hnat_is_decap(skb))
+				break;
+		}
 
 		skb_to_hnat_info(skb, out, entry, &hw_path);
 		break;
@@ -2936,14 +3401,14 @@
 		return NF_ACCEPT;
 
 drop:
-	if (skb)
-		trace_printk(
-			"%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n"
-			"sport=0x%x, reason=0x%x, alg=0x%x)\n",
-			__func__, skb_hnat_iface(skb), state->out->name,
-			HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
-			skb_hnat_sport(skb), skb_hnat_reason(skb),
-			skb_hnat_alg(skb));
+	if (skb && (debug_level >= 7))
+		printk_ratelimited(KERN_WARNING
+				   "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n"
+				   "sport=0x%x, reason=0x%x, alg=0x%x)\n",
+				   __func__, skb_hnat_iface(skb), state->out->name,
+				   HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
+				   skb_hnat_sport(skb), skb_hnat_reason(skb),
+				   skb_hnat_alg(skb));
 
 	return NF_DROP;
 }
@@ -2962,14 +3427,14 @@
 		return NF_ACCEPT;
 
 drop:
-	if (skb)
-		trace_printk(
-			"%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n"
-			"sport=0x%x, reason=0x%x, alg=0x%x)\n",
-			__func__, skb_hnat_iface(skb), state->out->name,
-			HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
-			skb_hnat_sport(skb), skb_hnat_reason(skb),
-			skb_hnat_alg(skb));
+	if (skb && (debug_level >= 7))
+		printk_ratelimited(KERN_WARNING
+				   "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n"
+				   "sport=0x%x, reason=0x%x, alg=0x%x)\n",
+				   __func__, skb_hnat_iface(skb), state->out->name,
+				   HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
+				   skb_hnat_sport(skb), skb_hnat_reason(skb),
+				   skb_hnat_alg(skb));
 
 	return NF_DROP;
 }
@@ -3014,14 +3479,14 @@
 	return NF_ACCEPT;
 
 drop:
-	if (skb)
+	if (skb && (debug_level >= 7))
 		printk_ratelimited(KERN_WARNING
-			"%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n"
-			"sport=0x%x, reason=0x%x, alg=0x%x)\n",
-			__func__, state->in->name, skb_hnat_iface(skb),
-			HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
-			skb_hnat_sport(skb), skb_hnat_reason(skb),
-			skb_hnat_alg(skb));
+				   "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n"
+				   "sport=0x%x, reason=0x%x, alg=0x%x)\n",
+				   __func__, state->in->name, skb_hnat_iface(skb),
+				   HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
+				   skb_hnat_sport(skb), skb_hnat_reason(skb),
+				   skb_hnat_alg(skb));
 
 	return NF_DROP;
 }
@@ -3042,14 +3507,14 @@
 		return NF_ACCEPT;
 
 drop:
-	if (skb)
-		trace_printk(
-			"%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n"
-			"sport=0x%x, reason=0x%x, alg=0x%x)\n",
-			__func__, skb_hnat_iface(skb), state->out->name,
-			HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
-			skb_hnat_sport(skb), skb_hnat_reason(skb),
-			skb_hnat_alg(skb));
+	if (skb && (debug_level >= 7))
+		printk_ratelimited(KERN_WARNING
+				   "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n"
+				   "sport=0x%x, reason=0x%x, alg=0x%x)\n",
+				   __func__, skb_hnat_iface(skb), state->out->name,
+				   HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb),
+				   skb_hnat_sport(skb), skb_hnat_reason(skb),
+				   skb_hnat_alg(skb));
 
 	return NF_DROP;
 }
@@ -3081,6 +3546,9 @@
 	if (iph->protocol == IPPROTO_IPV6) {
 		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_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);
 	}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
index d8e5709..d48175f 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
@@ -60,20 +60,51 @@
 	if (IS_ERR(dp))
 		return -ENODEV;
 
-	dsa_tag = BIT(port_index);
+	if (IS_DSA_TAG_PROTO_MXL862_8021Q(dp)) {
+		dsa_tag = port_index + BIT(11);
 
-	if (!entry->bfib1.vlan_layer)
-		entry->bfib1.vlan_layer = 1;
-	else
-		/* VLAN existence indicator */
-		dsa_tag |= BIT(8);
+		if (IS_IPV4_GRP(entry)) {
+			/* PPE can only be filled up to 2 VLAN layers,
+			 * outer VLAN(vlan1) is preserved for stag.
+			 */
+			if (unlikely(entry->ipv4_hnapt.vlan2))
+				return -EINVAL;
+			else if (entry->ipv4_hnapt.vlan1)
+				/* Move to inner VLAN if it's already set */
+				entry->ipv4_hnapt.vlan2 = entry->ipv4_hnapt.vlan1;
+			entry->ipv4_hnapt.vlan1 = dsa_tag;
 
-	if (IS_IPV4_GRP(entry))
-		entry->ipv4_hnapt.etype = dsa_tag;
-	else
-		entry->ipv6_5t_route.etype = dsa_tag;
+			entry->bfib1.vlan_layer = (entry->ipv4_hnapt.vlan1 != 0) +
+						  (entry->ipv4_hnapt.vlan2 != 0);
+		} else {
+			if (unlikely(entry->ipv6_5t_route.vlan2))
+				return -EINVAL;
+			else if (entry->ipv6_5t_route.vlan1)
+				/* Move to inner VLAN if it's already set */
+				entry->ipv6_5t_route.vlan2 = entry->ipv6_5t_route.vlan1;
+			entry->ipv6_5t_route.vlan1 = dsa_tag;
+
+			entry->bfib1.vlan_layer = (entry->ipv6_5t_route.vlan1 != 0) +
+						  (entry->ipv6_5t_route.vlan2 != 0);
+		}
+
+		entry->bfib1.vpm = 1;
+	} else {
+		dsa_tag = BIT(port_index);
+
+		if (!entry->bfib1.vlan_layer)
+			entry->bfib1.vlan_layer = 1;
+		else
+			/* VLAN existence indicator */
+			dsa_tag |= BIT(8);
+
+		if (IS_IPV4_GRP(entry))
+			entry->ipv4_hnapt.etype = dsa_tag;
+		else
+			entry->ipv6_5t_route.etype = dsa_tag;
 
-	entry->bfib1.vpm = 0;
+		entry->bfib1.vpm = 0;
+	}
 
 	return port_index;
 #else
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
index 0c4c7ed..21212b7 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
@@ -44,7 +44,11 @@
 	u32 is_sp : 1;
 	u32 hf : 1;
 	u32 amsdu : 1;
-	u32 resv3 : 19;
+	u32 tops : 6;
+	u32 is_decap : 1;
+	u32 cdrt : 8;
+	u32 is_decrypt : 1;
+	u32 resv3 : 3;
 	u32 magic_tag_protect : 16;
 } __packed;
 #elif defined(CONFIG_MEDIATEK_NETSYS_RX_V2)
@@ -92,6 +96,29 @@
 	((((skb_headroom(skb) >= FOE_INFO_LEN) ? 1 : 0)))
 
 #define skb_hnat_info(skb) ((struct hnat_desc *)(skb->head))
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+#define skb_hnat_tops(skb) (((struct hnat_desc *)((skb)->head))->tops)
+#define skb_hnat_is_decap(skb) (((struct hnat_desc *)((skb)->head))->is_decap)
+#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_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)
+#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_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)
 #define skb_hnat_entry(skb) (((struct hnat_desc *)(skb->head))->entry)
@@ -166,6 +193,8 @@
 #define HIT_PRE_BIND 0x1A
 #define HIT_BIND_PACKET_SAMPLING 0x1B
 #define HIT_BIND_EXCEED_MTU 0x1C
+#define IPVERSION_V4 0x04
+#define IPVERSION_V6 0x06
 
 #define TPORT_ID(x) ((x) & GENMASK(3, 0))
 #define TOPS_ENTRY(x) ((x) & GENMASK(5, 0))
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
index 51a9b1f..f33542c 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
@@ -45,7 +45,7 @@
 
 	np = of_parse_phandle(r, "mediatek,xfi_pll", 0);
 	if (!np)
-		return -1;
+		return 0;
 
 	ss->pll = syscon_node_to_regmap(np);
 	if (IS_ERR(ss->pll))
@@ -479,7 +479,7 @@
 
 	mutex_lock(&mpcs->regmap_lock);
 
-	if (mode >= 0 && mpcs->interface != interface) {
+	if (mode <= MLO_AN_INBAND && mpcs->interface != interface) {
 		mpcs->interface = interface;
 		mpcs->mode = mode;
 		linkmode_copy(mpcs->advertising, advertising);
@@ -552,7 +552,7 @@
 		goto exit;
 
 	if (!mtk_sgmii_link_status(mpcs))
-		mtk_sgmii_pcs_config(&mpcs->pcs, -1, mpcs->interface,
+		mtk_sgmii_pcs_config(&mpcs->pcs, UINT_MAX, mpcs->interface,
 				     mpcs->advertising, false);
 
 exit:
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 85aae88..a981b27 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
@@ -41,7 +41,7 @@
 
 	np = of_parse_phandle(r, "mediatek,xfi_pll", 0);
 	if (!np)
-		return -1;
+		return 0;
 
 	for (i = 0; i < MTK_MAX_DEVS; i++) {
 		ss->pll = syscon_node_to_regmap(np);
@@ -60,7 +60,7 @@
 
 	np = of_parse_phandle(r, "mediatek,toprgu", 0);
 	if (!np)
-		return -1;
+		return 0;
 
 	eth->toprgu = syscon_node_to_regmap(np);
 	if (IS_ERR(eth->toprgu))
@@ -710,7 +710,7 @@
 
 	mutex_lock(&mpcs->regmap_lock);
 
-	if (mode >= 0 && mpcs->interface != interface) {
+	if (mode <= MLO_AN_INBAND && mpcs->interface != interface) {
 		mpcs->interface = interface;
 		mpcs->mode = mode;
 		mode_changed = true;
@@ -786,7 +786,7 @@
 		goto exit;
 
 	if (!mtk_usxgmii_link_status(mpcs) || !mtk_usxgmii_is_valid_ctle(mpcs))
-		mtk_usxgmii_pcs_config(&mpcs->pcs, -1,
+		mtk_usxgmii_pcs_config(&mpcs->pcs, UINT_MAX,
 				       mpcs->interface, NULL, false);
 
 exit:
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/an8801.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/an8801.c
index 9b8f0e5..897baa8 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/an8801.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/an8801.c
@@ -67,13 +67,23 @@
 	"\nUsage: echo w [pbus_addr] [pbus_reg] [value] > /sys/" \
 	"kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \
 	"\n       echo r [pbus_addr] [pbus_reg] > /sys/" \
-	"kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \
+	"kernel/debug/mdio-bus\':[phy_addr]/pbus_op" \
 	"\nRead example: PBUS addr 0x19, Register 0x19a4" \
 	"\necho r 19 19a4 > /sys/" \
 	"kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \
 	"\nWrite example: PBUS addr 0x19, Register 0xcf8 0x1a01503" \
 	"\necho w 19 cf8 1a01503> /sys/" \
-	"kernel/debug/mdio-bus\':[phy_addr]/pbus_reg_op" \
+	"kernel/debug/mdio-bus\':[phy_addr]/pbus_op" \
+	"\n"
+#define AN8801_DEBUGFS_MDIO_HELP_STRING \
+	"\nUsage: echo cl22 w [phy_reg] [value]> /sys/" \
+	"kernel/debug/mdio-bus\':[phy_addr]/mdio" \
+	"\n       echo cl22 r [phy_reg] > /sys/" \
+	"kernel/debug/mdio-bus\':[phy_addr]/mdio" \
+	"\nUsage: echo cl45 w [devad] [phy_reg] [value]> /sys/" \
+	"kernel/debug/mdio-bus\':[phy_addr]/mdio" \
+	"\n       echo cl45 r [devad] [phy_reg] > /sys/" \
+	"kernel/debug/mdio-bus\':[phy_addr]/mdio" \
 	"\n"
 #endif
 
@@ -83,6 +93,17 @@
  *	GPIO3    <-> LED2,
  */
 /* User-defined.B */
+#define R50_SHIFT (-7)
+static const u16 r50ohm_table[] = {
+127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+127, 127, 127, 127, 127, 127, 127, 127, 127, 124,
+120, 116, 112, 108, 104, 100,  96,  93,  90,  86,
+84,   80,  77,  74,  72,  68,  65,  64,  61,  59,
+56,   54,  52,  48,  48,  45,  43,  40,  39,  36,
+35,   32,  32,  30,  28,  26,  24,  23,  21,  20,
+18,   16,  16,  14
+};
+static const u16 r50ohm_table_size = sizeof(r50ohm_table)/sizeof(u16);
 static const struct AIR_LED_CFG_T led_cfg_dlt[MAX_LED_SIZE] = {
 //   LED Enable,          GPIO,    LED Polarity,      LED ON,    LED Blink
 	/* LED0 */
@@ -92,7 +113,6 @@
 	/* LED2 */
 	{LED_ENABLE, AIR_LED_GPIO9, AIR_ACTIVE_LOW,  AIR_LED2_ON, AIR_LED2_BLK},
 };
-
 static const u16 led_blink_cfg_dlt = AIR_LED_BLK_DUR_64M;
 /* RGMII delay */
 static const u8 rxdelay_force = FALSE;
@@ -113,16 +133,12 @@
 	struct mii_bus *mbus = phydev_mdiobus(phydev);
 
 	err = mbus->write(mbus, phy_addr, 0x1F, 4);
-	if (err)
-		return err;
-
 	err |= mbus->write(mbus, phy_addr, 0x10, 0);
 	err |= mbus->write(mbus, phy_addr, 0x11, (u16)(addr >> 16));
 	err |= mbus->write(mbus, phy_addr, 0x12, (u16)(addr & 0xffff));
 	err |= mbus->write(mbus, phy_addr, 0x13, (u16)(data >> 16));
 	err |= mbus->write(mbus, phy_addr, 0x14, (u16)(data & 0xffff));
 	err |= mbus->write(mbus, phy_addr, 0x1F, 0);
-
 	return err;
 }
 
@@ -134,16 +150,15 @@
 	struct mii_bus *mbus = phydev_mdiobus(phydev);
 
 	err = mbus->write(mbus, phy_addr, 0x1F, 4);
-	if (err)
-		return err;
-
 	err |= mbus->write(mbus, phy_addr, 0x10, 0);
 	err |= mbus->write(mbus, phy_addr, 0x15, (u16)(addr >> 16));
 	err |= mbus->write(mbus, phy_addr, 0x16, (u16)(addr & 0xffff));
+	if (err < 0)
+		return INVALID_DATA;
 	data_h = mbus->read(mbus, phy_addr, 0x17);
 	data_l = mbus->read(mbus, phy_addr, 0x18);
 	err |= mbus->write(mbus, phy_addr, 0x1F, 0);
-	if (err)
+	if (err < 0)
 		return INVALID_DATA;
 
 	data = ((data_h & 0xffff) << 16) | (data_l & 0xffff);
@@ -189,6 +204,36 @@
 	return __air_buckpbus_reg_read(phydev, addr);
 }
 
+int __an8801_modify_cl45_changed(struct phy_device *phydev, int devad, u32 regnum,
+			     u16 mask, u16 set)
+{
+	int new, ret;
+
+	ret = __an8801_cl45_read(phydev, devad, regnum);
+	if (ret < 0)
+		return ret;
+
+	new = (ret & ~mask) | set;
+	if (new == ret)
+		return 0;
+
+	ret = __an8801_cl45_write(phydev, devad, regnum, new);
+
+	return ret < 0 ? ret : 1;
+}
+
+static int an8801_modify_cl45_changed(struct phy_device *phydev, int devad,
+		u32 regnum, u16 mask, u16 set)
+{
+	int err = 0;
+
+	mdiobus_lock(phydev);
+	err = __an8801_modify_cl45_changed(phydev, devad, regnum, mask, set);
+	mdiobus_unlock(phydev);
+
+	return err;
+}
+
 static int an8801_cl45_write(struct phy_device *phydev, int devad, u16 reg,
 			      u16 val)
 {
@@ -307,12 +352,12 @@
 
 	ret = an8801_cl45_write(phydev, 0x1f, LED_BLK_DUR,
 				 LED_BLINK_DURATION(led_blink_cfg));
-	if (ret)
+	if (ret < 0)
 		return ret;
 
 	ret = an8801_cl45_write(phydev, 0x1f, LED_ON_DUR,
 				 (LED_BLINK_DURATION(led_blink_cfg) >> 1));
-	if (ret)
+	if (ret < 0)
 		return ret;
 
 	ret = an8801_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
@@ -359,6 +404,144 @@
 	return 0;
 }
 
+static int findClosestNumber(const u16 *arr, u16 size, u16 target)
+{
+	int left = 0, right = size - 1;
+
+	while (left <= right) {
+		int mid = left + ((right - left) >> 2);
+
+		if (arr[mid] == target)
+			return mid;
+
+		if (arr[mid] < target)
+			right = mid - 1;
+		else
+			left = mid + 1;
+	}
+
+	if (left > size - 1)
+		return (size - 1);
+	else
+		return ((left - 1) >= 0 ? (left - 1) : 0);
+}
+
+static int an8801sb_i2mpb_config(struct phy_device *phydev)
+{
+	int ret = 0;
+	u16 cl45_value = 0, temp_cl45 = 0, set = 0;
+	u16 mask = 0;
+
+	ret = an8801_cl45_read(phydev, MMD_DEV_VSPEC1, 0x12, &cl45_value);
+	dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value);
+	cl45_value = (cl45_value & GENMASK(15, 10)) + (6 << 10);
+	ret = an8801_modify_cl45_changed(phydev, MMD_DEV_VSPEC1, 0x12, GENMASK(15, 10), cl45_value);
+	if (ret < 0)
+		return ret;
+	ret = an8801_cl45_read(phydev, MMD_DEV_VSPEC1, 0x16, &temp_cl45);
+	dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, temp_cl45);
+	mask = GENMASK(15, 10) | GENMASK(5, 0);
+	cl45_value = (temp_cl45 & GENMASK(15, 10)) + (9 << 10);
+	cl45_value = ((temp_cl45 & GENMASK(5, 0)) + 6) | cl45_value;
+	ret = an8801_modify_cl45_changed(phydev, MMD_DEV_VSPEC1, 0x16, mask, cl45_value);
+	if (ret < 0)
+		return ret;
+	ret = an8801_cl45_read(phydev, MMD_DEV_VSPEC1, 0x17, &cl45_value);
+	dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value);
+	cl45_value = (cl45_value & GENMASK(13, 8)) + (6 << 8);
+	ret = an8801_modify_cl45_changed(phydev, MMD_DEV_VSPEC1, 0x17, GENMASK(13, 8), cl45_value);
+	if (ret < 0)
+		return ret;
+	ret = an8801_cl45_read(phydev, MMD_DEV_VSPEC1, 0x18, &temp_cl45);
+	dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, temp_cl45);
+	mask = GENMASK(13, 8) | GENMASK(5, 0);
+	cl45_value = (temp_cl45 & GENMASK(13, 8)) + (9 << 8);
+	cl45_value = ((temp_cl45 & GENMASK(5, 0)) + 6) | cl45_value;
+	ret = an8801_modify_cl45_changed(phydev, MMD_DEV_VSPEC1, 0x18, mask, cl45_value);
+	if (ret < 0)
+		return ret;
+	ret = an8801_cl45_read(phydev, MMD_DEV_VSPEC1, 0x19, &cl45_value);
+	dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value);
+	cl45_value = (cl45_value & GENMASK(13, 8)) + (6 << 8);
+	ret = an8801_modify_cl45_changed(phydev, MMD_DEV_VSPEC1, 0x19, GENMASK(13, 8), cl45_value);
+	if (ret < 0)
+		return ret;
+	ret = an8801_cl45_read(phydev, MMD_DEV_VSPEC1, 0x20, &cl45_value);
+	dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value);
+	cl45_value = (cl45_value & GENMASK(5, 0)) + 6;
+	ret = an8801_modify_cl45_changed(phydev, MMD_DEV_VSPEC1, 0x20, GENMASK(5, 0), cl45_value);
+	if (ret < 0)
+		return ret;
+	ret = an8801_cl45_read(phydev, MMD_DEV_VSPEC1, 0x21, &cl45_value);
+	dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value);
+	cl45_value = (cl45_value & GENMASK(13, 8)) + (6 << 8);
+	ret = an8801_modify_cl45_changed(phydev, MMD_DEV_VSPEC1, 0x21, GENMASK(13, 8), cl45_value);
+	if (ret < 0)
+		return ret;
+	ret = an8801_cl45_read(phydev, MMD_DEV_VSPEC1, 0x22, &cl45_value);
+	dev_dbg(phydev_dev(phydev), "%s:%d cl45_value 0x%x!\n", __func__, __LINE__, cl45_value);
+	cl45_value = (cl45_value & GENMASK(5, 0)) + 6;
+	ret = an8801_modify_cl45_changed(phydev, MMD_DEV_VSPEC1, 0x22, GENMASK(5, 0), cl45_value);
+	if (ret < 0)
+		return ret;
+	ret = an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x23, 0x883);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x24, 0x883);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x25, 0x883);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x26, 0x883);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x0, 0x100);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x1, 0x1bc);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x2, 0x1d0);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x3, 0x186);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x4, 0x202);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x5, 0x20e);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x6, 0x300);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x7, 0x3c0);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x8, 0x3d0);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0x9, 0x317);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0xa, 0x206);
+	ret |= an8801_cl45_write(phydev, MMD_DEV_VSPEC1, 0xb, 0xe);
+	if (ret < 0)
+		return ret;
+
+	dev_info(phydev_dev(phydev), "I2MPB Initialize OK\n");
+	return ret;
+}
+
+void update_r50_value(struct phy_device *phydev,
+	u16 *cl45_value, int pos1, int pos2)
+{
+	*cl45_value &= ~(0x007f << 8);
+	*cl45_value |= ((r50ohm_table[pos1]) & 0x007f) << 8;
+	*cl45_value &= ~(0x007f);
+	*cl45_value |= (r50ohm_table[pos2]) & 0x007f;
+	dev_dbg(phydev_dev(phydev), "Read: r50ohm_tx_1=%d r50ohm_tx_2=%d\n",
+		r50ohm_table[pos1], r50ohm_table[pos2]);
+}
+
+int calculate_position(int pos, int shift, int table_size)
+{
+	if (shift > 0)
+		return (pos + shift < table_size) ? (pos + shift) : (table_size - 1);
+	else
+		return (pos + shift > 0) ? (pos + shift) : 0;
+}
+
+int process_r50(struct phy_device *phydev, int reg,
+	u16 *cl45_value, u16 *r50ohm_tx_a, u16 *r50ohm_tx_b)
+{
+	int pos1 = findClosestNumber(r50ohm_table, r50ohm_table_size, *r50ohm_tx_a);
+	int pos2 = findClosestNumber(r50ohm_table, r50ohm_table_size, *r50ohm_tx_b);
+
+	if (pos1 != -1 && pos2 != -1) {
+		pos1 = calculate_position(pos1, R50_SHIFT, r50ohm_table_size);
+		pos2 = calculate_position(pos2, R50_SHIFT, r50ohm_table_size);
+
+		update_r50_value(phydev, cl45_value, pos1, pos2);
+		return an8801_cl45_write(phydev, 0x1e, reg, *cl45_value);
+	}
+	return 0;
+}
+
 #ifdef CONFIG_OF
 static int an8801r_of_init(struct phy_device *phydev)
 {
@@ -373,10 +556,10 @@
 			return -1;
 		}
 		if (val < AIR_RGMII_DELAY_NOSTEP ||
-		    val > AIR_RGMII_DELAY_STEP_7) {
+			val > AIR_RGMII_DELAY_STEP_7) {
 			dev_err(phydev_dev(phydev),
-				   "airoha,rxclk-delay value %u out of range.",
-				   val);
+					"airoha,rxclk-delay value %u out of range.",
+					val);
 			return -1;
 		}
 		priv->rxdelay_force = TRUE;
@@ -389,14 +572,14 @@
 		if (of_property_read_u32(of_node, "airoha,txclk-delay",
 					 &val) != 0) {
 			dev_err(phydev_dev(phydev),
-				   "airoha,txclk-delay value is invalid.");
+					"airoha,txclk-delay value is invalid.");
 			return -1;
 		}
 		if (val < AIR_RGMII_DELAY_NOSTEP ||
-		    val > AIR_RGMII_DELAY_STEP_7) {
+			val > AIR_RGMII_DELAY_STEP_7) {
 			dev_err(phydev_dev(phydev),
-				   "airoha,txclk-delay value %u out of range.",
-				   val);
+					"airoha,txclk-delay value %u out of range.",
+					val);
 			return -1;
 		}
 		priv->txdelay_force = TRUE;
@@ -411,7 +594,6 @@
 	struct an8801_priv *priv = phydev_cfg(phydev);
 	u32 val = 0;
 
-	priv->pol = AIR_POL_TX_NOR_RX_NOR;
 	if (of_find_property(of_node, "airoha,polarity", NULL)) {
 		if (of_property_read_u32(of_node, "airoha,polarity",
 					 &val) != 0) {
@@ -419,14 +601,32 @@
 			return -1;
 		}
 		if (val < AIR_POL_TX_NOR_RX_REV ||
-		    val > AIR_POL_TX_REV_RX_NOR) {
+			val > AIR_POL_TX_REV_RX_NOR) {
 			dev_err(phydev_dev(phydev),
-				   "airoha,polarity value %u out of range.",
-				   val);
+					"airoha,polarity value %u out of range.",
+					val);
 			return -1;
 		}
 		priv->pol = val;
-	}
+	} else
+		priv->pol = AIR_POL_TX_NOR_RX_NOR;
+
+	if (of_find_property(of_node, "airoha,surge", NULL)) {
+		if (of_property_read_u32(of_node, "airoha,surge",
+					 &val) != 0) {
+			dev_err(phydev_dev(phydev), "airoha,surge value is invalid.");
+			return -1;
+		}
+		if (val < AIR_SURGE_0R ||
+			val > AIR_SURGE_5R) {
+			dev_err(phydev_dev(phydev),
+					"airoha,surge value %u out of range.",
+					val);
+			return -1;
+		}
+		priv->surge = val;
+	} else
+		priv->surge = AIR_SURGE_0R;
 
 	return 0;
 }
@@ -482,6 +682,46 @@
 	return 0;
 }
 
+int an8801sb_surge_protect_cfg(struct phy_device *phydev)
+{
+	int ret = 0;
+	struct device *dev = phydev_dev(phydev);
+	struct an8801_priv *priv = phydev->priv;
+	u16 r50ohm_tx_a = 0, r50ohm_tx_b = 0, r50ohm_tx_c = 0, r50ohm_tx_d = 0;
+	u16 cl45_value = 0;
+
+	if (priv->surge) {
+		ret = an8801_cl45_read(phydev, 0x1e, 0x174, &cl45_value);
+		if (ret < 0)
+			return ret;
+		r50ohm_tx_a = (cl45_value >> 8) & 0x007f;
+		r50ohm_tx_b = cl45_value & 0x007f;
+		dev_dbg(phydev_dev(phydev), "Read: (0x174) value=0x%04x r50ohm_tx_a=%d r50ohm_tx_b=%d\n",
+			cl45_value, r50ohm_tx_a, r50ohm_tx_b);
+		ret = process_r50(phydev, 0x174, &cl45_value, &r50ohm_tx_a, &r50ohm_tx_b);
+		if (ret < 0)
+			return ret;
+		ret = an8801_cl45_read(phydev, 0x1e, 0x175, &cl45_value);
+		if (ret < 0)
+			return ret;
+		r50ohm_tx_c = (cl45_value >> 8) & 0x007f;
+		r50ohm_tx_d = cl45_value & 0x007f;
+		dev_dbg(phydev_dev(phydev), "Read: (0x175) value=0x%04x r50ohm_tx_c=%d r50ohm_tx_d=%d\n",
+			cl45_value, r50ohm_tx_c, r50ohm_tx_d);
+		ret = process_r50(phydev, 0x175, &cl45_value, &r50ohm_tx_c, &r50ohm_tx_d);
+		if (ret < 0)
+			return ret;
+		ret = an8801sb_i2mpb_config(phydev);
+		if (ret < 0) {
+			dev_err(dev, "an8801sb_i2mpb_config fail\n");
+			return ret;
+		}
+		dev_info(dev, "surge protection mode - 5R\n");
+	} else
+		dev_info(dev, "surge protection mode - 0R\n");
+	return ret;
+}
+
 static int an8801sb_config_init(struct phy_device *phydev)
 {
 	int ret;
@@ -539,8 +779,23 @@
 		return ret;
 	}
 	air_buckpbus_reg_write(phydev, 0x10270100, 0x0f);
-	air_buckpbus_reg_write(phydev, 0x10270108, 0x10100303);
-
+	air_buckpbus_reg_write(phydev, 0x10270108, 0x0a0a0404);
+	ret = an8801sb_surge_protect_cfg(phydev);
+	if (ret < 0) {
+		dev_err(phydev_dev(phydev),
+			"an8801sb_surge_protect_cfg fail. (ret=%d)\n", ret);
+		return ret;
+	}
+	reg_value = phy_read(phydev, MII_CTRL1000);
+	reg_value |= ADVERTISE_1000FULL;
+	ret = phy_write(phydev, MII_CTRL1000, reg_value);
+	if (ret < 0)
+		return ret;
+	reg_value = phy_read(phydev, MII_BMCR);
+	reg_value |= BMCR_ANRESTART;
+	ret = phy_write(phydev, MII_BMCR, reg_value);
+	if (ret < 0)
+		return ret;
 	dev_info(phydev_dev(phydev),
 		"AN8801SB Initialize OK ! (%s)\n", AN8801_DRIVER_VERSION);
 	return 0;
@@ -602,7 +857,7 @@
 int an8801_set_polarity(struct phy_device *phydev, int tx_rx)
 {
 	int ret = 0;
-	unsigned long pbus_data = 0;
+	u32 pbus_data = 0;
 
 	pr_notice("\n[Write] Polarity %s\n", tx_rx_string[tx_rx]);
 	pbus_data = (air_buckpbus_reg_read(phydev, 0x1022a0f8) &
@@ -618,7 +873,7 @@
 		return ret;
 	pbus_data = air_buckpbus_reg_read(phydev, 0x1022a0f8);
 	tx_rx = pbus_data & (BIT(0) | BIT(1));
-	pr_notice("\n[Read] Polarity %s confirm....(%8lx)\n",
+	pr_notice("\n[Read] Polarity %s confirm....(%8x)\n",
 		tx_rx_string[tx_rx], pbus_data);
 
 	return ret;
@@ -682,6 +937,82 @@
 	return count;
 }
 
+static ssize_t an8801_mdio_write(struct file *file, const char __user *ptr,
+					size_t len, loff_t *off)
+{
+	struct phy_device *phydev = file->private_data;
+	char buf[64], param1[32], param2[32];
+	int count = len, ret = 0;
+	unsigned int reg, devad, val;
+	u16 reg_val;
+
+	memset(buf, 0, 64);
+	memset(param1, 0, 32);
+	memset(param2, 0, 32);
+
+	if (count > sizeof(buf) - 1)
+		return -EINVAL;
+	if (copy_from_user(buf, ptr, len))
+		return -EFAULT;
+
+	ret = sscanf(buf, "%s %s", param1, param2);
+	if (ret < 0)
+		return ret;
+
+	if (!strncmp("cl22", param1, strlen("cl22"))) {
+		if (!strncmp("w", param2, strlen("w"))) {
+			if (sscanf(buf, "cl22 w %x %x", &reg, &val) == -1)
+				return -EFAULT;
+			pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x\n",
+				phydev_phy_addr(phydev), reg, val);
+
+			ret = phy_write(phydev, reg, val);
+			if (ret < 0)
+				return ret;
+			pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x confirm..\n",
+				phydev_phy_addr(phydev), reg,
+				phy_read(phydev, reg));
+		} else if (!strncmp("r", param2, strlen("r"))) {
+			if (sscanf(buf, "cl22 r %x", &reg) == -1)
+				return -EFAULT;
+			pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x\n",
+				phydev_phy_addr(phydev), reg,
+				phy_read(phydev, reg));
+		} else {
+			pr_notice(AN8801_DEBUGFS_MDIO_HELP_STRING);
+			return -EINVAL;
+		}
+	} else if (!strncmp("cl45", param1, strlen("cl45"))) {
+		if (!strncmp("w", param2, strlen("w"))) {
+			if (sscanf(buf, "cl45 w %x %x %x", &devad, &reg, &val) == -1)
+				return -EFAULT;
+			pr_notice("\nphy=0x%x, devad=0x%x, reg=0x%x, val=0x%x\n",
+				phydev_phy_addr(phydev), devad, reg, val);
+
+			ret = an8801_cl45_write(phydev, devad, reg, val);
+			if (ret < 0)
+				return ret;
+			an8801_cl45_read(phydev, devad, reg, &reg_val);
+			pr_notice("\nphy=0x%x, devad=0x%x, reg=0x%x, val=0x%x confirm..\n",
+				phydev_phy_addr(phydev), devad, reg, reg_val);
+		} else if (!strncmp("r", param2, strlen("r"))) {
+			if (sscanf(buf, "cl45 r %x %x", &devad, &reg) == -1)
+				return -EFAULT;
+			an8801_cl45_read(phydev, devad, reg, &reg_val);
+			pr_notice("\nphy=0x%x, devad=0x%x, reg=0x%x, val=0x%x\n",
+				phydev_phy_addr(phydev), devad, reg, reg_val);
+		} else {
+			pr_notice(AN8801_DEBUGFS_MDIO_HELP_STRING);
+			return -EINVAL;
+		}
+	} else {
+		pr_notice(AN8801_DEBUGFS_MDIO_HELP_STRING);
+		return -EINVAL;
+	}
+
+	return count;
+}
+
 static int an8801_counter_show(struct seq_file *seq, void *v)
 {
 	struct phy_device *phydev = seq->private;
@@ -714,9 +1045,7 @@
 	ret = air_buckpbus_reg_write(phydev, 0x10226124, 0x55);
 	ret |= air_buckpbus_reg_write(phydev, 0x10226124, 0x0);
 	if (ret < 0)
-		seq_puts(seq, "| Clear Serdes Counter fail\n");
-	else
-		seq_puts(seq, "| Clear Serdes Counter!!\n");
+		return ret;
 
 	seq_puts(seq, "|\t<<EFIFO COUNTER>>\n");
 	seq_puts(seq, "| Rx from Line side_S     :");
@@ -746,9 +1075,7 @@
 
 	ret = air_buckpbus_reg_write(phydev, 0x1027011C, 0x3);
 	if (ret < 0)
-		seq_puts(seq, "| Clear EFIFO Counter fail\n");
-	else
-		seq_puts(seq, "| Clear EFIFO Counter!!\n");
+		return ret;
 
 	seq_puts(seq, "|\t<<LS Counter>>\n");
 	ret = phy_write(phydev, 0x1f, 1);
@@ -804,7 +1131,7 @@
 	char buf[64];
 	int ret = 0;
 	unsigned int reg, addr;
-	unsigned long val;
+	u32 val;
 
 	memset(buf, 0, 64);
 
@@ -812,34 +1139,28 @@
 		return -EFAULT;
 
 	if (buf[0] == 'w') {
-		if (sscanf(buf, "w %x %x %lx", &addr, &reg, &val) == -1)
+		if (sscanf(buf, "w %x %x", &reg, &val) == -1)
 			return -EFAULT;
-		if (addr > 0 && addr < 32) {
-			pr_notice("\nphy=0x%x, reg=0x%x, val=0x%lx\n",
-				addr, reg, val);
 
-			ret = air_buckpbus_reg_write(phydev, reg, val);
-			if (ret < 0)
-				return ret;
-			pr_notice("\nphy=%d, reg=0x%x, val=0x%lx confirm..\n",
-				addr, reg,
-				air_buckpbus_reg_read(phydev, reg));
-		} else {
-			pr_notice("addr is out of range(1~32)\n");
-		}
+		pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x\n",
+				phydev_phy_addr(phydev), reg, val);
+
+		ret = air_buckpbus_reg_write(phydev, reg, val);
+		if (ret < 0)
+			return ret;
+
+		pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
+			addr, reg,
+			air_buckpbus_reg_read(phydev, reg));
 	} else if (buf[0] == 'r') {
-		if (sscanf(buf, "r %x %x", &addr, &reg) == -1)
+		if (sscanf(buf, "r %x", &reg) == -1)
 			return -EFAULT;
-		if (addr > 0 && addr < 32) {
-			pr_notice("\nphy=0x%x, reg=0x%x, val=0x%lx\n",
-				addr, reg,
-				air_buckpbus_reg_read(phydev, reg));
-		} else {
-			pr_notice("addr is out of range(1~32)\n");
-		}
-	} else if (buf[0] == 'h') {
+
+		pr_notice("\nphy=0x%x, reg=0x%x, val=0x%x\n",
+			phydev_phy_addr(phydev), reg,
+			air_buckpbus_reg_read(phydev, reg));
+	} else if (buf[0] == 'h')
 		an8801_debugfs_pbus_help();
-	}
 
 	return count;
 }
@@ -849,20 +1170,26 @@
 	struct phy_device *phydev = seq->private;
 	unsigned int tx_rx =
 		(air_buckpbus_reg_read(phydev, 0x1022a0f8) & 0x3);
-	unsigned long pbus_data = 0;
+	u32 pbus_data = 0;
+	int reg = 0;
 
-	seq_puts(seq, "<<AIR AN8801 Driver Info>>\n");
-	pbus_data = air_buckpbus_reg_read(phydev, 0x1000009c);
-	seq_printf(seq, "| Product Version : E%ld\n", pbus_data & 0xf);
+	seq_puts(seq, "\t<<AIR AN8801SB Info>>\n");
+	pbus_data = air_buckpbus_reg_read(phydev, 0x10005004);
+	seq_printf(seq, "| Product Version : E%d\n", pbus_data);
 	seq_printf(seq, "| Driver Version  : %s\n", AN8801_DRIVER_VERSION);
 	pbus_data = air_buckpbus_reg_read(phydev, 0x10220b04);
 	seq_printf(seq, "| Serdes Status   : Rx_Sync(%01ld), AN_Done(%01ld)\n",
 		GET_BIT(pbus_data, 4), GET_BIT(pbus_data, 0));
 	seq_printf(seq, "| Tx, Rx Polarity : %s(%02d)\n",
 		tx_rx_string[tx_rx], tx_rx);
-
+	pbus_data = air_buckpbus_reg_read(phydev, 0x10000094);
+	seq_printf(seq, "| RG_HW_STRAP     : 0x%08x\n", pbus_data);
+	for (reg = MII_BMCR; reg <= MII_STAT1000; reg++) {
+		if ((reg <= MII_LPA) || (reg >= MII_CTRL1000))
+			seq_printf(seq, "| RG_MII 0x%02x     : 0x%08x\n",
+				reg, phy_read(phydev, reg));
+	}
 	seq_puts(seq, "\n");
-
 	return 0;
 }
 
@@ -901,6 +1228,13 @@
 	.llseek = noop_llseek,
 };
 
+static const struct file_operations an8801_mdio_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = an8801_mdio_write,
+	.llseek = noop_llseek,
+};
+
 int an8801_debugfs_init(struct phy_device *phydev)
 {
 	int ret = 0;
@@ -913,7 +1247,7 @@
 		dev_err(phydev_dev(phydev), "Debugfs init err\n");
 		ret = -ENOMEM;
 	}
-	debugfs_create_file(DEBUGFS_DRIVER_INFO, 0444,
+	debugfs_create_file(DEBUGFS_INFO, 0444,
 					priv->debugfs_root, phydev,
 					&an8801_info_fops);
 	debugfs_create_file(DEBUGFS_COUNTER, 0644,
@@ -925,6 +1259,9 @@
 	debugfs_create_file(DEBUGFS_POLARITY, S_IFREG | 0200,
 					priv->debugfs_root, phydev,
 					&an8801_polarity_fops);
+	debugfs_create_file(DEBUGFS_MDIO, S_IFREG | 0200,
+					priv->debugfs_root, phydev,
+					&an8801_mdio_fops);
 	return ret;
 }
 
@@ -1024,7 +1361,6 @@
 		ret |= an8801_cl45_write(
 			phydev, MMD_DEV_VSPEC2, PHY_PRE_SPEED_REG, prespeed);
 		dev_info(phydev_dev(phydev), "AN8801SB SPEED %d\n", prespeed);
-		air_buckpbus_reg_write(phydev, 0x10270108, 0x0a0a0404);
 		while (an_retry > 0) {
 			mdelay(1);       /* delay 1 ms */
 			reg_value = air_buckpbus_reg_read(
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/an8801.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/an8801.h
index 9d1d2fa..5f5ad7f 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/an8801.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/an8801.h
@@ -12,12 +12,13 @@
 
 /* NAMING DECLARATIONS
  */
-#define AN8801_DRIVER_VERSION  "1.1.3"
+#define AN8801_DRIVER_VERSION  "1.1.4"
 
 #define DEBUGFS_COUNTER         "counter"
-#define DEBUGFS_DRIVER_INFO     "driver_info"
+#define DEBUGFS_INFO            "info"
 #define DEBUGFS_PBUS_OP         "pbus_op"
 #define DEBUGFS_POLARITY        "polarity"
+#define DEBUGFS_MDIO            "mdio"
 
 #define AN8801_MDIO_PHY_ID      0x1
 #define AN8801_PHY_ID1          0xc0ff
@@ -97,6 +98,7 @@
 
 #define PHY_PRE_SPEED_REG               (0x2b)
 
+#define MMD_DEV_VSPEC1          (0x1E)
 #define MMD_DEV_VSPEC2          (0x1F)
 
 #define RGMII_DELAY_STEP_MASK       0x7
@@ -203,6 +205,7 @@
 	struct dentry        *debugfs_root;
 #endif
 	int                   pol;
+	int                   surge;
 };
 
 enum an8801_polarity {
@@ -212,4 +215,10 @@
 	AIR_POL_TX_REV_RX_NOR,
 };
 
+enum air_surge {
+	AIR_SURGE_0R,
+	AIR_SURGE_5R,
+	AIR_SURGE_LAST = 0xff
+};
+
 #endif /* End of __AN8801_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-2p5ge.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-2p5ge.c
deleted file mode 100644
index 4a60a68..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-2p5ge.c
+++ /dev/null
@@ -1,394 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-#include <linux/bitfield.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/nvmem-consumer.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/phy.h>
-#include <linux/pm_domain.h>
-#include <linux/pm_runtime.h>
-
-#define MT7988_2P5GE_PMB "mediatek/mt7988/i2p5ge-phy-pmb.bin"
-
-#define MD32_EN			BIT(0)
-#define PMEM_PRIORITY		BIT(8)
-#define DMEM_PRIORITY		BIT(16)
-
-#define BASE100T_STATUS_EXTEND		(0x10)
-#define BASE1000T_STATUS_EXTEND		(0x11)
-#define EXTEND_CTRL_AND_STATUS		(0x16)
-
-#define PHY_AUX_CTRL_STATUS		(0x1d)
-#define   PHY_AUX_DPX_MASK		GENMASK(5, 5)
-#define   PHY_AUX_SPEED_MASK		GENMASK(4, 2)
-
-/* Registers on MDIO_MMD_VEND1 */
-#define MTK_PHY_LINK_STATUS_MISC	(0xa2)
-#define   MTK_PHY_FDX_ENABLE		BIT(5)
-
-#define MTK_PHY_LPI_PCS_DSP_CTRL		(0x121)
-#define   MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK	GENMASK(12, 8)
-
-/* Registers on MDIO_MMD_VEND2 */
-#define MTK_PHY_LED0_ON_CTRL			(0x24)
-#define   MTK_PHY_LED0_ON_LINK1000		BIT(0)
-#define   MTK_PHY_LED0_ON_LINK100		BIT(1)
-#define   MTK_PHY_LED0_ON_LINK10		BIT(2)
-#define   MTK_PHY_LED0_ON_LINK2500		BIT(7)
-#define   MTK_PHY_LED0_POLARITY			BIT(14)
-
-#define MTK_PHY_LED1_ON_CTRL			(0x26)
-#define   MTK_PHY_LED1_ON_FDX			BIT(4)
-#define   MTK_PHY_LED1_ON_HDX			BIT(5)
-#define   MTK_PHY_LED1_POLARITY			BIT(14)
-
-#define MTK_EXT_PAGE_ACCESS			0x1f
-#define MTK_PHY_PAGE_STANDARD			0x0000
-#define MTK_PHY_PAGE_EXTENDED_1			0x1
-#define MTK_PHY_AUX_CTRL_AND_STATUS		(0x14)
-#define   MTK_PHY_ENABLE_DOWNSHIFT		BIT(4)
-
-/* Registers on Token Ring debug nodes */
-#define MTK_PHY_PAGE_EXTENDED_52B5		0x52b5
-
-/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */
-#define AUTO_NP_10XEN				BIT(6)
-
-struct mtk_i2p5ge_phy_priv {
-	bool fw_loaded;
-	u16 tr_low;
-	u16 tr_high;
-};
-
-enum {
-	PHY_AUX_SPD_10 = 0,
-	PHY_AUX_SPD_100,
-	PHY_AUX_SPD_1000,
-	PHY_AUX_SPD_2500,
-};
-
-static void tr_access(struct phy_device *phydev, bool read, u8 ch_addr, u8 node_addr, u8 data_addr)
-{
-	u16 tr_cmd = BIT(15); /* bit 14 & 0 are reserved */
-
-	if (read)
-		tr_cmd |= BIT(13);
-
-	tr_cmd |= (((ch_addr & 0x3) << 11) |
-		   ((node_addr & 0xf) << 7) |
-		   ((data_addr & 0x3f) << 1));
-	dev_dbg(&phydev->mdio.dev, "tr_cmd: 0x%x\n", tr_cmd);
-	__phy_write(phydev, 0x10, tr_cmd);
-}
-
-static void __tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, u8 data_addr)
-{
-	struct mtk_i2p5ge_phy_priv *priv = phydev->priv;
-
-	tr_access(phydev, true, ch_addr, node_addr, data_addr);
-	priv->tr_low = __phy_read(phydev, 0x11);
-	priv->tr_high = __phy_read(phydev, 0x12);
-	dev_dbg(&phydev->mdio.dev, "tr_high read: 0x%x, tr_low read: 0x%x\n",
-		priv->tr_high, priv->tr_low);
-}
-
-static void tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, u8 data_addr)
-{
-	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
-	__tr_read(phydev, ch_addr, node_addr, data_addr);
-	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
-}
-
-static void __tr_write(struct phy_device *phydev, u8 ch_addr, u8 node_addr, u8 data_addr,
-		       u32 tr_data)
-{
-	__phy_write(phydev, 0x11, tr_data & 0xffff);
-	__phy_write(phydev, 0x12, tr_data >> 16);
-	tr_access(phydev, false, ch_addr, node_addr, data_addr);
-}
-
-static void tr_write(struct phy_device *phydev, u8 ch_addr, u8 node_addr, u8 data_addr, u32 tr_data)
-{
-	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
-	__tr_write(phydev, ch_addr, node_addr, data_addr, tr_data);
-	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
-}
-
-static void tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, u8 data_addr,
-		     u32 mask, u32 set)
-{
-	u32 tr_data;
-	struct mtk_i2p5ge_phy_priv *priv = phydev->priv;
-
-	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
-	__tr_read(phydev, ch_addr, node_addr, data_addr);
-	tr_data = (priv->tr_high << 16) | priv->tr_low;
-	tr_data = (tr_data & ~mask) | set;
-	__tr_write(phydev, ch_addr, node_addr, data_addr, tr_data);
-	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
-}
-
-static int mtk_2p5ge_phy_read_page(struct phy_device *phydev)
-{
-	return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
-}
-
-static int mtk_2p5ge_phy_write_page(struct phy_device *phydev, int page)
-{
-	return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
-}
-
-static int mt7988_2p5ge_phy_probe(struct phy_device *phydev)
-{
-	struct mtk_i2p5ge_phy_priv *priv;
-
-	priv = devm_kzalloc(&phydev->mdio.dev,
-				sizeof(struct mtk_i2p5ge_phy_priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	phydev->priv = priv;
-
-	return 0;
-}
-
-static int mt7988_2p5ge_phy_config_init(struct phy_device *phydev)
-{
-	int ret, i;
-	const struct firmware *fw;
-	struct device *dev = &phydev->mdio.dev;
-	struct device_node *np;
-	void __iomem *pmb_addr;
-	void __iomem *md32_en_cfg_base;
-	struct mtk_i2p5ge_phy_priv *priv = phydev->priv;
-	u16 reg;
-	struct pinctrl *pinctrl;
-
-	if (!priv->fw_loaded) {
-		np = of_find_compatible_node(NULL, NULL, "mediatek,2p5gphy-fw");
-		if (!np)
-			return -ENOENT;
-		pmb_addr = of_iomap(np, 0);
-		if (!pmb_addr)
-			return -ENOMEM;
-		md32_en_cfg_base = of_iomap(np, 1);
-		if (!md32_en_cfg_base)
-			return -ENOMEM;
-
-		ret = request_firmware(&fw, MT7988_2P5GE_PMB, dev);
-		if (ret) {
-			dev_err(dev, "failed to load firmware: %s, ret: %d\n",
-				MT7988_2P5GE_PMB, ret);
-			return ret;
-		}
-
-		reg = readw(md32_en_cfg_base);
-		if (reg & MD32_EN) {
-			phy_set_bits(phydev, 0, BIT(15));
-			usleep_range(10000, 11000);
-		}
-		phy_set_bits(phydev, 0, BIT(11));
-
-		/* Write magic number to safely stall MCU */
-		phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x800e, 0x1100);
-		phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x800f, 0x00df);
-
-		for (i = 0; i < fw->size - 1; i += 4)
-			writel(*((uint32_t *)(fw->data + i)), pmb_addr + i);
-		release_firmware(fw);
-
-		writew(reg & ~MD32_EN, md32_en_cfg_base);
-		writew(reg | MD32_EN, md32_en_cfg_base);
-		phy_set_bits(phydev, 0, BIT(15));
-		/* We need a delay here to stabilize initialization of MCU */
-		usleep_range(7000, 8000);
-		dev_info(dev, "Firmware loading/trigger ok.\n");
-
-		priv->fw_loaded = true;
-	}
-
-	/* Setup LED */
-	phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
-			 MTK_PHY_LED0_POLARITY | MTK_PHY_LED0_ON_LINK10 |
-			 MTK_PHY_LED0_ON_LINK100 | MTK_PHY_LED0_ON_LINK1000 |
-			 MTK_PHY_LED0_ON_LINK2500);
-	phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
-			 MTK_PHY_LED1_ON_FDX | MTK_PHY_LED1_ON_HDX);
-
-	pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "i2p5gbe-led");
-	if (IS_ERR(pinctrl)) {
-		dev_err(&phydev->mdio.dev, "Fail to set LED pins!\n");
-		return PTR_ERR(pinctrl);
-	}
-
-	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LPI_PCS_DSP_CTRL,
-		       MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK, 0);
-
-	/* Enable 16-bit next page exchange bit if 1000-BT isn't advertizing */
-	tr_modify(phydev, 0x0, 0xf, 0x3c, AUTO_NP_10XEN,
-		  FIELD_PREP(AUTO_NP_10XEN, 0x1));
-
-	/* Enable downshift */
-	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_1);
-	__phy_set_bits(phydev, MTK_PHY_AUX_CTRL_AND_STATUS, MTK_PHY_ENABLE_DOWNSHIFT);
-	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
-
-	return 0;
-}
-
-static int mt7988_2p5ge_phy_config_aneg(struct phy_device *phydev)
-{
-	bool changed = false;
-	u32 adv;
-	int ret;
-
-	if (phydev->autoneg == AUTONEG_DISABLE) {
-		/* Configure half duplex with genphy_setup_forced,
-		 * because genphy_c45_pma_setup_forced does not support.
-		 */
-		return phydev->duplex != DUPLEX_FULL
-			? genphy_setup_forced(phydev)
-			: genphy_c45_pma_setup_forced(phydev);
-	}
-
-	ret = genphy_c45_an_config_aneg(phydev);
-	if (ret < 0)
-		return ret;
-	if (ret > 0)
-		changed = true;
-
-	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
-	ret = phy_modify_changed(phydev, MII_CTRL1000,
-				 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
-				 adv);
-	if (ret < 0)
-		return ret;
-	if (ret > 0)
-		changed = true;
-
-	return genphy_c45_check_and_restart_aneg(phydev, changed);
-}
-
-static int mt7988_2p5ge_phy_get_features(struct phy_device *phydev)
-{
-	int ret;
-
-	ret = genphy_read_abilities(phydev);
-	if (ret)
-		return ret;
-
-	/* We don't support HDX at MAC layer on mt7988.
-	 * So mask phy's HDX capabilities, too.
-	 */
-	linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_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_2500baseT_Full_BIT,
-			 phydev->supported);
-	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
-
-	return 0;
-}
-
-static int mt7988_2p5ge_phy_read_status(struct phy_device *phydev)
-{
-	int ret;
-	u16 status;
-
-	ret = genphy_update_link(phydev);
-	if (ret)
-		return ret;
-
-	phydev->speed = SPEED_UNKNOWN;
-	phydev->duplex = DUPLEX_UNKNOWN;
-	phydev->pause = 0;
-	phydev->asym_pause = 0;
-
-	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
-		ret = genphy_c45_read_lpa(phydev);
-		if (ret < 0)
-			return ret;
-
-		/* Read the link partner's 1G advertisement */
-		ret = phy_read(phydev, MII_STAT1000);
-		if (ret < 0)
-			return ret;
-		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
-	} else if (phydev->autoneg == AUTONEG_DISABLE) {
-		linkmode_zero(phydev->lp_advertising);
-	}
-
-	status = phy_read(phydev, MII_BMSR);
-	if (status & BMSR_LSTATUS) {
-		ret = phy_read(phydev, PHY_AUX_CTRL_STATUS);
-		if (ret < 0)
-			return ret;
-
-		switch (FIELD_GET(PHY_AUX_SPEED_MASK, ret)) {
-		case PHY_AUX_SPD_10:
-			phydev->speed = SPEED_10;
-			break;
-		case PHY_AUX_SPD_100:
-			phydev->speed = SPEED_100;
-			break;
-		case PHY_AUX_SPD_1000:
-			phydev->speed = SPEED_1000;
-			break;
-		case PHY_AUX_SPD_2500:
-			phydev->speed = SPEED_2500;
-			break;
-		}
-
-		ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC);
-		if (ret < 0)
-			return ret;
-		phydev->duplex = (ret & MTK_PHY_FDX_ENABLE) ? DUPLEX_FULL : DUPLEX_HALF;
-		/* FIXME: The current firmware always enables rate adaptation mode. */
-		phydev->rate_matching = RATE_MATCH_PAUSE;
-	}
-
-	return 0;
-}
-
-static int mt7988_2p5ge_phy_get_rate_matching(struct phy_device *phydev,
-					      phy_interface_t iface)
-{
-	if (iface == PHY_INTERFACE_MODE_XGMII)
-		return RATE_MATCH_PAUSE;
-	return RATE_MATCH_NONE;
-}
-
-static struct phy_driver mtk_gephy_driver[] = {
-	{
-		PHY_ID_MATCH_MODEL(0x00339c11),
-		.name		= "MediaTek MT798x 2.5GbE PHY",
-		.probe		= mt7988_2p5ge_phy_probe,
-		.config_init	= mt7988_2p5ge_phy_config_init,
-		.config_aneg    = mt7988_2p5ge_phy_config_aneg,
-		.get_features	= mt7988_2p5ge_phy_get_features,
-		.read_status	= mt7988_2p5ge_phy_read_status,
-		.get_rate_matching	= mt7988_2p5ge_phy_get_rate_matching,
-		.suspend	= genphy_suspend,
-		.resume		= genphy_resume,
-		.read_page	= mtk_2p5ge_phy_read_page,
-		.write_page	= mtk_2p5ge_phy_write_page,
-	},
-};
-
-module_phy_driver(mtk_gephy_driver);
-
-static struct mdio_device_id __maybe_unused mtk_2p5ge_phy_tbl[] = {
-	{ PHY_ID_MATCH_VENDOR(0x00339c00) },
-	{ }
-};
-
-MODULE_DESCRIPTION("MediaTek 2.5Gb Ethernet PHY driver");
-MODULE_AUTHOR("SkyLake Huang <SkyLake.Huang@mediatek.com>");
-MODULE_LICENSE("GPL");
-
-MODULE_DEVICE_TABLE(mdio, mtk_2p5ge_phy_tbl);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c
deleted file mode 100644
index 60ef22c..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c
+++ /dev/null
@@ -1,122 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-#include <linux/bitfield.h>
-#include <linux/module.h>
-#include <linux/phy.h>
-
-#define MTK_EXT_PAGE_ACCESS		0x1f
-#define MTK_PHY_PAGE_STANDARD		0x0000
-#define MTK_PHY_PAGE_EXTENDED		0x0001
-#define MTK_PHY_PAGE_EXTENDED_2		0x0002
-#define MTK_PHY_PAGE_EXTENDED_3		0x0003
-#define MTK_PHY_PAGE_EXTENDED_2A30	0x2a30
-#define MTK_PHY_PAGE_EXTENDED_52B5	0x52b5
-
-#define MTK_PHY_RG_DEV1E_REG2C7		0x2c7
-#define   MTK_PHY_MAX_GAIN_MASK		GENMASK(4, 0)
-#define   MTK_PHY_MIN_GAIN_MASK		GENMASK(12, 8)
-
-static int mtk_gephy_read_page(struct phy_device *phydev)
-{
-	return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
-}
-
-static int mtk_gephy_write_page(struct phy_device *phydev, int page)
-{
-	return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
-}
-
-static void mtk_gephy_config_init(struct phy_device *phydev)
-{
-	/* Disable EEE */
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0);
-
-	/* Enable HW auto downshift */
-	phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4));
-
-	/* Increase SlvDPSready time */
-	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
-	__phy_write(phydev, 0x10, 0xafae);
-	__phy_write(phydev, 0x12, 0x2f);
-	__phy_write(phydev, 0x10, 0x8fae);
-	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
-
-	/* Adjust 100_mse_threshold */
-	phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff);
-
-	/* Disable mcc */
-	phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300);
-}
-
-static int mt7530_phy_config_init(struct phy_device *phydev)
-{
-	mtk_gephy_config_init(phydev);
-
-	/* Increase post_update_timer */
-	phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b);
-
-	return 0;
-}
-
-static int mt7531_phy_config_init(struct phy_device *phydev)
-{
-	mtk_gephy_config_init(phydev);
-
-	/* PHY link down power saving enable */
-	phy_set_bits(phydev, 0x17, BIT(4));
-	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300);
-
-	/* Set TX Pair delay selection */
-	phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404);
-	phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404);
-
-	/* Adjust RX min/max gain to fix CH395 100Mbps link up fail */
-	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7,
-		      FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) |
-		      FIELD_PREP(MTK_PHY_MIN_GAIN_MASK, 0x13));
-
-	return 0;
-}
-
-static struct phy_driver mtk_gephy_driver[] = {
-	{
-		PHY_ID_MATCH_EXACT(0x03a29412),
-		.name		= "MediaTek MT7530 PHY",
-		.config_init	= mt7530_phy_config_init,
-		/* Interrupts are handled by the switch, not the PHY
-		 * itself.
-		 */
-		.config_intr	= genphy_no_config_intr,
-		.handle_interrupt = genphy_no_ack_interrupt,
-		.suspend	= genphy_suspend,
-		.resume		= genphy_resume,
-		.read_page	= mtk_gephy_read_page,
-		.write_page	= mtk_gephy_write_page,
-	},
-	{
-		PHY_ID_MATCH_EXACT(0x03a29441),
-		.name		= "MediaTek MT7531 PHY",
-		.config_init	= mt7531_phy_config_init,
-		/* Interrupts are handled by the switch, not the PHY
-		 * itself.
-		 */
-		.config_intr	= genphy_no_config_intr,
-		.handle_interrupt = genphy_no_ack_interrupt,
-		.suspend	= genphy_suspend,
-		.resume		= genphy_resume,
-		.read_page	= mtk_gephy_read_page,
-		.write_page	= mtk_gephy_write_page,
-	},
-};
-
-module_phy_driver(mtk_gephy_driver);
-
-static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = {
-	{ PHY_ID_MATCH_VENDOR(0x03a29400) },
-	{ }
-};
-
-MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver");
-MODULE_AUTHOR("DENG, Qingfang <dqfext@gmail.com>");
-MODULE_LICENSE("GPL");
-
-MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/Kconfig b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/Kconfig
new file mode 100644
index 0000000..1490352
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/Kconfig
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config MTK_NET_PHYLIB
+	tristate
+
+config MEDIATEK_GE_PHY
+	tristate "MediaTek Gigabit Ethernet PHYs"
+	select MTK_NET_PHYLIB
+	help
+	  Supports the MediaTek non-built-in Gigabit Ethernet PHYs.
+
+	  Non-built-in Gigabit Ethernet PHYs include mt7530/mt7531.
+	  You may find mt7530 inside mt7621. This driver shares some
+	  common operations with MediaTek SoC built-in Gigabit
+	  Ethernet PHYs.
+
+config MEDIATEK_GE_SOC_PHY
+	tristate "MediaTek SoC Ethernet PHYs"
+	depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
+	select NVMEM_MTK_EFUSE
+	select MTK_NET_PHYLIB
+	help
+	  Supports MediaTek SoC built-in Gigabit Ethernet PHYs.
+
+	  Include support for built-in Ethernet PHYs which are present in
+	  the MT7981 and MT7988 SoCs. These PHYs need calibration data
+	  present in the SoCs efuse and will dynamically calibrate VCM
+	  (common-mode voltage) during startup.
+
+config MEDIATEK_2P5GE_PHY
+	tristate "MediaTek 2.5Gb Ethernet PHYs"
+	depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
+	select MTK_NET_PHYLIB
+	help
+	  Supports MediaTek SoC built-in 2.5Gb Ethernet PHYs.
+
+	  This will load necessary firmware and add appropriate time delay.
+	  Accelerate this procedure through internal pbus instead of MDIO
+	  bus. Certain link-up issues will also be fixed here.
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/Makefile b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/Makefile
new file mode 100644
index 0000000..c6db8ab
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_MTK_NET_PHYLIB)		+= mtk-phy-lib.o
+obj-$(CONFIG_MEDIATEK_GE_PHY)		+= mtk-ge.o
+obj-$(CONFIG_MEDIATEK_GE_SOC_PHY)	+= mtk-ge-soc.o
+obj-$(CONFIG_MEDIATEK_2P5GE_PHY)	+= mtk-2p5ge.o
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-2p5ge.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-2p5ge.c
new file mode 100644
index 0000000..0a4cb25
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-2p5ge.c
@@ -0,0 +1,688 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/bitfield.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/phy.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+
+#include "mtk.h"
+
+#define MTK_2P5GPHY_ID_MT7987	(0x00339c91)
+#define MTK_2P5GPHY_ID_MT7988	(0x00339c11)
+
+#define MT7987_2P5GE_PMB_FW		"mediatek/mt7987/i2p5ge-phy-pmb.bin"
+#define MT7987_2P5GE_PMB_FW_SIZE	(0x18000)
+#define MT7987_2P5GE_DSPBITTB \
+	"mediatek/mt7987/i2p5ge-phy-DSPBitTb.bin"
+#define MT7987_2P5GE_DSPBITTB_SIZE	(0x7000)
+
+#define MT7988_2P5GE_PMB_FW		"mediatek/mt7988/i2p5ge-phy-pmb.bin"
+#define MT7988_2P5GE_PMB_FW_SIZE	(0x20000)
+#define MT7988_2P5GE_PMB_FW_BASE	(0x0f100000)
+#define MT7988_2P5GE_PMB_FW_LEN		(0x20000)
+
+#define MTK_2P5GPHY_PMD_REG_BASE	(0x0f010000)
+#define MTK_2P5GPHY_PMD_REG_LEN		(0x210)
+#define DO_NOT_RESET			(0x28)
+#define   DO_NOT_RESET_XBZ		BIT(0)
+#define   DO_NOT_RESET_PMA		BIT(3)
+#define   DO_NOT_RESET_RX		BIT(5)
+#define FNPLL_PWR_CTRL1			(0x208)
+#define   RG_SPEED_MASK			GENMASK(3, 0)
+#define   RG_SPEED_2500			BIT(3)
+#define   RG_SPEED_100			BIT(0)
+#define FNPLL_PWR_CTRL_STATUS		(0x20c)
+#define   RG_STABLE_MASK		GENMASK(3, 0)
+#define   RG_SPEED_2500_STABLE		BIT(3)
+#define   RG_SPEED_100_STABLE		BIT(0)
+
+#define MTK_2P5GPHY_XBZ_PCS_REG_BASE	(0x0f030000)
+#define MTK_2P5GPHY_XBZ_PCS_REG_LEN	(0x844)
+#define PHY_CTRL_CONFIG			(0x200)
+#define PMU_WP				(0x800)
+#define   WRITE_PROTECT_KEY		(0xCAFEF00D)
+#define PMU_PMA_AUTO_CFG		(0x820)
+#define   POWER_ON_AUTO_MODE		BIT(16)
+#define   PMU_AUTO_MODE_EN		BIT(0)
+#define PMU_PMA_STATUS			(0x840)
+#define   CLK_IS_DISABLED		BIT(3)
+
+#define MTK_2P5GPHY_XBZ_PMA_RX_BASE	(0x0f080000)
+#define MTK_2P5GPHY_XBZ_PMA_RX_LEN	(0x5228)
+#define SMEM_WDAT0			(0x5000)
+#define SMEM_WDAT1			(0x5004)
+#define SMEM_WDAT2			(0x5008)
+#define SMEM_WDAT3			(0x500c)
+#define SMEM_CTRL			(0x5024)
+#define   SMEM_HW_RDATA_ZERO		BIT(24)
+#define SMEM_ADDR_REF_ADDR		(0x502c)
+#define CM_CTRL_P01			(0x5100)
+#define CM_CTRL_P23			(0x5124)
+#define DM_CTRL_P01			(0x5200)
+#define DM_CTRL_P23			(0x5224)
+
+#define MTK_2P5GPHY_CHIP_SCU_BASE	(0x0f0cf800)
+#define MTK_2P5GPHY_CHIP_SCU_LEN	(0x12c)
+#define SYS_SW_RESET			(0x128)
+#define   RESET_RST_CNT			BIT(0)
+
+#define MTK_2P5GPHY_MCU_CSR_BASE	(0x0f0f0000)
+#define MTK_2P5GPHY_MCU_CSR_LEN		(0x20)
+#define MD32_EN_CFG			(0x18)
+#define   MD32_EN			BIT(0)
+
+#define MTK_2P5GPHY_PMB_FW_BASE		(0x0f100000)
+//#define MTK_2P5GPHY_PMB_FW_LEN		MT7988_2P5GE_PMB_FW_SIZE
+
+#define MTK_2P5GPHY_APB_BASE		(0x11c30000)
+#define MTK_2P5GPHY_APB_LEN		(0x9c)
+#define SW_RESET			(0x94)
+#define   MD32_RESTART_EN_CLEAR		BIT(9)
+
+#define BASE100T_STATUS_EXTEND		(0x10)
+#define BASE1000T_STATUS_EXTEND		(0x11)
+#define EXTEND_CTRL_AND_STATUS		(0x16)
+
+#define PHY_AUX_CTRL_STATUS		(0x1d)
+#define   PHY_AUX_DPX_MASK		GENMASK(5, 5)
+#define   PHY_AUX_SPEED_MASK		GENMASK(4, 2)
+
+/* Registers on MDIO_MMD_VEND1 */
+#define MTK_PHY_LINK_STATUS_RELATED		(0x147)
+#define   MTK_PHY_BYPASS_LINK_STATUS_OK		BIT(4)
+#define   MTK_PHY_FORCE_LINK_STATUS_HCD		BIT(3)
+
+#define MTK_PHY_AN_FORCE_SPEED_REG		(0x313)
+#define   MTK_PHY_MASTER_FORCE_SPEED_SEL_EN	BIT(7)
+#define   MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK	GENMASK(6, 0)
+
+#define MTK_PHY_LPI_PCS_DSP_CTRL		(0x121)
+#define   MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK	GENMASK(12, 8)
+
+/* Registers on Token Ring debug nodes */
+/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */
+#define AUTO_NP_10XEN				BIT(6)
+
+struct mtk_i2p5ge_phy_priv {
+	bool fw_loaded;
+};
+
+enum {
+	PHY_AUX_SPD_10 = 0,
+	PHY_AUX_SPD_100,
+	PHY_AUX_SPD_1000,
+	PHY_AUX_SPD_2500,
+};
+
+static int mt7987_2p5ge_phy_load_fw(struct phy_device *phydev)
+{
+	struct mtk_i2p5ge_phy_priv *priv = phydev->priv;
+	struct device *dev = &phydev->mdio.dev;
+	void __iomem *xbz_pcs_reg_base;
+	void __iomem *xbz_pma_rx_base;
+	void __iomem *chip_scu_base;
+	void __iomem *pmd_reg_base;
+	void __iomem *mcu_csr_base;
+	const struct firmware *fw;
+	void __iomem *apb_base;
+	void __iomem *pmb_addr;
+	int ret, i;
+	u32 reg;
+
+	if (priv->fw_loaded)
+		return 0;
+
+	apb_base = ioremap(MTK_2P5GPHY_APB_BASE,
+			   MTK_2P5GPHY_APB_LEN);
+	if (!apb_base)
+		return -ENOMEM;
+
+	pmd_reg_base = ioremap(MTK_2P5GPHY_PMD_REG_BASE,
+			       MTK_2P5GPHY_PMD_REG_LEN);
+	if (!pmd_reg_base) {
+		ret = -ENOMEM;
+		goto free_apb;
+	}
+
+	xbz_pcs_reg_base = ioremap(MTK_2P5GPHY_XBZ_PCS_REG_BASE,
+				   MTK_2P5GPHY_XBZ_PCS_REG_LEN);
+	if (!xbz_pcs_reg_base) {
+		ret = -ENOMEM;
+		goto free_pmd;
+	}
+
+	xbz_pma_rx_base = ioremap(MTK_2P5GPHY_XBZ_PMA_RX_BASE,
+				  MTK_2P5GPHY_XBZ_PMA_RX_LEN);
+	if (!xbz_pma_rx_base) {
+		ret = -ENOMEM;
+		goto free_pcs;
+	}
+
+	chip_scu_base = ioremap(MTK_2P5GPHY_CHIP_SCU_BASE,
+				MTK_2P5GPHY_CHIP_SCU_LEN);
+	if (!chip_scu_base) {
+		ret = -ENOMEM;
+		goto free_pma;
+	}
+
+	mcu_csr_base = ioremap(MTK_2P5GPHY_MCU_CSR_BASE,
+			       MTK_2P5GPHY_MCU_CSR_LEN);
+	if (!mcu_csr_base) {
+		ret = -ENOMEM;
+		goto free_chip_scu;
+	}
+
+	pmb_addr = ioremap(MTK_2P5GPHY_PMB_FW_BASE, MT7987_2P5GE_PMB_FW_SIZE);
+	if (!pmb_addr) {
+		return -ENOMEM;
+		goto free_mcu_csr;
+	}
+
+	ret = request_firmware(&fw, MT7987_2P5GE_PMB_FW, dev);
+	if (ret) {
+		dev_err(dev, "failed to load firmware: %s, ret: %d\n",
+			MT7987_2P5GE_PMB_FW, ret);
+		goto free_pmb_addr;
+	}
+
+	if (fw->size != MT7987_2P5GE_PMB_FW_SIZE) {
+		dev_err(dev, "PMb firmware size 0x%zx != 0x%x\n",
+			fw->size, MT7987_2P5GE_PMB_FW_SIZE);
+		ret = -EINVAL;
+		goto release_fw;
+	}
+
+	/* Force 2.5Gphy back to AN state */
+	phy_set_bits(phydev, MII_BMCR, BMCR_RESET);
+	usleep_range(5000, 6000);
+	phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
+
+	reg = readw(apb_base + SW_RESET);
+	writew(reg & ~MD32_RESTART_EN_CLEAR, apb_base + SW_RESET);
+	writew(reg | MD32_RESTART_EN_CLEAR, apb_base + SW_RESET);
+	writew(reg & ~MD32_RESTART_EN_CLEAR, apb_base + SW_RESET);
+
+	reg = readw(mcu_csr_base + MD32_EN_CFG);
+	writew(reg & ~MD32_EN, mcu_csr_base + MD32_EN_CFG);
+
+	for (i = 0; i < MT7987_2P5GE_PMB_FW_SIZE - 1; i += 4)
+		writel(*((uint32_t *)(fw->data + i)), pmb_addr + i);
+	dev_info(dev, "Firmware date code: %x/%x/%x, version: %x.%x\n",
+		 be16_to_cpu(*((__be16 *)(fw->data +
+					  MT7987_2P5GE_PMB_FW_SIZE - 8))),
+		 *(fw->data + MT7987_2P5GE_PMB_FW_SIZE - 6),
+		 *(fw->data + MT7987_2P5GE_PMB_FW_SIZE - 5),
+		 *(fw->data + MT7987_2P5GE_PMB_FW_SIZE - 2),
+		 *(fw->data + MT7987_2P5GE_PMB_FW_SIZE - 1));
+	release_firmware(fw);
+
+	/* Enable 100Mbps module clock. */
+	writel(FIELD_PREP(RG_SPEED_MASK, RG_SPEED_100),
+	       pmd_reg_base + FNPLL_PWR_CTRL1);
+
+	/* Check if 100Mbps module clock is ready. */
+	ret = readl_poll_timeout(pmd_reg_base + FNPLL_PWR_CTRL_STATUS, reg,
+				 reg & RG_SPEED_100_STABLE, 1, 10000);
+	if (ret)
+		dev_err(dev, "Fail to enable 100Mbps module clock: %d\n", ret);
+
+	/* Enable 2.5Gbps module clock. */
+	writel(FIELD_PREP(RG_SPEED_MASK, RG_SPEED_2500),
+	       pmd_reg_base + FNPLL_PWR_CTRL1);
+
+	/* Check if 2.5Gbps module clock is ready. */
+	ret = readl_poll_timeout(pmd_reg_base + FNPLL_PWR_CTRL_STATUS, reg,
+				 reg & RG_SPEED_2500_STABLE, 1, 10000);
+
+	if (ret)
+		dev_err(dev, "Fail to enable 2.5Gbps module clock: %d\n", ret);
+
+	/* Disable AN */
+	phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE);
+
+	/* Force to run at 2.5G speed */
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_AN_FORCE_SPEED_REG,
+		       MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK,
+		       MTK_PHY_MASTER_FORCE_SPEED_SEL_EN |
+		       FIELD_PREP(MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK, 0x1b));
+
+	phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_RELATED,
+			 MTK_PHY_BYPASS_LINK_STATUS_OK |
+			 MTK_PHY_FORCE_LINK_STATUS_HCD);
+
+	/* Set xbz, pma and rx as "do not reset" in order to input DSP code. */
+	reg = readl(pmd_reg_base + DO_NOT_RESET);
+	reg |= DO_NOT_RESET_XBZ | DO_NOT_RESET_PMA | DO_NOT_RESET_RX;
+	writel(reg, pmd_reg_base + DO_NOT_RESET);
+
+	reg = readl(chip_scu_base + SYS_SW_RESET);
+	writel(reg & ~RESET_RST_CNT, chip_scu_base + SYS_SW_RESET);
+
+	writel(WRITE_PROTECT_KEY, xbz_pcs_reg_base + PMU_WP);
+
+	reg = readl(xbz_pcs_reg_base + PMU_PMA_AUTO_CFG);
+	reg |= PMU_AUTO_MODE_EN | POWER_ON_AUTO_MODE;
+	writel(reg, xbz_pcs_reg_base + PMU_PMA_AUTO_CFG);
+
+	/* Check if clock in auto mode is disabled. */
+	ret = readl_poll_timeout(xbz_pcs_reg_base + PMU_PMA_STATUS, reg,
+				 (reg & CLK_IS_DISABLED) == 0x0, 1, 100000);
+	if (ret)
+		dev_err(dev, "Clock isn't disabled in auto mode: %d\n", ret);
+
+	reg = readl(xbz_pma_rx_base + SMEM_CTRL);
+	writel(reg | SMEM_HW_RDATA_ZERO, xbz_pma_rx_base + SMEM_CTRL);
+
+	reg = readl(xbz_pcs_reg_base + PHY_CTRL_CONFIG);
+	writel(reg | BIT(16), xbz_pcs_reg_base + PHY_CTRL_CONFIG);
+
+	/* Initialize data memory */
+	reg = readl(xbz_pma_rx_base + DM_CTRL_P01);
+	writel(reg | BIT(28), xbz_pma_rx_base + DM_CTRL_P01);
+	reg = readl(xbz_pma_rx_base + DM_CTRL_P23);
+	writel(reg | BIT(28), xbz_pma_rx_base + DM_CTRL_P23);
+
+	/* Initialize coefficient memory */
+	reg = readl(xbz_pma_rx_base + CM_CTRL_P01);
+	writel(reg | BIT(28), xbz_pma_rx_base + CM_CTRL_P01);
+	reg = readl(xbz_pma_rx_base + CM_CTRL_P23);
+	writel(reg | BIT(28), xbz_pma_rx_base + CM_CTRL_P23);
+
+	/* Initilize PM offset */
+	writel(0, xbz_pma_rx_base + SMEM_ADDR_REF_ADDR);
+
+	ret = request_firmware(&fw, MT7987_2P5GE_DSPBITTB, dev);
+	if (ret) {
+		dev_err(dev, "failed to load firmware: %s, ret: %d\n",
+			MT7987_2P5GE_DSPBITTB, ret);
+		goto free_pmb_addr;
+	}
+	if (fw->size != MT7987_2P5GE_DSPBITTB_SIZE) {
+		dev_err(dev, "DSPBITTB size 0x%zx != 0x%x\n",
+			fw->size, MT7987_2P5GE_DSPBITTB_SIZE);
+		ret = -EINVAL;
+		goto release_fw;
+	}
+
+	for (i = 0; i < fw->size - 1; i += 16) {
+		writel(*((uint32_t *)(fw->data + i)),
+		       xbz_pma_rx_base + SMEM_WDAT0);
+		writel(*((uint32_t *)(fw->data + i + 0x4)),
+		       xbz_pma_rx_base + SMEM_WDAT1);
+		writel(*((uint32_t *)(fw->data + i + 0x8)),
+		       xbz_pma_rx_base + SMEM_WDAT2);
+		writel(*((uint32_t *)(fw->data + i + 0xc)),
+		       xbz_pma_rx_base + SMEM_WDAT3);
+	}
+
+	reg = readl(xbz_pma_rx_base + DM_CTRL_P01);
+	writel(reg & ~BIT(28), xbz_pma_rx_base + DM_CTRL_P01);
+
+	reg = readl(xbz_pma_rx_base + DM_CTRL_P23);
+	writel(reg & ~BIT(28), xbz_pma_rx_base + DM_CTRL_P23);
+
+	reg = readl(xbz_pma_rx_base + CM_CTRL_P01);
+	writel(reg & ~BIT(28), xbz_pma_rx_base + CM_CTRL_P01);
+
+	reg = readl(xbz_pma_rx_base + CM_CTRL_P23);
+	writel(reg & ~BIT(28), xbz_pma_rx_base + CM_CTRL_P23);
+
+	reg = readw(mcu_csr_base + MD32_EN_CFG);
+	writew(reg | MD32_EN, mcu_csr_base + MD32_EN_CFG);
+	phy_set_bits(phydev, MII_BMCR, BMCR_RESET);
+	/* We need a delay here to stabilize initialization of MCU */
+	usleep_range(7000, 8000);
+	dev_info(dev, "Firmware loading/trigger ok.\n");
+
+	priv->fw_loaded = true;
+
+release_fw:
+	release_firmware(fw);
+free_pmb_addr:
+	iounmap(pmb_addr);
+free_mcu_csr:
+	iounmap(mcu_csr_base);
+free_chip_scu:
+	iounmap(chip_scu_base);
+free_pma:
+	iounmap(xbz_pma_rx_base);
+free_pcs:
+	iounmap(xbz_pcs_reg_base);
+free_pmd:
+	iounmap(pmd_reg_base);
+free_apb:
+	iounmap(apb_base);
+
+	return ret;
+}
+
+static int mt7988_2p5ge_phy_load_fw(struct phy_device *phydev)
+{
+	struct mtk_i2p5ge_phy_priv *priv = phydev->priv;
+	void __iomem *mcu_csr_base, *pmb_addr;
+	struct device *dev = &phydev->mdio.dev;
+	const struct firmware *fw;
+	int ret, i;
+	u32 reg;
+
+	if (priv->fw_loaded)
+		return 0;
+
+	pmb_addr = ioremap(MT7988_2P5GE_PMB_FW_BASE, MT7988_2P5GE_PMB_FW_LEN);
+	if (!pmb_addr)
+		return -ENOMEM;
+	mcu_csr_base = ioremap(MTK_2P5GPHY_MCU_CSR_BASE,
+			       MTK_2P5GPHY_MCU_CSR_LEN);
+	if (!mcu_csr_base) {
+		ret = -ENOMEM;
+		goto free_pmb;
+	}
+
+	ret = request_firmware(&fw, MT7988_2P5GE_PMB_FW, dev);
+	if (ret) {
+		dev_err(dev, "failed to load firmware: %s, ret: %d\n",
+			MT7988_2P5GE_PMB_FW, ret);
+		goto free;
+	}
+
+	if (fw->size != MT7988_2P5GE_PMB_FW_SIZE) {
+		dev_err(dev, "Firmware size 0x%zx != 0x%x\n",
+			fw->size, MT7988_2P5GE_PMB_FW_SIZE);
+		ret = -EINVAL;
+		goto release_fw;
+	}
+
+	reg = readw(mcu_csr_base + MD32_EN_CFG);
+	if (reg & MD32_EN) {
+		phy_set_bits(phydev, MII_BMCR, BMCR_RESET);
+		usleep_range(10000, 11000);
+	}
+	phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
+
+	/* Write magic number to safely stall MCU */
+	phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x800e, 0x1100);
+	phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x800f, 0x00df);
+
+	for (i = 0; i < MT7988_2P5GE_PMB_FW_SIZE - 1; i += 4)
+		writel(*((uint32_t *)(fw->data + i)), pmb_addr + i);
+	dev_info(dev, "Firmware date code: %x/%x/%x, version: %x.%x\n",
+		 be16_to_cpu(*((__be16 *)(fw->data +
+					  MT7988_2P5GE_PMB_FW_SIZE - 8))),
+		 *(fw->data + MT7988_2P5GE_PMB_FW_SIZE - 6),
+		 *(fw->data + MT7988_2P5GE_PMB_FW_SIZE - 5),
+		 *(fw->data + MT7988_2P5GE_PMB_FW_SIZE - 2),
+		 *(fw->data + MT7988_2P5GE_PMB_FW_SIZE - 1));
+
+	writew(reg & ~MD32_EN, mcu_csr_base + MD32_EN_CFG);
+	writew(reg | MD32_EN, mcu_csr_base + MD32_EN_CFG);
+	phy_set_bits(phydev, MII_BMCR, BMCR_RESET);
+	/* We need a delay here to stabilize initialization of MCU */
+	usleep_range(7000, 8000);
+	dev_info(dev, "Firmware loading/trigger ok.\n");
+
+	priv->fw_loaded = true;
+
+release_fw:
+	release_firmware(fw);
+free:
+	iounmap(mcu_csr_base);
+free_pmb:
+	iounmap(pmb_addr);
+
+	return ret;
+}
+
+static int mt798x_2p5ge_phy_config_init(struct phy_device *phydev)
+{
+	struct pinctrl *pinctrl;
+	int ret;
+
+	/* Check if PHY interface type is compatible */
+	if (phydev->interface != PHY_INTERFACE_MODE_INTERNAL)
+		return -ENODEV;
+
+	switch (phydev->drv->phy_id) {
+	case MTK_2P5GPHY_ID_MT7987:
+		ret = mt7987_2p5ge_phy_load_fw(phydev);
+		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
+				   MTK_PHY_LED_ON_POLARITY);
+		break;
+	case MTK_2P5GPHY_ID_MT7988:
+		ret = mt7988_2p5ge_phy_load_fw(phydev);
+		phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
+				 MTK_PHY_LED_ON_POLARITY);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ret < 0)
+		return ret;
+
+	/* Setup LED */
+	phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
+			 MTK_PHY_LED_ON_LINK10 | MTK_PHY_LED_ON_LINK100 |
+			 MTK_PHY_LED_ON_LINK1000 | MTK_PHY_LED_ON_LINK2500);
+	phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
+			 MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX);
+
+	/* Switch pinctrl after setting polarity to avoid bogus blinking */
+	pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "i2p5gbe-led");
+	if (IS_ERR(pinctrl))
+		dev_err(&phydev->mdio.dev, "Fail to set LED pins!\n");
+
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LPI_PCS_DSP_CTRL,
+		       MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK, 0);
+
+	/* Enable 16-bit next page exchange bit if 1000-BT isn't advertising */
+	mtk_tr_modify(phydev, 0x0, 0xf, 0x3c, AUTO_NP_10XEN,
+		      FIELD_PREP(AUTO_NP_10XEN, 0x1));
+
+	/* Enable HW auto downshift */
+	phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED_1,
+			 MTK_PHY_AUX_CTRL_AND_STATUS,
+			 0, MTK_PHY_ENABLE_DOWNSHIFT);
+
+	return 0;
+}
+
+static int mt798x_2p5ge_phy_config_aneg(struct phy_device *phydev)
+{
+	bool changed = false;
+	u32 adv;
+	int ret;
+
+	/* In fact, if we disable autoneg, we can't link up correctly:
+	 * 2.5G/1G: Need AN to exchange master/slave information.
+	 * 100M/10M: Without AN, link starts at half duplex (According to
+	 *           IEEE 802.3-2018), which this phy doesn't support.
+	 */
+	if (phydev->autoneg == AUTONEG_DISABLE)
+		return -EOPNOTSUPP;
+
+	ret = genphy_c45_an_config_aneg(phydev);
+	if (ret < 0)
+		return ret;
+	if (ret > 0)
+		changed = true;
+
+	/* Clause 45 doesn't define 1000BaseT support. Use Clause 22 instead in
+	 * our design.
+	 */
+	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
+	ret = phy_modify_changed(phydev, MII_CTRL1000, ADVERTISE_1000FULL, adv);
+	if (ret < 0)
+		return ret;
+	if (ret > 0)
+		changed = true;
+
+	return genphy_c45_check_and_restart_aneg(phydev, changed);
+}
+
+static int mt798x_2p5ge_phy_get_features(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = genphy_c45_pma_read_abilities(phydev);
+	if (ret)
+		return ret;
+
+	/* This phy can't handle collision, and neither can (XFI)MAC it's
+	 * connected to. Although it can do HDX handshake, it doesn't support
+	 * CSMA/CD that HDX requires.
+	 */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+			   phydev->supported);
+
+	return 0;
+}
+
+static int mt798x_2p5ge_phy_read_status(struct phy_device *phydev)
+{
+	int ret;
+
+	/* When MDIO_STAT1_LSTATUS is raised genphy_c45_read_link(), this phy
+	 * actually hasn't finished AN. So use CL22's link update function
+	 * instead.
+	 */
+	ret = genphy_update_link(phydev);
+	if (ret)
+		return ret;
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_UNKNOWN;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	/* We'll read link speed through vendor specific registers down below.
+	 * So remove phy_resolve_aneg_linkmode (AN on) & genphy_c45_read_pma
+	 * (AN off).
+	 */
+	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+		ret = genphy_c45_read_lpa(phydev);
+		if (ret < 0)
+			return ret;
+
+		/* Clause 45 doesn't define 1000BaseT support. Read the link
+		 * partner's 1G advertisement via Clause 22.
+		 */
+		ret = phy_read(phydev, MII_STAT1000);
+		if (ret < 0)
+			return ret;
+		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
+	} else if (phydev->autoneg == AUTONEG_DISABLE) {
+		linkmode_zero(phydev->lp_advertising);
+	}
+
+	if (phydev->link) {
+		ret = phy_read(phydev, PHY_AUX_CTRL_STATUS);
+		if (ret < 0)
+			return ret;
+
+		switch (FIELD_GET(PHY_AUX_SPEED_MASK, ret)) {
+		case PHY_AUX_SPD_10:
+			phydev->speed = SPEED_10;
+			break;
+		case PHY_AUX_SPD_100:
+			phydev->speed = SPEED_100;
+			break;
+		case PHY_AUX_SPD_1000:
+			phydev->speed = SPEED_1000;
+			break;
+		case PHY_AUX_SPD_2500:
+			phydev->speed = SPEED_2500;
+			break;
+		}
+
+		phydev->duplex = DUPLEX_FULL;
+		/* FIXME:
+		 * The current firmware always enables rate adaptation mode.
+		 */
+		phydev->rate_matching = RATE_MATCH_PAUSE;
+	}
+
+	return 0;
+}
+
+static int mt798x_2p5ge_phy_get_rate_matching(struct phy_device *phydev,
+					      phy_interface_t iface)
+{
+	return RATE_MATCH_PAUSE;
+}
+
+static int mt798x_2p5ge_phy_probe(struct phy_device *phydev)
+{
+	struct mtk_i2p5ge_phy_priv *priv;
+
+	priv = devm_kzalloc(&phydev->mdio.dev,
+			    sizeof(struct mtk_i2p5ge_phy_priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	switch (phydev->drv->phy_id) {
+	case MTK_2P5GPHY_ID_MT7987:
+	case MTK_2P5GPHY_ID_MT7988:
+		/* The original hardware only sets MDIO_DEVS_PMAPMD */
+		phydev->c45_ids.devices_in_package |= MDIO_DEVS_PCS |
+						      MDIO_DEVS_AN |
+						      MDIO_DEVS_VEND1 |
+						      MDIO_DEVS_VEND2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	priv->fw_loaded = false;
+	phydev->priv = priv;
+
+	return 0;
+}
+
+static struct phy_driver mtk_2p5gephy_driver[] = {
+	{
+		PHY_ID_MATCH_MODEL(MTK_2P5GPHY_ID_MT7987),
+		.name = "MediaTek MT7987 2.5GbE PHY",
+		.probe = mt798x_2p5ge_phy_probe,
+		.config_init = mt798x_2p5ge_phy_config_init,
+		.config_aneg = mt798x_2p5ge_phy_config_aneg,
+		.get_features = mt798x_2p5ge_phy_get_features,
+		.read_status = mt798x_2p5ge_phy_read_status,
+		.get_rate_matching = mt798x_2p5ge_phy_get_rate_matching,
+		.suspend = genphy_suspend,
+		.resume = genphy_resume,
+		.read_page = mtk_phy_read_page,
+		.write_page = mtk_phy_write_page,
+	},
+	{
+		PHY_ID_MATCH_MODEL(MTK_2P5GPHY_ID_MT7988),
+		.name = "MediaTek MT7988 2.5GbE PHY",
+		.probe = mt798x_2p5ge_phy_probe,
+		.config_init = mt798x_2p5ge_phy_config_init,
+		.config_aneg = mt798x_2p5ge_phy_config_aneg,
+		.get_features = mt798x_2p5ge_phy_get_features,
+		.read_status = mt798x_2p5ge_phy_read_status,
+		.get_rate_matching = mt798x_2p5ge_phy_get_rate_matching,
+		.suspend = genphy_suspend,
+		.resume = genphy_resume,
+		.read_page = mtk_phy_read_page,
+		.write_page = mtk_phy_write_page,
+	},
+};
+
+module_phy_driver(mtk_2p5gephy_driver);
+
+static struct mdio_device_id __maybe_unused mtk_2p5ge_phy_tbl[] = {
+	{ PHY_ID_MATCH_VENDOR(0x00339c00) },
+	{ }
+};
+
+MODULE_DESCRIPTION("MediaTek 2.5Gb Ethernet PHY driver");
+MODULE_AUTHOR("SkyLake Huang <SkyLake.Huang@mediatek.com>");
+MODULE_LICENSE("GPL");
+
+MODULE_DEVICE_TABLE(mdio, mtk_2p5ge_phy_tbl);
+MODULE_FIRMWARE(MT7988_2P5GE_PMB_FW);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-ge-soc.c
similarity index 73%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-ge-soc.c
index 40a5e0f..2c2a879 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-ge-soc.c
@@ -1,11 +1,14 @@
 // SPDX-License-Identifier: GPL-2.0+
 #include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/nvmem-consumer.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/phy.h>
+#include <linux/regmap.h>
+
+#include "mtk.h"
 
 #define MTK_GPHY_ID_MT7981			0x03a29461
 #define MTK_GPHY_ID_MT7988			0x03a29481
@@ -21,7 +24,108 @@
 #define MTK_PHY_SMI_DET_ON_THRESH_MASK		GENMASK(13, 8)
 
 #define MTK_PHY_PAGE_EXTENDED_2A30		0x2a30
-#define MTK_PHY_PAGE_EXTENDED_52B5		0x52b5
+
+/* Registers on Token Ring debug nodes */
+/* ch_addr = 0x0, node_addr = 0x7, data_addr = 0x15 */
+/* NormMseLoThresh */
+#define NORMAL_MSE_LO_THRESH_MASK		GENMASK(15, 8)
+
+/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */
+/* RemAckCntLimitCtrl */
+#define REMOTE_ACK_COUNT_LIMIT_CTRL_MASK	GENMASK(2, 1)
+
+/* ch_addr = 0x1, node_addr = 0xd, data_addr = 0x20 */
+/* VcoSlicerThreshBitsHigh */
+#define VCO_SLICER_THRESH_HIGH_MASK		GENMASK(23, 0)
+
+/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x0 */
+/* DfeTailEnableVgaThresh1000 */
+#define DFE_TAIL_EANBLE_VGA_TRHESH_1000		GENMASK(5, 1)
+
+/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x1 */
+/* MrvlTrFix100Kp */
+#define MRVL_TR_FIX_100KP_MASK			GENMASK(22, 20)
+/* MrvlTrFix100Kf */
+#define MRVL_TR_FIX_100KF_MASK			GENMASK(19, 17)
+/* MrvlTrFix1000Kp */
+#define MRVL_TR_FIX_1000KP_MASK			GENMASK(16, 14)
+/* MrvlTrFix1000Kf */
+#define MRVL_TR_FIX_1000KF_MASK			GENMASK(13, 11)
+
+/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x12 */
+/* VgaDecRate */
+#define VGA_DECIMATION_RATE_MASK		GENMASK(8, 5)
+
+/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x17 */
+/* SlvDSPreadyTime */
+#define SLAVE_DSP_READY_TIME_MASK		GENMASK(22, 15)
+/* MasDSPreadyTime */
+#define MASTER_DSP_READY_TIME_MASK		GENMASK(14, 7)
+
+/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x18 */
+/* EnabRandUpdTrig */
+#define ENABLE_RANDOM_UPDOWN_COUNTER_TRIGGER	BIT(8)
+
+/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x20 */
+/* ResetSyncOffset */
+#define RESET_SYNC_OFFSET_MASK			GENMASK(11, 8)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x0 */
+/* FfeUpdGainForceVal */
+#define FFE_UPDATE_GAIN_FORCE_VAL_MASK		GENMASK(9, 7)
+/* FfeUpdGainForce */
+#define FFE_UPDATE_GAIN_FORCE			BIT(6)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x3 */
+/* TrFreeze */
+#define TR_FREEZE_MASK				GENMASK(11, 0)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x6 */
+/* SS: Steady-state, KP: Proportional Gain */
+/* SSTrKp100 */
+#define SS_TR_KP100_MASK			GENMASK(21, 19)
+/* SSTrKf100 */
+#define SS_TR_KF100_MASK			GENMASK(18, 16)
+/* SSTrKp1000Mas */
+#define SS_TR_KP1000_MASTER_MASK		GENMASK(15, 13)
+/* SSTrKf1000Mas */
+#define SS_TR_KF1000_MASTER_MASK		GENMASK(12, 10)
+/* SSTrKp1000Slv */
+#define SS_TR_KP1000_SLAVE_MASK			GENMASK(9, 7)
+/* SSTrKf1000Slv */
+#define SS_TR_KF1000_SLAVE_MASK			GENMASK(6, 4)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x8 */
+/* clear this bit if wanna select from AFE */
+/* Regsigdet_sel_1000 */
+#define EEE1000_SELECT_SIGNAL_DETECTION_FROM_DFE	BIT(4)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0xd */
+/* RegEEE_st2TrKf1000 */
+#define EEE1000_STAGE2_TR_KF_MASK		GENMASK(13, 11)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0xf */
+/* RegEEE_slv_waketr_timer_tar */
+#define SLAVE_WAKETR_TIMER_MASK			GENMASK(20, 11)
+/* RegEEE_slv_remtx_timer_tar */
+#define SLAVE_REMTX_TIMER_MASK			GENMASK(10, 1)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x10 */
+/* RegEEE_slv_wake_int_timer_tar */
+#define SLAVE_WAKEINT_TIMER_MASK		GENMASK(10, 1)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x14 */
+/* RegEEE_trfreeze_timer2 */
+#define TR_FREEZE_TIMER2_MASK			GENMASK(9, 0)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x1c */
+/* RegEEE100Stg1_tar */
+#define EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK	GENMASK(8, 0)
+
+/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x25 */
+/* REGEEE_wake_slv_tr_wait_dfesigdet_en */
+#define WAKE_SLAVE_TR_WAIT_DFE_DETECTION_EN	BIT(11)
+
 
 #define ANALOG_INTERNAL_OPERATION_MAX_US	20
 #define TXRESERVE_MIN				0
@@ -209,57 +313,15 @@
 #define MTK_PHY_DA_TX_R50_PAIR_D		0x540
 
 /* Registers on MDIO_MMD_VEND2 */
-#define MTK_PHY_LED0_ON_CTRL			0x24
-#define   MTK_PHY_LED0_ON_MASK			GENMASK(6, 0)
-#define   MTK_PHY_LED0_ON_LINK1000		BIT(0)
-#define   MTK_PHY_LED0_ON_LINK100		BIT(1)
-#define   MTK_PHY_LED0_ON_LINK10		BIT(2)
-#define   MTK_PHY_LED0_ON_LINKDOWN		BIT(3)
-#define   MTK_PHY_LED0_ON_FDX			BIT(4) /* Full duplex */
-#define   MTK_PHY_LED0_ON_HDX			BIT(5) /* Half duplex */
-#define   MTK_PHY_LED0_FORCE_ON			BIT(6)
-#define   MTK_PHY_LED0_POLARITY			BIT(14)
-#define   MTK_PHY_LED0_ENABLE			BIT(15)
-
-#define MTK_PHY_LED0_BLINK_CTRL			0x25
-#define   MTK_PHY_LED0_1000TX			BIT(0)
-#define   MTK_PHY_LED0_1000RX			BIT(1)
-#define   MTK_PHY_LED0_100TX			BIT(2)
-#define   MTK_PHY_LED0_100RX			BIT(3)
-#define   MTK_PHY_LED0_10TX			BIT(4)
-#define   MTK_PHY_LED0_10RX			BIT(5)
-#define   MTK_PHY_LED0_COLLISION		BIT(6)
-#define   MTK_PHY_LED0_RX_CRC_ERR		BIT(7)
-#define   MTK_PHY_LED0_RX_IDLE_ERR		BIT(8)
-#define   MTK_PHY_LED0_FORCE_BLINK		BIT(9)
-
-#define MTK_PHY_LED1_ON_CTRL			0x26
-#define   MTK_PHY_LED1_ON_MASK			GENMASK(6, 0)
-#define   MTK_PHY_LED1_ON_LINK1000		BIT(0)
-#define   MTK_PHY_LED1_ON_LINK100		BIT(1)
-#define   MTK_PHY_LED1_ON_LINK10		BIT(2)
-#define   MTK_PHY_LED1_ON_LINKDOWN		BIT(3)
-#define   MTK_PHY_LED1_ON_FDX			BIT(4) /* Full duplex */
-#define   MTK_PHY_LED1_ON_HDX			BIT(5) /* Half duplex */
-#define   MTK_PHY_LED1_FORCE_ON			BIT(6)
-#define   MTK_PHY_LED1_POLARITY			BIT(14)
-#define   MTK_PHY_LED1_ENABLE			BIT(15)
-
-#define MTK_PHY_LED1_BLINK_CTRL			0x27
-#define   MTK_PHY_LED1_1000TX			BIT(0)
-#define   MTK_PHY_LED1_1000RX			BIT(1)
-#define   MTK_PHY_LED1_100TX			BIT(2)
-#define   MTK_PHY_LED1_100RX			BIT(3)
-#define   MTK_PHY_LED1_10TX			BIT(4)
-#define   MTK_PHY_LED1_10RX			BIT(5)
-#define   MTK_PHY_LED1_COLLISION		BIT(6)
-#define   MTK_PHY_LED1_RX_CRC_ERR		BIT(7)
-#define   MTK_PHY_LED1_RX_IDLE_ERR		BIT(8)
-#define   MTK_PHY_LED1_FORCE_BLINK		BIT(9)
+#define MTK_PHY_LED1_DEFAULT_POLARITIES		BIT(1)
 
 #define MTK_PHY_RG_BG_RASEL			0x115
 #define   MTK_PHY_RG_BG_RASEL_MASK		GENMASK(2, 0)
 
+/* 'boottrap' register reflecting the configuration of the 4 PHY LEDs */
+#define RG_GPIO_MISC_TPBANK0			0x6f0
+#define   RG_GPIO_MISC_TPBANK0_BOOTMODE		GENMASK(11, 8)
+
 /* These macro privides efuse parsing for internal phy. */
 #define EFS_DA_TX_I2MPB_A(x)			(((x) >> 0) & GENMASK(5, 0))
 #define EFS_DA_TX_I2MPB_B(x)			(((x) >> 6) & GENMASK(5, 0))
@@ -312,20 +374,10 @@
 	SW_M
 };
 
-struct mtk_socphy_shared_priv {
+struct mtk_socphy_shared {
 	u32			boottrap;
 };
 
-static int mtk_socphy_read_page(struct phy_device *phydev)
-{
-	return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
-}
-
-static int mtk_socphy_write_page(struct phy_device *phydev, int page)
-{
-	return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
-}
-
 /* One calibration cycle consists of:
  * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high
  *   until AD_CAL_COMP is ready to output calibration result.
@@ -346,7 +398,8 @@
 	ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
 					MTK_PHY_RG_AD_CAL_CLK, reg_val,
 					reg_val & MTK_PHY_DA_CAL_CLK, 500,
-					ANALOG_INTERNAL_OPERATION_MAX_US, false);
+					ANALOG_INTERNAL_OPERATION_MAX_US,
+					false);
 	if (ret) {
 		phydev_err(phydev, "Calibration cycle timeout\n");
 		return ret;
@@ -355,7 +408,7 @@
 	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN,
 			   MTK_PHY_DA_CALIN_FLAG);
 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >>
-			   MTK_PHY_AD_CAL_COMP_OUT_SHIFT;
+	      MTK_PHY_AD_CAL_COMP_OUT_SHIFT;
 	phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret);
 
 	return ret;
@@ -412,16 +465,17 @@
 
 static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf)
 {
-	int i;
-	int bias[16] = {};
-	const int vals_9461[16] = { 7, 1, 4, 7,
-				    7, 1, 4, 7,
-				    7, 1, 4, 7,
-				    7, 1, 4, 7 };
 	const int vals_9481[16] = { 10, 6, 6, 10,
 				    10, 6, 6, 10,
 				    10, 6, 6, 10,
 				    10, 6, 6, 10 };
+	const int vals_9461[16] = { 7, 1, 4, 7,
+				    7, 1, 4, 7,
+				    7, 1, 4, 7,
+				    7, 1, 4, 7 };
+	int bias[16] = {};
+	int i;
+
 	switch (phydev->drv->phy_id) {
 	case MTK_GPHY_ID_MT7981:
 		/* We add some calibration to efuse values
@@ -444,38 +498,46 @@
 	}
 
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG,
-		       MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10);
+		       MTK_PHY_DA_TX_I2MPB_A_GBE_MASK,
+		       (buf[0] + bias[0]) << 10);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG,
 		       MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2,
-		       MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10);
+		       MTK_PHY_DA_TX_I2MPB_A_HBT_MASK,
+		       (buf[0] + bias[2]) << 10);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2,
 		       MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]);
 
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1,
-		       MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8);
+		       MTK_PHY_DA_TX_I2MPB_B_GBE_MASK,
+		       (buf[1] + bias[4]) << 8);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1,
 		       MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2,
-		       MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8);
+		       MTK_PHY_DA_TX_I2MPB_B_HBT_MASK,
+		       (buf[1] + bias[6]) << 8);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2,
 		       MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]);
 
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1,
-		       MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8);
+		       MTK_PHY_DA_TX_I2MPB_C_GBE_MASK,
+		       (buf[2] + bias[8]) << 8);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1,
 		       MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2,
-		       MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8);
+		       MTK_PHY_DA_TX_I2MPB_C_HBT_MASK,
+		       (buf[2] + bias[10]) << 8);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2,
 		       MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]);
 
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1,
-		       MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8);
+		       MTK_PHY_DA_TX_I2MPB_D_GBE_MASK,
+		       (buf[3] + bias[12]) << 8);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1,
 		       MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2,
-		       MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8);
+		       MTK_PHY_DA_TX_I2MPB_D_HBT_MASK,
+		       (buf[3] + bias[14]) << 8);
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2,
 		       MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]);
 
@@ -666,7 +728,8 @@
 		goto restore;
 
 	/* We calibrate TX-VCM in different logic. Check upper index and then
-	 * lower index. If this calibration is valid, apply lower index's result.
+	 * lower index. If this calibration is valid, apply lower index's
+	 * result.
 	 */
 	ret = upper_ret - lower_ret;
 	if (ret == 1) {
@@ -679,7 +742,7 @@
 			  MTK_PHY_DA_RX_PSBN_LP_MASK,
 			  upper_idx << 12 | upper_idx << 8 |
 			  upper_idx << 4 | upper_idx);
-		phydev_info(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx);
+		phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx);
 	} else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 &&
 		   lower_ret == 1) {
 		ret = 0;
@@ -695,7 +758,8 @@
 	} else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 &&
 		   lower_ret == 0) {
 		ret = 0;
-		phydev_warn(phydev, "TX-VCM SW cal result at high margin 0x%x\n",
+		phydev_warn(phydev,
+			    "TX-VCM SW cal result at high margin 0x%x\n",
 			    upper_idx);
 	} else {
 		ret = -EINVAL;
@@ -718,40 +782,36 @@
 static void mt798x_phy_common_finetune(struct phy_device *phydev)
 {
 	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
-	/* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */
-	__phy_write(phydev, 0x11, 0xc71);
-	__phy_write(phydev, 0x12, 0xc);
-	__phy_write(phydev, 0x10, 0x8fae);
+	__mtk_tr_modify(phydev, 0x1, 0xf, 0x17,
+			SLAVE_DSP_READY_TIME_MASK | MASTER_DSP_READY_TIME_MASK,
+			FIELD_PREP(SLAVE_DSP_READY_TIME_MASK, 0x18) |
+			FIELD_PREP(MASTER_DSP_READY_TIME_MASK, 0x18));
 
-	/* EnabRandUpdTrig = 1 */
-	__phy_write(phydev, 0x11, 0x2f00);
-	__phy_write(phydev, 0x12, 0xe);
-	__phy_write(phydev, 0x10, 0x8fb0);
+	__mtk_tr_set_bits(phydev, 0x1, 0xf, 0x18,
+			  ENABLE_RANDOM_UPDOWN_COUNTER_TRIGGER);
 
-	/* NormMseLoThresh = 85 */
-	__phy_write(phydev, 0x11, 0x55a0);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x83aa);
+	__mtk_tr_modify(phydev, 0x0, 0x7, 0x15,
+			NORMAL_MSE_LO_THRESH_MASK,
+			FIELD_PREP(NORMAL_MSE_LO_THRESH_MASK, 0x55));
 
-	/* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */
-	__phy_write(phydev, 0x11, 0x240);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x9680);
+	__mtk_tr_modify(phydev, 0x2, 0xd, 0x0,
+			FFE_UPDATE_GAIN_FORCE_VAL_MASK,
+			FIELD_PREP(FFE_UPDATE_GAIN_FORCE_VAL_MASK, 0x4) |
+				   FFE_UPDATE_GAIN_FORCE);
 
-	/* TrFreeze = 0 (mt7988 default) */
-	__phy_write(phydev, 0x11, 0x0);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x9686);
+	__mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x3, TR_FREEZE_MASK);
 
-	/* SSTrKp100 = 5 */
-	/* SSTrKf100 = 6 */
-	/* SSTrKp1000Mas = 5 */
-	/* SSTrKf1000Mas = 6 */
-	/* SSTrKp1000Slv = 5 */
-	/* SSTrKf1000Slv = 6 */
-	__phy_write(phydev, 0x11, 0xbaef);
-	__phy_write(phydev, 0x12, 0x2e);
-	__phy_write(phydev, 0x10, 0x968c);
+	__mtk_tr_modify(phydev, 0x2, 0xd, 0x6,
+			SS_TR_KP100_MASK | SS_TR_KF100_MASK |
+			SS_TR_KP1000_MASTER_MASK | SS_TR_KF1000_MASTER_MASK |
+			SS_TR_KP1000_SLAVE_MASK | SS_TR_KF1000_SLAVE_MASK,
+			FIELD_PREP(SS_TR_KP100_MASK, 0x5) |
+			FIELD_PREP(SS_TR_KF100_MASK, 0x6) |
+			FIELD_PREP(SS_TR_KP1000_MASTER_MASK, 0x5) |
+			FIELD_PREP(SS_TR_KF1000_MASTER_MASK, 0x6) |
+			FIELD_PREP(SS_TR_KP1000_SLAVE_MASK, 0x5) |
+			FIELD_PREP(SS_TR_KF1000_SLAVE_MASK, 0x6));
+
 	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
 }
 
@@ -774,32 +834,35 @@
 	}
 
 	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
-	/* ResetSyncOffset = 6 */
-	__phy_write(phydev, 0x11, 0x600);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x8fc0);
+	__mtk_tr_modify(phydev, 0x1, 0xf, 0x20,
+			RESET_SYNC_OFFSET_MASK,
+			FIELD_PREP(RESET_SYNC_OFFSET_MASK, 0x6));
 
-	/* VgaDecRate = 1 */
-	__phy_write(phydev, 0x11, 0x4c2a);
-	__phy_write(phydev, 0x12, 0x3e);
-	__phy_write(phydev, 0x10, 0x8fa4);
+	__mtk_tr_modify(phydev, 0x1, 0xf, 0x12,
+			VGA_DECIMATION_RATE_MASK,
+			FIELD_PREP(VGA_DECIMATION_RATE_MASK, 0x1));
 
 	/* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2,
 	 * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2
 	 */
-	__phy_write(phydev, 0x11, 0xd10a);
-	__phy_write(phydev, 0x12, 0x34);
-	__phy_write(phydev, 0x10, 0x8f82);
+	__mtk_tr_modify(phydev, 0x1, 0xf, 0x1,
+			MRVL_TR_FIX_100KP_MASK | MRVL_TR_FIX_100KF_MASK |
+			MRVL_TR_FIX_1000KP_MASK | MRVL_TR_FIX_1000KF_MASK,
+			FIELD_PREP(MRVL_TR_FIX_100KP_MASK, 0x3) |
+			FIELD_PREP(MRVL_TR_FIX_100KF_MASK, 0x2) |
+			FIELD_PREP(MRVL_TR_FIX_1000KP_MASK, 0x3) |
+			FIELD_PREP(MRVL_TR_FIX_1000KF_MASK, 0x2));
 
 	/* VcoSlicerThreshBitsHigh */
-	__phy_write(phydev, 0x11, 0x5555);
-	__phy_write(phydev, 0x12, 0x55);
-	__phy_write(phydev, 0x10, 0x8ec0);
+	__mtk_tr_modify(phydev, 0x1, 0xd, 0x20,
+			VCO_SLICER_THRESH_HIGH_MASK,
+			FIELD_PREP(VCO_SLICER_THRESH_HIGH_MASK, 0x555555));
 	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
 
 	/* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234,
-		       MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK,
+		       MTK_PHY_TR_OPEN_LOOP_EN_MASK |
+		       MTK_PHY_LPF_X_AVERAGE_MASK,
 		       BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9));
 
 	/* rg_tr_lpf_cnt_val = 512 */
@@ -828,7 +891,6 @@
 	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0);
 	/* Adjust LDO output voltage */
 	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222);
-
 }
 
 static void mt7988_phy_finetune(struct phy_device *phydev)
@@ -846,35 +908,33 @@
 	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5);
 
 	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
-	/* ResetSyncOffset = 5 */
-	__phy_write(phydev, 0x11, 0x500);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x8fc0);
+	__mtk_tr_modify(phydev, 0x1, 0xf, 0x20,
+			RESET_SYNC_OFFSET_MASK,
+			FIELD_PREP(RESET_SYNC_OFFSET_MASK, 0x5));
 
 	/* VgaDecRate is 1 at default on mt7988 */
 
-	/* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7,
-	 * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7
-	 */
-	__phy_write(phydev, 0x11, 0xb90a);
-	__phy_write(phydev, 0x12, 0x6f);
-	__phy_write(phydev, 0x10, 0x8f82);
+	__mtk_tr_modify(phydev, 0x1, 0xf, 0x1,
+			MRVL_TR_FIX_100KP_MASK | MRVL_TR_FIX_100KF_MASK |
+			MRVL_TR_FIX_1000KP_MASK | MRVL_TR_FIX_1000KF_MASK,
+			FIELD_PREP(MRVL_TR_FIX_100KP_MASK, 0x6) |
+			FIELD_PREP(MRVL_TR_FIX_100KF_MASK, 0x7) |
+			FIELD_PREP(MRVL_TR_FIX_1000KP_MASK, 0x6) |
+			FIELD_PREP(MRVL_TR_FIX_1000KF_MASK, 0x7));
 
-	/* RemAckCntLimitCtrl = 1 */
-	__phy_write(phydev, 0x11, 0xfbba);
-	__phy_write(phydev, 0x12, 0xc3);
-	__phy_write(phydev, 0x10, 0x87f8);
-
+	__mtk_tr_modify(phydev, 0x0, 0xf, 0x3c,
+			REMOTE_ACK_COUNT_LIMIT_CTRL_MASK,
+			FIELD_PREP(REMOTE_ACK_COUNT_LIMIT_CTRL_MASK, 0x1));
 	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
 
 	/* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234,
-		       MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK,
+		       MTK_PHY_TR_OPEN_LOOP_EN_MASK |
+		       MTK_PHY_LPF_X_AVERAGE_MASK,
 		       BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa));
 
 	/* rg_tr_lpf_cnt_val = 1023 */
 	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x3ff);
-
 }
 
 static void mt798x_phy_eee(struct phy_device *phydev)
@@ -940,49 +1000,42 @@
 			 MTK_PHY_TR_READY_SKIP_AFE_WAKEUP);
 
 	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
-	/* Regsigdet_sel_1000 = 0 */
-	__phy_write(phydev, 0x11, 0xb);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x9690);
+	__mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x8,
+			  EEE1000_SELECT_SIGNAL_DETECTION_FROM_DFE);
 
-	/* REG_EEE_st2TrKf1000 = 2 */
-	__phy_write(phydev, 0x11, 0x114f);
-	__phy_write(phydev, 0x12, 0x2);
-	__phy_write(phydev, 0x10, 0x969a);
+	__mtk_tr_modify(phydev, 0x2, 0xd, 0xd,
+			EEE1000_STAGE2_TR_KF_MASK,
+			FIELD_PREP(EEE1000_STAGE2_TR_KF_MASK, 0x2));
 
-	/* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */
-	__phy_write(phydev, 0x11, 0x3028);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x969e);
+	__mtk_tr_modify(phydev, 0x2, 0xd, 0xf,
+			SLAVE_WAKETR_TIMER_MASK | SLAVE_REMTX_TIMER_MASK,
+			FIELD_PREP(SLAVE_WAKETR_TIMER_MASK, 0x6) |
+			FIELD_PREP(SLAVE_REMTX_TIMER_MASK, 0x14));
 
-	/* RegEEE_slv_wake_int_timer_tar = 8 */
-	__phy_write(phydev, 0x11, 0x5010);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x96a0);
+	__mtk_tr_modify(phydev, 0x2, 0xd, 0x10,
+			SLAVE_WAKEINT_TIMER_MASK,
+			FIELD_PREP(SLAVE_WAKEINT_TIMER_MASK, 0x8));
 
-	/* RegEEE_trfreeze_timer2 = 586 */
-	__phy_write(phydev, 0x11, 0x24a);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x96a8);
+	__mtk_tr_modify(phydev, 0x2, 0xd, 0x14,
+			TR_FREEZE_TIMER2_MASK,
+			FIELD_PREP(TR_FREEZE_TIMER2_MASK, 0x24a));
 
-	/* RegEEE100Stg1_tar = 16 */
-	__phy_write(phydev, 0x11, 0x3210);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x96b8);
+	__mtk_tr_modify(phydev, 0x2, 0xd, 0x1c,
+			EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK,
+			FIELD_PREP(EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK,
+				   0x10));
 
-	/* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */
-	__phy_write(phydev, 0x11, 0x1463);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x96ca);
+	__mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x25,
+			  WAKE_SLAVE_TR_WAIT_DFE_DETECTION_EN);
 
-	/* DfeTailEnableVgaThresh1000 = 27 */
-	__phy_write(phydev, 0x11, 0x36);
-	__phy_write(phydev, 0x12, 0x0);
-	__phy_write(phydev, 0x10, 0x8f80);
+	__mtk_tr_modify(phydev, 0x1, 0xf, 0x0,
+			DFE_TAIL_EANBLE_VGA_TRHESH_1000,
+			FIELD_PREP(DFE_TAIL_EANBLE_VGA_TRHESH_1000, 0x1b));
 	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
 
 	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3);
-	__phy_modify(phydev, MTK_PHY_LPI_REG_14, MTK_PHY_LPI_WAKE_TIMER_1000_MASK,
+	__phy_modify(phydev, MTK_PHY_LPI_REG_14,
+		     MTK_PHY_LPI_WAKE_TIMER_1000_MASK,
 		     FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c));
 
 	__phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK,
@@ -992,11 +1045,12 @@
 	phy_modify_mmd(phydev, MDIO_MMD_VEND1,
 		       MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122,
 		       MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK,
-		       FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, 0xff));
+		       FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK,
+				  0xff));
 }
 
 static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item,
-			 u8 start_pair, u8 end_pair)
+		  u8 start_pair, u8 end_pair)
 {
 	u8 pair_n;
 	int ret;
@@ -1017,7 +1071,7 @@
 }
 
 static int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item,
-			    u8 start_pair, u8 end_pair, u32 *buf)
+		     u8 start_pair, u8 end_pair, u32 *buf)
 {
 	u8 pair_n;
 	int ret;
@@ -1075,10 +1129,10 @@
 
 static int mt798x_phy_calibration(struct phy_device *phydev)
 {
+	struct nvmem_cell *cell;
 	int ret = 0;
-	u32 *buf;
 	size_t len;
-	struct nvmem_cell *cell;
+	u32 *buf;
 
 	cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data");
 	if (IS_ERR(cell)) {
@@ -1133,45 +1187,45 @@
 	mt798x_phy_common_finetune(phydev);
 	mt798x_phy_eee(phydev);
 
-	return 0;
+	return mt798x_phy_calibration(phydev);
 }
 
 static int mt7988_phy_setup_led(struct phy_device *phydev)
 {
-	struct mtk_socphy_shared_priv *priv = phydev->shared->priv;
+	struct mtk_socphy_shared *shared = phydev->shared->priv;
 	int port = phydev->mdio.addr;
-	u32 reg = priv->boottrap;
+	u32 reg = shared->boottrap;
 	struct pinctrl *pinctrl;
 
 	phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
-		      MTK_PHY_LED0_ENABLE | MTK_PHY_LED0_POLARITY |
-		      MTK_PHY_LED0_ON_LINK10 |
-		      MTK_PHY_LED0_ON_LINK100 |
-		      MTK_PHY_LED0_ON_LINK1000);
+		      MTK_PHY_LED_ON_ENABLE | MTK_PHY_LED_ON_POLARITY |
+		      MTK_PHY_LED_ON_LINK10 |
+		      MTK_PHY_LED_ON_LINK100 |
+		      MTK_PHY_LED_ON_LINK1000);
 	phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
-		      MTK_PHY_LED1_ENABLE | MTK_PHY_LED1_POLARITY |
-		      MTK_PHY_LED1_ON_LINK10 |
-		      MTK_PHY_LED1_ON_LINK100 |
-		      MTK_PHY_LED1_ON_LINK1000);
+		      MTK_PHY_LED_ON_ENABLE | MTK_PHY_LED_ON_POLARITY |
+		      MTK_PHY_LED_ON_LINK10 |
+		      MTK_PHY_LED_ON_LINK100 |
+		      MTK_PHY_LED_ON_LINK1000);
 
-	if ((port == GPHY_PORT0 && reg & BIT(8)) ||
-	    (port == GPHY_PORT1 && reg & BIT(9)) ||
-	    (port == GPHY_PORT2 && reg & BIT(10)) ||
-	    (port == GPHY_PORT3 && reg & BIT(11))) {
+	if ((port == GPHY_PORT0 && reg & BIT(0)) ||
+	    (port == GPHY_PORT1 && reg & BIT(1)) ||
+	    (port == GPHY_PORT2 && reg & BIT(2)) ||
+	    (port == GPHY_PORT3 && reg & BIT(3))) {
 		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
-				   MTK_PHY_LED0_POLARITY);
+				   MTK_PHY_LED_ON_POLARITY);
 		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
-				   MTK_PHY_LED1_POLARITY);
+				   MTK_PHY_LED_ON_POLARITY);
 	}
 
 	phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_BLINK_CTRL,
-		      MTK_PHY_LED0_1000TX | MTK_PHY_LED0_1000RX |
-		      MTK_PHY_LED0_100TX  | MTK_PHY_LED0_100RX  |
-		      MTK_PHY_LED0_10TX   | MTK_PHY_LED0_10RX);
+		      MTK_PHY_LED_BLINK_1000TX | MTK_PHY_LED_BLINK_1000RX |
+		      MTK_PHY_LED_BLINK_100TX  | MTK_PHY_LED_BLINK_100RX  |
+		      MTK_PHY_LED_BLINK_10TX   | MTK_PHY_LED_BLINK_10RX);
 	phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_BLINK_CTRL,
-		      MTK_PHY_LED1_1000TX | MTK_PHY_LED1_1000RX |
-		      MTK_PHY_LED1_100TX  | MTK_PHY_LED1_100RX  |
-		      MTK_PHY_LED1_10TX   | MTK_PHY_LED1_10RX);
+		      MTK_PHY_LED_BLINK_1000TX | MTK_PHY_LED_BLINK_1000RX |
+		      MTK_PHY_LED_BLINK_100TX  | MTK_PHY_LED_BLINK_100RX  |
+		      MTK_PHY_LED_BLINK_10TX   | MTK_PHY_LED_BLINK_10RX);
 
 	pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led");
 	if (IS_ERR(pinctrl)) {
@@ -1184,38 +1238,47 @@
 
 static int mt7988_phy_probe_shared(struct phy_device *phydev)
 {
-	struct mtk_socphy_shared_priv *priv = phydev->shared->priv;
-	void __iomem *boottrap;
-	struct device_node *np;
+	struct device_node *np = dev_of_node(&phydev->mdio.bus->dev);
+	struct mtk_socphy_shared *shared = phydev->shared->priv;
+	struct regmap *regmap;
 	u32 reg;
-
-	np = of_find_compatible_node(NULL, NULL, "mediatek,boottrap");
-	if (!np)
-		return -ENOENT;
+	int ret;
 
-	boottrap = of_iomap(np, 0);
-	if (!boottrap)
-		return -ENOMEM;
+	/* The LED0 of the 4 PHYs in MT7988 are wired to SoC pins LED_A, LED_B,
+	 * LED_C and LED_D respectively. At the same time those pins are used to
+	 * bootstrap configuration of the reference clock source (LED_A),
+	 * DRAM DDRx16b x2/x1 (LED_B) and boot device (LED_C, LED_D).
+	 * In practice this is done using a LED and a resistor pulling the pin
+	 * either to GND or to VIO.
+	 * The detected value at boot time is accessible at run-time using the
+	 * TPBANK0 register located in the gpio base of the pinctrl, in order
+	 * to read it here it needs to be referenced by a phandle called
+	 * 'mediatek,pio' in the MDIO bus hosting the PHY.
+	 * The 4 bits in TPBANK0 are kept as package shared data and are used to
+	 * set LED polarity for each of the LED0.
+	 */
+	regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,pio");
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
 
-	reg = readl(boottrap);
-	iounmap(boottrap);
+	ret = regmap_read(regmap, RG_GPIO_MISC_TPBANK0, &reg);
+	if (ret)
+		return ret;
 
-	priv->boottrap = reg;
+	shared->boottrap = FIELD_GET(RG_GPIO_MISC_TPBANK0_BOOTMODE, reg);
 
 	return 0;
 }
 
-static int mt7981_phy_probe(struct phy_device *phydev)
-{
-	return mt798x_phy_calibration(phydev);
-}
-
 static int mt7988_phy_probe(struct phy_device *phydev)
 {
 	int err;
 
+	if (phydev->mdio.addr > 3)
+		return -EINVAL;
+
 	err = devm_phy_package_join(&phydev->mdio.dev, phydev, 0,
-				    sizeof(struct mtk_socphy_shared_priv));
+				    sizeof(struct mtk_socphy_shared));
 	if (err)
 		return err;
 
@@ -1237,30 +1300,37 @@
 	return mt798x_phy_calibration(phydev);
 }
 
+static int mt7981_phy_probe(struct phy_device *phydev)
+{
+	return mt798x_phy_calibration(phydev);
+}
+
 static struct phy_driver mtk_socphy_driver[] = {
 	{
 		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981),
-		.name		= "MediaTek MT7981 PHY",
-		.config_init	= mt798x_phy_config_init,
-		.config_intr	= genphy_no_config_intr,
+		.name = "MediaTek MT7981 PHY",
+		.config_init = mt798x_phy_config_init,
+		.read_status = mtk_gphy_cl22_read_status,
+		.config_intr = genphy_no_config_intr,
 		.handle_interrupt = genphy_no_ack_interrupt,
-		.probe		= mt7981_phy_probe,
-		.suspend	= genphy_suspend,
-		.resume		= genphy_resume,
-		.read_page	= mtk_socphy_read_page,
-		.write_page	= mtk_socphy_write_page,
+		.probe = mt7981_phy_probe,
+		.suspend = genphy_suspend,
+		.resume = genphy_resume,
+		.read_page = mtk_phy_read_page,
+		.write_page = mtk_phy_write_page,
 	},
 	{
 		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988),
-		.name		= "MediaTek MT7988 PHY",
-		.config_init	= mt798x_phy_config_init,
-		.config_intr	= genphy_no_config_intr,
+		.name = "MediaTek MT7988 PHY",
+		.config_init = mt798x_phy_config_init,
+		.read_status = mtk_gphy_cl22_read_status,
+		.config_intr = genphy_no_config_intr,
 		.handle_interrupt = genphy_no_ack_interrupt,
-		.probe		= mt7988_phy_probe,
-		.suspend	= genphy_suspend,
-		.resume		= genphy_resume,
-		.read_page	= mtk_socphy_read_page,
-		.write_page	= mtk_socphy_write_page,
+		.probe = mt7988_phy_probe,
+		.suspend = genphy_suspend,
+		.resume = genphy_resume,
+		.read_page = mtk_phy_read_page,
+		.write_page = mtk_phy_write_page,
 	},
 };
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-ge.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-ge.c
new file mode 100644
index 0000000..ff5aac8
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-ge.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#include "mtk.h"
+
+#define MTK_GPHY_ID_MT7530		0x03a29412
+#define MTK_GPHY_ID_MT7531		0x03a29441
+
+#define MTK_PHY_PAGE_EXTENDED_2			0x0002
+#define MTK_PHY_PAGE_EXTENDED_3			0x0003
+#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11	0x11
+
+#define MTK_PHY_PAGE_EXTENDED_2A30		0x2a30
+
+/* Registers on Token Ring debug nodes */
+/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x17 */
+#define SLAVE_DSP_READY_TIME_MASK		GENMASK(22, 15)
+
+/* Registers on MDIO_MMD_VEND1 */
+#define MTK_PHY_GBE_MODE_TX_DELAY_SEL		0x13
+#define MTK_PHY_TEST_MODE_TX_DELAY_SEL		0x14
+#define   MTK_TX_DELAY_PAIR_B_MASK		GENMASK(10, 8)
+#define   MTK_TX_DELAY_PAIR_D_MASK		GENMASK(2, 0)
+
+#define MTK_PHY_MCC_CTRL_AND_TX_POWER_CTRL	0xa6
+#define   MTK_MCC_NEARECHO_OFFSET_MASK		GENMASK(15, 8)
+
+#define MTK_PHY_RXADC_CTRL_RG7			0xc6
+#define   MTK_PHY_DA_AD_BUF_BIAS_LP_MASK	GENMASK(9, 8)
+
+#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG123	0x123
+#define   MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK	GENMASK(15, 8)
+#define   MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK	GENMASK(7, 0)
+
+static void mtk_gephy_config_init(struct phy_device *phydev)
+{
+	/* Enable HW auto downshift */
+	phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED_1,
+			 MTK_PHY_AUX_CTRL_AND_STATUS,
+			 0, MTK_PHY_ENABLE_DOWNSHIFT);
+
+	/* Increase SlvDPSready time */
+	mtk_tr_modify(phydev, 0x1, 0xf, 0x17, SLAVE_DSP_READY_TIME_MASK,
+		      FIELD_PREP(SLAVE_DSP_READY_TIME_MASK, 0x5e));
+
+	/* Adjust 100_mse_threshold */
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1,
+		       MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG123,
+		       MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK |
+		       MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK,
+		       FIELD_PREP(MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK,
+				  0xff) |
+		       FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK,
+				  0xff));
+
+	/* If echo time is narrower than 0x3, it will be regarded as noise */
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1,
+		       MTK_PHY_MCC_CTRL_AND_TX_POWER_CTRL,
+		       MTK_MCC_NEARECHO_OFFSET_MASK,
+		       FIELD_PREP(MTK_MCC_NEARECHO_OFFSET_MASK, 0x3));
+}
+
+static int mt7530_phy_config_init(struct phy_device *phydev)
+{
+	mtk_gephy_config_init(phydev);
+
+	/* Increase post_update_timer */
+	phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3,
+			MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11, 0x4b);
+
+	return 0;
+}
+
+static int mt7531_phy_config_init(struct phy_device *phydev)
+{
+	mtk_gephy_config_init(phydev);
+
+	/* PHY link down power saving enable */
+	phy_set_bits(phydev, 0x17, BIT(4));
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7,
+		       MTK_PHY_DA_AD_BUF_BIAS_LP_MASK,
+		       FIELD_PREP(MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3));
+
+	/* Set TX Pair delay selection */
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_GBE_MODE_TX_DELAY_SEL,
+		       MTK_TX_DELAY_PAIR_B_MASK | MTK_TX_DELAY_PAIR_D_MASK,
+		       FIELD_PREP(MTK_TX_DELAY_PAIR_B_MASK, 0x4) |
+		       FIELD_PREP(MTK_TX_DELAY_PAIR_D_MASK, 0x4));
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TEST_MODE_TX_DELAY_SEL,
+		       MTK_TX_DELAY_PAIR_B_MASK | MTK_TX_DELAY_PAIR_D_MASK,
+		       FIELD_PREP(MTK_TX_DELAY_PAIR_B_MASK, 0x4) |
+		       FIELD_PREP(MTK_TX_DELAY_PAIR_D_MASK, 0x4));
+
+	return 0;
+}
+
+static struct phy_driver mtk_gephy_driver[] = {
+	{
+		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530),
+		.name = "MediaTek MT7530 PHY",
+		.config_init = mt7530_phy_config_init,
+		/* Interrupts are handled by the switch, not the PHY
+		 * itself.
+		 */
+		.config_intr = genphy_no_config_intr,
+		.handle_interrupt = genphy_no_ack_interrupt,
+		.suspend = genphy_suspend,
+		.resume = genphy_resume,
+		.read_page = mtk_phy_read_page,
+		.write_page = mtk_phy_write_page,
+	},
+	{
+		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531),
+		.name = "MediaTek MT7531 PHY",
+		.config_init = mt7531_phy_config_init,
+		.read_status = mtk_gphy_cl22_read_status,
+		/* Interrupts are handled by the switch, not the PHY
+		 * itself.
+		 */
+		.config_intr = genphy_no_config_intr,
+		.handle_interrupt = genphy_no_ack_interrupt,
+		.suspend = genphy_suspend,
+		.resume = genphy_resume,
+		.read_page = mtk_phy_read_page,
+		.write_page = mtk_phy_write_page,
+	},
+};
+
+module_phy_driver(mtk_gephy_driver);
+
+static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = {
+	{ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530) },
+	{ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531) },
+	{ }
+};
+
+MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver");
+MODULE_AUTHOR("DENG, Qingfang <dqfext@gmail.com>");
+MODULE_LICENSE("GPL");
+
+MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-phy-lib.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-phy-lib.c
new file mode 100644
index 0000000..9b85c8b
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk-phy-lib.c
@@ -0,0 +1,452 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/phy.h>
+#include <linux/module.h>
+
+#include <linux/netdevice.h>
+
+#include "mtk.h"
+
+/* Difference between functions with mtk_tr* and __mtk_tr* prefixes is
+ * mtk_tr* functions: wrapped by page switching operations
+ * __mtk_tr* functions: no page switching operations
+ */
+
+static void __mtk_tr_access(struct phy_device *phydev, bool read, u8 ch_addr,
+			    u8 node_addr, u8 data_addr)
+{
+	u16 tr_cmd = BIT(15); /* bit 14 & 0 are reserved */
+
+	if (read)
+		tr_cmd |= BIT(13);
+
+	tr_cmd |= (((ch_addr & 0x3) << 11) |
+		   ((node_addr & 0xf) << 7) |
+		   ((data_addr & 0x3f) << 1));
+	dev_dbg(&phydev->mdio.dev, "tr_cmd: 0x%x\n", tr_cmd);
+	__phy_write(phydev, 0x10, tr_cmd);
+}
+
+static void __mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+			  u8 data_addr, u16 *tr_high, u16 *tr_low)
+{
+	__mtk_tr_access(phydev, true, ch_addr, node_addr, data_addr);
+	*tr_low = __phy_read(phydev, 0x11);
+	*tr_high = __phy_read(phydev, 0x12);
+	dev_dbg(&phydev->mdio.dev, "tr_high read: 0x%x, tr_low read: 0x%x\n",
+		*tr_high, *tr_low);
+}
+
+u32 mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		u8 data_addr)
+{
+	u16 tr_high;
+	u16 tr_low;
+
+	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
+	__mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low);
+	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
+
+	return (tr_high << 16) | tr_low;
+}
+EXPORT_SYMBOL_GPL(mtk_tr_read);
+
+static void __mtk_tr_write(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+			   u8 data_addr, u32 tr_data)
+{
+	__phy_write(phydev, 0x11, tr_data & 0xffff);
+	__phy_write(phydev, 0x12, tr_data >> 16);
+	dev_dbg(&phydev->mdio.dev, "tr_high write: 0x%x, tr_low write: 0x%x\n",
+		tr_data >> 16, tr_data & 0xffff);
+	__mtk_tr_access(phydev, false, ch_addr, node_addr, data_addr);
+}
+
+void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		     u8 data_addr, u32 mask, u32 set)
+{
+	u32 tr_data;
+	u16 tr_high;
+	u16 tr_low;
+
+	__mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low);
+	tr_data = (tr_high << 16) | tr_low;
+	tr_data = (tr_data & ~mask) | set;
+	__mtk_tr_write(phydev, ch_addr, node_addr, data_addr, tr_data);
+}
+EXPORT_SYMBOL_GPL(__mtk_tr_modify);
+
+void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		   u8 data_addr, u32 mask, u32 set)
+{
+	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
+	__mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, mask, set);
+	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
+}
+EXPORT_SYMBOL_GPL(mtk_tr_modify);
+
+void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		       u8 data_addr, u32 set)
+{
+	__mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, 0, set);
+}
+EXPORT_SYMBOL_GPL(__mtk_tr_set_bits);
+
+void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		       u8 data_addr, u32 clr)
+{
+	__mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, clr, 0);
+}
+EXPORT_SYMBOL_GPL(__mtk_tr_clr_bits);
+
+int mtk_phy_read_page(struct phy_device *phydev)
+{
+	return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
+}
+EXPORT_SYMBOL_GPL(mtk_phy_read_page);
+
+int mtk_phy_write_page(struct phy_device *phydev, int page)
+{
+	return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
+}
+EXPORT_SYMBOL_GPL(mtk_phy_write_page);
+
+/* This function deals with the case that 1G AN starts but isn't completed. We
+ * set AN_NEW_LP_CNT_LIMIT with different values time after time to let our
+ * 1G->100Mbps hardware automatic downshift to fit more partner devices.
+ */
+static int extend_an_new_lp_cnt_limit(struct phy_device *phydev)
+{
+	int mmd_read_ret;
+	u32 reg_val;
+	int timeout;
+
+	/* According to table 28-9 & Figure 28-18 in IEEE 802.3,
+	 * link_fail_inhibit_timer of 10/100/1000 Mbps devices ranges from 750
+	 * to "1000ms". Once MTK_PHY_FINAL_SPEED_1000 is set, it means that we
+	 * enter "FLP LINK GOOD CHECK" state, link_fail_inhibit_timer starts and
+	 * this PHY's 1G training starts. If 1G training never starts, we do
+	 * nothing but leave.
+	 */
+	timeout = read_poll_timeout(mmd_read_ret = phy_read_mmd, reg_val,
+				    (mmd_read_ret < 0) ||
+				    reg_val & MTK_PHY_FINAL_SPEED_1000,
+				    10000, 1000000, false, phydev,
+				    MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC);
+	if (mmd_read_ret < 0)
+		return mmd_read_ret;
+
+	if (!timeout) {
+		/* Once we found MTK_PHY_FINAL_SPEED_1000 is set, no matter 1G
+		 * AN is completed or not, we'll set AN_NEW_LP_CNT_LIMIT again
+		 * and again.
+		 */
+		mtk_tr_modify(phydev, 0x0, 0xf, 0x3c, AN_NEW_LP_CNT_LIMIT_MASK,
+			      FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK, 0xf));
+		mdelay(1500);
+
+		timeout = read_poll_timeout(mtk_tr_read, reg_val,
+					    (reg_val & AN_STATE_MASK) !=
+					    (AN_STATE_TX_DISABLE <<
+					     AN_STATE_SHIFT),
+					    10000, 1000000, false, phydev,
+					    0x0, 0xf, 0x2);
+		if (!timeout) {
+			mdelay(625);
+			mtk_tr_modify(phydev, 0x0, 0xf, 0x3c,
+				      AN_NEW_LP_CNT_LIMIT_MASK,
+				      FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK,
+						 0x8));
+			mdelay(500);
+			mtk_tr_modify(phydev, 0x0, 0xf, 0x3c,
+				      AN_NEW_LP_CNT_LIMIT_MASK,
+				      FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK,
+						 0xf));
+		} else {
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+int mtk_gphy_cl22_read_status(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = genphy_read_status(phydev);
+	if (ret)
+		return ret;
+
+	if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) {
+		ret = phy_read_paged(phydev, MTK_PHY_PAGE_EXTENDED_1,
+				     MTK_PHY_AUX_CTRL_AND_STATUS);
+		if (ret < 0)
+			return ret;
+
+		/* Once LP_DETECTED is set, it means that"ability_match" in
+		 * IEEE 802.3 Figure 28-18 is set. This happens after we plug in
+		 * cable. Also, LP_DETECTED will be cleared after AN complete.
+		 */
+		if (!FIELD_GET(MTK_PHY_LP_DETECTED_MASK, ret))
+			return 0;
+
+		ret = phy_read(phydev, MII_CTRL1000);
+		if (ret & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) {
+			ret = extend_an_new_lp_cnt_limit(phydev);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_gphy_cl22_read_status);
+
+#if 0
+int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
+				unsigned long rules,
+				unsigned long supported_triggers)
+{
+	if (index > 1)
+		return -EINVAL;
+
+	/* All combinations of the supported triggers are allowed */
+	if (rules & ~supported_triggers)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_led_hw_is_supported);
+
+int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index,
+			    unsigned long *rules, unsigned long *led_state,
+			    u16 on_set, u16 rx_blink_set, u16 tx_blink_set)
+{
+	unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK +
+				 (index ? 16 : 0);
+	unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0);
+	unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0);
+	int on, blink;
+
+	if (index > 1)
+		return -EINVAL;
+
+	on = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+			  index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL);
+
+	if (on < 0)
+		return -EIO;
+
+	blink = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+			     index ? MTK_PHY_LED1_BLINK_CTRL :
+				     MTK_PHY_LED0_BLINK_CTRL);
+	if (blink < 0)
+		return -EIO;
+
+	if ((on & (on_set | MTK_PHY_LED_ON_FDX |
+		   MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) ||
+	    (blink & (rx_blink_set | tx_blink_set)))
+		set_bit(bit_netdev, led_state);
+	else
+		clear_bit(bit_netdev, led_state);
+
+	if (on & MTK_PHY_LED_ON_FORCE_ON)
+		set_bit(bit_on, led_state);
+	else
+		clear_bit(bit_on, led_state);
+
+	if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK)
+		set_bit(bit_blink, led_state);
+	else
+		clear_bit(bit_blink, led_state);
+
+	if (!rules)
+		return 0;
+
+	if (on & on_set)
+		*rules |= BIT(TRIGGER_NETDEV_LINK);
+
+	if (on & MTK_PHY_LED_ON_LINK10)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_10);
+
+	if (on & MTK_PHY_LED_ON_LINK100)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_100);
+
+	if (on & MTK_PHY_LED_ON_LINK1000)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_1000);
+
+	if (on & MTK_PHY_LED_ON_LINK2500)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_2500);
+
+	if (on & MTK_PHY_LED_ON_FDX)
+		*rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX);
+
+	if (on & MTK_PHY_LED_ON_HDX)
+		*rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX);
+
+	if (blink & rx_blink_set)
+		*rules |= BIT(TRIGGER_NETDEV_RX);
+
+	if (blink & tx_blink_set)
+		*rules |= BIT(TRIGGER_NETDEV_TX);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_get);
+
+int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index,
+			    unsigned long rules, unsigned long *led_state,
+			    u16 on_set, u16 rx_blink_set, u16 tx_blink_set)
+{
+	unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0);
+	u16 on = 0, blink = 0;
+	int ret;
+
+	if (index > 1)
+		return -EINVAL;
+
+	if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
+		on |= MTK_PHY_LED_ON_FDX;
+
+	if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
+		on |= MTK_PHY_LED_ON_HDX;
+
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK)))
+		on |= MTK_PHY_LED_ON_LINK10;
+
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
+		on |= MTK_PHY_LED_ON_LINK100;
+
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
+		on |= MTK_PHY_LED_ON_LINK1000;
+
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
+		on |= MTK_PHY_LED_ON_LINK2500;
+
+	if (rules & BIT(TRIGGER_NETDEV_RX)) {
+		if (on & on_set) {
+			if (on & MTK_PHY_LED_ON_LINK10)
+				blink |= MTK_PHY_LED_BLINK_10RX;
+			if (on & MTK_PHY_LED_ON_LINK100)
+				blink |= MTK_PHY_LED_BLINK_100RX;
+			if (on & MTK_PHY_LED_ON_LINK1000)
+				blink |= MTK_PHY_LED_BLINK_1000RX;
+			if (on & MTK_PHY_LED_ON_LINK2500)
+				blink |= MTK_PHY_LED_BLINK_2500RX;
+		} else {
+			blink |= rx_blink_set;
+		}
+	}
+
+	if (rules & BIT(TRIGGER_NETDEV_TX)) {
+		if (on & on_set) {
+			if (on & MTK_PHY_LED_ON_LINK10)
+				blink |= MTK_PHY_LED_BLINK_10TX;
+			if (on & MTK_PHY_LED_ON_LINK100)
+				blink |= MTK_PHY_LED_BLINK_100TX;
+			if (on & MTK_PHY_LED_ON_LINK1000)
+				blink |= MTK_PHY_LED_BLINK_1000TX;
+			if (on & MTK_PHY_LED_ON_LINK2500)
+				blink |= MTK_PHY_LED_BLINK_2500TX;
+		} else {
+			blink |= tx_blink_set;
+		}
+	}
+
+	if (blink || on)
+		set_bit(bit_netdev, led_state);
+	else
+		clear_bit(bit_netdev, led_state);
+
+	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ?
+			     MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL,
+			     MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | on_set,
+			     on);
+
+	if (ret)
+		return ret;
+
+	return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ?
+			     MTK_PHY_LED1_BLINK_CTRL :
+			     MTK_PHY_LED0_BLINK_CTRL, blink);
+}
+EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_set);
+
+int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on,
+			    unsigned long *delay_off, bool *blinking)
+{
+	if (index > 1)
+		return -EINVAL;
+
+	if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) {
+		*blinking = true;
+		*delay_on = 50;
+		*delay_off = 50;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_led_num_dly_cfg);
+
+int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index,
+			  unsigned long *led_state, u16 led_on_mask, bool on)
+{
+	unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0);
+	bool changed;
+
+	if (on)
+		changed = !test_and_set_bit(bit_on, led_state);
+	else
+		changed = !!test_and_clear_bit(bit_on, led_state);
+
+	changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV +
+					(index ? 16 : 0), led_state);
+	if (changed)
+		return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ?
+				      MTK_PHY_LED1_ON_CTRL :
+				      MTK_PHY_LED0_ON_CTRL,
+				      led_on_mask,
+				      on ? MTK_PHY_LED_ON_FORCE_ON : 0);
+	else
+		return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_hw_led_on_set);
+
+int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index,
+			     unsigned long *led_state, bool blinking)
+{
+	unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK +
+				 (index ? 16 : 0);
+	bool changed;
+
+	if (blinking)
+		changed = !test_and_set_bit(bit_blink, led_state);
+	else
+		changed = !!test_and_clear_bit(bit_blink, led_state);
+
+	changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV +
+			      (index ? 16 : 0), led_state);
+	if (changed)
+		return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ?
+				     MTK_PHY_LED1_BLINK_CTRL :
+				     MTK_PHY_LED0_BLINK_CTRL,
+				     blinking ?
+				     MTK_PHY_LED_BLINK_FORCE_BLINK : 0);
+	else
+		return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_hw_led_blink_set);
+
+void mtk_phy_leds_state_init(struct phy_device *phydev)
+{
+	int i;
+
+	for (i = 0; i < 2; ++i)
+		phydev->drv->led_hw_control_get(phydev, i, NULL);
+}
+EXPORT_SYMBOL_GPL(mtk_phy_leds_state_init);
+#endif
+
+MODULE_DESCRIPTION("MediaTek Ethernet PHY driver common");
+MODULE_AUTHOR("Sky Huang <SkyLake.Huang@mediatek.com>");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_LICENSE("GPL");
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk.h
new file mode 100644
index 0000000..2a50bf2
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek/mtk.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Common definition for Mediatek Ethernet PHYs
+ * Author: SkyLake Huang <SkyLake.Huang@mediatek.com>
+ * Copyright (c) 2024 MediaTek Inc.
+ */
+
+#ifndef _MTK_EPHY_H_
+#define _MTK_EPHY_H_
+
+#define MTK_EXT_PAGE_ACCESS			0x1f
+#define MTK_PHY_PAGE_STANDARD			0x0000
+#define MTK_PHY_PAGE_EXTENDED_1			0x0001
+#define MTK_PHY_AUX_CTRL_AND_STATUS		0x14
+/* suprv_media_select_RefClk */
+#define   MTK_PHY_LP_DETECTED_MASK		GENMASK(7, 6)
+#define   MTK_PHY_ENABLE_DOWNSHIFT		BIT(4)
+
+#define MTK_PHY_PAGE_EXTENDED_52B5		0x52b5
+
+/* Registers on Token Ring debug nodes */
+/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x2 */
+#define   AN_STATE_MASK			GENMASK(22, 19)
+#define   AN_STATE_SHIFT		19
+#define   AN_STATE_TX_DISABLE		1
+
+/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */
+#define AN_NEW_LP_CNT_LIMIT_MASK		GENMASK(23, 20)
+#define AUTO_NP_10XEN				BIT(6)
+
+/* Registers on MDIO_MMD_VEND1 */
+#define MTK_PHY_LINK_STATUS_MISC	(0xa2)
+#define   MTK_PHY_FINAL_SPEED_1000	BIT(3)
+
+/* Registers on MDIO_MMD_VEND2 */
+#define MTK_PHY_LED0_ON_CTRL			0x24
+#define MTK_PHY_LED1_ON_CTRL			0x26
+#define   MTK_GPHY_LED_ON_MASK			GENMASK(6, 0)
+#define   MTK_2P5GPHY_LED_ON_MASK		GENMASK(7, 0)
+#define   MTK_PHY_LED_ON_LINK1000		BIT(0)
+#define   MTK_PHY_LED_ON_LINK100		BIT(1)
+#define   MTK_PHY_LED_ON_LINK10			BIT(2)
+#define   MTK_PHY_LED_ON_LINKDOWN		BIT(3)
+#define   MTK_PHY_LED_ON_FDX			BIT(4) /* Full duplex */
+#define   MTK_PHY_LED_ON_HDX			BIT(5) /* Half duplex */
+#define   MTK_PHY_LED_ON_FORCE_ON		BIT(6)
+#define   MTK_PHY_LED_ON_LINK2500		BIT(7)
+#define   MTK_PHY_LED_ON_POLARITY		BIT(14)
+#define   MTK_PHY_LED_ON_ENABLE			BIT(15)
+
+#define MTK_PHY_LED0_BLINK_CTRL			0x25
+#define MTK_PHY_LED1_BLINK_CTRL			0x27
+#define   MTK_PHY_LED_BLINK_1000TX		BIT(0)
+#define   MTK_PHY_LED_BLINK_1000RX		BIT(1)
+#define   MTK_PHY_LED_BLINK_100TX		BIT(2)
+#define   MTK_PHY_LED_BLINK_100RX		BIT(3)
+#define   MTK_PHY_LED_BLINK_10TX		BIT(4)
+#define   MTK_PHY_LED_BLINK_10RX		BIT(5)
+#define   MTK_PHY_LED_BLINK_COLLISION		BIT(6)
+#define   MTK_PHY_LED_BLINK_RX_CRC_ERR		BIT(7)
+#define   MTK_PHY_LED_BLINK_RX_IDLE_ERR		BIT(8)
+#define   MTK_PHY_LED_BLINK_FORCE_BLINK		BIT(9)
+#define   MTK_PHY_LED_BLINK_2500TX		BIT(10)
+#define   MTK_PHY_LED_BLINK_2500RX		BIT(11)
+
+#define MTK_GPHY_LED_ON_SET			(MTK_PHY_LED_ON_LINK1000 | \
+						 MTK_PHY_LED_ON_LINK100 | \
+						 MTK_PHY_LED_ON_LINK10)
+#define MTK_GPHY_LED_RX_BLINK_SET		(MTK_PHY_LED_BLINK_1000RX | \
+						 MTK_PHY_LED_BLINK_100RX | \
+						 MTK_PHY_LED_BLINK_10RX)
+#define MTK_GPHY_LED_TX_BLINK_SET		(MTK_PHY_LED_BLINK_1000RX | \
+						 MTK_PHY_LED_BLINK_100RX | \
+						 MTK_PHY_LED_BLINK_10RX)
+
+#define MTK_2P5GPHY_LED_ON_SET			(MTK_PHY_LED_ON_LINK2500 | \
+						 MTK_GPHY_LED_ON_SET)
+#define MTK_2P5GPHY_LED_RX_BLINK_SET		(MTK_PHY_LED_BLINK_2500RX | \
+						 MTK_GPHY_LED_RX_BLINK_SET)
+#define MTK_2P5GPHY_LED_TX_BLINK_SET		(MTK_PHY_LED_BLINK_2500RX | \
+						 MTK_GPHY_LED_TX_BLINK_SET)
+
+#define MTK_PHY_LED_STATE_FORCE_ON	0
+#define MTK_PHY_LED_STATE_FORCE_BLINK	1
+#define MTK_PHY_LED_STATE_NETDEV	2
+
+u32 mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		u8 data_addr);
+void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		     u8 data_addr, u32 mask, u32 set);
+void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		   u8 data_addr, u32 mask, u32 set);
+void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		       u8 data_addr, u32 set);
+void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,
+		       u8 data_addr, u32 clr);
+
+int mtk_phy_read_page(struct phy_device *phydev);
+int mtk_phy_write_page(struct phy_device *phydev, int page);
+
+int mtk_gphy_cl22_read_status(struct phy_device *phydev);
+/*int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
+				unsigned long rules,
+				unsigned long supported_triggers);
+int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index,
+			    unsigned long rules, unsigned long *led_state,
+			    u16 on_set, u16 rx_blink_set, u16 tx_blink_set);
+int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index,
+			    unsigned long *rules, unsigned long *led_state,
+			    u16 on_set, u16 rx_blink_set, u16 tx_blink_set);
+int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on,
+			    unsigned long *delay_off, bool *blinking);
+int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index,
+			  unsigned long *led_state, u16 led_on_mask, bool on);
+int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index,
+			     unsigned long *led_state, bool blinking);
+void mtk_phy_leds_state_init(struct phy_device *phydev);*/
+
+#endif /* _MTK_EPHY_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c
index 4cfb0ef..d79b5fe 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c
@@ -491,6 +491,7 @@
 		mt7531_set_port_rgmii(gsw, port);
 		break;
 	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_2500BASEX:
 		if (port_cfg->force_link)
 			mt7531_set_port_sgmii_force_mode(gsw, port, port_cfg);
 		else
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/net/dsa/tag_mxl862xx.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/net/dsa/tag_mxl862xx.c
new file mode 100644
index 0000000..470c708
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/net/dsa/tag_mxl862xx.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * net/dsa/tag_mxl862xx.c - DSA driver Special Tag support for MaxLinear 862xx switch chips
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <net/dsa.h>
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#else
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+#include "dsa_priv.h"
+#else
+#include "tag.h"
+#endif
+
+#define MXL862_NAME	"mxl862xx"
+
+/* To define the outgoing port and to discover the incoming port a special
+ * tag is used by the GSW1xx.
+ *
+ *       Dest MAC       Src MAC    special TAG        EtherType
+ * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |...
+ *                                |<--------------->|
+ */
+
+/* special tag in TX path header */
+#define MXL862_TX_HEADER_LEN 8
+
+#define MXL862_RX_HEADER_LEN 8
+
+/* Byte 7 */
+#define MXL862_IGP_EGP_SHIFT 0
+#define MXL862_IGP_EGP_MASK GENMASK(3, 0)
+
+static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb,
+				       struct net_device *dev)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))
+	int err;
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0))
+	struct dsa_port *dp = dsa_slave_to_port(dev);
+#else
+	struct dsa_port *dp = dsa_user_to_port(dev);
+#endif
+	struct dsa_port *cpu_dp = dp->cpu_dp;
+	unsigned int cpu_port = cpu_dp->index + 1;
+	unsigned int usr_port = dp->index + 1;
+
+	u8 *mxl862_tag;
+
+	if (skb == NULL)
+		return skb;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))
+	err = skb_cow_head(skb, MXL862_TX_HEADER_LEN);
+	if (err)
+		return NULL;
+#endif
+
+	/* provide additional space 'MXL862_TX_HEADER_LEN' bytes */
+	skb_push(skb, MXL862_TX_HEADER_LEN);
+
+	/* shift MAC address to the beginnig of the enlarged buffer,
+	 * releasing the space required for DSA tag (between MAC address and Ethertype) */
+	memmove(skb->data, skb->data + MXL862_TX_HEADER_LEN, 2 * ETH_ALEN);
+
+	/* special tag ingress */
+	mxl862_tag = skb->data + 2 * ETH_ALEN;
+	mxl862_tag[0] = 0x88;
+	mxl862_tag[1] = 0xc3;
+	mxl862_tag[2] = 0;
+	mxl862_tag[3] = 0;
+	mxl862_tag[4] = 0;
+	mxl862_tag[5] = usr_port + 16 - cpu_port;
+	mxl862_tag[6] = 0;
+	mxl862_tag[7] = (cpu_port)&MXL862_IGP_EGP_MASK;
+
+	return skb;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0))
+static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb,
+				      struct net_device *dev,
+				      struct packet_type *pt)
+#else
+static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb,
+				      struct net_device *dev)
+#endif
+{
+	int port;
+	u8 *mxl862_tag;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+	struct dsa_port *dp;
+#endif
+
+	if (unlikely(!pskb_may_pull(skb, MXL862_RX_HEADER_LEN))) {
+		dev_warn_ratelimited(&dev->dev,
+				     "Dropping packet, cannot pull SKB\n");
+		return NULL;
+	}
+
+	mxl862_tag = skb->data - 2;
+
+	if ((mxl862_tag[0] != 0x88) && (mxl862_tag[1] != 0xc3)) {
+		dev_warn_ratelimited(
+			&dev->dev,
+			"Dropping packet due to invalid special tag marker\n");
+		dev_warn_ratelimited(
+			&dev->dev,
+			"Rx Packet Tag: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+			mxl862_tag[0], mxl862_tag[1], mxl862_tag[2],
+			mxl862_tag[3], mxl862_tag[4], mxl862_tag[5],
+			mxl862_tag[6], mxl862_tag[7]);
+		return NULL;
+	}
+
+	/* Get source port information */
+	port = (mxl862_tag[7] & MXL862_IGP_EGP_MASK) >> MXL862_IGP_EGP_SHIFT;
+	port = port - 1;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0))
+	skb->dev = dsa_master_find_slave(dev, 0, port);
+#else
+	skb->dev = dsa_conduit_find_user(dev, 0, port);
+#endif
+	if (!skb->dev) {
+		dev_warn_ratelimited(
+			&dev->dev,
+			"Dropping packet due to invalid source port\n");
+		dev_warn_ratelimited(
+			&dev->dev,
+			"Rx Packet Tag: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+			mxl862_tag[0], mxl862_tag[1], mxl862_tag[2],
+			mxl862_tag[3], mxl862_tag[4], mxl862_tag[5],
+			mxl862_tag[6], mxl862_tag[7]);
+		return NULL;
+	}
+
+	/* remove the MxL862xx special tag between the MAC addresses and the current ethertype field. */
+	skb_pull_rcsum(skb, MXL862_RX_HEADER_LEN);
+	memmove(skb->data - ETH_HLEN,
+		skb->data - (ETH_HLEN + MXL862_RX_HEADER_LEN), 2 * ETH_ALEN);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+	dp = dsa_slave_to_port(skb->dev);
+	if (dp->bridge_dev)
+		skb->offload_fwd_mark = 1;
+#else
+	dsa_default_offload_fwd_mark(skb);
+#endif
+
+	return skb;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0))
+const struct dsa_device_ops mxl862_netdev_ops = {
+	.xmit = mxl862_tag_xmit,
+	.rcv = mxl862_tag_rcv,
+};
+#else
+
+static const struct dsa_device_ops mxl862_netdev_ops = {
+	.name = "mxl862",
+	.proto = DSA_TAG_PROTO_MXL862,
+	.xmit = mxl862_tag_xmit,
+	.rcv = mxl862_tag_rcv,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0))
+	.overhead = MXL862_RX_HEADER_LEN,
+#else
+	.needed_headroom = MXL862_RX_HEADER_LEN,
+#endif
+};
+
+MODULE_LICENSE("GPL");
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862);
+#else
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862, MXL862_NAME);
+#endif
+
+module_dsa_tag_driver(mxl862_netdev_ops);
+#endif
+
+MODULE_LICENSE("GPL");
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/net/dsa/tag_mxl862xx_8021q.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/net/dsa/tag_mxl862xx_8021q.c
new file mode 100644
index 0000000..cf35fdf
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/net/dsa/tag_mxl862xx_8021q.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * net/dsa/tag_mxl862xx_8021q.c - DSA driver 802.1q based Special Tag support for MaxLinear 862xx switch chips
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#else
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#include <linux/dsa/8021q.h>
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(6, 1, 0))
+#include "tag_8021q.h"
+#endif
+#include <net/dsa.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+#include "dsa_priv.h"
+#else
+#include "tag.h"
+#endif
+
+
+#define MXL862_NAME	"mxl862xx"
+
+/* To define the outgoing port and to discover the incoming port
+ * a special 4-byte outer VLAN tag is used by the MxL862xx.
+ *
+ *       Dest MAC       Src MAC    special   optional  EtherType
+ *                                 outer     inner
+ *                                 VLAN tag  tag(s)
+ * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 | 1 2 3 4 | 1 2 |...
+ *                                |<------->|
+ */
+
+/* special tag in TX path header */
+
+/* The mxl862_8021q 4-byte tagging is not yet supported in
+ * kernels >= 5.16 due to differences in DSA 8021q tagging handlers.
+ * DSA tx/rx vid functions are not avaliable, so dummy
+ * functions are here to make the code compilable. */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION (5, 16, 0))
+static u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port)
+{
+   return 0;
+}
+
+static u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port)
+{
+	return 0;
+}
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION (5, 14, 0))
+static void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id)
+{
+	u16 vid, tci;
+
+	skb_push_rcsum(skb, ETH_HLEN);
+	if (skb_vlan_tag_present(skb)) {
+		tci = skb_vlan_tag_get(skb);
+		__vlan_hwaccel_clear_tag(skb);
+	} else {
+		__skb_vlan_pop(skb, &tci);
+	}
+	skb_pull_rcsum(skb, ETH_HLEN);
+
+	vid = tci & VLAN_VID_MASK;
+
+	*source_port = dsa_8021q_rx_source_port(vid);
+	*switch_id = dsa_8021q_rx_switch_id(vid);
+	skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+}
+
+/* If the ingress port offloads the bridge, we mark the frame as autonomously
+ * forwarded by hardware, so the software bridge doesn't forward in twice, back
+ * to us, because we already did. However, if we're in fallback mode and we do
+ * software bridging, we are not offloading it, therefore the dp->bridge_dev
+ * pointer is not populated, and flooding needs to be done by software (we are
+ * effectively operating in standalone ports mode).
+ */
+static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb)
+{
+	struct dsa_port *dp = dsa_slave_to_port(skb->dev);
+
+	skb->offload_fwd_mark = !!(dp->bridge_dev);
+}
+#endif
+
+static struct sk_buff *mxl862_8021q_tag_xmit(struct sk_buff *skb,
+				      struct net_device *dev)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0))
+	struct dsa_port *dp = dsa_slave_to_port(dev);
+#else
+	struct dsa_port *dp = dsa_user_to_port(dev);
+#endif
+	unsigned int port = dp->index ;
+
+	u16 tx_vid = dsa_8021q_tx_vid(dp->ds, port);
+	u16 queue_mapping = skb_get_queue_mapping(skb);
+	u8 pcp = netdev_txq_to_tc(dev, queue_mapping);
+
+
+	dsa_8021q_xmit(skb, dev, ETH_P_8021Q,
+			      ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
+
+	return skb;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0))
+static struct sk_buff *mxl862_8021q_tag_rcv(struct sk_buff *skb,
+				      struct net_device *dev,
+				      struct packet_type *pt)
+#else
+static struct sk_buff *mxl862_8021q_tag_rcv(struct sk_buff *skb,
+				      struct net_device *dev)
+#endif
+{
+	uint16_t vlan = ntohs(*(uint16_t*)(skb->data));
+	int port = dsa_8021q_rx_source_port(vlan);
+	int src_port = -1;
+	int switch_id = -1;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0))
+	skb->dev = dsa_master_find_slave(dev, 0, port);
+#else
+	skb->dev = dsa_conduit_find_user(dev, 0, port);
+#endif
+	if (!skb->dev) {
+		dev_warn_ratelimited(&dev->dev,"Dropping packet due to invalid source port:%d\n", port);
+		return NULL;
+	}
+	/* removes Outer VLAN tag */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0))
+	dsa_8021q_rcv(skb, &src_port, &switch_id);
+#else
+	dsa_8021q_rcv(skb, &src_port, &switch_id, NULL);
+#endif
+
+	dsa_default_offload_fwd_mark(skb);
+
+	return skb;
+}
+
+static const struct dsa_device_ops mxl862_8021q_netdev_ops = {
+	.name = "mxl862_8021q",
+	.proto = DSA_TAG_PROTO_MXL862_8021Q,
+	.xmit = mxl862_8021q_tag_xmit,
+	.rcv = mxl862_8021q_tag_rcv,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0))
+	.overhead = VLAN_HLEN,
+#else
+	.needed_headroom	= VLAN_HLEN,
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0) && \
+	 LINUX_VERSION_CODE > KERNEL_VERSION(5, 10, 0))
+	.promisc_on_master	= true,
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(6, 7, 0))
+	.promisc_on_conduit = true,
+#endif
+};
+
+
+MODULE_LICENSE("GPL");
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862_8021Q);
+#else
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862_8021Q, MXL862_NAME);
+#endif
+
+module_dsa_tag_driver(mxl862_8021q_netdev_ops);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
index ec39c96..c700dcc 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
@@ -223,9 +223,9 @@
  	bool		has_sram;
  	struct {
 @@ -1947,6 +1958,9 @@ struct mtk_eth {
- 	spinlock_t			syscfg0_lock;
+ 	int				ip_align;
+	spinlock_t			syscfg0_lock;
  	struct notifier_block		netdevice_notifier;
- 	struct timer_list		mtk_dma_monitor_timer;
 +
 +	struct mtk_ppe			ppe;
 +	struct rhashtable		flow_table;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3008-add-wed-rx-support-for-netsys2.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3008-add-wed-rx-support-for-netsys2.patch
index 15a8c7e..41beadc 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3008-add-wed-rx-support-for-netsys2.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3008-add-wed-rx-support-for-netsys2.patch
@@ -1,4 +1,4 @@
-From f4cfa86dd8d8f2a129572c1df66a68ba999d8eb5 Mon Sep 17 00:00:00 2001
+From 0f661756eee5a5940985def830aa90307ee53559 Mon Sep 17 00:00:00 2001
 From: Sujuan Chen <sujuan.chen@mediatek.com>
 Date: Mon, 18 Sep 2023 11:04:53 +0800
 Subject: [PATCH] add-wed-rx-support-for-netsys2
@@ -10,16 +10,16 @@
  drivers/net/ethernet/mediatek/Makefile        |   2 +-
  drivers/net/ethernet/mediatek/mtk_wed.c       | 645 ++++++++++++++++--
  drivers/net/ethernet/mediatek/mtk_wed.h       |  51 ++
- drivers/net/ethernet/mediatek/mtk_wed_ccif.c  | 133 ++++
+ drivers/net/ethernet/mediatek/mtk_wed_ccif.c  | 136 ++++
  drivers/net/ethernet/mediatek/mtk_wed_ccif.h  |  45 ++
  .../net/ethernet/mediatek/mtk_wed_debugfs.c   |  90 +++
- drivers/net/ethernet/mediatek/mtk_wed_mcu.c   | 604 ++++++++++++++++
+ drivers/net/ethernet/mediatek/mtk_wed_mcu.c   | 605 ++++++++++++++++
  drivers/net/ethernet/mediatek/mtk_wed_mcu.h   |  97 +++
  drivers/net/ethernet/mediatek/mtk_wed_regs.h  | 143 +++-
- drivers/net/ethernet/mediatek/mtk_wed_wo.c    | 564 +++++++++++++++
+ drivers/net/ethernet/mediatek/mtk_wed_wo.c    | 568 +++++++++++++++
  drivers/net/ethernet/mediatek/mtk_wed_wo.h    | 325 +++++++++
  include/linux/soc/mediatek/mtk_wed.h          | 114 +++-
- 15 files changed, 2822 insertions(+), 98 deletions(-)
+ 15 files changed, 2830 insertions(+), 98 deletions(-)
  create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_ccif.c
  create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_ccif.h
  create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_mcu.c
@@ -225,7 +225,7 @@
 +obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o mtk_wed_wo.o mtk_wed_mcu.o mtk_wed_ccif.o
  obj-$(CONFIG_NET_MEDIATEK_HNAT)			+= mtk_hnat/
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
-index 33b4b37..251549d 100644
+index 33b4b37..288bed8 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 @@ -13,10 +13,13 @@
@@ -329,16 +329,16 @@
 +	mtk_wdma_tx_reset(dev);
 +
 +	mtk_wed_reset(dev, MTK_WED_RESET_WED);
++	if (wo) {
++		mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
++				     MTK_WED_WO_CMD_CHANGE_STATE, &state,
++				     sizeof(state), false);
 +
-+	mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
-+			     MTK_WED_WO_CMD_CHANGE_STATE, &state,
-+			     sizeof(state), false);
-+
-+	if (readx_poll_timeout(mtk_wed_wo_read_status, dev, val,
-+			       val == WOIF_DISABLE_DONE,
-+			       100, WOCPU_TIMEOUT))
-+		dev_err(dev->hw->dev, "failed to disable wed-wo\n");
-+
++		if (readx_poll_timeout(mtk_wed_wo_read_status, dev, val,
++				       val == WOIF_DISABLE_DONE,
++				       100, WOCPU_TIMEOUT))
++			dev_err(dev->hw->dev, "failed to disable wed-wo\n");
++	}
 +	reg = ioremap(WOCPU_MCUSYS_RESET_ADDR, 4);
 +	val = readl((void *)reg);
 +	switch(dev->hw->index) {
@@ -1338,10 +1338,10 @@
  #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_ccif.c b/drivers/net/ethernet/mediatek/mtk_wed_ccif.c
 new file mode 100644
-index 0000000..951278b
+index 0000000..7839c0a
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_ccif.c
-@@ -0,0 +1,133 @@
+@@ -0,0 +1,136 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +
 +#include <linux/soc/mediatek/mtk_wed.h>
@@ -1415,7 +1415,7 @@
 +	ret = request_irq(wo->ccif.irq, isr, IRQF_TRIGGER_HIGH,
 +			  "wo_ccif_isr", wo);
 +	if (ret)
-+		goto free_irq;
++		return ret;
 +
 +	queues.desc_base = MTK_WED_WO_CCIF_DUMMY1;
 +	queues.ring_size = MTK_WED_WO_CCIF_DUMMY2;
@@ -1438,13 +1438,13 @@
 +				 MTK_WED_WO_CMD_LEN, MTK_WED_WO_RXCH_NUM,
 +				 &queues);
 +	if (ret)
-+		goto free_irq;
++		goto free_txq;
 +
 +	wo->ccif.q_int_mask = MTK_WED_WO_RXCH_INT_MASK;
 +
 +	ret = mtk_wed_wo_q_init(wo, mtk_wed_wo_rx_poll);
 +	if (ret)
-+		goto free_irq;
++		goto free_rxq;
 +
 +	wo->ccif.q_exep_mask = MTK_WED_WO_EXCEPTION_INT_MASK;
 +	wo->ccif.irqmask = MTK_WED_WO_ALL_INT_MASK;
@@ -1454,6 +1454,10 @@
 +
 +	return 0;
 +
++free_rxq:
++	mtk_wed_wo_q_free(wo, &wo->q_rx);
++free_txq:
++	mtk_wed_wo_q_free(wo, &wo->q_tx);
 +free_irq:
 +	free_irq(wo->ccif.irq, wo);
 +
@@ -1463,13 +1467,12 @@
 +void wed_wo_hardware_exit(struct mtk_wed_wo *wo)
 +{
 +	wo->drv_ops->set_isr(wo, 0);
-+
-+	disable_irq(wo->ccif.irq);
-+	free_irq(wo->ccif.irq, wo);
-+
-+	tasklet_disable(&wo->irq_tasklet);
-+	netif_napi_del(&wo->napi);
++	if (wo->q_rx.desc) {
++		disable_irq(wo->ccif.irq);
++		free_irq(wo->ccif.irq, wo);
 +
++		netif_napi_del(&wo->napi);
++	}
 +	mtk_wed_wo_q_tx_clean(wo, &wo->q_tx);
 +	mtk_wed_wo_q_rx_clean(wo, &wo->q_rx);
 +	mtk_wed_wo_q_free(wo, &wo->q_tx);
@@ -1669,10 +1672,10 @@
  }
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 new file mode 100644
-index 0000000..be63406
+index 0000000..f071eeb
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -0,0 +1,604 @@
+@@ -0,0 +1,605 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +
 +#include <linux/skbuff.h>
@@ -1841,6 +1844,7 @@
 +
 +free:
 +	kfree(exp->log);
++	exp->log = NULL;
 +	return -ENOMEM;
 +}
 +
@@ -2636,10 +2640,10 @@
  #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.c b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
 new file mode 100644
-index 0000000..54b7787
+index 0000000..7e5bd17
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-@@ -0,0 +1,564 @@
+@@ -0,0 +1,568 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +
 +#include <linux/kernel.h>
@@ -2831,6 +2835,7 @@
 +
 +	size = q->ndesc * sizeof(struct wed_wo_desc);
 +	dma_free_coherent(dev->hw->dev, size, q->desc, q->desc_dma);
++	q->desc = NULL;
 +}
 +
 +static void
@@ -3182,10 +3187,8 @@
 +		goto error;
 +
 +	return ret;
-+
 +error:
-+	kfree(wo);
-+
++	mtk_wed_wo_exit(hw);
 +	return ret;
 +}
 +
@@ -3193,7 +3196,11 @@
 +{
 +	struct mtk_wed_wo *wo = hw->wed_wo;
 +
++	if(!wo)
++		return;
++
 +	wed_wo_hardware_exit(wo);
++	tasklet_disable(&wo->irq_tasklet);
 +
 +	if (wo->exp.log) {
 +		dma_unmap_single(wo->hw->dev, wo->exp.phys, wo->exp.log_size, DMA_FROM_DEVICE);
@@ -3203,6 +3210,7 @@
 +	wo->hw = NULL;
 +	memset(wo, 0, sizeof(*wo));
 +	kfree(wo);
++	hw->wed_wo = NULL;
 +}
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.h b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
 new file mode 100644
@@ -3750,5 +3758,5 @@
  
  #endif
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3009-add-wed-ser-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3009-add-wed-ser-support.patch
index 2aa0131..fcca9cf 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3009-add-wed-ser-support.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3009-add-wed-ser-support.patch
@@ -1,58 +1,58 @@
-From 7f1319357888271ea4aeeda81723b19a8f5ef2c0 Mon Sep 17 00:00:00 2001
+From 13171455fd9ff6fe043a8e1c9f67c89cfab8493d Mon Sep 17 00:00:00 2001
 From: Sujuan Chen <sujuan.chen@mediatek.com>
 Date: Mon, 18 Sep 2023 11:05:45 +0800
 Subject: [PATCH] add-wed-ser-support
 
 ---
  drivers/net/ethernet/mediatek/mtk_eth_soc.c  |   8 +
- drivers/net/ethernet/mediatek/mtk_wed.c      | 391 ++++++++++++++-----
+ drivers/net/ethernet/mediatek/mtk_wed.c      | 385 ++++++++++++++-----
  drivers/net/ethernet/mediatek/mtk_wed.h      |  10 +
  drivers/net/ethernet/mediatek/mtk_wed_regs.h |   9 +
  include/linux/soc/mediatek/mtk_wed.h         |  25 +-
- 5 files changed, 342 insertions(+), 101 deletions(-)
+ 5 files changed, 338 insertions(+), 99 deletions(-)
 
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 268c9e7..a24b223 100644
+index fc43617..a7c73f5 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4619,6 +4619,9 @@ static void mtk_pending_work(struct work_struct *work)
- 	for (i = 0; i < MTK_MAC_COUNT; i++) {
+@@ -4794,6 +4794,9 @@ static void mtk_pending_work(struct work_struct *work)
  		if (!eth->netdev[i])
  			continue;
+
 +#ifdef CONFIG_NET_MEDIATEK_SOC_WED
 +		mtk_wed_fe_reset();
 +#else
- 		if (mtk_reset_flag == MTK_FE_STOP_TRAFFIC) {
- 			pr_info("send MTK_FE_STOP_TRAFFIC event\n");
+ 		if (eth->reset.event == MTK_FE_STOP_TRAFFIC) {
+ 			pr_info("send MTK_FE_STOP_TRAFFIC event !\n");
  			call_netdevice_notifiers(MTK_FE_STOP_TRAFFIC,
-@@ -4644,6 +4647,7 @@ static void mtk_pending_work(struct work_struct *work)
- 			pr_warn("wait for MTK_FE_START_RESET\n");
+@@ -4820,6 +4823,7 @@ static void mtk_pending_work(struct work_struct *work)
+ 			pr_warn("wait for WiFi response timeout !\n");
  		}
  		rtnl_lock();
 +#endif
  		break;
  	}
  
-@@ -4682,6 +4686,9 @@ static void mtk_pending_work(struct work_struct *work)
- 	for (i = 0; i < MTK_MAC_COUNT; i++) {
+@@ -4858,6 +4862,9 @@ static void mtk_pending_work(struct work_struct *work)
  		if (!eth->netdev[i])
  			continue;
+
 +#ifdef CONFIG_NET_MEDIATEK_SOC_WED
 +		mtk_wed_fe_reset_complete();
 +#else
- 		if (mtk_reset_flag == MTK_FE_STOP_TRAFFIC) {
- 			pr_info("send MTK_FE_START_TRAFFIC event\n");
+ 		if (eth->reset.event == MTK_FE_STOP_TRAFFIC) {
+ 			pr_info("send MTK_FE_START_TRAFFIC event !\n");
  			call_netdevice_notifiers(MTK_FE_START_TRAFFIC,
-@@ -4691,6 +4698,7 @@ static void mtk_pending_work(struct work_struct *work)
+@@ -4867,6 +4874,7 @@ static void mtk_pending_work(struct work_struct *work)
  			call_netdevice_notifiers(MTK_FE_RESET_DONE,
- 				eth->netdev[i]);
+ 						 eth->netdev[i]);
  		}
 +#endif
  		call_netdevice_notifiers(MTK_FE_RESET_NAT_DONE,
- 			eth->netdev[i]);
+ 					 eth->netdev[i]);
  		break;
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
-index ad9f3d5..b993f0e 100644
+index 288bed8..40943ee 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 @@ -13,8 +13,10 @@
@@ -199,7 +199,7 @@
  	wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0);
  	wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0);
  	wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
-@@ -543,39 +594,49 @@ mtk_wed_stop(struct mtk_wed_device *dev)
+@@ -543,31 +594,39 @@ mtk_wed_stop(struct mtk_wed_device *dev)
  
  	wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
  	wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0);
@@ -254,20 +254,16 @@
  	mtk_wed_free_tx_buffer(dev);
  	mtk_wed_free_tx_rings(dev);
  
- 	if (mtk_wed_get_rx_capa(dev)) {
--		mtk_wed_wo_reset(dev);
-+		if(hw->wed_wo)
-+			mtk_wed_wo_reset(dev);
+@@ -575,7 +634,7 @@ mtk_wed_detach(struct mtk_wed_device *dev)
+ 		mtk_wed_wo_reset(dev);
  		mtk_wed_free_rx_rings(dev);
--		mtk_wed_wo_exit(hw);
+ 		mtk_wed_wo_exit(hw);
 -		mtk_wdma_rx_reset(dev);
-+		if(hw->wed_wo)
-+			mtk_wed_wo_exit(hw);
 +		mtk_wdma_tx_reset(dev);
  	}
  
  	if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
-@@ -593,6 +654,13 @@ mtk_wed_detach(struct mtk_wed_device *dev)
+@@ -593,6 +652,13 @@ mtk_wed_detach(struct mtk_wed_device *dev)
  	module_put(THIS_MODULE);
  
  	hw->wed_dev = NULL;
@@ -281,7 +277,7 @@
  	mutex_unlock(&hw_lock);
  }
  
-@@ -665,7 +733,7 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev)
+@@ -665,7 +731,7 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev)
  {
  	u32 mask, set;
  
@@ -290,7 +286,7 @@
  	mtk_wed_reset(dev, MTK_WED_RESET_WED);
  	mtk_wed_set_wpdma(dev);
  
-@@ -715,7 +783,6 @@ mtk_wed_rro_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
+@@ -715,7 +781,6 @@ mtk_wed_rro_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
  
  	ring->desc_size = sizeof(*ring->desc);
  	ring->size = size;
@@ -298,7 +294,7 @@
  
  	return 0;
  }
-@@ -938,44 +1005,140 @@ mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx)
+@@ -938,44 +1003,140 @@ mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx)
  }
  
  static u32
@@ -463,7 +459,7 @@
  static void
  mtk_wed_reset_dma(struct mtk_wed_device *dev)
  {
-@@ -991,22 +1154,25 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+@@ -991,22 +1152,25 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
  				   true);
  	}
  
@@ -497,7 +493,7 @@
  
  	if (busy) {
  		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
-@@ -1023,6 +1189,9 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+@@ -1023,6 +1187,9 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
  			MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE);
  	}
  
@@ -507,7 +503,7 @@
  	for (i = 0; i < 100; i++) {
  		val = wed_r32(dev, MTK_WED_TX_BM_INTF);
  		if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
-@@ -1030,8 +1199,21 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+@@ -1030,8 +1197,21 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
  	}
  
  	mtk_wed_reset(dev, MTK_WED_RESET_TX_FREE_AGENT);
@@ -529,7 +525,7 @@
  	if (busy) {
  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV);
-@@ -1043,6 +1225,16 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+@@ -1043,6 +1223,16 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
  		wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0);
  	}
  
@@ -546,7 +542,7 @@
  }
  
  static int
-@@ -1062,7 +1254,8 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
+@@ -1062,7 +1252,8 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
  }
  
  static int
@@ -556,7 +552,7 @@
  {
  	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
  	struct mtk_wed_ring *wdma;
-@@ -1071,8 +1264,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
+@@ -1071,8 +1262,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
  		return -EINVAL;
  
  	wdma = &dev->rx_wdma[idx];
@@ -567,7 +563,7 @@
  		return -ENOMEM;
  
  	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
-@@ -1090,7 +1283,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
+@@ -1090,7 +1281,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
  }
  
  static int
@@ -577,7 +573,7 @@
  {
  	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
  	struct mtk_wed_ring *wdma;
-@@ -1099,8 +1293,8 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
+@@ -1099,8 +1291,8 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
  		return -EINVAL;
  
  	wdma = &dev->tx_wdma[idx];
@@ -588,7 +584,7 @@
  		return -ENOMEM;
  
  	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
-@@ -1112,6 +1306,9 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
+@@ -1112,6 +1304,9 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
  	wdma_w32(dev,
  		 MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0);
  
@@ -598,7 +594,7 @@
  	if (!idx)  {
  		wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_BASE,
  			wdma->desc_phys);
-@@ -1267,9 +1464,12 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -1267,9 +1462,12 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
  {
  	int i;
  
@@ -612,7 +608,7 @@
  
  
  	mtk_wed_hw_init(dev);
-@@ -1278,10 +1478,9 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -1278,10 +1476,9 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
  	mtk_wed_set_ext_int(dev, true);
  
  	if (dev->hw->version == 1) {
@@ -626,7 +622,7 @@
  
  		val |= BIT(0) | (BIT(1) * !!dev->hw->index);
  		regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
-@@ -1353,10 +1552,6 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -1353,10 +1550,6 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  		goto out;
  
  	if (mtk_wed_get_rx_capa(dev)) {
@@ -637,7 +633,7 @@
  		ret = mtk_wed_rro_alloc(dev);
  		if (ret)
  			goto out;
-@@ -1364,6 +1559,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -1364,6 +1557,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  
  	mtk_wed_hw_init_early(dev);
  
@@ -648,20 +644,20 @@
  	if (hw->version == 1) {
  		regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
  				   BIT(hw->index), 0);
-@@ -1373,8 +1572,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -1373,8 +1570,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  	}
  
  out:
 -	if (ret)
 -		mtk_wed_detach(dev);
 +	if (ret) {
-+		dev_err(dev->hw->dev, "failed to attach wed device\n");
++		dev_err(dev->hw->dev, "failed to attach wed device ret %d\n", ret);
 +		__mtk_wed_detach(dev);
 +	}
  unlock:
  	mutex_unlock(&hw_lock);
  
-@@ -1382,7 +1583,8 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -1382,7 +1581,8 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  }
  
  static int
@@ -671,7 +667,7 @@
  {
  	struct mtk_wed_ring *ring = &dev->tx_ring[idx];
  
-@@ -1401,11 +1603,12 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
+@@ -1401,11 +1601,12 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
  	if (WARN_ON(idx >= ARRAY_SIZE(dev->tx_ring)))
  		return -EINVAL;
  
@@ -687,7 +683,7 @@
  		return -ENOMEM;
  
  	ring->reg_base = MTK_WED_RING_TX(idx);
-@@ -1450,18 +1653,20 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
+@@ -1450,18 +1651,20 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
  }
  
  static int
@@ -856,5 +852,5 @@
  
  #endif
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-flow-offload-add-mtkhnat-qdma-qos.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-flow-offload-add-mtkhnat-qdma-qos.patch
index 85ea255..944278f 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-flow-offload-add-mtkhnat-qdma-qos.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-flow-offload-add-mtkhnat-qdma-qos.patch
@@ -75,7 +75,7 @@
  };
  
 @@ -6103,6 +6109,7 @@ static const struct mtk_soc_data mt7986_data = {
- 		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
+ 		.rx_dma_l4_valid = RX_DMA_L4_VALID,
  		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
  		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
 +		.qdma_tx_sch = 4,
@@ -83,7 +83,7 @@
  };
  
 @@ -6127,6 +6134,7 @@ static const struct mtk_soc_data mt7981_data = {
- 		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
+ 		.rx_dma_l4_valid = RX_DMA_L4_VALID,
  		.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/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3019-mtk-wed-add-wed3-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3019-mtk-wed-add-wed3-support.patch
index fe68b04..8bb6e79 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3019-mtk-wed-add-wed3-support.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3019-mtk-wed-add-wed3-support.patch
@@ -1,4 +1,4 @@
-From 1865b8b9a152b199ab1590859621aef31c5499b3 Mon Sep 17 00:00:00 2001
+From cd1673703b8a5340f58445d3a30738b9f5f20248 Mon Sep 17 00:00:00 2001
 From: Sujuan Chen <sujuan.chen@mediatek.com>
 Date: Mon, 18 Sep 2023 13:21:15 +0800
 Subject: [PATCH] mtk:wed:add wed3 support
@@ -22,7 +22,7 @@
  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 f38b2b7..3ae7b9f 100644
+index b2b2fde..d6737fe 100644
 --- a/arch/arm64/boot/dts/mediatek/mt7988.dtsi
 +++ b/arch/arm64/boot/dts/mediatek/mt7988.dtsi
 @@ -241,44 +241,49 @@
@@ -212,7 +212,7 @@
  	};
  
  	reserved-memory {
-@@ -947,6 +984,7 @@
+@@ -941,6 +978,7 @@
  					 <&topckgen CK_TOP_CB_SGM_325M>;
  		mediatek,ethsys = <&ethsys>;
  		mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>;
@@ -221,10 +221,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 24c7799..c283c5e 100644
+index e0f1326..2f50104 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
-@@ -434,9 +434,23 @@
+@@ -435,9 +435,23 @@
  	status = "okay";
  };
  
@@ -251,10 +251,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 f72c5d3..6503700 100644
+index 3df84cc..37b86dc 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
-@@ -444,9 +444,23 @@
+@@ -445,9 +445,23 @@
  	status = "okay";
  };
  
@@ -283,7 +283,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 0e9c0bd..c9a13c4
+index 2ff0426..71a161f
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 @@ -9,6 +9,7 @@
@@ -316,10 +316,10 @@
  	if (wdma_idx)
  		*ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-index 2a8b6ef..c19a91d 100644
+index 43fbe1d..38a3776 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);
+@@ -414,7 +414,7 @@ int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
  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,
@@ -329,7 +329,7 @@
  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);
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-index f362771..e5003a8 100644
+index b9983d7..64b1d9e 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 @@ -88,7 +88,7 @@ mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
@@ -379,7 +379,7 @@
  			return -EOPNOTSUPP;
  #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
-index edc41c4..a72484c 100644
+index 45af859..71d57cd 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 @@ -23,9 +23,45 @@
@@ -1123,15 +1123,15 @@
  	mtk_wed_free_tx_buffer(dev);
  	mtk_wed_free_tx_rings(dev);
  
-@@ -636,7 +951,6 @@ __mtk_wed_detach(struct mtk_wed_device *dev)
+@@ -634,7 +949,6 @@ __mtk_wed_detach(struct mtk_wed_device *dev)
+ 		mtk_wed_wo_reset(dev);
  		mtk_wed_free_rx_rings(dev);
- 		if(hw->wed_wo)
- 			mtk_wed_wo_exit(hw);
+ 		mtk_wed_wo_exit(hw);
 -		mtk_wdma_tx_reset(dev);
  	}
  
  	if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
-@@ -664,21 +978,36 @@ mtk_wed_detach(struct mtk_wed_device *dev)
+@@ -662,21 +976,36 @@ mtk_wed_detach(struct mtk_wed_device *dev)
  	mutex_unlock(&hw_lock);
  }
  
@@ -1175,7 +1175,7 @@
  
  		wed_w32(dev, MTK_WED_PCIE_INT_CTRL,
  			FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2));
-@@ -686,19 +1015,9 @@ mtk_wed_bus_init(struct mtk_wed_device *dev)
+@@ -684,19 +1013,9 @@ mtk_wed_bus_init(struct mtk_wed_device *dev)
  		/* pcie interrupt control: pola/source selection */
  		wed_set(dev, MTK_WED_PCIE_INT_CTRL,
  			MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
@@ -1198,7 +1198,7 @@
  		break;
  	}
  	case MTK_WED_BUS_AXI:
-@@ -714,45 +1033,71 @@ mtk_wed_bus_init(struct mtk_wed_device *dev)
+@@ -712,45 +1031,71 @@ mtk_wed_bus_init(struct mtk_wed_device *dev)
  static void
  mtk_wed_set_wpdma(struct mtk_wed_device *dev)
  {
@@ -1290,7 +1290,7 @@
  	} else {
  		wed_w32(dev, MTK_WED_WDMA_CFG_BASE, dev->hw->wdma_phy);
  		wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_ETH_DMAD_FMT);
-@@ -844,7 +1189,8 @@ mtk_wed_rro_cfg(struct mtk_wed_device *dev)
+@@ -842,7 +1187,8 @@ mtk_wed_rro_cfg(struct mtk_wed_device *dev)
  		},
  	};
  
@@ -1300,7 +1300,7 @@
  				    &req, sizeof(req), true);
  }
  
-@@ -895,11 +1241,18 @@ mtk_wed_route_qm_hw_init(struct mtk_wed_device *dev)
+@@ -893,11 +1239,18 @@ mtk_wed_route_qm_hw_init(struct mtk_wed_device *dev)
  	}
  
  	/* configure RX_ROUTE_QM */
@@ -1324,7 +1324,7 @@
  
  	/* enable RX_ROUTE_QM */
  	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
-@@ -918,35 +1271,26 @@ mtk_wed_hw_init(struct mtk_wed_device *dev)
+@@ -916,35 +1269,26 @@ mtk_wed_hw_init(struct mtk_wed_device *dev)
  
  	wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
  
@@ -1365,7 +1365,7 @@
  		wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
  			FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) |
  			MTK_WED_TX_BM_DYN_THR_HI_V2);
-@@ -956,32 +1300,73 @@ mtk_wed_hw_init(struct mtk_wed_device *dev)
+@@ -954,32 +1298,73 @@ mtk_wed_hw_init(struct mtk_wed_device *dev)
  				   dev->tx_buf_ring.size / 128) |
  			FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM,
  				   dev->tx_buf_ring.size / 128));
@@ -1446,7 +1446,7 @@
  }
  
  static void
-@@ -1099,13 +1484,8 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+@@ -1097,13 +1482,8 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
  	if (ret) {
  		mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA);
  	} else {
@@ -1462,7 +1462,7 @@
  		wed_w32(dev, MTK_WED_RESET_IDX, 0);
  	}
  
-@@ -1162,7 +1542,8 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+@@ -1160,7 +1540,8 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
  	if (busy) {
  		mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA);
  	} else {
@@ -1472,7 +1472,7 @@
  		wed_w32(dev, MTK_WED_RESET_IDX, 0);
  	}
  
-@@ -1226,7 +1607,7 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+@@ -1224,7 +1605,7 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
  	}
  
  	dev->init_done = false;
@@ -1481,7 +1481,7 @@
  		return;
  
  	if (!busy) {
-@@ -1257,7 +1638,6 @@ static int
+@@ -1255,7 +1636,6 @@ static int
  mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
  			   bool reset)
  {
@@ -1489,7 +1489,7 @@
  	struct mtk_wed_ring *wdma;
  
  	if (idx >= ARRAY_SIZE(dev->rx_wdma))
-@@ -1265,9 +1645,11 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
+@@ -1263,9 +1643,11 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
  
  	wdma = &dev->rx_wdma[idx];
  	if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
@@ -1502,7 +1502,7 @@
  	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
  		 wdma->desc_phys);
  	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_COUNT,
-@@ -1286,7 +1668,6 @@ static int
+@@ -1284,7 +1666,6 @@ static int
  mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
  			   bool reset)
  {
@@ -1510,7 +1510,7 @@
  	struct mtk_wed_ring *wdma;
  
  	if (idx >= ARRAY_SIZE(dev->tx_wdma))
-@@ -1294,9 +1675,29 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
+@@ -1292,9 +1673,29 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
  
  	wdma = &dev->tx_wdma[idx];
  	if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
@@ -1541,7 +1541,7 @@
  	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
  		 wdma->desc_phys);
  	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT,
-@@ -1354,7 +1755,7 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -1352,7 +1753,7 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
  		MTK_WED_CTRL_WED_TX_BM_EN |
  		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
  
@@ -1550,7 +1550,7 @@
  		wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER,
  			MTK_WED_PCIE_INT_TRIGGER_STATUS);
  
-@@ -1364,8 +1765,9 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -1362,8 +1763,9 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
  
  		wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
  	} else {
@@ -1562,7 +1562,7 @@
  		/* initail tx interrupt trigger */
  		wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
  			MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
-@@ -1384,19 +1786,25 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -1382,19 +1784,25 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
  			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG,
  				    dev->wlan.txfree_tbit));
  
@@ -1598,7 +1598,7 @@
  	}
  	/* initail wdma interrupt agent */
  	wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask);
-@@ -1407,58 +1815,295 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -1405,58 +1813,295 @@ mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
  	wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
  }
  
@@ -1924,7 +1924,7 @@
  static void
  mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
  {
-@@ -1471,20 +2116,29 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -1469,20 +2114,29 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
  		if (!dev->rx_wdma[i].desc)
  			mtk_wed_wdma_rx_ring_setup(dev, i, 16, false);
  
@@ -1956,7 +1956,7 @@
  		/* driver set mid ready and only once */
  		wed_w32(dev, MTK_WED_EXT_INT_MASK1,
  			MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
-@@ -1494,12 +2148,19 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+@@ -1492,12 +2146,19 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
  		wed_r32(dev, MTK_WED_EXT_INT_MASK1);
  		wed_r32(dev, MTK_WED_EXT_INT_MASK2);
  
@@ -1976,7 +1976,7 @@
  
  	mtk_wed_dma_enable(dev);
  	dev->running = true;
-@@ -1516,9 +2177,7 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -1514,9 +2175,7 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  	RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
  			 "mtk_wed_attach without holding the RCU read lock");
  
@@ -1987,7 +1987,7 @@
  		return -ENODEV;
  
  	rcu_read_unlock();
-@@ -1537,11 +2196,13 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -1535,11 +2194,13 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  				       : &dev->wlan.platform_dev->dev;
  	dev_info(device, "attaching wed device %d version %d\n",
  		 hw->index, hw->version);
@@ -2001,7 +2001,7 @@
  
  	if (hw->eth->dma_dev == hw->eth->dev &&
  	    of_dma_is_coherent(hw->eth->dev->of_node))
-@@ -1551,6 +2212,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -1549,6 +2210,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  	if (ret)
  		goto out;
  
@@ -2012,7 +2012,7 @@
  	if (mtk_wed_get_rx_capa(dev)) {
  		ret = mtk_wed_rro_alloc(dev);
  		if (ret)
-@@ -1563,13 +2228,14 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -1561,13 +2226,14 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  	init_completion(&dev->wlan_reset_done);
  	atomic_set(&dev->fe_reset, 0);
  
@@ -2030,7 +2030,7 @@
  
  out:
  	if (ret) {
-@@ -1613,6 +2279,24 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx,
+@@ -1611,6 +2277,24 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx,
  
  	ring->reg_base = MTK_WED_RING_TX(idx);
  	ring->wpdma = regs;
@@ -2055,7 +2055,7 @@
  
  	/* WED -> WPDMA */
  	wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys);
-@@ -1632,7 +2316,7 @@ static int
+@@ -1630,7 +2314,7 @@ static int
  mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
  {
  	struct mtk_wed_ring *ring = &dev->txfree_ring;
@@ -2064,7 +2064,7 @@
  
  	/*
  	 * For txfree event handling, the same DMA ring is shared between WED
-@@ -1692,9 +2376,13 @@ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
+@@ -1690,9 +2374,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);
@@ -2081,7 +2081,7 @@
  	if (val && net_ratelimit())
  		pr_err("mtk_wed%d: error status=%08x\n", dev->hw->index, val);
  
-@@ -1718,19 +2406,20 @@ mtk_wed_irq_set_mask(struct mtk_wed_device *dev, u32 mask)
+@@ -1716,19 +2404,20 @@ mtk_wed_irq_set_mask(struct mtk_wed_device *dev, u32 mask)
  int mtk_wed_flow_add(int index)
  {
  	struct mtk_wed_hw *hw = hw_list[index];
@@ -2111,7 +2111,7 @@
  		goto out;
  	}
  
-@@ -1749,14 +2438,15 @@ void mtk_wed_flow_remove(int index)
+@@ -1747,14 +2436,15 @@ void mtk_wed_flow_remove(int index)
  {
  	struct mtk_wed_hw *hw = hw_list[index];
  
@@ -2133,7 +2133,7 @@
  		goto out;
  
  	hw->wed_dev->wlan.offload_disable(hw->wed_dev);
-@@ -1799,6 +2489,10 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1797,6 +2487,10 @@ 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,
@@ -2144,7 +2144,7 @@
  	};
  	struct device_node *eth_np = eth->dev->of_node;
  	struct platform_device *pdev;
-@@ -1840,14 +2534,22 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1838,14 +2532,22 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
  	hw->wdma_phy = wdma_phy;
  	hw->index = index;
  	hw->irq = irq;
@@ -2172,7 +2172,7 @@
  		if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) {
  			kfree(hw);
  			goto unlock;
-@@ -1857,8 +2559,10 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -1855,8 +2557,10 @@ 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);
  		}
@@ -3014,7 +3014,7 @@
  	}
  }
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-index be63406..18d1fb1 100644
+index f071eeb..20d0ad9 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 @@ -91,7 +91,7 @@ mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data, int len)
@@ -3026,7 +3026,7 @@
  		return 0;
  
  	if (WARN_ON(!wo))
-@@ -248,7 +248,7 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
+@@ -249,7 +249,7 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
  		u8 reserved1[15];
  	} __packed *region;
  
@@ -3035,7 +3035,7 @@
  	const struct mtk_wed_fw_trailer *hdr;
  	static u8 shared[MAX_REGION_SIZE] = {0};
  	const struct firmware *fw;
-@@ -256,13 +256,24 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
+@@ -257,13 +257,24 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
  	u32 ofs = 0;
  	u32 boot_cr, val;
  
@@ -3066,7 +3066,7 @@
  	if (ret)
  		return ret;
  
-@@ -307,8 +318,11 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
+@@ -308,8 +319,11 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
  	}
  
  	/* write the start address */
@@ -3080,7 +3080,7 @@
  	wo_w32(wo, boot_cr, (wo->region[WO_REGION_EMI].addr_pa >> 16));
  
  	/* wo firmware reset */
-@@ -316,8 +330,10 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
+@@ -317,8 +331,10 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
  
  	val = wo_r32(wo, WOX_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR);
  
@@ -3094,7 +3094,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 dbb17ae..6d4c9a7 100644
+index 1b4c3b7..27d7c18 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
 @@ -17,8 +17,11 @@
@@ -3856,5 +3856,5 @@
  
  #endif
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3021-mtk-wed-add-dma-mask-limitation-and-GFP_DMA32-for-bo.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3021-mtk-wed-add-dma-mask-limitation-and-GFP_DMA32-for-bo.patch
index 0ad8ba7..58cf34b 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3021-mtk-wed-add-dma-mask-limitation-and-GFP_DMA32-for-bo.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3021-mtk-wed-add-dma-mask-limitation-and-GFP_DMA32-for-bo.patch
@@ -1,4 +1,4 @@
-From f83c601c7b525c0adedcf1f5e8041acd350c7e2d Mon Sep 17 00:00:00 2001
+From 1946b547d6e2297b841f77b03af6e17a7c26ea10 Mon Sep 17 00:00:00 2001
 From: Sujuan Chen <sujuan.chen@mediatek.com>
 Date: Mon, 18 Sep 2023 13:23:56 +0800
 Subject: [PATCH] mtk: wed: add dma mask limitation and GFP_DMA32 for board >=
@@ -12,7 +12,7 @@
  4 files changed, 16 insertions(+), 9 deletions(-)
 
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
-index 5eeb3ed..094bc94 100644
+index 662891c..4f40f83 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 @@ -625,7 +625,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
@@ -68,7 +68,7 @@
  			desc++;
  			buf += MTK_WED_PAGE_BUF_SIZE;
  			buf_phys += MTK_WED_PAGE_BUF_SIZE;
-@@ -2457,6 +2459,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+@@ -2456,6 +2458,10 @@ mtk_wed_attach(struct mtk_wed_device *dev)
  	dev->version = hw->version;
  	dev->hw->pci_base = mtk_wed_get_pci_base(dev);
  
@@ -80,7 +80,7 @@
  	    of_dma_is_coherent(hw->eth->dev->of_node))
  		mtk_eth_set_dma_device(hw->eth, hw->dev);
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-index 18d1fb1..a88061c 100644
+index 20d0ad9..cd14d47 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 @@ -145,7 +145,7 @@ int mtk_wed_exception_init(struct mtk_wed_wo *wo)
@@ -98,11 +98,11 @@
  free:
 -	kfree(exp->log);
 +	skb_free_frag(exp->log);
+ 	exp->log = NULL;
  	return -ENOMEM;
  }
- 
 diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.c b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-index 54b7787..e991d20 100644
+index ea102f4..b9e80ae 100644
 --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
 +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
 @@ -88,7 +88,7 @@ woif_q_rx_fill(struct mtk_wed_wo *wo, struct wed_wo_queue *q, bool rx)
@@ -114,7 +114,7 @@
  		if (!buf)
  			break;
  
-@@ -555,7 +555,7 @@ void mtk_wed_wo_exit(struct mtk_wed_hw *hw)
+@@ -553,7 +553,7 @@ void mtk_wed_wo_exit(struct mtk_wed_hw *hw)
  
  	if (wo->exp.log) {
  		dma_unmap_single(wo->hw->dev, wo->exp.phys, wo->exp.log_size, DMA_FROM_DEVICE);
@@ -136,5 +136,5 @@
  	struct net_device napi_dev;
  	spinlock_t rx_lock;
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3025-flow-offload-add-mtkhnat-roaming.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3025-flow-offload-add-mtkhnat-roaming.patch
index 85030dc..ecd063c 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3025-flow-offload-add-mtkhnat-roaming.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3025-flow-offload-add-mtkhnat-roaming.patch
@@ -1,21 +1,21 @@
-From cbd7e4c840a459f14434bcc82a3e4af5673f9b3d Mon Sep 17 00:00:00 2001
-From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
-Date: Wed, 5 Jun 2024 13:22:08 +0800
+From 1173eb589ef1676ccae92d669e98e7a4716aa323 Mon Sep 17 00:00:00 2001
+From: "chak-kei.lam" <chak-kei.lam@mediatek.com>
+Date: Tue, 10 Sep 2024 14:56:41 +0800
 Subject: [PATCH] flow offload add mtkhnat roaming
 
 ---
  drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   8 +
  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   4 +
- drivers/net/ethernet/mediatek/mtk_ppe.c       | 171 ++++++++++++++++++
+ drivers/net/ethernet/mediatek/mtk_ppe.c       | 191 ++++++++++++++++++
  drivers/net/ethernet/mediatek/mtk_ppe.h       |   2 +
- .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |  55 ++++++
- 5 files changed, 240 insertions(+)
+ .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |  55 +++++
+ 5 files changed, 260 insertions(+)
 
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 2ae6370..7ac034d 100644
+index 72fb677..60994c5 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4055,6 +4055,12 @@ static int mtk_open(struct net_device *dev)
+@@ -4233,6 +4233,12 @@ static int mtk_open(struct net_device *dev)
  		if (err)
  			return err;
  
@@ -28,7 +28,7 @@
  
  		/* Indicates CDM to parse the MTK special tag from CPU */
  		if (netdev_uses_dsa(dev)) {
-@@ -4233,6 +4239,8 @@ static int mtk_stop(struct net_device *dev)
+@@ -4420,6 +4426,8 @@ static int mtk_stop(struct net_device *dev)
  	if (eth->soc->offload_version) {
  		for (i = 0; i < eth->ppe_num; i++)
  			mtk_ppe_stop(eth->ppe[i]);
@@ -38,10 +38,10 @@
  
  	return 0;
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-index 5d66707..fcba486 100644
+index dc0530c..81111b0 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1983,10 +1983,14 @@ struct mtk_eth {
+@@ -2008,10 +2008,14 @@ struct mtk_eth {
  	struct notifier_block		netdevice_notifier;
  	struct timer_list		mtk_dma_monitor_timer;
  
@@ -57,7 +57,7 @@
  
  /* struct mtk_mac -	the structure that holds the info about the MACs of the
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
-index 547b5a0..fa1992f 100644
+index 7aa9a8c..eb701ad 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 @@ -10,6 +10,7 @@
@@ -68,7 +68,7 @@
  #include "mtk_eth_soc.h"
  #include "mtk_ppe.h"
  #include "mtk_ppe_regs.h"
-@@ -519,6 +520,28 @@ bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data
+@@ -510,6 +511,28 @@ bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data
  	return !memcmp(&entry->data, &data->data, len - 4);
  }
  
@@ -96,13 +96,32 @@
 +
  int mtk_foe_entry_set_sp(struct mtk_ppe *ppe, struct mtk_foe_entry *entry)
  {
- 	struct mtk_foe_entry *hwe;
-@@ -1114,3 +1137,151 @@ int mtk_ppe_stop(struct mtk_ppe *ppe)
+ 	struct mtk_eth *eth = ppe->eth;
+@@ -1136,3 +1159,171 @@ int mtk_ppe_stop(struct mtk_ppe *ppe)
  
  	return 0;
  }
 +
-+int mtk_flow_offload_teardown_by_mac(struct mtk_ppe *ppe, u8 *mac)
++static int
++mtk_ppe_check_wdma_path(struct mtk_eth *eth, struct mtk_foe_entry *foe)
++{
++	u32 sp, winfo;
++	u32 *ib2;
++
++	ib2 = mtk_foe_entry_ib2(foe);
++	sp = mtk_get_ib1_sp(eth, foe);
++	winfo = FIELD_GET(MTK_FOE_IB2_WDMA_WINFO, *ib2);
++
++	if (winfo || (sp == PSE_WDMA0_PORT ||
++		      sp == PSE_WDMA1_PORT ||
++		      sp == PSE_WDMA2_PORT))
++		return 1;
++
++	return 0;
++}
++
++static int
++mtk_flow_offload_teardown_by_roaming(struct mtk_ppe *ppe, u8 *mac)
 +{
 +	struct mtk_eth *eth = ppe->eth;
 +	int i, j, count = 0;
@@ -113,7 +132,8 @@
 +		int type, state;
 +
 +		state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
-+		if (state != MTK_FOE_STATE_BIND || !mtk_foe_mac_match(entry, mac))
++		if (state != MTK_FOE_STATE_BIND || !mtk_foe_mac_match(entry, mac) ||
++		    !mtk_ppe_check_wdma_path(eth, entry))
 +			continue;
 +
 +		memset(&tuple, 0, sizeof(tuple));
@@ -191,7 +211,7 @@
 +		if (nla_type(nla) == NDA_LLADDR) {
 +			memcpy(mac, nla_data(nla), ETH_ALEN);
 +			for (i = 0; i < eth->ppe_num; i++)
-+				mtk_flow_offload_teardown_by_mac(eth->ppe[i], mac);
++				mtk_flow_offload_teardown_by_roaming(eth->ppe[i], mac);
 +			if (eth->debug_level >= 6)
 +				pr_info("mtk_ppe: the neighbor (%pM) has been updated\n", mac);
 +		}
@@ -250,11 +270,11 @@
 +	return 0;
 +}
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-index 66c7f10..31ca702 100644
+index 38a3776..c74deae 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -384,6 +384,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int versio
- 			     int accounting);
+@@ -381,6 +381,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
+ void mtk_ppe_deinit(struct mtk_eth *eth);
  int mtk_ppe_start(struct mtk_ppe *ppe);
  int mtk_ppe_stop(struct mtk_ppe *ppe);
 +int mtk_ppe_roaming_start(struct mtk_eth *eth);
@@ -263,7 +283,7 @@
  void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
  
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-index d713e2e..a97c1d7 100644
+index 40da60f..3d3a7f2 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
 @@ -206,6 +206,52 @@ mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file)
@@ -343,5 +363,5 @@
  	return 0;
  }
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
index fa956d1..203c887 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
@@ -359,8 +359,11 @@
 CONFIG_NET_DSA=y
 # CONFIG_NET_DSA_AN8855 is not set
 CONFIG_NET_DSA_MT7530=y
+# CONFIG_NET_DSA_MXL862 is not set
 # CONFIG_NET_DSA_TAG_AIROHA is not set
 CONFIG_NET_DSA_TAG_MTK=y
+# CONFIG_NET_DSA_TAG_MXL862 is not set
+# CONFIG_NET_DSA_TAG_MXL862_8021Q is not set
 CONFIG_NET_FLOW_LIMIT=y
 # CONFIG_NET_MEDIATEK_HNAT is not set
 CONFIG_NET_MEDIATEK_SOC=y
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
index dcd85c1..224d4b5 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
@@ -337,8 +337,12 @@
 CONFIG_NET_DSA=y
 # CONFIG_NET_DSA_AN8855 is not set
 CONFIG_NET_DSA_MT7530=y
+CONFIG_NET_DSA_MXL862=y
+CONFIG_NET_DSA_TAG_8021Q=y
 # CONFIG_NET_DSA_TAG_AIROHA is not set
 CONFIG_NET_DSA_TAG_MTK=y
+CONFIG_NET_DSA_TAG_MXL862=y
+CONFIG_NET_DSA_TAG_MXL862_8021Q=y
 CONFIG_NET_FLOW_LIMIT=y
 CONFIG_NET_MEDIATEK_SOC=y
 CONFIG_NET_SWITCHDEV=y
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2718-backport-6.1x-mediatek-net-internal-phy-drivers.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2718-backport-6.1x-mediatek-net-internal-phy-drivers.patch
new file mode 100644
index 0000000..eae6d72
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2718-backport-6.1x-mediatek-net-internal-phy-drivers.patch
@@ -0,0 +1,44 @@
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -592,27 +592,7 @@ config MESON_GXL_PHY
+ 	---help---
+ 	  Currently has a driver for the Amlogic Meson GXL Internal PHY
+ 
+-config MEDIATEK_GE_PHY
+-	tristate "MediaTek Gigabit Ethernet PHYs"
+-	help
+-	  Supports the MediaTek Gigabit Ethernet PHYs.
+-
+-config MEDIATEK_GE_SOC_PHY
+-	bool "MediaTek SoC Ethernet PHYs"
+-	depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
+-	select NVMEM_MTK_EFUSE
+-	help
+-	  Supports MediaTek SoC built-in Gigabit Ethernet PHYs.
+-
+-	  Include support for built-in Ethernet PHYs which are present in
+-	  the MT7981 and MT7988 SoCs. These PHYs need calibration data
+-	  present in the SoCs efuse and will dynamically calibrate VCM
+-	  (common-mode voltage) during startup.
+-
+-config MEDIATEK_2P5GE_PHY
+-	tristate "MediaTek 2.5Gb Ethernet PHYs"
+-	---help---
+-	  Supports MediaTek internal 2.5Gb Ethernet PHYs.
++source "drivers/net/phy/mediatek/Kconfig"
+ 
+ config MICREL_PHY
+ 	tristate "Micrel PHYs"
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -105,9 +105,7 @@ obj-$(CONFIG_LXT_PHY)		+= lxt.o
+ obj-$(CONFIG_MARVELL_PHY)	+= marvell.o
+ obj-$(CONFIG_MARVELL_10G_PHY)	+= marvell10g.o
+ obj-$(CONFIG_MAXLINEAR_GPHY)	+= mxl-gpy.o
+-obj-$(CONFIG_MEDIATEK_GE_PHY)	+= mediatek-ge.o
+-obj-$(CONFIG_MEDIATEK_GE_SOC_PHY)	+= mediatek-ge-soc.o
+-obj-$(CONFIG_MEDIATEK_2P5GE_PHY)+= mediatek-2p5ge.o
++obj-y				+= mediatek/
+ obj-$(CONFIG_MESON_GXL_PHY)	+= meson-gxl.o
+ obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o
+ obj-$(CONFIG_MICREL_PHY)	+= micrel.o
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
deleted file mode 100644
index 4f62103..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2729-net-phy-remove-reporting-line-rate-to-mac.patch
+++ /dev/null
@@ -1,14 +0,0 @@
---- 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-2742-drivers-net-dsa-mxl862xx.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2742-drivers-net-dsa-mxl862xx.patch
new file mode 100644
index 0000000..034c688
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2742-drivers-net-dsa-mxl862xx.patch
@@ -0,0 +1,94 @@
+From ae07baf1efdbd3705be90691b5ae606057b225b0 Mon Sep 17 00:00:00 2001
+From: "neal.yen" <neal.yen@mediatek.com>
+Date: Fri, 26 Jul 2024 15:05:19 +0800
+Subject: [PATCH] 999-2742-drivers-net-dsa-mxl862xx
+
+---
+ drivers/net/dsa/Kconfig  |  2 ++
+ drivers/net/dsa/Makefile |  1 +
+ include/net/dsa.h        |  4 ++++
+ net/dsa/Kconfig          | 13 +++++++++++++
+ net/dsa/Makefile         |  2 ++
+ 6 files changed, 23 insertions(+)
+
+diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
+index a15dd0d..13ef0f0 100644
+--- a/drivers/net/dsa/Kconfig
++++ b/drivers/net/dsa/Kconfig
+@@ -54,6 +54,8 @@ source "drivers/net/dsa/microchip/Kconfig"
+ 
+ source "drivers/net/dsa/mv88e6xxx/Kconfig"
+ 
++source "drivers/net/dsa/mxl862xx/Kconfig"
++
+ source "drivers/net/dsa/sja1105/Kconfig"
+ 
+ config NET_DSA_QCA8K
+diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
+index c36e793..263c052 100644
+--- a/drivers/net/dsa/Makefile
++++ b/drivers/net/dsa/Makefile
+@@ -23,3 +23,4 @@ obj-y				+= b53/
+ obj-y				+= microchip/
+ obj-y				+= mv88e6xxx/
+ obj-y				+= sja1105/
++obj-y				+= mxl862xx/
+diff --git a/include/net/dsa.h b/include/net/dsa.h
+index 65cfa41..0c138e5 100644
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -44,6 +44,8 @@ struct phylink_link_state;
+ #define DSA_TAG_PROTO_KSZ8795_VALUE		14
+ #define DSA_TAG_PROTO_RTL4_A_VALUE		17
+ #define DSA_TAG_PROTO_ARHT_VALUE		28
++#define DSA_TAG_PROTO_MXL862_VALUE		29
++#define DSA_TAG_PROTO_MXL862_8021Q_VALUE	30
+ 
+ enum dsa_tag_protocol {
+ 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
+@@ -63,6 +65,8 @@ enum dsa_tag_protocol {
+ 	DSA_TAG_PROTO_KSZ8795		= DSA_TAG_PROTO_KSZ8795_VALUE,
+ 	DSA_TAG_PROTO_RTL4_A		= DSA_TAG_PROTO_RTL4_A_VALUE,
+ 	DSA_TAG_PROTO_ARHT		= DSA_TAG_PROTO_ARHT_VALUE,
++	DSA_TAG_PROTO_MXL862		= DSA_TAG_PROTO_MXL862_VALUE,
++	DSA_TAG_PROTO_MXL862_8021Q	= DSA_TAG_PROTO_MXL862_8021Q_VALUE,
+ };
+ 
+ struct packet_type;
+diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
+index 5da7a23..b0b9df1 100644
+--- a/net/dsa/Kconfig
++++ b/net/dsa/Kconfig
+@@ -86,6 +86,19 @@ config NET_DSA_TAG_KSZ
+ 	  Say Y if you want to enable support for tagging frames for the
+ 	  Microchip 8795/9477/9893 families of switches.
+ 
++config NET_DSA_TAG_MXL862
++	tristate "Tag driver for MxL862xx switches"
++	help
++	  Say Y or M if you want to enable support for tagging frames for the
++	  Maxlinear MxL862xx switches.
++
++config NET_DSA_TAG_MXL862_8021Q
++	tristate "Tag driver for MxL862xx switches, based on VLAN tags"
++	help
++	  Say Y or M if you want to enable support for tagging frames for the
++	  Maxlinear MxL862xx switches. This tagging variant is based on 4-byte wide VLAN
++	  tags
++
+ config NET_DSA_TAG_RTL4_A
+ 	tristate "Tag driver for Realtek 4 byte protocol A tags"
+ 	help
+diff --git a/net/dsa/Makefile b/net/dsa/Makefile
+index b58ac0f..692b70f 100644
+--- a/net/dsa/Makefile
++++ b/net/dsa/Makefile
+@@ -17,3 +17,5 @@ obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
+ obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
+ obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
+ obj-$(CONFIG_NET_DSA_TAG_AIROHA) += tag_arht.o
++obj-$(CONFIG_NET_DSA_TAG_MXL862) += tag_mxl862xx.o
++obj-$(CONFIG_NET_DSA_TAG_MXL862_8021Q) += tag_mxl862xx_8021q.o
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2743-drivers-net-dsa-mxl862xx-kernel-compatible.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2743-drivers-net-dsa-mxl862xx-kernel-compatible.patch
new file mode 100644
index 0000000..8ef1877
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2743-drivers-net-dsa-mxl862xx-kernel-compatible.patch
@@ -0,0 +1,71 @@
+From 5feba07f7e9ccf9c9a3d862e321c84ac4fd089a9 Mon Sep 17 00:00:00 2001
+From: "neal.yen" <neal.yen@mediatek.com>
+Date: Thu, 26 Sep 2024 20:19:57 +0800
+Subject: [PATCH] 999-2743-drivers-net-dsa-mxl862xx-kernel-compatible
+
+---
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 32 -----------------------------
+ 1 file changed, 32 deletions(-)
+
+diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c
+index 5cab346..cbf2fe4 100755
+--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
+@@ -1494,7 +1494,6 @@ static void mxl862xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
+ 	}
+ }
+ 
+-#if (KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE)
+ static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+ 					 unsigned int mode,
+ 					 phy_interface_t interface,
+@@ -1589,32 +1588,6 @@ static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+ 	return;
+ }
+ 
+-#else
+-static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+-					 unsigned int mode,
+-					 phy_interface_t interface,
+-					 struct phy_device *phydev)
+-{
+-	mxl862xx_port_link_cfg_t port_link_cfg = { 0 };
+-	int ret;
+-
+-	if (dsa_is_cpu_port(ds, port))
+-		return;
+-
+-	port_link_cfg.port_id = port + 1;
+-
+-	port_link_cfg.link_force = true;
+-	port_link_cfg.link = MXL862XX_PORT_LINK_UP;
+-
+-	ret = mxl862xx_port_link_cfg_set(&mxl_dev, &port_link_cfg);
+-	if (ret != MXL862XX_STATUS_OK) {
+-		dev_err(ds->dev,
+-			"%s: Port link configuration for port %d failed with %d\n",
+-			__func__, port, ret);
+-		return;
+-	}
+-}
+-#endif
+ #endif
+ 
+ static void mxl862xx_get_ethtool_stats(struct dsa_switch *ds, int port,
+@@ -4398,13 +4371,8 @@ static int mxl862xx_change_tag_protocol(struct dsa_switch *ds,
+ }
+ #endif
+ 
+-#if (KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE)
+-static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
+-						       int port)
+-#else
+ static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
+ 						       int port, enum dsa_tag_protocol m)
+-#endif
+ {
+ 	enum dsa_tag_protocol tag_proto;
+ 
+-- 
+2.45.2
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2744-drivers-net-phy-mxl862xx-mxl-gpy.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2744-drivers-net-phy-mxl862xx-mxl-gpy.patch
new file mode 100644
index 0000000..37a1ba9
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2744-drivers-net-phy-mxl862xx-mxl-gpy.patch
@@ -0,0 +1,79 @@
+From 2b6996b2827db88d87ad28e0c28cbe7382eb375c Mon Sep 17 00:00:00 2001
+From: "neal.yen" <neal.yen@mediatek.com>
+Date: Mon, 12 Aug 2024 16:32:48 +0800
+Subject: [PATCH] 999-2745-drivers-net-phy-mxl862xx-mxl-gpy
+
+---
+ drivers/net/phy/mxl-gpy.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
+index 7304278..988eb77 100644
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -14,6 +14,7 @@
+ /* PHY ID */
+ #define PHY_ID_GPYx15B_MASK	0xFFFFFFFC
+ #define PHY_ID_GPY21xB_MASK	0xFFFFFFF9
++#define PHY_ID_MXL862XX_MASK	0xFFFFFF00
+ #define PHY_ID_GPY2xx		0x67C9DC00
+ #define PHY_ID_GPY115B		0x67C9DF00
+ #define PHY_ID_GPY115C		0x67C9DF10
+@@ -26,6 +27,7 @@
+ #define PHY_ID_GPY241B		0x67C9DE40
+ #define PHY_ID_GPY241BM		0x67C9DE80
+ #define PHY_ID_GPY245B		0x67C9DEC0
++#define PHY_ID_MXL862XX		0xC1335500
+ 
+ #define PHY_MIISTAT		0x18	/* MII state */
+ #define PHY_IMASK		0x19	/* interrupt mask */
+@@ -504,6 +506,15 @@ static int gpy115_loopback(struct phy_device *phydev, bool enable)
+ 	return genphy_soft_reset(phydev);
+ }
+ 
++static int gpy_c45_pma_read_abilities(struct phy_device *phydev)
++{
++	phydev->c45_ids.devices_in_package  |= MDIO_DEVS_AN;
++
++	genphy_c45_pma_read_abilities(phydev);
++
++	return 0;
++}
++
+ static struct phy_driver gpy_drivers[] = {
+ 	{
+ 		PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
+@@ -713,6 +724,22 @@ static struct phy_driver gpy_drivers[] = {
+ 		.get_wol	= gpy_get_wol,
+ 		.set_loopback	= gpy_loopback,
+ 	},
++	{
++		.phy_id		= PHY_ID_MXL862XX,
++		.phy_id_mask	= PHY_ID_MXL862XX_MASK,
++		.name		= "MaxLinear Ethernet MxL862XX",
++		.get_features	= gpy_c45_pma_read_abilities,
++		.config_init	= gpy_config_init,
++		.probe		= gpy_probe,
++		.suspend	= genphy_suspend,
++		.resume		= genphy_resume,
++		.config_aneg	= gpy_config_aneg,
++		.aneg_done	= genphy_c45_aneg_done,
++		.read_status	= gpy_read_status,
++		.config_intr	= gpy_config_intr,
++		.handle_interrupt = gpy_handle_interrupt,
++		.set_loopback	= gpy_loopback,
++	},
+ };
+ module_phy_driver(gpy_drivers);
+ 
+@@ -729,6 +756,7 @@ static struct mdio_device_id __maybe_unused gpy_tbl[] = {
+ 	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
+ 	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
+ 	{PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
++	{PHY_ID_MXL862XX, PHY_ID_MXL862XX_MASK},
+ 	{ }
+ };
+ MODULE_DEVICE_TABLE(mdio, gpy_tbl);
+-- 
+2.45.2
+
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 6ab5265..4e8e55c 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
@@ -198,6 +198,7 @@
     file://999-2715-add-gpy211-phy-support.patch \
     file://999-2716-en8801sc-gphy-support.patch \
     file://999-2717-add-mediatek-2p5ge-phy-support.patch \
+    file://999-2718-backport-6.1x-mediatek-net-internal-phy-drivers.patch \
     file://999-2719-net-phy-aquantia-add-firmware-download.patch \
     file://999-2722-dt-bindings-phy-Add-PHY_TYPE_DP-definition.patch \
     file://999-2723-dt-bindings-phy-Add-PHY_TYPE_XPCS-definition.patch \
@@ -205,7 +206,6 @@
     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-2731-add-default-setting-to-dsa-unused-port.patch \
     file://999-2732-net-dsa-add-MT7531-Gigabit-Ethernet-PHY-setting.patch \
@@ -223,6 +223,9 @@
     file://999-2739-net_dsa_add_tag_arht.patch \
     file://999-2740-crypto-add-eip197-aes-ctr-support.patch \
     file://999-2741-net-phy-aquantia-add-automatic-firmware-selection.patch \
+    file://999-2742-drivers-net-dsa-mxl862xx.patch \
+    file://999-2743-drivers-net-dsa-mxl862xx-kernel-compatible.patch;apply=no \
+    file://999-2744-drivers-net-phy-mxl862xx-mxl-gpy.patch \
     file://999-2800-misc-add-mtk-platform.patch \
     file://999-2850-fips-140-3-compliance.patch \
     file://999-2900-dts-mt7622-enable-new-mtk-snand-for-ubi.patch \
diff --git a/recipes-kernel/linux/linux-mediatek_5.4.bb b/recipes-kernel/linux/linux-mediatek_5.4.bb
index 643bab7..ef926bc 100644
--- a/recipes-kernel/linux/linux-mediatek_5.4.bb
+++ b/recipes-kernel/linux/linux-mediatek_5.4.bb
@@ -110,8 +110,8 @@
             patch -p1 < ${WORKDIR}/999-1712-v6.2-net-phy-add-phylink-rate-matching-support.patch
             patch -p1 < ${WORKDIR}/999-1716-v6.6-net-phy-add-phylink-pcs_enable-and-pcs_disable.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
             patch -p1 < ${WORKDIR}/999-2739-drivers_net_ethernet_mediatek_hnat.patch
+            patch -p1 < ${WORKDIR}/999-2743-drivers-net-dsa-mxl862xx-kernel-compatible.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