[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 = <®_3p3v>;
- vqmmc-supply = <®_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 = <®_3p3v>;
+ vqmmc-supply = <®_3p3v>;
+ status = "okay";
};
ð {
@@ -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";
+};
+
+ð {
+ 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, ¶m);
+ 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, ¶m);
+ 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, ¶m);
+ 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, ®ister_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, ®ister_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, ®ister_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, ®ister_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, ®ister_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, ¶m);
+ 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(ð->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(ð->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 = ð->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 = ð->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 = ð->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 = ð->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 = ð->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 = ð->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 = ð->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 = ð->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 = ð->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(ð->pending_work);
}
break;
}
}
-
- mod_timer(ð->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(ð->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 = ð->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, ð->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, ð->state))
+ goto out;
+
+ /* DMA stuck checks */
+ mtk_hw_reset_monitor(eth);
+
+out:
+ schedule_delayed_work(ð->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(ð->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(ð->mtk_dma_monitor_timer, mtk_dma_monitor, 0);
- eth->mtk_dma_monitor_timer.expires = jiffies;
- add_timer(ð->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, ð->state);
rtnl_unlock();
@@ -4887,6 +5038,7 @@
mtk_unreg_dev(eth);
mtk_free_dev(eth);
cancel_work_sync(ð->pending_work);
+ cancel_delayed_work_sync(ð->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(ð->txrx_irq_lock);
spin_lock_init(ð->syscfg0_lock);
+ INIT_DELAYED_WORK(ð->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(ð->netdevice_notifier);
#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
- timer_setup(ð->mtk_dma_monitor_timer, mtk_dma_monitor, 0);
- eth->mtk_dma_monitor_timer.expires = jiffies;
- add_timer(ð->mtk_dma_monitor_timer);
+ schedule_delayed_work(ð->reset.monitor_work,
+ MTK_DMA_MONITOR_TIMEOUT);
#endif
return 0;
@@ -6008,7 +6163,6 @@
mtk_cleanup(eth);
mtk_mdio_cleanup(eth);
unregister_netdevice_notifier(ð->netdevice_notifier);
- del_timer_sync(ð->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 *)ð->h_dest[4]));
- entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source));
- entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)ð->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 *)ð->h_dest[4]));
- entry.ipv6_5t_route.smac_hi = swab32(*((u32 *)eth->h_source));
- entry.ipv6_5t_route.smac_lo =
- swab16(*((u16 *)ð->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(ð->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(ð->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 *)ð->h_dest[4]));
+ entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source));
+ entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)ð->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 *)ð->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, ð))
+ 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", ®, &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", ®) == -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, ®, &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, ®_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, ®) == -1)
+ return -EFAULT;
+ an8801_cl45_read(phydev, devad, 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, ®, &val) == -1)
+ if (sscanf(buf, "w %x %x", ®, &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, ®) == -1)
+ if (sscanf(buf, "r %x", ®) == -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, ®);
+ 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 = <ðsys>;
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