diff options
Diffstat (limited to 'pkgs/linux-openwrt-one.patch')
| -rw-r--r-- | pkgs/linux-openwrt-one.patch | 2508 |
1 files changed, 2508 insertions, 0 deletions
diff --git a/pkgs/linux-openwrt-one.patch b/pkgs/linux-openwrt-one.patch new file mode 100644 index 0000000..62c20ad --- /dev/null +++ b/pkgs/linux-openwrt-one.patch @@ -0,0 +1,2508 @@ +From 9c7cb0b21491ba7fd804f669b969418e33270142 Mon Sep 17 00:00:00 2001 +From: Daniel Golle <daniel@makrotopia.org> +Date: Wed, 28 Dec 2022 23:44:42 +0000 +Subject: [PATCH 01/12] complete mt7981 dts + +working: + * Ethernet (fully working incl. ppe) + * UART + * SPI-NAND flash + * thermal sensors (SoC and mxl-gpy) + * random number generator via SMC + * USB 1.1, 2.0 and 3.0 + * WiFi with MT7976C 2.4G+5G DBDC incl. WED offloading + * PWM +--- + arch/arm64/boot/dts/mediatek/mt7981b.dtsi | 566 +++++++++++++++++++++- + 1 file changed, 554 insertions(+), 12 deletions(-) + +diff --git a/arch/arm64/boot/dts/mediatek/mt7981b.dtsi b/arch/arm64/boot/dts/mediatek/mt7981b.dtsi +index 5cbea9cd411f..6a816e5c1f46 100644 +--- a/arch/arm64/boot/dts/mediatek/mt7981b.dtsi ++++ b/arch/arm64/boot/dts/mediatek/mt7981b.dtsi +@@ -1,7 +1,14 @@ + // SPDX-License-Identifier: GPL-2.0-only OR MIT + + #include <dt-bindings/clock/mediatek,mt7981-clk.h> ++#include <dt-bindings/gpio/gpio.h> ++#include <dt-bindings/input/linux-event-codes.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> ++#include <dt-bindings/interrupt-controller/irq.h> ++#include <dt-bindings/leds/common.h> ++#include <dt-bindings/mux/mux.h> ++#include <dt-bindings/pinctrl/mt65xx.h> ++#include <dt-bindings/phy/phy.h> + #include <dt-bindings/reset/mt7986-resets.h> + + / { +@@ -41,6 +48,57 @@ psci { + method = "smc"; + }; + ++ fan: pwm-fan { ++ compatible = "pwm-fan"; ++ /* cooling level (0, 1, 2, 3) : (0% duty, 50% duty, 75% duty, 100% duty) */ ++ cooling-levels = <0 128 192 255>; ++ #cooling-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ reg_3p3v: regulator-3p3v { ++ compatible = "regulator-fixed"; ++ regulator-name = "fixed-3.3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* 64 KiB reserved for ramoops/pstore */ ++ ramoops@42ff0000 { ++ compatible = "ramoops"; ++ reg = <0 0x42ff0000 0 0x10000>; ++ record-size = <0x1000>; ++ }; ++ ++ /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ ++ secmon_reserved: secmon@43000000 { ++ reg = <0 0x43000000 0 0x30000>; ++ no-map; ++ }; ++ ++ wmcpu_emi: wmcpu-reserved@47c80000 { ++ reg = <0 0x47c80000 0 0x100000>; ++ no-map; ++ }; ++ ++ wo_emi0: wo-emi@47d80000 { ++ reg = <0 0x47d80000 0 0x40000>; ++ no-map; ++ }; ++ ++ wo_data: wo-data@47dc0000 { ++ reg = <0 0x47dc0000 0 0x240000>; ++ no-map; ++ }; ++ }; ++ + soc { + compatible = "simple-bus"; + ranges; +@@ -76,13 +134,13 @@ watchdog: watchdog@1001c000 { + #reset-cells = <1>; + }; + +- clock-controller@1001e000 { +- compatible = "mediatek,mt7981-apmixedsys"; ++ apmixedsys: clock-controller@1001e000 { ++ compatible = "mediatek,mt7981-apmixedsys", "syscon"; + reg = <0 0x1001e000 0 0x1000>; + #clock-cells = <1>; + }; + +- pwm@10048000 { ++ pwm: pwm@10048000 { + compatible = "mediatek,mt7981-pwm"; + reg = <0 0x10048000 0 0x1000>; + clocks = <&infracfg CLK_INFRA_PWM_STA>, +@@ -94,7 +152,21 @@ pwm@10048000 { + #pwm-cells = <2>; + }; + +- serial@11002000 { ++ crypto: crypto@10320000 { ++ compatible = "inside-secure,safexcel-eip97"; ++ reg = <0 0x10320000 0 0x40000>; ++ interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "ring0", "ring1", "ring2", "ring3"; ++ clocks = <&topckgen CLK_TOP_EIP97B>; ++ clock-names = "top_eip97_ck"; ++ assigned-clocks = <&topckgen CLK_TOP_EIP97B_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_CB_NET1_D5>; ++ }; ++ ++ uart0: serial@11002000 { + compatible = "mediatek,mt7981-uart", "mediatek,mt6577-uart"; + reg = <0 0x11002000 0 0x100>; + interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>; +@@ -105,7 +177,7 @@ serial@11002000 { + status = "disabled"; + }; + +- serial@11003000 { ++ uart1: serial@11003000 { + compatible = "mediatek,mt7981-uart", "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x100>; + interrupts = <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>; +@@ -116,7 +188,7 @@ serial@11003000 { + status = "disabled"; + }; + +- serial@11004000 { ++ uart2: serial@11004000 { + compatible = "mediatek,mt7981-uart", "mediatek,mt6577-uart"; + reg = <0 0x11004000 0 0x100>; + interrupts = <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>; +@@ -127,11 +199,12 @@ serial@11004000 { + status = "disabled"; + }; + +- i2c@11007000 { ++ i2c0: i2c@11007000 { + compatible = "mediatek,mt7981-i2c"; + reg = <0 0x11007000 0 0x1000>, + <0 0x10217080 0 0x80>; + interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>; ++ clock-div = <1>; + clocks = <&infracfg CLK_INFRA_I2C0_CK>, + <&infracfg CLK_INFRA_AP_DMA_CK>, + <&infracfg CLK_INFRA_I2C_MCK_CK>, +@@ -142,7 +215,32 @@ i2c@11007000 { + status = "disabled"; + }; + +- spi@11009000 { ++ thermal: thermal@1100c800 { ++ #thermal-sensor-cells = <1>; ++ compatible = "mediatek,mt7981-thermal", "mediatek,mt7986-thermal"; ++ reg = <0 0x1100c800 0 0x800>; ++ interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&infracfg CLK_INFRA_THERM_CK>, ++ <&infracfg CLK_INFRA_ADC_26M_CK>; ++ clock-names = "therm", "auxadc"; ++ mediatek,auxadc = <&auxadc>; ++ mediatek,apmixedsys = <&apmixedsys>; ++ nvmem-cells = <&thermal_calibration>; ++ nvmem-cell-names = "calibration-data"; ++ }; ++ ++ auxadc: adc@1100d000 { ++ compatible = "mediatek,mt7981-auxadc", ++ "mediatek,mt7986-auxadc", ++ "mediatek,mt7622-auxadc"; ++ reg = <0 0x1100d000 0 0x1000>; ++ clocks = <&infracfg CLK_INFRA_ADC_26M_CK>, ++ <&infracfg CLK_INFRA_ADC_FRC_CK>; ++ clock-names = "main", "32k"; ++ #io-channel-cells = <1>; ++ }; ++ ++ spi2: spi@11009000 { + compatible = "mediatek,mt7981-spi-ipm", "mediatek,spi-ipm"; + reg = <0 0x11009000 0 0x1000>; + interrupts = <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>; +@@ -156,7 +254,7 @@ spi@11009000 { + status = "disabled"; + }; + +- spi@1100a000 { ++ spi0: spi@1100a000 { + compatible = "mediatek,mt7981-spi-ipm", "mediatek,spi-ipm"; + reg = <0 0x1100a000 0 0x1000>; + interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>; +@@ -170,7 +268,7 @@ spi@1100a000 { + status = "disabled"; + }; + +- spi@1100b000 { ++ spi1: spi@1100b000 { + compatible = "mediatek,mt7981-spi-ipm", "mediatek,spi-ipm"; + reg = <0 0x1100b000 0 0x1000>; + interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>; +@@ -184,6 +282,41 @@ spi@1100b000 { + status = "disabled"; + }; + ++ pcie: pcie@11280000 { ++ compatible = "mediatek,mt7981-pcie", ++ "mediatek,mt8192-pcie"; ++ device_type = "pci"; ++ reg = <0 0x11280000 0 0x4000>; ++ reg-names = "pcie-mac"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>; ++ bus-range = <0x00 0xff>; ++ ranges = <0x82000000 0 0x20000000 ++ 0x0 0x20000000 0 0x10000000>; ++ status = "disabled"; ++ ++ clocks = <&infracfg CLK_INFRA_IPCIE_CK>, ++ <&infracfg CLK_INFRA_IPCIE_PIPE_CK>, ++ <&infracfg CLK_INFRA_IPCIER_CK>, ++ <&infracfg CLK_INFRA_IPCIEB_CK>; ++ ++ phys = <&u3port0 PHY_TYPE_PCIE>; ++ phy-names = "pcie-phy"; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie_intc 0>, ++ <0 0 0 2 &pcie_intc 1>, ++ <0 0 0 3 &pcie_intc 2>, ++ <0 0 0 4 &pcie_intc 3>; ++ pcie_intc: interrupt-controller { ++ interrupt-controller; ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ }; ++ }; ++ + pio: pinctrl@11d00000 { + compatible = "mediatek,mt7981-pinctrl"; + reg = <0 0x11d00000 0 0x1000>, +@@ -204,6 +337,49 @@ pio: pinctrl@11d00000 { + gpio-controller; + #gpio-cells = <2>; + #interrupt-cells = <2>; ++ ++ mdio_pins: mdc-mdio-pins { ++ mux { ++ function = "eth"; ++ groups = "smi_mdc_mdio"; ++ }; ++ }; ++ ++ uart0_pins: uart0-pins { ++ mux { ++ function = "uart"; ++ groups = "uart0"; ++ }; ++ }; ++ ++ wifi_dbdc_pins: wifi-dbdc-pins { ++ mux { ++ function = "eth"; ++ groups = "wf0_mode1"; ++ }; ++ conf { ++ pins = "WF_HB1", "WF_HB2", "WF_HB3", "WF_HB4", ++ "WF_HB0", "WF_HB0_B", "WF_HB5", "WF_HB6", ++ "WF_HB7", "WF_HB8", "WF_HB9", "WF_HB10", ++ "WF_TOP_CLK", "WF_TOP_DATA", "WF_XO_REQ", ++ "WF_CBA_RESETB", "WF_DIG_RESETB"; ++ drive-strength = <MTK_DRIVE_4mA>; ++ }; ++ }; ++ ++ gbe_led0_pins: gbe-led0-pins { ++ mux { ++ function = "led"; ++ groups = "gbe_led0"; ++ }; ++ }; ++ ++ gbe_led1_pins: gbe-led1-pins { ++ mux { ++ function = "led"; ++ groups = "gbe_led1"; ++ }; ++ }; + }; + + efuse@11f20000 { +@@ -211,17 +387,318 @@ efuse@11f20000 { + reg = <0 0x11f20000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; ++ ++ thermal_calibration: thermal-calib@274 { ++ reg = <0x274 0xc>; ++ }; ++ ++ phy_calibration: phy-calib@8dc { ++ reg = <0x8dc 0x10>; ++ }; ++ ++ comb_rx_imp_p0: usb3-rx-imp@8c8 { ++ reg = <0x8c8 1>; ++ bits = <0 5>; ++ }; ++ ++ comb_tx_imp_p0: usb3-tx-imp@8c8 { ++ reg = <0x8c8 2>; ++ bits = <5 5>; ++ }; ++ ++ comb_intr_p0: usb3-intr@8c9 { ++ reg = <0x8c9 1>; ++ bits = <2 6>; ++ }; + }; + +- clock-controller@15000000 { ++ ethsys: clock-controller@15000000 { + compatible = "mediatek,mt7981-ethsys", "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + +- wifi@18000000 { ++ wed: wed@15010000 { ++ compatible = "mediatek,mt7981-wed", ++ "mediatek,mt7986-wed", ++ "syscon"; ++ reg = <0 0x15010000 0 0x1000>; ++ interrupt-parent = <&gic>; ++ interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>; ++ memory-region = <&wo_emi0>, <&wo_data>; ++ memory-region-names = "wo-emi", "wo-data"; ++ mediatek,wo-ccif = <&wo_ccif0>; ++ mediatek,wo-ilm = <&wo_ilm0>; ++ mediatek,wo-dlm = <&wo_dlm0>; ++ mediatek,wo-cpuboot = <&wo_cpuboot>; ++ }; ++ ++ eth: ethernet@15100000 { ++ compatible = "mediatek,mt7981-eth"; ++ reg = <0 0x15100000 0 0x80000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 198 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 199 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <ðsys CLK_ETH_FE_EN>, ++ <ðsys CLK_ETH_GP2_EN>, ++ <ðsys CLK_ETH_GP1_EN>, ++ <ðsys CLK_ETH_WOCPU0_EN>, ++ <&sgmiisys0 CLK_SGM0_TX_EN>, ++ <&sgmiisys0 CLK_SGM0_RX_EN>, ++ <&sgmiisys0 CLK_SGM0_CK0_EN>, ++ <&sgmiisys0 CLK_SGM0_CDR_CK0_EN>, ++ <&sgmiisys1 CLK_SGM1_TX_EN>, ++ <&sgmiisys1 CLK_SGM1_RX_EN>, ++ <&sgmiisys1 CLK_SGM1_CK1_EN>, ++ <&sgmiisys1 CLK_SGM1_CDR_CK1_EN>, ++ <&topckgen CLK_TOP_SGM_REG>, ++ <&topckgen CLK_TOP_NETSYS_SEL>, ++ <&topckgen CLK_TOP_NETSYS_500M_SEL>; ++ clock-names = "fe", "gp2", "gp1", "wocpu0", ++ "sgmii_tx250m", "sgmii_rx250m", ++ "sgmii_cdr_ref", "sgmii_cdr_fb", ++ "sgmii2_tx250m", "sgmii2_rx250m", ++ "sgmii2_cdr_ref", "sgmii2_cdr_fb", ++ "sgmii_ck", "netsys0", "netsys1"; ++ assigned-clocks = <&topckgen CLK_TOP_NETSYS_2X_SEL>, ++ <&topckgen CLK_TOP_SGM_325M_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_CB_NET2_800M>, ++ <&topckgen CLK_TOP_CB_SGM_325M>; ++ mediatek,ethsys = <ðsys>; ++ mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; ++ mediatek,infracfg = <&topmisc>; ++ mediatek,wed = <&wed>; ++ status = "disabled"; ++ ++ mdio_bus: mdio-bus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ int_gbe_phy: ethernet-phy@0 { ++ reg = <0>; ++ compatible = "ethernet-phy-ieee802.3-c22"; ++ phy-mode = "gmii"; ++ phy-is-integrated; ++ nvmem-cells = <&phy_calibration>; ++ nvmem-cell-names = "phy-cal-data"; ++ ++ leds { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ int_gbe_phy_led0: int-gbe-phy-led0@0 { ++ reg = <0>; ++ function = LED_FUNCTION_LAN; ++ pinctrl-0 = <&gbe_led0_pins>; ++ pinctrl-names = "default"; ++ status = "disabled"; ++ }; ++ ++ int_gbe_phy_led1: int-gbe-phy-led1@1 { ++ reg = <1>; ++ function = LED_FUNCTION_LAN; ++ pinctrl-0 = <&gbe_led1_pins>; ++ pinctrl-names = "default"; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ }; ++ }; ++ ++ wdma: wdma@15104800 { ++ compatible = "mediatek,wed-wdma"; ++ reg = <0 0x15104800 0 0x400>, ++ <0 0x15104c00 0 0x400>; ++ }; ++ ++ ap2woccif: ap2woccif@151a5000 { ++ compatible = "mediatek,ap2woccif"; ++ reg = <0 0x151a5000 0 0x1000>, ++ <0 0x151ad000 0 0x1000>; ++ interrupt-parent = <&gic>; ++ interrupts = <GIC_SPI 211 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 212 IRQ_TYPE_LEVEL_HIGH>; ++ }; ++ ++ wo_dlm0: syscon@151e8000 { ++ compatible = "mediatek,mt7986-wo-dlm", "syscon"; ++ reg = <0 0x151e8000 0 0x2000>; ++ }; ++ ++ wo_ilm0: syscon@151e0000 { ++ compatible = "mediatek,mt7986-wo-ilm", "syscon"; ++ reg = <0 0x151e0000 0 0x8000>; ++ }; ++ ++ wo_cpuboot: syscon@15194000 { ++ compatible = "mediatek,mt7986-wo-cpuboot", "syscon"; ++ reg = <0 0x15194000 0 0x1000>; ++ }; ++ ++ wo_ccif0: syscon@151a5000 { ++ compatible = "mediatek,mt7986-wo-ccif", "syscon"; ++ reg = <0 0x151a5000 0 0x1000>; ++ interrupt-parent = <&gic>; ++ interrupts = <GIC_SPI 211 IRQ_TYPE_LEVEL_HIGH>; ++ }; ++ ++ sgmiisys0: syscon@10060000 { ++ compatible = "mediatek,mt7981-sgmiisys_0", "mediatek,mt7986-sgmiisys_0", "syscon"; ++ reg = <0 0x10060000 0 0x1000>; ++ mediatek,pnswap; ++ #clock-cells = <1>; ++ }; ++ ++ sgmiisys1: syscon@10070000 { ++ compatible = "mediatek,mt7981-sgmiisys_1", "mediatek,mt7986-sgmiisys_1", "syscon"; ++ reg = <0 0x10070000 0 0x1000>; ++ #clock-cells = <1>; ++ }; ++ ++ topmisc: topmisc@11d10000 { ++ compatible = "mediatek,mt7981-topmisc", "syscon"; ++ reg = <0 0x11d10000 0 0x10000>; ++ #clock-cells = <1>; ++ }; ++ ++ snand: snfi@11005000 { ++ compatible = "mediatek,mt7986-snand"; ++ reg = <0 0x11005000 0 0x1000>, <0 0x11006000 0 0x1000>; ++ reg-names = "nfi", "ecc"; ++ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&infracfg CLK_INFRA_SPINFI1_CK>, ++ <&infracfg CLK_INFRA_NFI1_CK>, ++ <&infracfg CLK_INFRA_NFI_HCK_CK>; ++ clock-names = "pad_clk", "nfi_clk", "nfi_hclk"; ++ assigned-clocks = <&topckgen CLK_TOP_SPINFI_SEL>, ++ <&topckgen CLK_TOP_NFI1X_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_CB_M_D8>, ++ <&topckgen CLK_TOP_CB_M_D8>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ 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 = <&infracfg CLK_INFRA_MSDC_CK>, ++ <&infracfg CLK_INFRA_MSDC_HCK_CK>, ++ <&infracfg CLK_INFRA_MSDC_66M_CK>, ++ <&infracfg CLK_INFRA_MSDC_133M_CK>; ++ assigned-clocks = <&topckgen CLK_TOP_EMMC_208M_SEL>, ++ <&topckgen CLK_TOP_EMMC_400M_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_CB_M_D2>, ++ <&topckgen CLK_TOP_CB_NET2_D2>; ++ clock-names = "source", "hclk", "axi_cg", "ahb_cg"; ++ status = "disabled"; ++ }; ++ ++ wed_pcie: wed_pcie@10003000 { ++ compatible = "mediatek,wed_pcie"; ++ reg = <0 0x10003000 0 0x10>; ++ }; ++ ++ consys: consys@10000000 { ++ compatible = "mediatek,mt7981-consys"; ++ reg = <0 0x10000000 0 0x8600000>; ++ memory-region = <&wmcpu_emi>; ++ }; ++ ++ xhci: usb@11200000 { ++ compatible = "mediatek,mt7986-xhci", ++ "mediatek,mtk-xhci"; ++ reg = <0 0x11200000 0 0x2e00>, ++ <0 0x11203e00 0 0x0100>; ++ reg-names = "mac", "ippc"; ++ interrupts = <GIC_SPI 173 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&infracfg CLK_INFRA_IUSB_SYS_CK>, ++ <&infracfg CLK_INFRA_IUSB_CK>, ++ <&infracfg CLK_INFRA_IUSB_133_CK>, ++ <&infracfg CLK_INFRA_IUSB_66M_CK>, ++ <&topckgen CLK_TOP_U2U3_XHCI_SEL>; ++ clock-names = "sys_ck", ++ "ref_ck", ++ "mcu_ck", ++ "dma_ck", ++ "xhci_ck"; ++ phys = <&u2port0 PHY_TYPE_USB2>, ++ <&u3port0 PHY_TYPE_USB3>; ++ vusb33-supply = <®_3p3v>; ++ status = "disabled"; ++ }; ++ ++ usb_phy: usb-phy@11e10000 { ++ compatible = "mediatek,mt7981", ++ "mediatek,generic-tphy-v2"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0 0x11e10000 0x1700>; ++ status = "disabled"; ++ ++ u2port0: usb-phy@0 { ++ reg = <0x0 0x700>; ++ clocks = <&topckgen CLK_TOP_USB_FRMCNT_SEL>; ++ clock-names = "ref"; ++ #phy-cells = <1>; ++ }; ++ ++ u3port0: usb-phy@700 { ++ reg = <0x700 0x900>; ++ clocks = <&topckgen CLK_TOP_USB3_PHY_SEL>; ++ clock-names = "ref"; ++ #phy-cells = <1>; ++ mediatek,syscon-type = <&topmisc 0x218 0>; ++ status = "okay"; ++ }; ++ }; ++ ++ ++ afe: audio-controller@11210000 { ++ compatible = "mediatek,mt79xx-audio"; ++ reg = <0 0x11210000 0 0x9000>; ++ interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&infracfg CLK_INFRA_AUD_BUS_CK>, ++ <&infracfg CLK_INFRA_AUD_26M_CK>, ++ <&infracfg CLK_INFRA_AUD_L_CK>, ++ <&infracfg CLK_INFRA_AUD_AUD_CK>, ++ <&infracfg CLK_INFRA_AUD_EG2_CK>, ++ <&topckgen CLK_TOP_AUD_SEL>; ++ clock-names = "aud_bus_ck", ++ "aud_26m_ck", ++ "aud_l_ck", ++ "aud_aud_ck", ++ "aud_eg2_ck", ++ "aud_sel"; ++ assigned-clocks = <&topckgen CLK_TOP_AUD_SEL>, ++ <&topckgen CLK_TOP_A1SYS_SEL>, ++ <&topckgen CLK_TOP_AUD_L_SEL>, ++ <&topckgen CLK_TOP_A_TUNER_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_CB_APLL2_196M>, ++ <&topckgen CLK_TOP_APLL2_D4>, ++ <&topckgen CLK_TOP_CB_APLL2_196M>, ++ <&topckgen CLK_TOP_APLL2_D4>; ++ status = "disabled"; ++ }; ++ ++ ice: ice_debug { ++ compatible = "mediatek,mt7981-ice_debug", ++ "mediatek,mt2701-ice_debug"; ++ clocks = <&infracfg CLK_INFRA_DBG_CK>; ++ clock-names = "ice_dbg"; ++ }; ++ ++ wifi: wifi@18000000 { + compatible = "mediatek,mt7981-wmac"; ++ pinctrl-0 = <&wifi_dbdc_pins>; ++ pinctrl-names = "dbdc"; + reg = <0 0x18000000 0 0x1000000>, + <0 0x10003000 0 0x1000>, + <0 0x11d10000 0 0x1000>; +@@ -234,6 +711,67 @@ wifi@18000000 { + clock-names = "mcu", "ap2conn"; + resets = <&watchdog MT7986_TOPRGU_CONSYS_SW_RST>; + reset-names = "consys"; ++ memory-region = <&wmcpu_emi>; ++ status = "disabled"; ++ }; ++ }; ++ ++ thermal-zones { ++ cpu_thermal: cpu-thermal { ++ polling-delay-passive = <1000>; ++ polling-delay = <1000>; ++ thermal-sensors = <&thermal 0>; ++ trips { ++ cpu_trip_crit: crit { ++ temperature = <125000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu_trip_hot: hot { ++ temperature = <120000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ ++ cpu_trip_active_high: active-high { ++ temperature = <115000>; ++ hysteresis = <2000>; ++ type = "active"; ++ }; ++ ++ cpu_trip_active_med: active-med { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "active"; ++ }; ++ ++ cpu_trip_active_low: active-low { ++ temperature = <60000>; ++ hysteresis = <2000>; ++ type = "active"; ++ }; ++ }; ++ ++ cooling-maps { ++ cpu-active-high { ++ /* active: set fan to cooling level 3 */ ++ cooling-device = <&fan 3 3>; ++ trip = <&cpu_trip_active_high>; ++ }; ++ ++ cpu-active-med { ++ /* active: set fan to cooling level 2 */ ++ cooling-device = <&fan 2 2>; ++ trip = <&cpu_trip_active_med>; ++ }; ++ ++ cpu-active-low { ++ /* passive: set fan to cooling level 1 */ ++ cooling-device = <&fan 1 1>; ++ trip = <&cpu_trip_active_low>; ++ }; ++ }; + }; + }; + +@@ -245,4 +783,8 @@ timer { + <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>, + <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>; + }; ++ ++ trng { ++ compatible = "mediatek,mt7981-rng"; ++ }; + }; +-- +2.51.0 + + +From c99275204c5709fa159c2cbbdc6dd25bad49f882 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= <cynerd@email.cz> +Date: Thu, 2 Oct 2025 11:54:23 +0200 +Subject: [PATCH 02/12] Pull OpenWrt One device tree from OpenWrt master + +The commit used: 915a57ccd9086db88ee74cfa531ff13f8652a447 +--- + .../boot/dts/mediatek/mt7981b-openwrt-one.dts | 460 +++++++++++++++++- + 1 file changed, 457 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/boot/dts/mediatek/mt7981b-openwrt-one.dts b/arch/arm64/boot/dts/mediatek/mt7981b-openwrt-one.dts +index 4f6cbb491287..5f54506b2cba 100644 +--- a/arch/arm64/boot/dts/mediatek/mt7981b-openwrt-one.dts ++++ b/arch/arm64/boot/dts/mediatek/mt7981b-openwrt-one.dts +@@ -1,15 +1,469 @@ +-// SPDX-License-Identifier: GPL-2.0-only OR MIT ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) + + /dts-v1/; +- + #include "mt7981b.dtsi" + + / { +- compatible = "openwrt,one", "mediatek,mt7981b"; + model = "OpenWrt One"; ++ compatible = "openwrt,one", "mediatek,mt7981"; ++ ++ aliases { ++ ethernet0 = &gmac1; ++ label-mac-device = &gmac0; ++ led-boot = &led_status_white; ++ led-failsafe = &led_status_red; ++ led-running = &led_status_green; ++ led-upgrade = &led_status_green; ++ serial0 = &uart0; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ rootdisk = <&ubi_fit_volume>; ++ }; + + memory@40000000 { + reg = <0 0x40000000 0 0x40000000>; + device_type = "memory"; + }; ++ ++ reg_3p3v: regulator-3p3v { ++ compatible = "regulator-fixed"; ++ regulator-name = "fixed-3.3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ reg_5v: regulator-5v { ++ compatible = "regulator-fixed"; ++ regulator-name = "fixed-5V"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ ++ user { ++ label = "user"; ++ linux,code = <BTN_0>; ++ gpios = <&pio 0 GPIO_ACTIVE_LOW>; ++ }; ++ ++ reset { ++ label = "reset"; ++ linux,code = <KEY_RESTART>; ++ gpios = <&pio 1 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++ ++ pwm-leds { ++ compatible = "pwm-leds"; ++ ++ led_status_white: led-0 { ++ color = <LED_COLOR_ID_WHITE>; ++ function = LED_FUNCTION_STATUS; ++ pwms = <&pwm 0 10000>; ++ linux,default-trigger = "pattern"; ++ led-pattern = <0 500 25 500>; ++ }; ++ ++ led_status_green: led-1 { ++ color = <LED_COLOR_ID_GREEN>; ++ function = LED_FUNCTION_STATUS; ++ pwms = <&pwm 1 10000>; ++ }; ++ }; ++ ++ gpio-leds { ++ compatible = "gpio-leds"; ++ ++ led_status_red: led-0 { ++ color = <LED_COLOR_ID_RED>; ++ function = LED_FUNCTION_STATUS; ++ gpios = <&pio 9 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ led-1 { ++ function = LED_FUNCTION_LAN; ++ color = <LED_COLOR_ID_AMBER>; ++ gpios = <&pio 34 GPIO_ACTIVE_LOW>; ++ }; ++ ++ led-2 { ++ function = LED_FUNCTION_LAN; ++ color = <LED_COLOR_ID_GREEN>; ++ gpios = <&pio 35 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++ ++ gpio-export { ++ compatible = "gpio-export"; ++ ++ gpio-0 { ++ gpio-export,name = "mikrobus-reset"; ++ gpio-export,output = <1>; ++ gpios = <&pio 2 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ gpio-1 { ++ gpio-export,name = "watchdog-enable"; ++ gpio-export,output = <1>; ++ gpios = <&pio 11 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ gpio-2 { ++ gpio-export,name = "usb-enable"; ++ gpio-export,output = <1>; ++ gpios = <&pio 14 GPIO_ACTIVE_HIGH>; ++ }; ++ }; ++ ++ gpio-watchdog { ++ compatible = "linux,wdt-gpio"; ++ gpios = <&pio 8 GPIO_ACTIVE_LOW>; ++ hw_algo = "toggle"; ++ hw_margin_ms = <25000>; ++ always-running; ++ }; ++}; ++ ++ð { ++ status = "okay"; ++ ++ gmac0: mac@0 { ++ compatible = "mediatek,eth-mac"; ++ reg = <0>; ++ phy-handle = <&phy15>; ++ phy-mode = "2500base-x"; ++ nvmem-cell-names = "mac-address"; ++ nvmem-cells = <&macaddr_factory_24>; ++ }; ++ ++ gmac1: mac@1 { ++ compatible = "mediatek,eth-mac"; ++ reg = <1>; ++ phy-mode = "gmii"; ++ phy-handle = <&int_gbe_phy>; ++ }; ++}; ++ ++&mdio_bus { ++ phy15: phy@f { ++ reg = <0xf>; ++ ++ airoha,pnswap-rx; ++ ++ interrupt-parent = <&pio>; ++ interrupts = <38 IRQ_TYPE_EDGE_FALLING>; ++ reset-gpios = <&pio 39 GPIO_ACTIVE_LOW>; ++ reset-assert-us = <10000>; ++ reset-deassert-us = <20000>; ++ ++ phy-mode = "2500base-x"; ++ full-duplex; ++ pause; ++ ++ leds { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ led@0 { ++ reg = <0>; ++ function = LED_FUNCTION_WAN; ++ color = <LED_COLOR_ID_AMBER>; ++ }; ++ ++ led@1 { ++ reg = <1>; ++ function = LED_FUNCTION_WAN; ++ color = <LED_COLOR_ID_GREEN>; ++ }; ++ }; ++ }; ++}; ++ ++&crypto { ++ status = "okay"; ++}; ++ ++&pio { ++ spi0_flash_pins: spi0-pins { ++ mux { ++ function = "spi"; ++ groups = "spi0", "spi0_wp_hold"; ++ }; ++ ++ conf-pu { ++ pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; ++ drive-strength = <MTK_DRIVE_8mA>; ++ bias-pull-up = <MTK_PUPD_SET_R1R0_11>; ++ }; ++ ++ conf-pd { ++ pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; ++ drive-strength = <MTK_DRIVE_8mA>; ++ bias-pull-down = <MTK_PUPD_SET_R1R0_11>; ++ }; ++ }; ++ ++ spi1_flash_pins: spi1-pins { ++ mux { ++ function = "spi"; ++ groups = "spi1_1"; ++ }; ++ ++ conf-pu { ++ pins = "SPI1_CS"; ++ drive-strength = <MTK_DRIVE_8mA>; ++ bias-pull-up = <MTK_PUPD_SET_R1R0_11>; ++ }; ++ ++ conf-pd { ++ pins = "SPI1_CLK", "SPI1_MOSI", "SPI1_MISO"; ++ drive-strength = <MTK_DRIVE_8mA>; ++ bias-pull-down = <MTK_PUPD_SET_R1R0_11>; ++ }; ++ }; ++ ++ spi2_flash_pins: spi2-pins { ++ mux { ++ function = "spi"; ++ groups = "spi2"; ++ }; ++ ++ conf-pu { ++ pins = "SPI2_CS", "SPI2_WP"; ++ drive-strength = <MTK_DRIVE_8mA>; ++ bias-pull-up = <MTK_PUPD_SET_R1R0_11>; ++ }; ++ ++ conf-pd { ++ pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; ++ drive-strength = <MTK_DRIVE_8mA>; ++ bias-pull-down = <MTK_PUPD_SET_R1R0_11>; ++ }; ++ }; ++ ++ i2c_pins: i2c-pins { ++ mux { ++ function = "i2c"; ++ groups = "i2c0_0"; ++ }; ++ }; ++ ++ uart2_pins: uart2-pins { ++ mux { ++ function = "uart"; ++ groups = "uart2_0_tx_rx"; ++ }; ++ }; ++ ++ pwm_pins: pwm-pins { ++ mux { ++ function = "pwm"; ++ groups = "pwm0_0", "pwm1_1"; ++ }; ++ }; ++ ++ pcie_pins: pcie-pins { ++ mux { ++ function = "pcie"; ++ groups = "pcie_pereset"; ++ }; ++ }; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pins>; ++ status = "okay"; ++}; ++ ++&i2c0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c_pins>; ++ status = "okay"; ++ ++ rtc@51 { ++ compatible = "nxp,pcf8563"; ++ reg = <0x51>; ++ }; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_flash_pins>; ++ cs-gpios = <0>, <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ flash@1 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "spi-nand"; ++ reg = <1>; ++ spi-max-frequency = <52000000>; ++ ++ 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>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "bl2"; ++ reg = <0x0 0x100000>; ++ read-only; ++ }; ++ ++ partition@580000 { ++ label = "ubi"; ++ reg = <0x100000 0xFF00000>; ++ compatible = "linux,ubi"; ++ ++ volumes { ++ ubi_fit_volume: ubi-volume-fit { ++ volname = "fit"; ++ }; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&spi1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_flash_pins>; ++ status = "okay"; ++}; ++ ++&spi2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi2_flash_pins>; ++ status = "okay"; ++ ++ flash@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ ++ spi-max-frequency = <52000000>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "bl2-nor"; ++ reg = <0x00000 0x40000>; ++ }; ++ ++ partition@40000 { ++ label = "factory"; ++ reg = <0x40000 0xc0000>; ++ read-only; ++ ++ nvmem-layout { ++ compatible = "fixed-layout"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ eeprom_factory_0: eeprom@0 { ++ reg = <0x0 0x1000>; ++ }; ++ ++ macaddr_factory_4: macaddr@4 { ++ reg = <0x4 0x6>; ++ compatible = "mac-base"; ++ #nvmem-cell-cells = <1>; ++ }; ++ ++ macaddr_factory_24: macaddr@24 { ++ reg = <0x24 0x6>; ++ compatible = "mac-base"; ++ }; ++ }; ++ }; ++ ++ partition@100000 { ++ label = "fip-nor"; ++ reg = <0x100000 0x80000>; ++ }; ++ ++ partition@180000 { ++ label = "recovery"; ++ reg = <0x180000 0xc80000>; ++ }; ++ }; ++ }; ++}; ++ ++&xhci { ++ phys = <&u2port0 PHY_TYPE_USB2>; ++ vusb33-supply = <®_3p3v>; ++ vbus-supply = <®_5v>; ++ mediatek,u3p-dis-msk = <0x01>; ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2_pins>; ++ status = "okay"; ++}; ++ ++&usb_phy { ++ status = "okay"; ++}; ++ ++&watchdog { ++ status = "okay"; ++}; ++ ++&wifi { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ nvmem-cells = <&eeprom_factory_0>; ++ nvmem-cell-names = "eeprom"; ++ status = "okay"; ++ ++ band@0 { ++ reg = <0>; ++ nvmem-cells = <&macaddr_factory_4 0>; ++ nvmem-cell-names = "mac-address"; ++ }; ++ ++ band@1 { ++ reg = <1>; ++ nvmem-cells = <&macaddr_factory_4 7>; ++ nvmem-cell-names = "mac-address"; ++ }; ++}; ++ ++&pcie { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pcie_pins>; ++ status = "okay"; ++}; ++ ++&sgmiisys0 { ++ /delete-node/ mediatek,pnswap; + }; +-- +2.51.0 + + +From faeb104b5fbd6383664672f1324d74eef55507b5 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Thu, 27 Oct 2022 23:39:52 +0200 +Subject: [PATCH 03/12] net: ethernet: mtk_eth_soc: compile out netsys v2 code + on mt7621 + +Avoid some branches in the hot path on low-end devices with limited CPU power, +and reduce code size + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +index 0168e2fbc619..2f89a06471e3 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1383,6 +1383,22 @@ struct mtk_mac { + /* the struct describing the SoC. these are declared in the soc_xyz.c files */ + extern const struct of_device_id of_mtk_match[]; + ++#ifdef CONFIG_SOC_MT7621 ++static inline bool mtk_is_netsys_v1(struct mtk_eth *eth) ++{ ++ return true; ++} ++ ++static inline bool mtk_is_netsys_v2_or_greater(struct mtk_eth *eth) ++{ ++ return false; ++} ++ ++static inline bool mtk_is_netsys_v3_or_greater(struct mtk_eth *eth) ++{ ++ return false; ++} ++#else + static inline bool mtk_is_netsys_v1(struct mtk_eth *eth) + { + return eth->soc->version == 1; +@@ -1397,6 +1413,7 @@ static inline bool mtk_is_netsys_v3_or_greater(struct mtk_eth *eth) + { + return eth->soc->version > 2; + } ++#endif + + static inline struct mtk_foe_entry * + mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash) +-- +2.51.0 + + +From 6e848e9de009eeafe0d970f0914c3d6441c843bb Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Thu, 3 Nov 2022 12:38:49 +0100 +Subject: [PATCH 04/12] net: ethernet: mtk_eth_soc: work around issue with + sending small fragments + +When lots of frames are sent with a number of very small fragments, an +internal FIFO can overflow, causing the DMA engine to lock up lock up and +transmit attempts time out. + +Fix this on MT7986 by increasing the reserved FIFO space. +Fix this on older chips by detecting the presence of small fragments and use +skb_gso_segment + skb_linearize to deal with them. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 39 +++++++++++++++++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +- + 2 files changed, 38 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index e68997a29191..2931376785f2 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -26,6 +26,7 @@ + #include <linux/bitfield.h> + #include <net/dsa.h> + #include <net/dst_metadata.h> ++#include <net/gso.h> + #include <net/page_pool/helpers.h> + #include <linux/genalloc.h> + +@@ -1752,12 +1753,28 @@ static void mtk_wake_queue(struct mtk_eth *eth) + } + } + ++static bool mtk_skb_has_small_frag(struct sk_buff *skb) ++{ ++ int min_size = 16; ++ int i; ++ ++ if (skb_headlen(skb) < min_size) ++ return true; ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) ++ if (skb_frag_size(&skb_shinfo(skb)->frags[i]) < min_size) ++ return true; ++ ++ return false; ++} ++ + static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) + { + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_ring *ring = ð->tx_ring; + struct net_device_stats *stats = &dev->stats; ++ struct sk_buff *segs, *next; + bool gso = false; + int tx_num; + +@@ -1786,6 +1803,18 @@ static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) + return NETDEV_TX_BUSY; + } + ++ if (mtk_is_netsys_v1(eth) && ++ skb_is_gso(skb) && mtk_skb_has_small_frag(skb)) { ++ segs = skb_gso_segment(skb, dev->features & ~NETIF_F_ALL_TSO); ++ if (IS_ERR(segs)) ++ goto drop; ++ ++ if (segs) { ++ consume_skb(skb); ++ skb = segs; ++ } ++ } ++ + /* TSO: fill MSS info in tcp checksum field */ + if (skb_is_gso(skb)) { + if (skb_cow_head(skb, 0)) { +@@ -1801,8 +1830,14 @@ static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) + } + } + +- if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) +- goto drop; ++ skb_list_walk_safe(skb, skb, next) { ++ if ((mtk_is_netsys_v1(eth) && ++ mtk_skb_has_small_frag(skb) && skb_linearize(skb)) || ++ mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) { ++ stats->tx_dropped++; ++ dev_kfree_skb_any(skb); ++ } ++ } + + if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) + netif_tx_stop_all_queues(dev); +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +index 2f89a06471e3..ee7bb3b7b93a 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -280,7 +280,7 @@ + #define MTK_CHK_DDONE_EN BIT(28) + #define MTK_DMAD_WR_WDONE BIT(26) + #define MTK_WCOMP_EN BIT(24) +-#define MTK_RESV_BUF (0x40 << 16) ++#define MTK_RESV_BUF (0x80 << 16) + #define MTK_MUTLI_CNT (0x4 << 12) + #define MTK_LEAKY_BUCKET_EN BIT(11) + +-- +2.51.0 + + +From 0cdfa5eb67e05aeb2b35960db1b797a945902091 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Mon, 14 Jul 2025 10:52:59 +0200 +Subject: [PATCH 05/12] net: ethernet: mtk_eth_soc: shrink struct mtk_tx_buf + +There is no need to track the difference between dma_map_page +and dma_map_single, since they're unmapped in exactly the same way. +Also reorder fields in order to avoid padding. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 42 ++++++--------------- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 13 +------ + 2 files changed, 14 insertions(+), 41 deletions(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index 2931376785f2..3b81ea22bbea 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1386,32 +1386,19 @@ static int txd_to_idx(struct mtk_tx_ring *ring, void *dma, u32 txd_size) + static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, + struct xdp_frame_bulk *bq, bool napi) + { +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { +- if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) { +- dma_unmap_single(eth->dma_dev, +- dma_unmap_addr(tx_buf, dma_addr0), +- dma_unmap_len(tx_buf, dma_len0), +- DMA_TO_DEVICE); +- } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) { +- dma_unmap_page(eth->dma_dev, +- dma_unmap_addr(tx_buf, dma_addr0), +- dma_unmap_len(tx_buf, dma_len0), +- DMA_TO_DEVICE); +- } +- } else { +- if (dma_unmap_len(tx_buf, dma_len0)) { +- dma_unmap_page(eth->dma_dev, +- dma_unmap_addr(tx_buf, dma_addr0), +- dma_unmap_len(tx_buf, dma_len0), +- DMA_TO_DEVICE); +- } ++ if (dma_unmap_len(tx_buf, dma_len0)) { ++ dma_unmap_page(eth->dma_dev, ++ dma_unmap_addr(tx_buf, dma_addr0), ++ dma_unmap_len(tx_buf, dma_len0), ++ DMA_TO_DEVICE); ++ } + +- if (dma_unmap_len(tx_buf, dma_len1)) { +- dma_unmap_page(eth->dma_dev, +- dma_unmap_addr(tx_buf, dma_addr1), +- dma_unmap_len(tx_buf, dma_len1), +- DMA_TO_DEVICE); +- } ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) && ++ dma_unmap_len(tx_buf, dma_len1)) { ++ dma_unmap_page(eth->dma_dev, ++ dma_unmap_addr(tx_buf, dma_addr1), ++ dma_unmap_len(tx_buf, dma_len1), ++ DMA_TO_DEVICE); + } + + if (tx_buf->data && tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { +@@ -1433,7 +1420,6 @@ static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, + xdp_return_frame(xdpf); + } + } +- tx_buf->flags = 0; + tx_buf->data = NULL; + } + +@@ -1598,7 +1584,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, + + mtk_tx_set_dma_desc(dev, itxd, &txd_info); + +- itx_buf->flags |= MTK_TX_FLAGS_SINGLE0; + itx_buf->mac_id = mac->id; + setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size, + k++); +@@ -1646,7 +1631,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, + if (new_desc) + memset(tx_buf, 0, sizeof(*tx_buf)); + tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; +- tx_buf->flags |= MTK_TX_FLAGS_PAGE0; + tx_buf->mac_id = mac->id; + + setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, +@@ -1979,8 +1963,6 @@ static int mtk_xdp_frame_map(struct mtk_eth *eth, struct net_device *dev, + txd_info->size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, txd_info->addr))) + return -ENOMEM; +- +- tx_buf->flags |= MTK_TX_FLAGS_SINGLE0; + } else { + struct page *page = virt_to_head_page(data); + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +index ee7bb3b7b93a..19381abcdfc6 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -728,14 +728,6 @@ struct mtk_hw_stats { + struct u64_stats_sync syncp; + }; + +-enum mtk_tx_flags { +- /* PDMA descriptor can point at 1-2 segments. This enum allows us to +- * track how memory was allocated so that it can be freed properly. +- */ +- MTK_TX_FLAGS_SINGLE0 = 0x01, +- MTK_TX_FLAGS_PAGE0 = 0x02, +-}; +- + /* This enum allows us to identify how the clock is defined on the array of the + * clock in the order + */ +@@ -908,13 +900,12 @@ enum mtk_tx_buf_type { + */ + struct mtk_tx_buf { + enum mtk_tx_buf_type type; ++ u16 mac_id; + void *data; + +- u16 mac_id; +- u16 flags; + DEFINE_DMA_UNMAP_ADDR(dma_addr0); +- DEFINE_DMA_UNMAP_LEN(dma_len0); + DEFINE_DMA_UNMAP_ADDR(dma_addr1); ++ DEFINE_DMA_UNMAP_LEN(dma_len0); + DEFINE_DMA_UNMAP_LEN(dma_len1); + }; + +-- +2.51.0 + + +From 498cd003f1c801ad2e11f51dddf9aa075f412477 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Thu, 22 Aug 2024 18:02:17 +0200 +Subject: [PATCH 06/12] net: bridge: fix switchdev host mdb entry updates + +When a mdb entry is removed, the bridge switchdev code can issue a +switchdev_port_obj_del call for a port that was not offloaded. + +This leads to an imbalance in switchdev_port_obj_add/del calls, since +br_switchdev_mdb_replay has not been called for the port before. + +This can lead to potential multicast forwarding issues and messages such as: +mt7915e 0000:01:00.0 wl1-ap0: Failed to del Host Multicast Database entry + (object id=3) with error: -ENOENT (-2). + +Fix this issue by checking the port offload status when iterating over +lower devs. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/bridge/br_switchdev.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c +index fe3f7bbe86ee..b898e1a4b5ff 100644 +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -576,10 +576,18 @@ static void br_switchdev_host_mdb(struct net_device *dev, + struct net_bridge_mdb_entry *mp, int type) + { + struct net_device *lower_dev; ++ struct net_bridge_port *port; + struct list_head *iter; + +- netdev_for_each_lower_dev(dev, lower_dev, iter) ++ rcu_read_lock(); ++ netdev_for_each_lower_dev(dev, lower_dev, iter) { ++ port = br_port_get_rcu(lower_dev); ++ if (!port || !port->offload_count) ++ continue; ++ + br_switchdev_host_mdb_one(dev, lower_dev, mp, type); ++ } ++ rcu_read_unlock(); + } + + static int +-- +2.51.0 + + +From 96b0666b743311a15d8e01016462d7cf57764f62 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Mon, 21 Mar 2022 20:39:59 +0100 +Subject: [PATCH 07/12] net: ethernet: mtk_eth_soc: enable threaded NAPI + +This can improve performance under load by ensuring that NAPI processing is +not pinned on CPU 0. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index 3b81ea22bbea..a5586bd06490 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -5306,6 +5306,8 @@ static int mtk_probe(struct platform_device *pdev) + dev_err(eth->dev, "failed to allocated dummy device\n"); + goto err_unreg_netdev; + } ++ eth->dummy_dev->threaded = 1; ++ strcpy(eth->dummy_dev->name, "mtk_eth"); + netif_napi_add(eth->dummy_dev, ð->tx_napi, mtk_napi_tx); + netif_napi_add(eth->dummy_dev, ð->rx_napi, mtk_napi_rx); + +-- +2.51.0 + + +From 0666439429f6983cde79712125d76e21fea034f8 Mon Sep 17 00:00:00 2001 +From: Chad Monroe <chad@monroe.io> +Date: Mon, 16 Sep 2024 19:29:03 -0700 +Subject: [PATCH 08/12] net: ethernet: mediatek: increase QDMA RESV_BUF size + +Increase QDMA RESV_BUF from 2K to 3K for netsys v2 to match Mediatek SDK[1]. +This helps reduce the possibility of Ethernet transmit timeouts. + +[1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/19d8456c3051e5f6dabf42fa770916a2126ea4bf + +Signed-off-by: Chad Monroe <chad@monroe.io> +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 6 ++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 + + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index a5586bd06490..9d6f395f73dc 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3487,12 +3487,14 @@ static int mtk_start_dma(struct mtk_eth *eth) + MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | + MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE; + +- if (mtk_is_netsys_v2_or_greater(eth)) ++ if (mtk_is_netsys_v2_or_greater(eth)) { ++ val &= ~MTK_RESV_BUF_MASK; + val |= MTK_MUTLI_CNT | MTK_RESV_BUF | + MTK_WCOMP_EN | MTK_DMAD_WR_WDONE | + MTK_CHK_DDONE_EN; +- else ++ } else { + val |= MTK_RX_BT_32DWORDS; ++ } + mtk_w32(eth, val, reg_map->qdma.glo_cfg); + + mtk_w32(eth, +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +index 19381abcdfc6..b9da762fcc70 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -282,6 +282,7 @@ + #define MTK_WCOMP_EN BIT(24) + #define MTK_RESV_BUF (0x80 << 16) + #define MTK_MUTLI_CNT (0x4 << 12) ++#define MTK_RESV_BUF_MASK (0xff << 16) + #define MTK_LEAKY_BUCKET_EN BIT(11) + + /* QDMA Flow Control Register */ +-- +2.51.0 + + +From 80b5de03406650271a4a5fafb06d06dbe4fd8846 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Thu, 23 Mar 2023 10:24:11 +0100 +Subject: [PATCH 09/12] net: ethernet: mtk_eth_soc: improve keeping track of + offloaded flows + +Unify tracking of L2 and L3 flows. Use the generic list field in struct +mtk_foe_entry for tracking L2 subflows. Preparation for improving +flow accounting support. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 162 ++++++++++++------------ + drivers/net/ethernet/mediatek/mtk_ppe.h | 15 +-- + 2 files changed, 86 insertions(+), 91 deletions(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c +index ada852adc5f7..5b352ab64ef7 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -479,42 +479,43 @@ int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry, + return 0; + } + ++static int ++mtk_flow_entry_match_len(struct mtk_eth *eth, struct mtk_foe_entry *entry) ++{ ++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1); ++ ++ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) ++ return offsetof(struct mtk_foe_entry, ipv6._rsv); ++ else ++ return offsetof(struct mtk_foe_entry, ipv4.ib2); ++} ++ + static bool + mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry, +- struct mtk_foe_entry *data) ++ struct mtk_foe_entry *data, int len) + { +- int type, len; +- + if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP) + return false; + +- type = mtk_get_ib1_pkt_type(eth, entry->data.ib1); +- if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) +- len = offsetof(struct mtk_foe_entry, ipv6._rsv); +- else +- len = offsetof(struct mtk_foe_entry, ipv4.ib2); +- + return !memcmp(&entry->data.data, &data->data, len - 4); + } + + static void +-__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ bool set_state) + { +- struct hlist_head *head; + struct hlist_node *tmp; + + if (entry->type == MTK_FLOW_TYPE_L2) { + rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node, + mtk_flow_l2_ht_params); + +- head = &entry->l2_flows; +- hlist_for_each_entry_safe(entry, tmp, head, l2_data.list) +- __mtk_foe_entry_clear(ppe, entry); ++ hlist_for_each_entry_safe(entry, tmp, &entry->l2_flows, l2_list) ++ __mtk_foe_entry_clear(ppe, entry, set_state); + return; + } + +- hlist_del_init(&entry->list); +- if (entry->hash != 0xffff) { ++ if (entry->hash != 0xffff && set_state) { + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); + + hwe->ib1 &= ~MTK_FOE_IB1_STATE; +@@ -535,7 +536,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) + return; + +- hlist_del_init(&entry->l2_data.list); ++ hlist_del_init(&entry->l2_list); ++ hlist_del_init(&entry->list); + kfree(entry); + } + +@@ -551,68 +553,57 @@ static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1) + return now - timestamp; + } + ++static bool ++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ struct mtk_foe_entry foe = {}; ++ struct mtk_foe_entry *hwe; ++ u16 hash = entry->hash; ++ int len; ++ ++ if (hash == 0xffff) ++ return false; ++ ++ hwe = mtk_foe_get_entry(ppe, hash); ++ len = mtk_flow_entry_match_len(ppe->eth, &entry->data); ++ memcpy(&foe, hwe, len); ++ ++ if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || ++ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) ++ return false; ++ ++ entry->data.ib1 = foe.ib1; ++ ++ return true; ++} ++ + static void + mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); + struct mtk_flow_entry *cur; +- struct mtk_foe_entry *hwe; + struct hlist_node *tmp; + int idle; + + idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); +- hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) { ++ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { + int cur_idle; +- u32 ib1; +- +- hwe = mtk_foe_get_entry(ppe, cur->hash); +- ib1 = READ_ONCE(hwe->ib1); + +- if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) { +- cur->hash = 0xffff; +- __mtk_foe_entry_clear(ppe, cur); ++ if (!mtk_flow_entry_update(ppe, cur)) { ++ __mtk_foe_entry_clear(ppe, entry, false); + continue; + } + +- cur_idle = __mtk_foe_entry_idle_time(ppe, ib1); ++ cur_idle = __mtk_foe_entry_idle_time(ppe, cur->data.ib1); + if (cur_idle >= idle) + continue; + + idle = cur_idle; + entry->data.ib1 &= ~ib1_ts_mask; +- entry->data.ib1 |= ib1 & ib1_ts_mask; ++ entry->data.ib1 |= cur->data.ib1 & ib1_ts_mask; + } + } + +-static void +-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) +-{ +- struct mtk_foe_entry foe = {}; +- struct mtk_foe_entry *hwe; +- +- spin_lock_bh(&ppe_lock); +- +- if (entry->type == MTK_FLOW_TYPE_L2) { +- mtk_flow_entry_update_l2(ppe, entry); +- goto out; +- } +- +- if (entry->hash == 0xffff) +- goto out; +- +- hwe = mtk_foe_get_entry(ppe, entry->hash); +- memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size); +- if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) { +- entry->hash = 0xffff; +- goto out; +- } +- +- entry->data.ib1 = foe.ib1; +- +-out: +- spin_unlock_bh(&ppe_lock); +-} +- + static void + __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, + u16 hash) +@@ -653,7 +644,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, + void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + spin_lock_bh(&ppe_lock); +- __mtk_foe_entry_clear(ppe, entry); ++ __mtk_foe_entry_clear(ppe, entry, true); ++ hlist_del_init(&entry->list); + spin_unlock_bh(&ppe_lock); + } + +@@ -700,8 +692,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, + { + const struct mtk_soc_data *soc = ppe->eth->soc; + struct mtk_flow_entry *flow_info; +- struct mtk_foe_entry foe = {}, *hwe; + struct mtk_foe_mac_info *l2; ++ struct mtk_foe_entry *hwe; + u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; + int type; + +@@ -709,30 +701,30 @@ mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, + if (!flow_info) + return; + +- flow_info->l2_data.base_flow = entry; + flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW; + flow_info->hash = hash; + hlist_add_head(&flow_info->list, + &ppe->foe_flow[hash / soc->hash_offset]); +- hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); ++ hlist_add_head(&flow_info->l2_list, &entry->l2_flows); + + hwe = mtk_foe_get_entry(ppe, hash); +- memcpy(&foe, hwe, soc->foe_entry_size); +- foe.ib1 &= ib1_mask; +- foe.ib1 |= entry->data.ib1 & ~ib1_mask; ++ memcpy(&flow_info->data, hwe, soc->foe_entry_size); ++ flow_info->data.ib1 &= ib1_mask; ++ flow_info->data.ib1 |= entry->data.ib1 & ~ib1_mask; + +- l2 = mtk_foe_entry_l2(ppe->eth, &foe); ++ l2 = mtk_foe_entry_l2(ppe->eth, &flow_info->data); + memcpy(l2, &entry->data.bridge.l2, sizeof(*l2)); + +- type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1); ++ type = mtk_get_ib1_pkt_type(ppe->eth, flow_info->data.ib1); + if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT) +- memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new)); ++ memcpy(&flow_info->data.ipv4.new, &flow_info->data.ipv4.orig, ++ sizeof(flow_info->data.ipv4.new)); + else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP) + l2->etype = ETH_P_IPV6; + +- *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2; ++ *mtk_foe_entry_ib2(ppe->eth, &flow_info->data) = entry->data.bridge.ib2; + +- __mtk_foe_entry_commit(ppe, &foe, hash); ++ __mtk_foe_entry_commit(ppe, &flow_info->data, hash); + } + + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) +@@ -742,9 +734,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); + struct mtk_flow_entry *entry; + struct mtk_foe_bridge key = {}; ++ struct mtk_foe_entry foe = {}; + struct hlist_node *n; + struct ethhdr *eh; + bool found = false; ++ int entry_len; + u8 *tag; + + spin_lock_bh(&ppe_lock); +@@ -752,20 +746,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) + if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) + goto out; + +- hlist_for_each_entry_safe(entry, n, head, list) { +- if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) { +- if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == +- MTK_FOE_STATE_BIND)) +- continue; +- +- entry->hash = 0xffff; +- __mtk_foe_entry_clear(ppe, entry); +- continue; +- } ++ entry_len = mtk_flow_entry_match_len(ppe->eth, hwe); ++ memcpy(&foe, hwe, entry_len); + +- if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) { ++ hlist_for_each_entry_safe(entry, n, head, list) { ++ if (found || ++ !mtk_flow_entry_match(ppe->eth, entry, &foe, entry_len)) { + if (entry->hash != 0xffff) +- entry->hash = 0xffff; ++ __mtk_foe_entry_clear(ppe, entry, false); + continue; + } + +@@ -816,9 +804,17 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) + + int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { +- mtk_flow_entry_update(ppe, entry); ++ int idle; ++ ++ spin_lock_bh(&ppe_lock); ++ if (entry->type == MTK_FLOW_TYPE_L2) ++ mtk_flow_entry_update_l2(ppe, entry); ++ else ++ mtk_flow_entry_update(ppe, entry); ++ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ spin_unlock_bh(&ppe_lock); + +- return __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ return idle; + } + + int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h +index 223f709e2704..aa0799c1ce95 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -286,7 +286,12 @@ enum { + + struct mtk_flow_entry { + union { +- struct hlist_node list; ++ /* regular flows + L2 subflows */ ++ struct { ++ struct hlist_node list; ++ struct hlist_node l2_list; ++ }; ++ /* L2 flows */ + struct { + struct rhash_head l2_node; + struct hlist_head l2_flows; +@@ -296,13 +301,7 @@ struct mtk_flow_entry { + s8 wed_index; + u8 ppe_index; + u16 hash; +- union { +- struct mtk_foe_entry data; +- struct { +- struct mtk_flow_entry *base_flow; +- struct hlist_node list; +- } l2_data; +- }; ++ struct mtk_foe_entry data; + struct rhash_head node; + unsigned long cookie; + }; +-- +2.51.0 + + +From ecc4b72e231898cf8e61dd3dac99b8a36224cd41 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Thu, 23 Mar 2023 11:05:22 +0100 +Subject: [PATCH 10/12] net: ethernet: mediatek: fix ppe flow accounting for L2 + flows + +For L2 flows, the packet/byte counters should report the sum of the +counters of their subflows, both current and expired. +In order to make this work, change the way that accounting data is tracked. +Reset counters when a flow enters bind. Once it expires (or enters unbind), +store the last counter value in struct mtk_flow_entry. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 140 ++++++++++-------- + drivers/net/ethernet/mediatek/mtk_ppe.h | 8 +- + .../net/ethernet/mediatek/mtk_ppe_debugfs.c | 2 +- + .../net/ethernet/mediatek/mtk_ppe_offload.c | 17 +-- + 4 files changed, 89 insertions(+), 78 deletions(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c +index 5b352ab64ef7..96365a75b198 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -83,9 +83,9 @@ static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe) + int ret; + u32 val; + +- ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val, +- !(val & MTK_PPE_MIB_SER_CR_ST), +- 20, MTK_PPE_WAIT_TIMEOUT_US); ++ ret = readl_poll_timeout_atomic(ppe->base + MTK_PPE_MIB_SER_CR, val, ++ !(val & MTK_PPE_MIB_SER_CR_ST), ++ 20, MTK_PPE_WAIT_TIMEOUT_US); + + if (ret) + dev_err(ppe->dev, "MIB table busy"); +@@ -93,17 +93,31 @@ static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe) + return ret; + } + +-static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets) ++static inline struct mtk_foe_accounting * ++mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index) ++{ ++ if (!ppe->acct_table) ++ return NULL; ++ ++ return ppe->acct_table + index * sizeof(struct mtk_foe_accounting); ++} ++ ++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index) + { + u32 val, cnt_r0, cnt_r1, cnt_r2; ++ struct mtk_foe_accounting *acct; + int ret; + + val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST; + ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val); + ++ acct = mtk_ppe_acct_data(ppe, index); ++ if (!acct) ++ return NULL; ++ + ret = mtk_ppe_mib_wait_busy(ppe); + if (ret) +- return ret; ++ return acct; + + cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0); + cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1); +@@ -112,19 +126,19 @@ static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *p + if (mtk_is_netsys_v3_or_greater(ppe->eth)) { + /* 64 bit for each counter */ + u32 cnt_r3 = readl(ppe->base + MTK_PPE_MIB_SER_R3); +- *bytes = ((u64)cnt_r1 << 32) | cnt_r0; +- *packets = ((u64)cnt_r3 << 32) | cnt_r2; ++ acct->bytes += ((u64)cnt_r1 << 32) | cnt_r0; ++ acct->packets += ((u64)cnt_r3 << 32) | cnt_r2; + } else { + /* 48 bit byte counter, 40 bit packet counter */ + u32 byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0); + u32 byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1); + u32 pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1); + u32 pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2); +- *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low; +- *packets = ((u64)pkt_cnt_high << 16) | pkt_cnt_low; ++ acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low; ++ acct->packets += ((u64)pkt_cnt_high << 16) | pkt_cnt_low; + } + +- return 0; ++ return acct; + } + + static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) +@@ -522,14 +536,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, + hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); + dma_wmb(); + mtk_ppe_cache_clear(ppe); +- +- if (ppe->accounting) { +- struct mtk_foe_accounting *acct; +- +- acct = ppe->acct_table + entry->hash * sizeof(*acct); +- acct->packets = 0; +- acct->bytes = 0; +- } + } + entry->hash = 0xffff; + +@@ -554,11 +560,14 @@ static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1) + } + + static bool +-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ u64 *packets, u64 *bytes) + { ++ struct mtk_foe_accounting *acct; + struct mtk_foe_entry foe = {}; + struct mtk_foe_entry *hwe; + u16 hash = entry->hash; ++ bool ret = false; + int len; + + if (hash == 0xffff) +@@ -569,18 +578,35 @@ mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + memcpy(&foe, hwe, len); + + if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || +- FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) +- return false; ++ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) { ++ acct = mtk_ppe_acct_data(ppe, hash); ++ if (acct) { ++ entry->prev_packets += acct->packets; ++ entry->prev_bytes += acct->bytes; ++ } ++ ++ goto out; ++ } + + entry->data.ib1 = foe.ib1; ++ acct = mtk_ppe_mib_entry_read(ppe, hash); ++ ret = true; ++ ++out: ++ if (acct) { ++ *packets += acct->packets; ++ *bytes += acct->bytes; ++ } + +- return true; ++ return ret; + } + + static void + mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); ++ u64 *packets = &entry->packets; ++ u64 *bytes = &entry->bytes; + struct mtk_flow_entry *cur; + struct hlist_node *tmp; + int idle; +@@ -589,7 +615,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { + int cur_idle; + +- if (!mtk_flow_entry_update(ppe, cur)) { ++ if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) { ++ entry->prev_packets += cur->prev_packets; ++ entry->prev_bytes += cur->prev_bytes; + __mtk_foe_entry_clear(ppe, entry, false); + continue; + } +@@ -604,10 +632,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + } + } + ++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ int *idle) ++{ ++ entry->packets = entry->prev_packets; ++ entry->bytes = entry->prev_bytes; ++ ++ spin_lock_bh(&ppe_lock); ++ ++ if (entry->type == MTK_FLOW_TYPE_L2) ++ mtk_flow_entry_update_l2(ppe, entry); ++ else ++ mtk_flow_entry_update(ppe, entry, &entry->packets, &entry->bytes); ++ ++ *idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ ++ spin_unlock_bh(&ppe_lock); ++} ++ + static void + __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, + u16 hash) + { ++ struct mtk_foe_accounting *acct; + struct mtk_eth *eth = ppe->eth; + u16 timestamp = mtk_eth_timestamp(eth); + struct mtk_foe_entry *hwe; +@@ -638,6 +685,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, + + dma_wmb(); + ++ acct = mtk_ppe_mib_entry_read(ppe, hash); ++ if (acct) { ++ acct->packets = 0; ++ acct->bytes = 0; ++ } ++ + mtk_ppe_cache_clear(ppe); + } + +@@ -802,21 +855,6 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) + spin_unlock_bh(&ppe_lock); + } + +-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) +-{ +- int idle; +- +- spin_lock_bh(&ppe_lock); +- if (entry->type == MTK_FLOW_TYPE_L2) +- mtk_flow_entry_update_l2(ppe, entry); +- else +- mtk_flow_entry_update(ppe, entry); +- idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); +- spin_unlock_bh(&ppe_lock); +- +- return idle; +-} +- + int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) + { + if (!ppe) +@@ -844,32 +882,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) + return mtk_ppe_wait_busy(ppe); + } + +-struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, +- struct mtk_foe_accounting *diff) +-{ +- struct mtk_foe_accounting *acct; +- int size = sizeof(struct mtk_foe_accounting); +- u64 bytes, packets; +- +- if (!ppe->accounting) +- return NULL; +- +- if (mtk_mib_entry_read(ppe, index, &bytes, &packets)) +- return NULL; +- +- acct = ppe->acct_table + index * size; +- +- acct->bytes += bytes; +- acct->packets += packets; +- +- if (diff) { +- diff->bytes = bytes; +- diff->packets = packets; +- } +- +- return acct; +-} +- + struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) + { + bool accounting = eth->soc->has_accounting; +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h +index aa0799c1ce95..9a4990aa5fa5 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -304,6 +304,8 @@ struct mtk_flow_entry { + struct mtk_foe_entry data; + struct rhash_head node; + unsigned long cookie; ++ u64 prev_packets, prev_bytes; ++ u64 packets, bytes; + }; + + struct mtk_mib_entry { +@@ -348,6 +350,7 @@ void mtk_ppe_deinit(struct mtk_eth *eth); + void mtk_ppe_start(struct mtk_ppe *ppe); + int mtk_ppe_stop(struct mtk_ppe *ppe); + int mtk_ppe_prepare_reset(struct mtk_ppe *ppe); ++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index); + + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); + +@@ -397,9 +400,8 @@ int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry, + unsigned int queue); + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); +-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index); +-struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, +- struct mtk_foe_accounting *diff); ++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ int *idle); + + #endif +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c +index 570ebf91f693..9259d0925b88 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c +@@ -97,7 +97,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind) + if (bind && state != MTK_FOE_STATE_BIND) + continue; + +- acct = mtk_foe_entry_get_mib(ppe, i, NULL); ++ acct = mtk_ppe_mib_entry_read(ppe, i); + + type = mtk_get_ib1_pkt_type(ppe->eth, entry->ib1); + seq_printf(m, "%05x %s %7s", i, +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +index e9bd32741983..af8e0107fb93 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -522,24 +522,21 @@ static int + mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) + { + struct mtk_flow_entry *entry; +- struct mtk_foe_accounting diff; +- u32 idle; ++ u64 packets, bytes; ++ int idle; + + entry = rhashtable_lookup(ð->flow_table, &f->cookie, + mtk_flow_ht_params); + if (!entry) + return -ENOENT; + +- idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry); ++ packets = entry->packets; ++ bytes = entry->bytes; ++ mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle); ++ f->stats.pkts += entry->packets - packets; ++ f->stats.bytes += entry->bytes - bytes; + f->stats.lastused = jiffies - idle * HZ; + +- if (entry->hash != 0xFFFF && +- mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash, +- &diff)) { +- f->stats.pkts += diff.packets; +- f->stats.bytes += diff.bytes; +- } +- + return 0; + } + +-- +2.51.0 + + +From 0d9be0a839c47582a77b1c63164b2e93a48973ac Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 12 Sep 2025 14:18:14 +0200 +Subject: [PATCH 11/12] net: ethernet: mtk_eth_soc: zero initialize PPE flow + table + +Avoid picking up flows from last boot or other invalid data + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c +index 96365a75b198..cb08d132bad9 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -914,6 +914,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) + if (!foe) + goto err_free_l2_flows; + ++ memset(foe, 0, MTK_PPE_ENTRIES * soc->foe_entry_size); + ppe->foe_table = foe; + + foe_flow_size = (MTK_PPE_ENTRIES / soc->hash_offset) * +@@ -928,6 +929,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) + if (!mib) + return NULL; + ++ memset(mib, 0, MTK_PPE_ENTRIES * sizeof(*mib)); + ppe->mib_table = mib; + + acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct), +-- +2.51.0 + + +From 3b613fe4850f9bbb32417ef3c2c48c5f65f0111d Mon Sep 17 00:00:00 2001 +From: Bo-Cun Chen <bc-bocun.chen@mediatek.com> +Date: Wed, 27 Nov 2024 13:36:49 +0800 +Subject: [PATCH 12/12] net: ethernet: mtk_eth_soc: add hw dump for forced + reset + +Without this patch, the ETH driver is unable to dump the registers +before triggering a forced reset. + +Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com> +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 55 +++++++++++++++++++++ + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 + + 2 files changed, 56 insertions(+) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index 9d6f395f73dc..110dac8248df 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -65,6 +65,7 @@ static const struct mtk_reg_map mtk_reg_map = { + .rx_ptr = 0x1900, + .rx_cnt_cfg = 0x1904, + .qcrx_ptr = 0x1908, ++ .page = 0x19f0, + .glo_cfg = 0x1a04, + .rst_idx = 0x1a08, + .delay_irq = 0x1a0c, +@@ -131,6 +132,7 @@ static const struct mtk_reg_map mt7986_reg_map = { + .rx_ptr = 0x4500, + .rx_cnt_cfg = 0x4504, + .qcrx_ptr = 0x4508, ++ .page = 0x45f0, + .glo_cfg = 0x4604, + .rst_idx = 0x4608, + .delay_irq = 0x460c, +@@ -182,6 +184,7 @@ static const struct mtk_reg_map mt7988_reg_map = { + .rx_ptr = 0x4500, + .rx_cnt_cfg = 0x4504, + .qcrx_ptr = 0x4508, ++ .page = 0x45f0, + .glo_cfg = 0x4604, + .rst_idx = 0x4608, + .delay_irq = 0x460c, +@@ -3913,6 +3916,56 @@ static void mtk_set_mcr_max_rx(struct mtk_mac *mac, u32 val) + mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); + } + ++static void mtk_hw_dump_reg(struct mtk_eth *eth, char *name, u32 offset, u32 range) ++{ ++ u32 cur = offset; ++ ++ pr_info("\n==================== %s ====================\n", name); ++ while (cur < offset + range) { ++ pr_info("0x%08x: %08x %08x %08x %08x\n", ++ cur, mtk_r32(eth, cur), mtk_r32(eth, cur + 0x4), ++ mtk_r32(eth, cur + 0x8), mtk_r32(eth, cur + 0xc)); ++ cur += 0x10; ++ } ++} ++ ++static void mtk_hw_dump(struct mtk_eth *eth) ++{ ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; ++ u32 id; ++ ++ mtk_hw_dump_reg(eth, "FE", 0x0, 0x600); ++ mtk_hw_dump_reg(eth, "FE", 0x1400, 0x300); ++ mtk_hw_dump_reg(eth, "ADMA", reg_map->pdma.rx_ptr, 0x300); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { ++ for (id = 0; id < MTK_QDMA_NUM_QUEUES / 16; id++) { ++ mtk_w32(eth, id, reg_map->qdma.page); ++ pr_info("\nQDMA PAGE:%x ", mtk_r32(eth, reg_map->qdma.page)); ++ mtk_hw_dump_reg(eth, "QDMA", reg_map->qdma.qtx_cfg, 0x100); ++ mtk_w32(eth, 0, reg_map->qdma.page); ++ } ++ mtk_hw_dump_reg(eth, "QDMA", reg_map->qdma.rx_ptr, 0x300); ++ } ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { ++ mtk_hw_dump_reg(eth, "WDMA0", reg_map->wdma_base[0], 0x400); ++ mtk_hw_dump_reg(eth, "WDMA1", reg_map->wdma_base[1], 0x400); ++ if (mtk_is_netsys_v3_or_greater(eth)) ++ mtk_hw_dump_reg(eth, "WDMA2", reg_map->wdma_base[2], 0x400); ++ } ++ mtk_hw_dump_reg(eth, "PPE0", reg_map->ppe_base + 0x200, 0x200); ++ if (!mtk_is_netsys_v1(eth)) ++ mtk_hw_dump_reg(eth, "PPE1", reg_map->ppe_base + 0x600, 0x200); ++ if (mtk_is_netsys_v3_or_greater(eth)) ++ mtk_hw_dump_reg(eth, "PPE2", reg_map->ppe_base + 0xE00, 0x200); ++ mtk_hw_dump_reg(eth, "GMAC", 0x10000, 0x300); ++ if (mtk_is_netsys_v3_or_greater(eth)) ++ mtk_hw_dump_reg(eth, "GMAC", 0x10300, 0x100); ++ if (mtk_is_netsys_v3_or_greater(eth)) { ++ mtk_hw_dump_reg(eth, "XGMAC0", 0x12000, 0x300); ++ mtk_hw_dump_reg(eth, "XGMAC1", 0x13000, 0x300); ++ } ++} ++ + static void mtk_hw_reset(struct mtk_eth *eth) + { + u32 val; +@@ -4392,6 +4445,8 @@ static void mtk_pending_work(struct work_struct *work) + rtnl_lock(); + set_bit(MTK_RESETTING, ð->state); + ++ mtk_hw_dump(eth); ++ + mtk_prepare_for_reset(eth); + mtk_wed_fe_reset(); + /* Run again reset preliminary configuration in order to avoid any +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +index b9da762fcc70..64b94b21b493 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1152,6 +1152,7 @@ struct mtk_reg_map { + u32 rx_ptr; /* rx base pointer */ + u32 rx_cnt_cfg; /* rx max count configuration */ + u32 qcrx_ptr; /* rx cpu pointer */ ++ u32 page; /* page configuration */ + u32 glo_cfg; /* global configuration */ + u32 rst_idx; /* reset index */ + u32 delay_irq; /* delay interrupt */ +-- +2.51.0 + |
