diff --git a/board/aarch64/microchip-ev23x71a/Config.in b/board/aarch64/microchip-ev23x71a/Config.in index dc3e8254db..83683518d2 100644 --- a/board/aarch64/microchip-ev23x71a/Config.in +++ b/board/aarch64/microchip-ev23x71a/Config.in @@ -1,6 +1,7 @@ config BR2_PACKAGE_MICROCHIP_EV23X71A bool "Microchip EV23X71A (Laguna)" depends on BR2_aarch64 + select BR2_PACKAGE_SYMREG help Support for the Microchip EV23X71A evaluation board, based on the LAN969x (Laguna) family of TSN capable switches. 24 GbE diff --git a/board/aarch64/microchip-ev23x71a/README.md b/board/aarch64/microchip-ev23x71a/README.md index 7ccf57208d..166a6dfa6c 100644 --- a/board/aarch64/microchip-ev23x71a/README.md +++ b/board/aarch64/microchip-ev23x71a/README.md @@ -28,11 +28,13 @@ Not yet done: - TSN queueing. PSFP and time aware shaping need `NET_SCH_TAPRIO`, `NET_ACT_GATE`, `NET_SCH_ETS`, and `NET_SCH_CBS`, all currently off - HSR/PRP offload, see above -- MAC addresses. With no environment in flash the board falls back to - `lan969x_otp_get_mac()`, which derives 30 addresses for this board - from OTP, but the OTP address block is blank on our sample, so - interfaces come up random. Provisioning it is a one way operation, - `boot-monitor.rb --otp-data :` in monitor mode +- MAC addresses from OTP. With no environment in flash the board falls + back to `lan969x_otp_get_mac()`, which derives 30 addresses for this + board from OTP, but the OTP address block is blank on our sample. + Provisioning it is a one way operation, `boot-monitor.rb --otp-data + :` in monitor mode. Until then, give the board a + base MAC in `/mnt/aux/vpd.bin`, see [VPD](../../../doc/vpd.md#vpd-file-on-the-aux-partition), + or its interfaces come up random ## Boot Chain diff --git a/board/aarch64/microchip-ev23x71a/dts/microchip/infix.dtsi b/board/aarch64/microchip-ev23x71a/dts/microchip/infix.dtsi index e39f876f83..255fa8ae5a 100644 --- a/board/aarch64/microchip-ev23x71a/dts/microchip/infix.dtsi +++ b/board/aarch64/microchip-ev23x71a/dts/microchip/infix.dtsi @@ -13,3 +13,15 @@ }; }; }; + +/* Register access by name for the symreg tool, debugfs only */ +&axi { + symreg@e00c0000 { + compatible = "microchip,sparx5-symreg"; + reg = <0xe00c0000 0x10000>, + <0xe2000000 0x2000000>; + reg-names = "cpu", "dev"; + microchip,symreg = <0xe00c0000 0x10000>, + <0xe2000000 0x2000000>; + }; +}; diff --git a/board/aarch64/microchip-ev23x71a/dts/microchip/lan9696-ev23x71a-vpd.dtsi b/board/aarch64/microchip-ev23x71a/dts/microchip/lan9696-ev23x71a-vpd.dtsi new file mode 100644 index 0000000000..9e2276114d --- /dev/null +++ b/board/aarch64/microchip-ev23x71a/dts/microchip/lan9696-ev23x71a-vpd.dtsi @@ -0,0 +1,44 @@ +/* + * Product VPD, an ONIE TLV that the boot loader copies from the aux + * partition into reserved memory. The region is zeroed when there is + * no VPD, which leaves the layout without cells. Ports reference the + * base MAC address with an index, <&base_mac N>, and get base + N as + * their permanent address. + */ +/ { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + vpd: vpd@63e00000 { + compatible = "nvmem-rmem"; + reg = <0x63e00000 0x10000>; + no-map; + infix,board = "product"; + + nvmem-layout { + compatible = "onie,tlv-layout"; + + base_mac: mac-address { + #nvmem-cell-cells = <1>; + }; + }; + }; + }; +}; + +&switch { + nvmem-cells = <&base_mac 0>; + nvmem-cell-names = "mac-address"; +}; + +/* The driver gives port@N base + N + 1, e1 .. e28. e29 is base + 29. */ +&switch { + ethernet-ports { + port@29 { + nvmem-cells = <&base_mac 29>; + nvmem-cell-names = "mac-address"; + }; + }; +}; diff --git a/board/aarch64/microchip-ev23x71a/dts/microchip/lan9696-ev23x71a.dts b/board/aarch64/microchip-ev23x71a/dts/microchip/lan9696-ev23x71a.dts index 105ad23175..517e31ef93 100644 --- a/board/aarch64/microchip-ev23x71a/dts/microchip/lan9696-ev23x71a.dts +++ b/board/aarch64/microchip-ev23x71a/dts/microchip/lan9696-ev23x71a.dts @@ -5,3 +5,4 @@ #include #include "infix.dtsi" +#include "lan9696-ev23x71a-vpd.dtsi" diff --git a/board/aarch64/microchip-lan969x/README.md b/board/aarch64/microchip-lan969x/README.md new file mode 100644 index 0000000000..acf492f7d4 --- /dev/null +++ b/board/aarch64/microchip-lan969x/README.md @@ -0,0 +1,72 @@ +# Microchip LAN969x (Laguna) + +Files shared by the boards built on the LAN969x switch family: + +| Board | Package | +|-------------------------------------------------|------------------------------------| +| [Microchip EV23X71A](../microchip-ev23x71a) | `BR2_PACKAGE_MICROCHIP_EV23X71A` | +| [Novarq Tactical 1000](../novarq-tactical-1000) | `BR2_PACKAGE_NOVARQ_TACTICAL_1000` | + +`uboot/` holds the U-Boot config fragment and environment used by the +`*_boot_defconfig` of each board. Kernel device tree and configuration +fixups stay with each board, see its `dts/` directory and `.mk` file. + +A new Laguna board should select `BR2_PACKAGE_SYMREG` in its `Config.in` +and carry the `microchip,sparx5-symreg` node in its `dts/microchip/infix.dtsi`, +copied from one of the boards above, so the tool below works on it too. + +## Debugging + +### Register access by name + +Every Laguna board ships `symreg`, Microchip's tool for reading and +writing switch registers by their datasheet names. It talks to the +`symreg` debugfs driver, which maps the register space the device tree +node describes and exposes it as `/sys/kernel/debug/symreg/mem`. Root +only, since debugfs is. + +``` +symreg --help # options and the register syntax +symreg -m # MAC table +symreg -v # VLAN table +symreg -c 0 # VCAP instance 0 +symreg -s # stream table + +symreg 'DEV2G5[29]*' # all registers of port 29's device block +symreg 'ASM*[29]*' # ingress side of the same port +symreg -a 'DSM*[29]*' # same, with the physical addresses + +symreg REG[idx] # read one register +symreg REG[idx].FIELD # read one field +symreg REG[idx].FIELD 1 # write one field, add -f for wildcards +``` + +Register and field names follow the LAN969x datasheet. Quote the +wildcards or the shell expands them. The dispatcher `symreg` picks the +SoC from `/proc/device-tree/compatible` and runs `symreg_lan969x`, which +can also be called directly. + +The tool reads live hardware, so the useful pattern for an intermittent +fault is a snapshot while the port works, another once it has failed, +and a diff of the two. For the management port, which is switch port 29 +on both boards: + +```bash +for blk in 'DEV2G5[29]*' 'ASM*[29]*' 'DSM*[29]*' 'QSYS*[29]*'; do + symreg "$blk" +done >/tmp/port29-$(date +%s).txt +``` + +Check the PHY as well. The management PHY sits on the GCB MDIO bus and +shares one level triggered interrupt line with the copper PHYs, so a +stale link state can come from a missed PHY interrupt as well as from +the switch. Read it with `mdio` from mdio-tools, see the +[EV23X71A README](../microchip-ev23x71a/README.md#debug-and-analysis), +which also covers the VCAP debugfs and `ethtool` counters; both apply to +either board. + +### Writing registers + +Writes take effect immediately and bypass the driver, which keeps no +record of them. Expect the driver's own view to disagree afterwards, +and reboot before trusting any behaviour again. diff --git a/board/aarch64/microchip-lan969x/uboot/lan969x-env.dtsi b/board/aarch64/microchip-lan969x/uboot/lan969x-env.dtsi index cf5f1f682f..3ead6bf165 100644 --- a/board/aarch64/microchip-lan969x/uboot/lan969x-env.dtsi +++ b/board/aarch64/microchip-lan969x/uboot/lan969x-env.dtsi @@ -36,6 +36,7 @@ ixboard = "if setexpr.l cid *0xe2010000 && setexpr cid ${cid} / 0x1000 && setexpr cid ${cid} % 0x10000 && test ${cid} = 9697; then setenv board novarq,tactical-1000; setenv fdtfile microchip/lan9696-tactical-1000.dtb; fi"; loadaddr = "0x63000000"; + vpd_addr_r = "0x63e00000"; fdt_addr_r = "0x63f00000"; kernel_addr_r = "0x64000000"; fdtoverlay_addr_r = "0x67f00000"; diff --git a/board/aarch64/novarq-tactical-1000/Config.in b/board/aarch64/novarq-tactical-1000/Config.in index 4c66e6205f..440bfc28cc 100644 --- a/board/aarch64/novarq-tactical-1000/Config.in +++ b/board/aarch64/novarq-tactical-1000/Config.in @@ -1,6 +1,7 @@ config BR2_PACKAGE_NOVARQ_TACTICAL_1000 bool "Novarq Tactical 1000 (Laguna)" depends on BR2_aarch64 + select BR2_PACKAGE_SYMREG help Support for the Novarq Tactical 1000, based on the LAN969x (Laguna) family of TSN capable switches. 24 GbE copper ports, diff --git a/board/aarch64/novarq-tactical-1000/README.md b/board/aarch64/novarq-tactical-1000/README.md index 3503048781..36cc856af3 100644 --- a/board/aarch64/novarq-tactical-1000/README.md +++ b/board/aarch64/novarq-tactical-1000/README.md @@ -104,6 +104,26 @@ Storage][7]. A unit still on the vendor U-Boot netboots as described in [Netboot with a Vendor U-Boot][8]. The first boot grows `var` to fill the eMMC and reboots once by itself. +## Identity + +The board has no MAC address in OTP, so without help every boot gets a +random base MAC and random port addresses, which also change the +link-local addresses the test rig finds the unit by. `provision-tactical` +offers to write a base MAC and a serial number to `vpd.bin` on the `aux` +partition, which the bootloader hands to the system as its product VPD. +The vendor U-Boot does not, it takes the Infix one, see +[Bootloader](#bootloader). On a unit already provisioned, write it by +hand and reboot: + +```bash +echo '{"mac-address":"02:00:00:7a:c7:00","serial-number":"tact1","vendor":"Novarq","product-name":"Tactical-1000"}' \ + | onieprom -e | sudo tee /mnt/aux/vpd.bin >/dev/null +``` + +The base MAC becomes the chassis address, `e1` gets base + 1, `e2` base + 2, +and so on, `mgmt` base + 29, as permanent port addresses. See [VPD][9] +for the format. + ## Device Tree Cherry-picked from [Novarq's kernel tree][2]. One commit adapts it to @@ -152,3 +172,4 @@ unused. [6]: https://github.com/novarq/tactical-1000/blob/main/docs/boot-modes.md [7]: ../../../doc/netboot.md#installing-to-onboard-storage [8]: ../../../doc/netboot.md#netboot-with-a-vendor-u-boot +[9]: ../../../doc/vpd.md#vpd-file-on-the-aux-partition diff --git a/board/aarch64/novarq-tactical-1000/dts/microchip/infix.dtsi b/board/aarch64/novarq-tactical-1000/dts/microchip/infix.dtsi index e39f876f83..255fa8ae5a 100644 --- a/board/aarch64/novarq-tactical-1000/dts/microchip/infix.dtsi +++ b/board/aarch64/novarq-tactical-1000/dts/microchip/infix.dtsi @@ -13,3 +13,15 @@ }; }; }; + +/* Register access by name for the symreg tool, debugfs only */ +&axi { + symreg@e00c0000 { + compatible = "microchip,sparx5-symreg"; + reg = <0xe00c0000 0x10000>, + <0xe2000000 0x2000000>; + reg-names = "cpu", "dev"; + microchip,symreg = <0xe00c0000 0x10000>, + <0xe2000000 0x2000000>; + }; +}; diff --git a/board/aarch64/novarq-tactical-1000/dts/microchip/lan9696-tactical-1000-vpd.dtsi b/board/aarch64/novarq-tactical-1000/dts/microchip/lan9696-tactical-1000-vpd.dtsi new file mode 100644 index 0000000000..f657cf5a6a --- /dev/null +++ b/board/aarch64/novarq-tactical-1000/dts/microchip/lan9696-tactical-1000-vpd.dtsi @@ -0,0 +1,181 @@ +/* + * Product VPD, an ONIE TLV that the boot loader copies from the aux + * partition into reserved memory. The region is zeroed when there is + * no VPD, which leaves the layout without cells. Ports reference the + * base MAC address with an index, <&base_mac N>, and get base + N as + * their permanent address. + */ +/ { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + vpd: vpd@63e00000 { + compatible = "nvmem-rmem"; + reg = <0x63e00000 0x10000>; + no-map; + infix,board = "product"; + + nvmem-layout { + compatible = "onie,tlv-layout"; + + base_mac: mac-address { + #nvmem-cell-cells = <1>; + }; + }; + }; + }; +}; + +&switch { + nvmem-cells = <&base_mac 0>; + nvmem-cell-names = "mac-address"; +}; + +/* Port addresses in front panel order: e1 is base + 1, mgmt base + 29 */ + +&port0 { + nvmem-cells = <&base_mac 4>; + nvmem-cell-names = "mac-address"; +}; + +&port1 { + nvmem-cells = <&base_mac 3>; + nvmem-cell-names = "mac-address"; +}; + +&port2 { + nvmem-cells = <&base_mac 2>; + nvmem-cell-names = "mac-address"; +}; + +&port3 { + nvmem-cells = <&base_mac 1>; + nvmem-cell-names = "mac-address"; +}; + +&port4 { + nvmem-cells = <&base_mac 8>; + nvmem-cell-names = "mac-address"; +}; + +&port5 { + nvmem-cells = <&base_mac 7>; + nvmem-cell-names = "mac-address"; +}; + +&port6 { + nvmem-cells = <&base_mac 6>; + nvmem-cell-names = "mac-address"; +}; + +&port7 { + nvmem-cells = <&base_mac 5>; + nvmem-cell-names = "mac-address"; +}; + +&port8 { + nvmem-cells = <&base_mac 12>; + nvmem-cell-names = "mac-address"; +}; + +&port9 { + nvmem-cells = <&base_mac 11>; + nvmem-cell-names = "mac-address"; +}; + +&port10 { + nvmem-cells = <&base_mac 10>; + nvmem-cell-names = "mac-address"; +}; + +&port11 { + nvmem-cells = <&base_mac 9>; + nvmem-cell-names = "mac-address"; +}; + +&port12 { + nvmem-cells = <&base_mac 16>; + nvmem-cell-names = "mac-address"; +}; + +&port13 { + nvmem-cells = <&base_mac 15>; + nvmem-cell-names = "mac-address"; +}; + +&port14 { + nvmem-cells = <&base_mac 14>; + nvmem-cell-names = "mac-address"; +}; + +&port15 { + nvmem-cells = <&base_mac 13>; + nvmem-cell-names = "mac-address"; +}; + +&port16 { + nvmem-cells = <&base_mac 20>; + nvmem-cell-names = "mac-address"; +}; + +&port17 { + nvmem-cells = <&base_mac 19>; + nvmem-cell-names = "mac-address"; +}; + +&port18 { + nvmem-cells = <&base_mac 18>; + nvmem-cell-names = "mac-address"; +}; + +&port19 { + nvmem-cells = <&base_mac 17>; + nvmem-cell-names = "mac-address"; +}; + +&port20 { + nvmem-cells = <&base_mac 24>; + nvmem-cell-names = "mac-address"; +}; + +&port21 { + nvmem-cells = <&base_mac 23>; + nvmem-cell-names = "mac-address"; +}; + +&port22 { + nvmem-cells = <&base_mac 22>; + nvmem-cell-names = "mac-address"; +}; + +&port23 { + nvmem-cells = <&base_mac 21>; + nvmem-cell-names = "mac-address"; +}; + +&port24 { + nvmem-cells = <&base_mac 25>; + nvmem-cell-names = "mac-address"; +}; + +&port25 { + nvmem-cells = <&base_mac 26>; + nvmem-cell-names = "mac-address"; +}; + +&port26 { + nvmem-cells = <&base_mac 27>; + nvmem-cell-names = "mac-address"; +}; + +&port27 { + nvmem-cells = <&base_mac 28>; + nvmem-cell-names = "mac-address"; +}; + +&port29 { + nvmem-cells = <&base_mac 29>; + nvmem-cell-names = "mac-address"; +}; diff --git a/board/aarch64/novarq-tactical-1000/dts/microchip/lan9696-tactical-1000.dts b/board/aarch64/novarq-tactical-1000/dts/microchip/lan9696-tactical-1000.dts index b1830dbff2..d9967c69cd 100644 --- a/board/aarch64/novarq-tactical-1000/dts/microchip/lan9696-tactical-1000.dts +++ b/board/aarch64/novarq-tactical-1000/dts/microchip/lan9696-tactical-1000.dts @@ -5,3 +5,4 @@ #include #include "infix.dtsi" +#include "lan9696-tactical-1000-vpd.dtsi" diff --git a/board/aarch64/novarq-tactical-1000/rootfs/usr/libexec/infix/prod/provision-tactical b/board/aarch64/novarq-tactical-1000/rootfs/usr/libexec/infix/prod/provision-tactical index c5983fa798..e14eaf3f2b 100755 --- a/board/aarch64/novarq-tactical-1000/rootfs/usr/libexec/infix/prod/provision-tactical +++ b/board/aarch64/novarq-tactical-1000/rootfs/usr/libexec/infix/prod/provision-tactical @@ -114,9 +114,24 @@ if ask "Create aux, primary ${imgsize}, secondary ${imgsize}, cfg ${cfgsize}, an mkfs.ext4 -qF -L aux -m 0 -i 4096 -O ^metadata_csum,^metadata_csum_seed,uninit_bg "${disk}p4" mkfs.ext4 -qF -L cfg -m 0 -i 4096 -O uninit_bg "${disk}p7" mkfs.ext4 -qF -L var -m 0 -i 4096 -O uninit_bg "${disk}p8" + # + # RAUC tracks slot boot state in uboot.env, seed it as prod/provision does. mnt=$(mktemp -d) mount -t ext4 "${disk}p4" "$mnt" echo 1 >"$mnt"/resized + mkenvimage -s 0x4000 -o "$mnt"/uboot.env - <"$mnt"/vpd.bin + fi umount "$mnt" rmdir "$mnt" fi diff --git a/board/common/rootfs/etc/bash.bashrc b/board/common/rootfs/etc/bash.bashrc index 8e916c9390..a7c56c1989 100644 --- a/board/common/rootfs/etc/bash.bashrc +++ b/board/common/rootfs/etc/bash.bashrc @@ -33,21 +33,7 @@ bind "set show-all-if-ambiguous on" export LANG=C.UTF-8 -log() -{ - local fn="/var/log/syslog" - [ -n "$1" ] && fn="/var/log/$1" - less +G -r "$fn" -} - -follow () -{ - local fn="/var/log/syslog" - [ -n "$1" ] && fn="/var/log/$1" - - tail -F -n +1 "$fn" -} - +# log and follow are defined in /etc/profile.d/log.sh _logfile_completions() { local cur=${COMP_WORDS[COMP_CWORD]} diff --git a/board/common/rootfs/etc/finit.d/available/phc2sys@.conf b/board/common/rootfs/etc/finit.d/available/phc2sys@.conf index 3dfe8e5085..5b4dc78bc0 100644 --- a/board/common/rootfs/etc/finit.d/available/phc2sys@.conf +++ b/board/common/rootfs/etc/finit.d/available/phc2sys@.conf @@ -1,3 +1,3 @@ service name:phc2sys :%i log:prio:daemon,tag:phc2sys-%i \ - [2345] phc2sys -a -z /var/run/ptp4l-%i \ + [2345] phc2sys -a -f /etc/linuxptp/ptp4l-%i.conf \ -- PHC synchronization for PTP instance %i diff --git a/board/common/rootfs/etc/profile.d/log.sh b/board/common/rootfs/etc/profile.d/log.sh new file mode 100644 index 0000000000..3dada6c13e --- /dev/null +++ b/board/common/rootfs/etc/profile.d/log.sh @@ -0,0 +1,15 @@ +# Page or follow a log file, default syslog. Plain sh, so BusyBox ash +# and bash both get it; the bash completions live in /etc/bash.bashrc. +log() +{ + local fn="/var/log/syslog" + [ -n "$1" ] && fn="/var/log/$1" + less +G -r "$fn" +} + +follow() +{ + local fn="/var/log/syslog" + [ -n "$1" ] && fn="/var/log/$1" + tail -F -n +1 "$fn" +} diff --git a/configs/aarch64_minimal_defconfig b/configs/aarch64_minimal_defconfig index 657d9a8663..4d36f13253 100644 --- a/configs/aarch64_minimal_defconfig +++ b/configs/aarch64_minimal_defconfig @@ -111,6 +111,7 @@ BR2_PACKAGE_HOST_GENEXT2FS=y BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y +BR2_PACKAGE_ACER_CONNECT_VERO_W6M=y BR2_PACKAGE_ALDER_ALDER=y BR2_PACKAGE_BANANAPI_BPI_R3=y BR2_PACKAGE_BANANAPI_BPI_R4=y @@ -118,6 +119,8 @@ BR2_PACKAGE_BANANAPI_BPI_R64=y BR2_PACKAGE_FRIENDLYARM_NANOPI_R2S=y BR2_PACKAGE_MARVELL_CN9130_CRB=y BR2_PACKAGE_MARVELL_ESPRESSOBIN=y +BR2_PACKAGE_MICROCHIP_EV23X71A=y +BR2_PACKAGE_NOVARQ_TACTICAL_1000=y BR2_PACKAGE_RASPBERRYPI_RPI64=y BR2_PACKAGE_STYX_DCP_SC_28P=y IX_VENDOR_HOME="https://www.kernelkit.org" diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index 55d7ca5c8c..9059703bb7 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -8,6 +8,10 @@ All notable changes to the project are documented in this file. ### Changes +- Changing a setting of an interface that does not need it, e.g., an IP + address, its description, or a bridge port's STP priority or cost, no + longer takes the link down and up. A copper link no longer drops for a + few seconds and STP no longer reconverges after such changes - Upgrade Linux kernel to 6.18.57 (LTS) - Switch boards advertise one of their port addresses as the LLDP chassis ID. Previously it could be an address no port on the device carried @@ -17,9 +21,9 @@ All notable changes to the project are documented in this file. - Boards without a VPD EEPROM can be given a fixed identity, base MAC included, by storing an ONIE TLV as `/mnt/aux/vpd.bin`, see [VPD documentation](vpd.md#vpd-file-on-the-aux-partition) -- Banana Pi BPI-R3 and Acer Connect Vero W6m: ports get permanent - addresses derived from the chassis MAC instead of random ones at - every boot +- Banana Pi BPI-R3, Acer Connect Vero W6m, Tactical 1000 and EV23X71A: + ports get permanent addresses derived from the chassis MAC instead of + random ones at every boot - WiFi interfaces get a unique address derived from the chassis MAC, so several APs on one radio, or two devices of the same model, no longer need `custom-phys-address` @@ -86,6 +90,9 @@ All notable changes to the project are documented in this file. board documentation - Banana Pi BPI-R4: ports get permanent addresses derived from the chassis MAC of a `/mnt/aux/vpd.bin`, see [VPD documentation](vpd.md#vpd-file-on-the-aux-partition) +- LAN969x boards ship `symreg`, which reads and writes switch registers by + name and dumps the MAC, VLAN and VCAP tables, for debugging the switch + core, see `board/aarch64/microchip-lan969x/README.md` ### Fixes @@ -134,6 +141,89 @@ All notable changes to the project are documented in this file. unlocked, now the port is locked - Banana Pi BPI-R4: no PCIe card was detected, e.g., an NVMe disk or a WiFi card +- LAN969x: a port outside any bridge stopped receiving multicast, including + IPv6 neighbour discovery and mDNS, when another port outside a bridge went + down or joined a bridge. The port looked dead on the LAN until it was + taken down and up again, which is how the Tactical 1000 management port + kept dropping out of the test rig +- LAN969x: kernel BUG "scheduling while atomic" when a multicast address was + added to or removed from a port outside any bridge +- LAN969x: the IP address of a bridge was unreachable from its ports, while + forwarding between the ports worked. Broadcast from a bridge port never + reached the CPU, so ARP went unanswered, and the bridge's own MAC address + was missing from the switch MAC table, so unicast to the bridge was + flooded to the ports instead of delivered +- LAN969x: disabling the first port of a bridge, whose MAC address the + bridge uses, cut the bridge IP address off until the port was enabled + again +- LAN969x: a bridge without VLANs forwarded nothing between its ports +- LAN969x: a VLAN interface on a port in a VLAN filtering bridge never + received anything, unless the bridge had the same VLAN on the port +- LAN969x: traffic from a VLAN interface on a bridge, sent out a port that + carries all its VLANs tagged, left with an extra VLAN 0 tag +- LAN969x: bridging a VLAN interface on top of a port applied the bridge + port settings, STP state, flooding and VLAN filtering, to the port + itself and logged "Controlling non-bridged port" +- LAN969x: STP did not converge and LACP failed over aggregates in a + bridge. The switch flooded BPDUs and LACPDUs to the other bridge ports + instead of handing them to the CPU +- LAN969x: a link aggregate in a bridge did not forward, the switch now + offloads link aggregates. Based on the LAG support by Daniel Machon in + Microchip's BSP +- LAN969x: taking down one port of a link aggregate stopped unicast to the + aggregate on the remaining ports, and removing the aggregate cut off the + port whose address it had used until that port was disabled and enabled + again +- LAN969x: PTP could stop on every port until reboot. Each transmit + timestamp that arrived late used up one of 512 slots for good +- Firewall: dynamic address-set entries were lost on every configuration + change on slow systems, where reloading the firewall takes longer than + 25 seconds, and on any system the hosts they allow were blocked until + the reload was done, half a minute on a single-core system +- LAN969x: IGMP did not pass through a bridge. With IGMP snooping across + several switches, a group joined behind one switch never reached the + querier behind another, so the multicast never got there either +- PTP ports with PHY timestamping, e.g. the copper ports on LAN969x boards, + kept dropping to the faulty state because the transmit timestamp arrived + later than the 10 ms the PTP daemon waited for it +- PTP never worked on the copper ports of the Tactical 1000, whose LAN8804 + PHYs are a LAN8814 variant without a 1588 unit. The kernel took them for + full LAN8814s and waited for PHY timestamps that never came; it now + recognizes the variant and times stamps in the switch core instead +- PTP: the IEEE 802.1AS link delay threshold, above which a port never + leaves the listening state, defaults to 10 us with hardware time stamping + instead of 800 ns, and can be set per port with `mean-link-delay-thresh` +- PTP: transparent clocks, and boundary clocks with the IEEE 802.1AS + profile or a domain other than 0, logged "Waiting for ptp4l..." every + second. The helper that keeps the clocks of a multi-chip switch in step + never reached the PTP daemon. It now uses the domain and profile of its + instance, and is no longer started for transparent clocks, which do not + answer it +- PTP: the operational delay mechanism of every port read `e2e`, also for + ports using peer delay, e.g. with the IEEE 802.1AS profile +- PTP: the operational data never showed the observed clock phase change + rate of the parent clock +- LAN969x: the LAN8814 and LAN8804 copper PHYs, on the Tactical 1000 and + EV23X71A, ran with power-on defaults where Microchip's silicon errata + require other settings on every port: center-tap power management, the + EEE wake timer, and the PLL trim +- LAN969x: a copper port on the Tactical 1000 could come up with one + direction dead, the link up at 1000 Mbps but nothing it sent reaching the + other end, most often after the port had been reconfigured. The PHYs + now resynchronize their QSGMII link to the switch after every PHY reset, + the way Microchip's PHY API does +- LAN969x: the LAN8814 and LAN8804 copper PHYs advertised Energy Efficient + Ethernet, which the switch cannot use, and could enter its low power + state by mistake after a cable was unplugged. EEE is now off on these + ports +- LAN969x: a copper link could stay down for good when autonegotiation on + the LAN8814 or LAN8804 PHY got stuck. It is now restarted after three + seconds with a link partner present but no link +- LAN969x: a copper port forced to 100 Mbps could fail to link up with the + LAN8814 or LAN8804 PHY. The PHY is now reset every three seconds while + a link partner is present but no link comes up +- LAN969x: the LAN8814 and LAN8804 PHYs now get Microchip's settings for + 10BASE-T transmit and for 10/100 Mbps half duplex [pppoe]: https://www.kernelkit.org/infix/latest/pppoe/ [wds]: https://www.kernelkit.org/infix/latest/wifi/#wds-backhaul-and-repeaters diff --git a/doc/vpd.md b/doc/vpd.md index 577a0a150b..6a6b61db21 100644 --- a/doc/vpd.md +++ b/doc/vpd.md @@ -80,7 +80,8 @@ theirs from the base MAC as described in [WiFi](wifi.md#multi-ssid-configuration). This is currently supported on the Banana Pi BPI-R3 family, the Banana -Pi BPI-R4 family, and the Acer Connect Vero W6m. +Pi BPI-R4 family, the Acer Connect Vero W6m, and the LAN969x boards: the +Tactical 1000 and the EV23X71A. ## Product VPD diff --git a/package/Config.in b/package/Config.in index d75c106982..12954bc7bb 100644 --- a/package/Config.in +++ b/package/Config.in @@ -43,6 +43,7 @@ source "$BR2_EXTERNAL_INFIX_PATH/package/python-libyang/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/python-yangdoc/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/skeleton-init-finit/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/spacemit-k1-fsbl/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/package/symreg/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/tetris/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/libyang-cpp/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/sysrepo-cpp/Config.in" diff --git a/package/symreg/Config.in b/package/symreg/Config.in new file mode 100644 index 0000000000..bb3265fcd6 --- /dev/null +++ b/package/symreg/Config.in @@ -0,0 +1,10 @@ +config BR2_PACKAGE_SYMREG + bool "symreg - Microchip switch registers by name" + depends on BR2_aarch64 + help + Read and write Microchip LAN969x switch registers by symbolic + name, and dump the MAC, VLAN, VCAP and stream tables, through + the symreg debugfs driver. Debug aid, root only. Selected by + the Laguna boards, see board/aarch64/microchip-lan969x/README.md + + https://github.com/microchip-ung/symreg-tool diff --git a/package/symreg/symreg.hash b/package/symreg/symreg.hash new file mode 100644 index 0000000000..5889d1fb6f --- /dev/null +++ b/package/symreg/symreg.hash @@ -0,0 +1,3 @@ +# Locally calculated +sha256 0d7eee2dbdd8e79bb91103d06ee057cb477764a046c5d6d8045e978166a70490 symreg-cb96993eeb34558926a8f5851a945ad4cd674690.tar.gz +sha256 d620d728cc1c0efebe173f8840dd957a56d9ff40867c062d0571b4110ea15f1e LICENSE diff --git a/package/symreg/symreg.mk b/package/symreg/symreg.mk new file mode 100644 index 0000000000..53b77b5d25 --- /dev/null +++ b/package/symreg/symreg.mk @@ -0,0 +1,11 @@ +SYMREG_VERSION = cb96993eeb34558926a8f5851a945ad4cd674690 +SYMREG_SITE = $(call github,microchip-ung,symreg-tool,$(SYMREG_VERSION)) +SYMREG_LICENSE = MIT +SYMREG_LICENSE_FILES = LICENSE +SYMREG_CONF_OPTS = -DBUILD_LAN969X=ON + +define SYMREG_LINUX_CONFIG_FIXUPS + $(call KCONFIG_ENABLE_OPT,CONFIG_SPARX5_SYMREG) +endef + +$(eval $(cmake-package)) diff --git a/patches/linux/6.18.57/0001-net-phy-marvell10g-Support-firmware-loading-on-88X33.patch b/patches/linux/6.18.57/0001-net-phy-marvell10g-Support-firmware-loading-on-88X33.patch index 8d67c3df60..a7ae9de883 100644 --- a/patches/linux/6.18.57/0001-net-phy-marvell10g-Support-firmware-loading-on-88X33.patch +++ b/patches/linux/6.18.57/0001-net-phy-marvell10g-Support-firmware-loading-on-88X33.patch @@ -1,7 +1,7 @@ From a3d812890f51189fc70b419a37f2fe038fe2395d Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 19 Sep 2023 18:38:10 +0200 -Subject: [PATCH 001/102] net: phy: marvell10g: Support firmware loading on +Subject: [PATCH 001/128] net: phy: marvell10g: Support firmware loading on 88X3310 When probing, if a device is waiting for firmware to be loaded into diff --git a/patches/linux/6.18.57/0002-net-phy-marvell10g-Fix-power-up-when-strapped-to-sta.patch b/patches/linux/6.18.57/0002-net-phy-marvell10g-Fix-power-up-when-strapped-to-sta.patch index 37b285f2a1..79e0e94745 100644 --- a/patches/linux/6.18.57/0002-net-phy-marvell10g-Fix-power-up-when-strapped-to-sta.patch +++ b/patches/linux/6.18.57/0002-net-phy-marvell10g-Fix-power-up-when-strapped-to-sta.patch @@ -1,7 +1,7 @@ From 2a59f9d7a2c7d3c847db7ee4e5bf2f4ddf7b4702 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 21 Nov 2023 20:15:24 +0100 -Subject: [PATCH 002/102] net: phy: marvell10g: Fix power-up when strapped to +Subject: [PATCH 002/128] net: phy: marvell10g: Fix power-up when strapped to start powered down On devices which are hardware strapped to start powered down (PDSTATE diff --git a/patches/linux/6.18.57/0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch b/patches/linux/6.18.57/0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch index 74cdd4fd5e..6e571b9971 100644 --- a/patches/linux/6.18.57/0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch +++ b/patches/linux/6.18.57/0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch @@ -1,7 +1,7 @@ From a43ab064f5f15038c30db9b6d9f3ccdc7eaf57ab Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 15 Nov 2023 20:58:42 +0100 -Subject: [PATCH 003/102] net: phy: marvell10g: Add LED support for 88X3310 +Subject: [PATCH 003/128] net: phy: marvell10g: Add LED support for 88X3310 Pickup the LEDs from the state in which the hardware reset or bootloader left them, but also support further configuration via diff --git a/patches/linux/6.18.57/0004-net-phy-marvell10g-Support-LEDs-tied-to-a-single-med.patch b/patches/linux/6.18.57/0004-net-phy-marvell10g-Support-LEDs-tied-to-a-single-med.patch index 2ff7e9dbe6..a6b4be9a4f 100644 --- a/patches/linux/6.18.57/0004-net-phy-marvell10g-Support-LEDs-tied-to-a-single-med.patch +++ b/patches/linux/6.18.57/0004-net-phy-marvell10g-Support-LEDs-tied-to-a-single-med.patch @@ -1,7 +1,7 @@ From c2c0be4516d1c42574bf5ec923cd1b28749ed079 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 12 Dec 2023 09:51:05 +0100 -Subject: [PATCH 004/102] net: phy: marvell10g: Support LEDs tied to a single +Subject: [PATCH 004/128] net: phy: marvell10g: Support LEDs tied to a single media side In a combo-port setup, i.e. where both the copper and fiber interface diff --git a/patches/linux/6.18.57/0005-net-phy-Do-not-resume-PHY-when-attaching.patch b/patches/linux/6.18.57/0005-net-phy-Do-not-resume-PHY-when-attaching.patch index f3c7377df7..4e62e70f58 100644 --- a/patches/linux/6.18.57/0005-net-phy-Do-not-resume-PHY-when-attaching.patch +++ b/patches/linux/6.18.57/0005-net-phy-Do-not-resume-PHY-when-attaching.patch @@ -1,7 +1,7 @@ From 531ee82493ae9b2419a0d00891cab20fa1c99751 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 27 Mar 2024 10:10:19 +0100 -Subject: [PATCH 005/102] net: phy: Do not resume PHY when attaching +Subject: [PATCH 005/128] net: phy: Do not resume PHY when attaching The PHY should not start negotiating with its link-partner until explicitly instructed to do so. diff --git a/patches/linux/6.18.57/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch b/patches/linux/6.18.57/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch index 563ab50e75..6e0b279c46 100644 --- a/patches/linux/6.18.57/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch +++ b/patches/linux/6.18.57/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch @@ -1,7 +1,7 @@ From 9d9a85e79b3ed5844e47efbab43c854b5cd50695 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 4 Mar 2024 16:47:28 +0100 -Subject: [PATCH 006/102] net: bridge: avoid classifying unknown multicast as +Subject: [PATCH 006/128] net: bridge: avoid classifying unknown multicast as mrouters_only Unknown multicast, MAC/IPv4/IPv6, should always be flooded according to diff --git a/patches/linux/6.18.57/0007-net-bridge-Ignore-router-ports-when-forwarding-L2-mu.patch b/patches/linux/6.18.57/0007-net-bridge-Ignore-router-ports-when-forwarding-L2-mu.patch index 0886ae84bb..2e00cee435 100644 --- a/patches/linux/6.18.57/0007-net-bridge-Ignore-router-ports-when-forwarding-L2-mu.patch +++ b/patches/linux/6.18.57/0007-net-bridge-Ignore-router-ports-when-forwarding-L2-mu.patch @@ -1,7 +1,7 @@ From 8650a96e792f68b3fe6b0e48a56bc1dbe478b899 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 5 Mar 2024 06:44:41 +0100 -Subject: [PATCH 007/102] net: bridge: Ignore router ports when forwarding L2 +Subject: [PATCH 007/128] net: bridge: Ignore router ports when forwarding L2 multicast Multicast router ports are either statically configured or learned from diff --git a/patches/linux/6.18.57/0008-net-bridge-drop-delay-for-applying-strict-multicast-.patch b/patches/linux/6.18.57/0008-net-bridge-drop-delay-for-applying-strict-multicast-.patch index 3f6f2d3f4e..c63022511b 100644 --- a/patches/linux/6.18.57/0008-net-bridge-drop-delay-for-applying-strict-multicast-.patch +++ b/patches/linux/6.18.57/0008-net-bridge-drop-delay-for-applying-strict-multicast-.patch @@ -1,7 +1,7 @@ From 3168dbb99768d0967714da20f1d59ba2b039e287 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Thu, 4 Apr 2024 16:36:30 +0200 -Subject: [PATCH 008/102] net: bridge: drop delay for applying strict multicast +Subject: [PATCH 008/128] net: bridge: drop delay for applying strict multicast filtering This *local* patch drops the initial delay before applying strict multicast diff --git a/patches/linux/6.18.57/0009-net-bridge-Differentiate-MDB-additions-from-modifica.patch b/patches/linux/6.18.57/0009-net-bridge-Differentiate-MDB-additions-from-modifica.patch index c98cf44308..356c9c9986 100644 --- a/patches/linux/6.18.57/0009-net-bridge-Differentiate-MDB-additions-from-modifica.patch +++ b/patches/linux/6.18.57/0009-net-bridge-Differentiate-MDB-additions-from-modifica.patch @@ -1,7 +1,7 @@ From 80187190309db1097de8442aa2bce72276af00b2 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 16 May 2024 14:51:54 +0200 -Subject: [PATCH 009/102] net: bridge: Differentiate MDB additions from +Subject: [PATCH 009/128] net: bridge: Differentiate MDB additions from modifications Before this change, the reception of an IGMPv3 report (and analogously diff --git a/patches/linux/6.18.57/0010-nvmem-layouts-onie-tlv-Let-device-probe-even-when-TL.patch b/patches/linux/6.18.57/0010-nvmem-layouts-onie-tlv-Let-device-probe-even-when-TL.patch index 178dfee78d..2e907624ac 100644 --- a/patches/linux/6.18.57/0010-nvmem-layouts-onie-tlv-Let-device-probe-even-when-TL.patch +++ b/patches/linux/6.18.57/0010-nvmem-layouts-onie-tlv-Let-device-probe-even-when-TL.patch @@ -1,7 +1,7 @@ From cfd0d7f5f1762efebab226a88910c9852f0b71f1 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Fri, 24 Nov 2023 23:29:55 +0100 -Subject: [PATCH 010/102] nvmem: layouts: onie-tlv: Let device probe even when +Subject: [PATCH 010/128] nvmem: layouts: onie-tlv: Let device probe even when TLV is invalid Before this change, probing an NVMEM device, expected to contain a diff --git a/patches/linux/6.18.57/0011-net-usb-r8152-add-r8153b-support-for-link-activity-L.patch b/patches/linux/6.18.57/0011-net-usb-r8152-add-r8153b-support-for-link-activity-L.patch index b50b5b2d8a..bffda052df 100644 --- a/patches/linux/6.18.57/0011-net-usb-r8152-add-r8153b-support-for-link-activity-L.patch +++ b/patches/linux/6.18.57/0011-net-usb-r8152-add-r8153b-support-for-link-activity-L.patch @@ -1,7 +1,7 @@ From 1fa2ebb08f708cd6c002dbdd7b2061ed1e011764 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 11 Aug 2024 11:27:35 +0200 -Subject: [PATCH 011/102] net: usb: r8152: add r8153b support for link/activity +Subject: [PATCH 011/128] net: usb: r8152: add r8153b support for link/activity LEDs This patch adds support for the link/activity LEDs on the NanoPi R2S diff --git a/patches/linux/6.18.57/0012-arm64-dts-mediatek-mt7986a-rename-BPi-R3-ports-to-ma.patch b/patches/linux/6.18.57/0012-arm64-dts-mediatek-mt7986a-rename-BPi-R3-ports-to-ma.patch index 5782f77e82..829cb5ae99 100644 --- a/patches/linux/6.18.57/0012-arm64-dts-mediatek-mt7986a-rename-BPi-R3-ports-to-ma.patch +++ b/patches/linux/6.18.57/0012-arm64-dts-mediatek-mt7986a-rename-BPi-R3-ports-to-ma.patch @@ -1,7 +1,7 @@ From 70da24ec1e91985b5c039082bfc1fa2bac974f63 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 10 Aug 2025 18:52:54 +0200 -Subject: [PATCH 012/102] arm64: dts: mediatek: mt7986a: rename BPi R3 ports to +Subject: [PATCH 012/128] arm64: dts: mediatek: mt7986a: rename BPi R3 ports to match case For ref. see: https://wiki.banana-pi.org/File:Bpi-r3_Metal_case.jpg diff --git a/patches/linux/6.18.57/0013-drm-panel-simple-Add-a-timing-for-the-Raspberry-Pi-7.patch b/patches/linux/6.18.57/0013-drm-panel-simple-Add-a-timing-for-the-Raspberry-Pi-7.patch index 653e02341b..cd9efc7717 100644 --- a/patches/linux/6.18.57/0013-drm-panel-simple-Add-a-timing-for-the-Raspberry-Pi-7.patch +++ b/patches/linux/6.18.57/0013-drm-panel-simple-Add-a-timing-for-the-Raspberry-Pi-7.patch @@ -1,7 +1,7 @@ From e12c89125b2d6a8cda9004f11f0851e443cd1723 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 20 Aug 2025 21:38:24 +0200 -Subject: [PATCH 013/102] drm/panel-simple: Add a timing for the Raspberry Pi +Subject: [PATCH 013/128] drm/panel-simple: Add a timing for the Raspberry Pi 7" panel The Raspberry Pi 7" 800x480 panel uses a Toshiba TC358762 DSI diff --git a/patches/linux/6.18.57/0014-input-touchscreen-edt-ft5x06-Add-polled-mode.patch b/patches/linux/6.18.57/0014-input-touchscreen-edt-ft5x06-Add-polled-mode.patch index e1fea2ef8d..859c0f1537 100644 --- a/patches/linux/6.18.57/0014-input-touchscreen-edt-ft5x06-Add-polled-mode.patch +++ b/patches/linux/6.18.57/0014-input-touchscreen-edt-ft5x06-Add-polled-mode.patch @@ -1,7 +1,7 @@ From dc8a3b2bfa67af9ef458571b69a0614375deefa2 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Thu, 21 Aug 2025 11:20:23 +0200 -Subject: [PATCH 014/102] input:touchscreen:edt-ft5x06: Add polled mode +Subject: [PATCH 014/128] input:touchscreen:edt-ft5x06: Add polled mode Not all hardware has interrupts therefore we need to poll the touchscreen. diff --git a/patches/linux/6.18.57/0015-FIX-net-dsa-mv88e6xxx-Fix-timeout-on-waiting-for-PPU.patch b/patches/linux/6.18.57/0015-FIX-net-dsa-mv88e6xxx-Fix-timeout-on-waiting-for-PPU.patch index 044a0f0e70..7e64c0a596 100644 --- a/patches/linux/6.18.57/0015-FIX-net-dsa-mv88e6xxx-Fix-timeout-on-waiting-for-PPU.patch +++ b/patches/linux/6.18.57/0015-FIX-net-dsa-mv88e6xxx-Fix-timeout-on-waiting-for-PPU.patch @@ -1,7 +1,7 @@ From 10a5c6575f2fd6c1534c88171e6de89a25e07ae3 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 12 Mar 2024 10:27:24 +0100 -Subject: [PATCH 015/102] [FIX] net: dsa: mv88e6xxx: Fix timeout on waiting for +Subject: [PATCH 015/128] [FIX] net: dsa: mv88e6xxx: Fix timeout on waiting for PPU on 6393X In a multi-chip setup, delays of up to 750ms are observed before the diff --git a/patches/linux/6.18.57/0016-net-dsa-mv88e6xxx-Improve-indirect-register-access-p.patch b/patches/linux/6.18.57/0016-net-dsa-mv88e6xxx-Improve-indirect-register-access-p.patch index 99007915bd..8733a56d43 100644 --- a/patches/linux/6.18.57/0016-net-dsa-mv88e6xxx-Improve-indirect-register-access-p.patch +++ b/patches/linux/6.18.57/0016-net-dsa-mv88e6xxx-Improve-indirect-register-access-p.patch @@ -1,7 +1,7 @@ From 3d4aa3d2ef925c1048c99b31760b3acdba4450e1 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 27 Mar 2024 15:52:43 +0100 -Subject: [PATCH 016/102] net: dsa: mv88e6xxx: Improve indirect register access +Subject: [PATCH 016/128] net: dsa: mv88e6xxx: Improve indirect register access perf on 6393 When operating in multi-chip mode, the 6393 family maps a subset of diff --git a/patches/linux/6.18.57/0017-net-dsa-mv88e6xxx-Honor-ports-being-managed-via-in-b.patch b/patches/linux/6.18.57/0017-net-dsa-mv88e6xxx-Honor-ports-being-managed-via-in-b.patch index ce6281e06d..f7fec7f9d8 100644 --- a/patches/linux/6.18.57/0017-net-dsa-mv88e6xxx-Honor-ports-being-managed-via-in-b.patch +++ b/patches/linux/6.18.57/0017-net-dsa-mv88e6xxx-Honor-ports-being-managed-via-in-b.patch @@ -1,7 +1,7 @@ From 2b2ba8743e3bd4405502d30b459bcf55d567e6a1 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Mon, 22 Apr 2024 23:18:01 +0200 -Subject: [PATCH 017/102] net: dsa: mv88e6xxx: Honor ports being managed via +Subject: [PATCH 017/128] net: dsa: mv88e6xxx: Honor ports being managed via in-band-status Keep all link parameters in their unforced states when the port is diff --git a/patches/linux/6.18.57/0018-net-dsa-mv88e6xxx-Limit-rsvd2cpu-policy-to-user-port.patch b/patches/linux/6.18.57/0018-net-dsa-mv88e6xxx-Limit-rsvd2cpu-policy-to-user-port.patch index 2046e327ee..9dd763a852 100644 --- a/patches/linux/6.18.57/0018-net-dsa-mv88e6xxx-Limit-rsvd2cpu-policy-to-user-port.patch +++ b/patches/linux/6.18.57/0018-net-dsa-mv88e6xxx-Limit-rsvd2cpu-policy-to-user-port.patch @@ -1,7 +1,7 @@ From 78b6eab5fff4b3d733a62120da3b8c1866fcc783 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 24 Apr 2024 22:41:04 +0200 -Subject: [PATCH 018/102] net: dsa: mv88e6xxx: Limit rsvd2cpu policy to user +Subject: [PATCH 018/128] net: dsa: mv88e6xxx: Limit rsvd2cpu policy to user ports on 6393X For packets with a DA in the IEEE reserved L2 group range, originating diff --git a/patches/linux/6.18.57/0019-net-dsa-tag_dsa-Use-tag-priority-as-initial-skb-prio.patch b/patches/linux/6.18.57/0019-net-dsa-tag_dsa-Use-tag-priority-as-initial-skb-prio.patch index 3179ab3243..656dba749d 100644 --- a/patches/linux/6.18.57/0019-net-dsa-tag_dsa-Use-tag-priority-as-initial-skb-prio.patch +++ b/patches/linux/6.18.57/0019-net-dsa-tag_dsa-Use-tag-priority-as-initial-skb-prio.patch @@ -1,7 +1,7 @@ From 3be8bf956b25b37a99ba802351bfd4c260ab44a0 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 28 May 2024 10:38:42 +0200 -Subject: [PATCH 019/102] net: dsa: tag_dsa: Use tag priority as initial +Subject: [PATCH 019/128] net: dsa: tag_dsa: Use tag priority as initial skb->priority Use the 3-bit priority field from the DSA tag as the initial packet diff --git a/patches/linux/6.18.57/0020-net-dsa-Support-MDB-memberships-whose-L2-addresses-o.patch b/patches/linux/6.18.57/0020-net-dsa-Support-MDB-memberships-whose-L2-addresses-o.patch index 9e57137664..0265932c77 100644 --- a/patches/linux/6.18.57/0020-net-dsa-Support-MDB-memberships-whose-L2-addresses-o.patch +++ b/patches/linux/6.18.57/0020-net-dsa-Support-MDB-memberships-whose-L2-addresses-o.patch @@ -1,7 +1,7 @@ From 1193f95e9eb34da7a8056c676aeda694212e31fa Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 16 Jan 2024 16:00:55 +0100 -Subject: [PATCH 020/102] net: dsa: Support MDB memberships whose L2 addresses +Subject: [PATCH 020/128] net: dsa: Support MDB memberships whose L2 addresses overlap Multiple IP multicast groups (32 for v4, 2^80 for v6) map to the same diff --git a/patches/linux/6.18.57/0021-net-dsa-Support-EtherType-based-priority-overrides.patch b/patches/linux/6.18.57/0021-net-dsa-Support-EtherType-based-priority-overrides.patch index e82ffa3b95..91f50218b0 100644 --- a/patches/linux/6.18.57/0021-net-dsa-Support-EtherType-based-priority-overrides.patch +++ b/patches/linux/6.18.57/0021-net-dsa-Support-EtherType-based-priority-overrides.patch @@ -1,7 +1,7 @@ From 7f07711bf7200141a3963592126a5395cf50483c Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 21 Mar 2024 19:12:15 +0100 -Subject: [PATCH 021/102] net: dsa: Support EtherType based priority overrides +Subject: [PATCH 021/128] net: dsa: Support EtherType based priority overrides --- include/net/dsa.h | 4 ++++ diff --git a/patches/linux/6.18.57/0022-net-dsa-mv88e6xxx-Support-EtherType-based-priority-o.patch b/patches/linux/6.18.57/0022-net-dsa-mv88e6xxx-Support-EtherType-based-priority-o.patch index d3518e8cb0..bf319178ab 100644 --- a/patches/linux/6.18.57/0022-net-dsa-mv88e6xxx-Support-EtherType-based-priority-o.patch +++ b/patches/linux/6.18.57/0022-net-dsa-mv88e6xxx-Support-EtherType-based-priority-o.patch @@ -1,7 +1,7 @@ From e15ceaac9b2ebe24422e42c6b7fa9f2b7daee2f8 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Fri, 22 Mar 2024 16:15:43 +0100 -Subject: [PATCH 022/102] net: dsa: mv88e6xxx: Support EtherType based priority +Subject: [PATCH 022/128] net: dsa: mv88e6xxx: Support EtherType based priority overrides --- diff --git a/patches/linux/6.18.57/0023-net-dsa-mv88e6xxx-Add-mqprio-qdisc-support.patch b/patches/linux/6.18.57/0023-net-dsa-mv88e6xxx-Add-mqprio-qdisc-support.patch index e0ac0a5027..a5c793ee3f 100644 --- a/patches/linux/6.18.57/0023-net-dsa-mv88e6xxx-Add-mqprio-qdisc-support.patch +++ b/patches/linux/6.18.57/0023-net-dsa-mv88e6xxx-Add-mqprio-qdisc-support.patch @@ -1,7 +1,7 @@ From 94fa7cbb59c02912a1ec8d203ea2923c422d48c1 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 28 May 2024 11:04:22 +0200 -Subject: [PATCH 023/102] net: dsa: mv88e6xxx: Add mqprio qdisc support +Subject: [PATCH 023/128] net: dsa: mv88e6xxx: Add mqprio qdisc support Add support for attaching mqprio qdisc's to mv88e6xxx ports and use the packet's traffic class as the outgoing priority when no PCP bits diff --git a/patches/linux/6.18.57/0024-net-dsa-mv88e6xxx-Use-VLAN-prio-over-IP-when-both-ar.patch b/patches/linux/6.18.57/0024-net-dsa-mv88e6xxx-Use-VLAN-prio-over-IP-when-both-ar.patch index 1bcfccdfe5..06b68a3741 100644 --- a/patches/linux/6.18.57/0024-net-dsa-mv88e6xxx-Use-VLAN-prio-over-IP-when-both-ar.patch +++ b/patches/linux/6.18.57/0024-net-dsa-mv88e6xxx-Use-VLAN-prio-over-IP-when-both-ar.patch @@ -1,7 +1,7 @@ From 57ed0e3c9767f4b57a0643d207f062a7fb776190 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 29 May 2024 13:20:41 +0200 -Subject: [PATCH 024/102] net: dsa: mv88e6xxx: Use VLAN prio over IP when both +Subject: [PATCH 024/128] net: dsa: mv88e6xxx: Use VLAN prio over IP when both are available Switch the priority sourcing precdence to prefer VLAN PCP over IP diff --git a/patches/linux/6.18.57/0025-FIX-net-dsa-mv88e6xxx-Trap-locally-terminated-VLANs.patch b/patches/linux/6.18.57/0025-FIX-net-dsa-mv88e6xxx-Trap-locally-terminated-VLANs.patch index a00f160f4d..4e4c7dca25 100644 --- a/patches/linux/6.18.57/0025-FIX-net-dsa-mv88e6xxx-Trap-locally-terminated-VLANs.patch +++ b/patches/linux/6.18.57/0025-FIX-net-dsa-mv88e6xxx-Trap-locally-terminated-VLANs.patch @@ -1,7 +1,7 @@ From 0cba0f7b7ed4589b57ddb43c2e310104aec83766 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 26 Nov 2024 19:45:59 +0100 -Subject: [PATCH 025/102] [FIX] net: dsa: mv88e6xxx: Trap locally terminated +Subject: [PATCH 025/128] [FIX] net: dsa: mv88e6xxx: Trap locally terminated VLANs Before this change, in a setup like the following, packets assigned to diff --git a/patches/linux/6.18.57/0026-net-dsa-mv88e6xxx-collapse-disabled-state-into-block.patch b/patches/linux/6.18.57/0026-net-dsa-mv88e6xxx-collapse-disabled-state-into-block.patch index 4270701d35..382385b025 100644 --- a/patches/linux/6.18.57/0026-net-dsa-mv88e6xxx-collapse-disabled-state-into-block.patch +++ b/patches/linux/6.18.57/0026-net-dsa-mv88e6xxx-collapse-disabled-state-into-block.patch @@ -1,7 +1,7 @@ From 2e7660ff03b93bf10fbe2fa7a0f3cb6daf515966 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Thu, 16 Jan 2025 12:35:12 +0100 -Subject: [PATCH 026/102] net: dsa: mv88e6xxx: collapse disabled state into +Subject: [PATCH 026/128] net: dsa: mv88e6xxx: collapse disabled state into blocking This patch changes the behavior of switchcore ports wrt. the port state. diff --git a/patches/linux/6.18.57/0027-net-dsa-mv88e6xxx-Only-activate-LAG-offloading-when-.patch b/patches/linux/6.18.57/0027-net-dsa-mv88e6xxx-Only-activate-LAG-offloading-when-.patch index 5351f0e75b..9bed9dc8fb 100644 --- a/patches/linux/6.18.57/0027-net-dsa-mv88e6xxx-Only-activate-LAG-offloading-when-.patch +++ b/patches/linux/6.18.57/0027-net-dsa-mv88e6xxx-Only-activate-LAG-offloading-when-.patch @@ -1,7 +1,7 @@ From 3a7e7ca03128893fa0f4e879c726e46c88730dd6 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 12 Feb 2025 22:03:14 +0100 -Subject: [PATCH 027/102] net: dsa: mv88e6xxx: Only activate LAG offloading +Subject: [PATCH 027/128] net: dsa: mv88e6xxx: Only activate LAG offloading when bridged The current port isolation scheme for mv88e6xxx is detailed here: diff --git a/patches/linux/6.18.57/0028-net-dsa-mv88e6xxx-Add-LED-support-for-6393X.patch b/patches/linux/6.18.57/0028-net-dsa-mv88e6xxx-Add-LED-support-for-6393X.patch index c87c6d3fd4..dc27ecb945 100644 --- a/patches/linux/6.18.57/0028-net-dsa-mv88e6xxx-Add-LED-support-for-6393X.patch +++ b/patches/linux/6.18.57/0028-net-dsa-mv88e6xxx-Add-LED-support-for-6393X.patch @@ -1,7 +1,7 @@ From fc9673985eb18fb570381aafe73919b5f4964a86 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 14 Jan 2026 18:22:41 +0100 -Subject: [PATCH 028/102] net: dsa: mv88e6xxx: Add LED support for 6393X +Subject: [PATCH 028/128] net: dsa: mv88e6xxx: Add LED support for 6393X Original commit: commit 462277b926140ee2d231317e92afb6cabf640268 diff --git a/patches/linux/6.18.57/0029-wifi-brcmfmac-check-connection-state-before-querying.patch b/patches/linux/6.18.57/0029-wifi-brcmfmac-check-connection-state-before-querying.patch index 5830945c0a..573cbb1f20 100644 --- a/patches/linux/6.18.57/0029-wifi-brcmfmac-check-connection-state-before-querying.patch +++ b/patches/linux/6.18.57/0029-wifi-brcmfmac-check-connection-state-before-querying.patch @@ -1,7 +1,7 @@ From 708cc18d4ebb8ab95cb998a8b9b0966f17329fa7 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Mon, 19 Jan 2026 13:06:53 +0100 -Subject: [PATCH 029/102] wifi: brcmfmac: check connection state before +Subject: [PATCH 029/128] wifi: brcmfmac: check connection state before querying station info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0030-wifi-brcmfmac-suppress-log-spam-for-regulatory-restr.patch b/patches/linux/6.18.57/0030-wifi-brcmfmac-suppress-log-spam-for-regulatory-restr.patch index 05ac7db3f3..628e6a9d77 100644 --- a/patches/linux/6.18.57/0030-wifi-brcmfmac-suppress-log-spam-for-regulatory-restr.patch +++ b/patches/linux/6.18.57/0030-wifi-brcmfmac-suppress-log-spam-for-regulatory-restr.patch @@ -1,7 +1,7 @@ From 71c8a7ad6583d59f611f4b2a14c66570ec2d73fb Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 20 Jan 2026 20:12:10 +0100 -Subject: [PATCH 030/102] wifi: brcmfmac: suppress log spam for +Subject: [PATCH 030/128] wifi: brcmfmac: suppress log spam for regulatory-restricted channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0031-wifi-brcmfmac-reduce-log-noise-during-AP-to-station-.patch b/patches/linux/6.18.57/0031-wifi-brcmfmac-reduce-log-noise-during-AP-to-station-.patch index 87278995de..7832c5e879 100644 --- a/patches/linux/6.18.57/0031-wifi-brcmfmac-reduce-log-noise-during-AP-to-station-.patch +++ b/patches/linux/6.18.57/0031-wifi-brcmfmac-reduce-log-noise-during-AP-to-station-.patch @@ -1,7 +1,7 @@ From 2e7e5bd36b3618cfa2f5aa299100154aa789ee57 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 20 Jan 2026 20:18:45 +0100 -Subject: [PATCH 031/102] wifi: brcmfmac: reduce log noise during AP to station +Subject: [PATCH 031/128] wifi: brcmfmac: reduce log noise during AP to station transition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0032-net-phy-air_en8811h-add-OF-device-table-for-auto-loa.patch b/patches/linux/6.18.57/0032-net-phy-air_en8811h-add-OF-device-table-for-auto-loa.patch index 006b3a7639..a72568bc06 100644 --- a/patches/linux/6.18.57/0032-net-phy-air_en8811h-add-OF-device-table-for-auto-loa.patch +++ b/patches/linux/6.18.57/0032-net-phy-air_en8811h-add-OF-device-table-for-auto-loa.patch @@ -1,7 +1,7 @@ From a5fc393818f3b0784538aaa169fb19cd7b7a7af4 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 17 Feb 2026 21:59:59 +0100 -Subject: [PATCH 032/102] net: phy: air_en8811h: add OF device table for +Subject: [PATCH 032/128] net: phy: air_en8811h: add OF device table for auto-loading mdio_uevent() only emits an OF-style MODALIAS via diff --git a/patches/linux/6.18.57/0033-drm-vc4-dsi-enable-video-and-then-retry-failed-trans.patch b/patches/linux/6.18.57/0033-drm-vc4-dsi-enable-video-and-then-retry-failed-trans.patch index ae787536dd..6efb4f25ba 100644 --- a/patches/linux/6.18.57/0033-drm-vc4-dsi-enable-video-and-then-retry-failed-trans.patch +++ b/patches/linux/6.18.57/0033-drm-vc4-dsi-enable-video-and-then-retry-failed-trans.patch @@ -1,7 +1,7 @@ From 73fdfca5d09d2a9784eb453065d6f2bb0aea2e52 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 20 Sep 2024 12:05:18 +0100 -Subject: [PATCH 033/102] drm: vc4: dsi: enable video and then retry failed +Subject: [PATCH 033/128] drm: vc4: dsi: enable video and then retry failed transfers The DSI block appears to be able to come up stuck in a condition where diff --git a/patches/linux/6.18.57/0034-drm-vc4-dsi-Clocks-should-be-running-before-reset.patch b/patches/linux/6.18.57/0034-drm-vc4-dsi-Clocks-should-be-running-before-reset.patch index d7ba9435b2..13915591b4 100644 --- a/patches/linux/6.18.57/0034-drm-vc4-dsi-Clocks-should-be-running-before-reset.patch +++ b/patches/linux/6.18.57/0034-drm-vc4-dsi-Clocks-should-be-running-before-reset.patch @@ -1,7 +1,7 @@ From ac4c2819f28196619f842bd72a92c372f69f3ff2 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 8 Jun 2022 17:23:47 +0100 -Subject: [PATCH 034/102] drm: vc4: dsi: Clocks should be running before reset +Subject: [PATCH 034/128] drm: vc4: dsi: Clocks should be running before reset The initialisation sequence differs slightly from the documentation in that the clocks are meant to be running before resets and diff --git a/patches/linux/6.18.57/0035-drm-vc4-Ensure-DSI-is-enabled-for-FIFO-resets.patch b/patches/linux/6.18.57/0035-drm-vc4-Ensure-DSI-is-enabled-for-FIFO-resets.patch index ac6d5b5156..d1405bebc4 100644 --- a/patches/linux/6.18.57/0035-drm-vc4-Ensure-DSI-is-enabled-for-FIFO-resets.patch +++ b/patches/linux/6.18.57/0035-drm-vc4-Ensure-DSI-is-enabled-for-FIFO-resets.patch @@ -1,7 +1,7 @@ From 4083619702e09ef4fb60273bf788dcf092cfd3d1 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 5 Apr 2024 17:51:55 +0100 -Subject: [PATCH 035/102] drm/vc4: Ensure DSI is enabled for FIFO resets +Subject: [PATCH 035/128] drm/vc4: Ensure DSI is enabled for FIFO resets The block must be enabled for the FIFO resets to be actioned, so ensure this is the case. diff --git a/patches/linux/6.18.57/0036-drm-vc4-Reset-DSI-AFE-on-disable.patch b/patches/linux/6.18.57/0036-drm-vc4-Reset-DSI-AFE-on-disable.patch index fcc6fae513..b21f5b95ce 100644 --- a/patches/linux/6.18.57/0036-drm-vc4-Reset-DSI-AFE-on-disable.patch +++ b/patches/linux/6.18.57/0036-drm-vc4-Reset-DSI-AFE-on-disable.patch @@ -1,7 +1,7 @@ From 58d4282e5dd75669d962b9538962e74fa534742a Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 26 May 2022 18:56:19 +0100 -Subject: [PATCH 036/102] drm: vc4: Reset DSI AFE on disable +Subject: [PATCH 036/128] drm: vc4: Reset DSI AFE on disable vc4_dsi_bridge_disable wasn't resetting things during shutdown, so add that in. diff --git a/patches/linux/6.18.57/0037-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch b/patches/linux/6.18.57/0037-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch index fcbf79bee0..b845c7d7d5 100644 --- a/patches/linux/6.18.57/0037-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch +++ b/patches/linux/6.18.57/0037-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch @@ -1,7 +1,7 @@ From 6ad4174eafa120b81cc9b6aa500c3a798fcdb03b Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 20 Nov 2024 13:58:08 +0000 -Subject: [PATCH 037/102] drm: vc4: dsi: Handle the different command FIFO +Subject: [PATCH 037/128] drm: vc4: dsi: Handle the different command FIFO widths DSI0 and DSI1 have different widths for the command FIFO (24bit diff --git a/patches/linux/6.18.57/0038-drm-bridge-tc358762-Program-the-DPI-mode-into-the-ch.patch b/patches/linux/6.18.57/0038-drm-bridge-tc358762-Program-the-DPI-mode-into-the-ch.patch index f92453f65f..d2c3f36a13 100644 --- a/patches/linux/6.18.57/0038-drm-bridge-tc358762-Program-the-DPI-mode-into-the-ch.patch +++ b/patches/linux/6.18.57/0038-drm-bridge-tc358762-Program-the-DPI-mode-into-the-ch.patch @@ -1,7 +1,7 @@ From cb7f2812a0484abf0c0140a7b9f686b6e772f28b Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 9 Jan 2024 17:37:00 +0000 -Subject: [PATCH 038/102] drm/bridge: tc358762: Program the DPI mode into the +Subject: [PATCH 038/128] drm/bridge: tc358762: Program the DPI mode into the chip The autodetection of resolution/timing by the TC358762 can lead diff --git a/patches/linux/6.18.57/0039-drm-bridge-tc358762-revert-move-ops-to-enable.patch b/patches/linux/6.18.57/0039-drm-bridge-tc358762-revert-move-ops-to-enable.patch index e2c89dc4e1..0b5705e07f 100644 --- a/patches/linux/6.18.57/0039-drm-bridge-tc358762-revert-move-ops-to-enable.patch +++ b/patches/linux/6.18.57/0039-drm-bridge-tc358762-revert-move-ops-to-enable.patch @@ -1,7 +1,7 @@ From bdebf5183c87e4e023e08a252556e79053ecc997 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 9 Jan 2024 18:44:49 +0000 -Subject: [PATCH 039/102] drm/bridge: tc358762: revert move ops to enable +Subject: [PATCH 039/128] drm/bridge: tc358762: revert move ops to enable Reverts 8a4b2fc9c91a ("drm/bridge: tc358762: Split register programming from pre-enable to enable") as we want the config commands sent before video starts. diff --git a/patches/linux/6.18.57/0040-drm-bridge-tc358762-Set-pre_enabled-on-pre_enable-to.patch b/patches/linux/6.18.57/0040-drm-bridge-tc358762-Set-pre_enabled-on-pre_enable-to.patch index b91b3e320a..b4a1448067 100644 --- a/patches/linux/6.18.57/0040-drm-bridge-tc358762-Set-pre_enabled-on-pre_enable-to.patch +++ b/patches/linux/6.18.57/0040-drm-bridge-tc358762-Set-pre_enabled-on-pre_enable-to.patch @@ -1,7 +1,7 @@ From a7a7bfa99431333b70b791e41477b691c70ecb56 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Sat, 4 Apr 2026 18:04:19 +0200 -Subject: [PATCH 040/102] drm/bridge: tc358762: Set pre_enabled on pre_enable +Subject: [PATCH 040/128] drm/bridge: tc358762: Set pre_enabled on pre_enable to prevent regulator imbalance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0041-net-pcs-add-standalone-PCS-registration-infrastructu.patch b/patches/linux/6.18.57/0041-net-pcs-add-standalone-PCS-registration-infrastructu.patch index 3e858155f2..a58b0d7578 100644 --- a/patches/linux/6.18.57/0041-net-pcs-add-standalone-PCS-registration-infrastructu.patch +++ b/patches/linux/6.18.57/0041-net-pcs-add-standalone-PCS-registration-infrastructu.patch @@ -1,7 +1,7 @@ From bfdeabb27eb5e9b40eaa9605ee5d49d4a44be648 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 5 Apr 2026 11:33:00 +0200 -Subject: [PATCH 041/102] net/pcs: add standalone PCS registration +Subject: [PATCH 041/128] net/pcs: add standalone PCS registration infrastructure Add a simple registration mechanism that allows platform PCS drivers to diff --git a/patches/linux/6.18.57/0042-net-pcs-add-MediaTek-MT7988-USXGMII-PCS-driver.patch b/patches/linux/6.18.57/0042-net-pcs-add-MediaTek-MT7988-USXGMII-PCS-driver.patch index 7ae2bb8d85..ede4fa60ff 100644 --- a/patches/linux/6.18.57/0042-net-pcs-add-MediaTek-MT7988-USXGMII-PCS-driver.patch +++ b/patches/linux/6.18.57/0042-net-pcs-add-MediaTek-MT7988-USXGMII-PCS-driver.patch @@ -1,7 +1,7 @@ From 372e882a4d8eba6dc3bf3523a2a6f854aec7a277 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 6 Apr 2026 14:14:23 +0200 -Subject: [PATCH 042/102] net/pcs: add MediaTek MT7988 USXGMII PCS driver +Subject: [PATCH 042/128] net/pcs: add MediaTek MT7988 USXGMII PCS driver Add a PCS driver for the USXGMII subsystem found in the MediaTek MT7988 SoC (usxgmiisys0 at 0x10080000, usxgmiisys1 at 0x10081000). The hardware diff --git a/patches/linux/6.18.57/0043-net-ethernet-mediatek-add-USXGMII-support-for-MT7988.patch b/patches/linux/6.18.57/0043-net-ethernet-mediatek-add-USXGMII-support-for-MT7988.patch index cd75fad633..03e42ea95a 100644 --- a/patches/linux/6.18.57/0043-net-ethernet-mediatek-add-USXGMII-support-for-MT7988.patch +++ b/patches/linux/6.18.57/0043-net-ethernet-mediatek-add-USXGMII-support-for-MT7988.patch @@ -1,7 +1,7 @@ From b15db9efa791d47f2d39beff1a25d5f795caf1f4 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 6 Apr 2026 14:15:43 +0200 -Subject: [PATCH 043/102] net: ethernet: mediatek: add USXGMII support for +Subject: [PATCH 043/128] net: ethernet: mediatek: add USXGMII support for MT7988 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0044-arm64-dts-mediatek-mt7988a-add-USXGMII-PCS-nodes.patch b/patches/linux/6.18.57/0044-arm64-dts-mediatek-mt7988a-add-USXGMII-PCS-nodes.patch index abe48fb5ad..4604cc903c 100644 --- a/patches/linux/6.18.57/0044-arm64-dts-mediatek-mt7988a-add-USXGMII-PCS-nodes.patch +++ b/patches/linux/6.18.57/0044-arm64-dts-mediatek-mt7988a-add-USXGMII-PCS-nodes.patch @@ -1,7 +1,7 @@ From 96b51720c66858d5ad611c6d287775ae5a209153 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 6 Apr 2026 14:15:56 +0200 -Subject: [PATCH 044/102] arm64: dts: mediatek: mt7988a: add USXGMII PCS nodes +Subject: [PATCH 044/128] arm64: dts: mediatek: mt7988a: add USXGMII PCS nodes Add device nodes for the two USXGMII subsystem blocks (usxgmiisys0 at 0x10080000 and usxgmiisys1 at 0x10081000), each referencing its clock, diff --git a/patches/linux/6.18.57/0045-arm64-dts-mediatek-bananapi-bpi-r4-enable-SFP-ports-.patch b/patches/linux/6.18.57/0045-arm64-dts-mediatek-bananapi-bpi-r4-enable-SFP-ports-.patch index 63da9bf593..dc56d54367 100644 --- a/patches/linux/6.18.57/0045-arm64-dts-mediatek-bananapi-bpi-r4-enable-SFP-ports-.patch +++ b/patches/linux/6.18.57/0045-arm64-dts-mediatek-bananapi-bpi-r4-enable-SFP-ports-.patch @@ -1,7 +1,7 @@ From 036272e6796ba961028a1a99a8eeebc40403cbeb Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 6 Apr 2026 14:16:11 +0200 -Subject: [PATCH 045/102] arm64: dts: mediatek: bananapi-bpi-r4: enable SFP+ +Subject: [PATCH 045/128] arm64: dts: mediatek: bananapi-bpi-r4: enable SFP+ ports and WPS button Enable the SFP+ cages wired to gmac1 and gmac2. The USXGMII PCS nodes diff --git a/patches/linux/6.18.57/0046-net-phy-sfp-add-OEM-SFP-10G-T-I-quirk.patch b/patches/linux/6.18.57/0046-net-phy-sfp-add-OEM-SFP-10G-T-I-quirk.patch index 1067b96aee..46fef3ac58 100644 --- a/patches/linux/6.18.57/0046-net-phy-sfp-add-OEM-SFP-10G-T-I-quirk.patch +++ b/patches/linux/6.18.57/0046-net-phy-sfp-add-OEM-SFP-10G-T-I-quirk.patch @@ -1,7 +1,7 @@ From b9a7c9f7a0a95a0fba5a652007eb596f6010b155 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 7 Apr 2026 07:34:52 +0200 -Subject: [PATCH 046/102] net: phy: sfp: add OEM SFP-10G-T-I quirk +Subject: [PATCH 046/128] net: phy: sfp: add OEM SFP-10G-T-I quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/patches/linux/6.18.57/0047-net-dsa-mv88e6xxx-Trap-PTP-frames-on-timestamping-po.patch b/patches/linux/6.18.57/0047-net-dsa-mv88e6xxx-Trap-PTP-frames-on-timestamping-po.patch index a69056d257..ab3173f799 100644 --- a/patches/linux/6.18.57/0047-net-dsa-mv88e6xxx-Trap-PTP-frames-on-timestamping-po.patch +++ b/patches/linux/6.18.57/0047-net-dsa-mv88e6xxx-Trap-PTP-frames-on-timestamping-po.patch @@ -1,7 +1,7 @@ From 54ac0d29323338b43bbde3d894540ae80951d90b Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Fri, 17 Apr 2026 09:13:04 +0000 -Subject: [PATCH 047/102] net: dsa: mv88e6xxx: Trap PTP frames on timestamping +Subject: [PATCH 047/128] net: dsa: mv88e6xxx: Trap PTP frames on timestamping ports, on 6393X Similar to the Peridot (6390), the designation of PTP frames as diff --git a/patches/linux/6.18.57/0048-wifi-mt76-mt7615-add-MODULE_DEVICE_TABLE-for-mt7622-.patch b/patches/linux/6.18.57/0048-wifi-mt76-mt7615-add-MODULE_DEVICE_TABLE-for-mt7622-.patch index 92c5a74a3a..4c90781ca0 100644 --- a/patches/linux/6.18.57/0048-wifi-mt76-mt7615-add-MODULE_DEVICE_TABLE-for-mt7622-.patch +++ b/patches/linux/6.18.57/0048-wifi-mt76-mt7615-add-MODULE_DEVICE_TABLE-for-mt7622-.patch @@ -1,7 +1,7 @@ From ad6aefc83c5592f3d02b8d3f48d50d486b871abc Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 28 Apr 2026 15:30:01 +0200 -Subject: [PATCH 048/102] wifi: mt76: mt7615: add MODULE_DEVICE_TABLE for +Subject: [PATCH 048/128] wifi: mt76: mt7615: add MODULE_DEVICE_TABLE for mt7622 wmac Without MODULE_DEVICE_TABLE(of, ...) the OF compatible alias is never diff --git a/patches/linux/6.18.57/0049-PCI-mediatek-gen3-Fix-PERST-control-timing-during-sy.patch b/patches/linux/6.18.57/0049-PCI-mediatek-gen3-Fix-PERST-control-timing-during-sy.patch index 842f5df4b2..3d5fecd7d9 100644 --- a/patches/linux/6.18.57/0049-PCI-mediatek-gen3-Fix-PERST-control-timing-during-sy.patch +++ b/patches/linux/6.18.57/0049-PCI-mediatek-gen3-Fix-PERST-control-timing-during-sy.patch @@ -1,7 +1,7 @@ From ba2b448ecb61c6ddd3a4227bdc577d74e3a4cba1 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 22 Apr 2026 10:24:43 +0200 -Subject: [PATCH 049/102] PCI: mediatek-gen3: Fix PERST# control timing during +Subject: [PATCH 049/128] PCI: mediatek-gen3: Fix PERST# control timing during system startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0050-net-dsa-mv88e6xxx-Derive-LED-names-from-device-name.patch b/patches/linux/6.18.57/0050-net-dsa-mv88e6xxx-Derive-LED-names-from-device-name.patch index 6ca04401be..ff06e9efdc 100644 --- a/patches/linux/6.18.57/0050-net-dsa-mv88e6xxx-Derive-LED-names-from-device-name.patch +++ b/patches/linux/6.18.57/0050-net-dsa-mv88e6xxx-Derive-LED-names-from-device-name.patch @@ -1,7 +1,7 @@ From a70a80b94fe0737ff3b8d092713984ff1ffbdf8b Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 12 Aug 2026 10:08:53 +0200 -Subject: [PATCH 050/102] net: dsa: mv88e6xxx: Derive LED names from device +Subject: [PATCH 050/128] net: dsa: mv88e6xxx: Derive LED names from device name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0051-phy-sparx5-serdes-make-it-selectable-for-ARCH_LAN969.patch b/patches/linux/6.18.57/0051-phy-sparx5-serdes-make-it-selectable-for-ARCH_LAN969.patch index aa1f8e388e..29a1de18b2 100644 --- a/patches/linux/6.18.57/0051-phy-sparx5-serdes-make-it-selectable-for-ARCH_LAN969.patch +++ b/patches/linux/6.18.57/0051-phy-sparx5-serdes-make-it-selectable-for-ARCH_LAN969.patch @@ -1,7 +1,7 @@ From 7b587fadb354fc4bafc4a441cded3cc9ef0aa160 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 31 Oct 2025 13:18:12 +0100 -Subject: [PATCH 051/102] phy: sparx5-serdes: make it selectable for +Subject: [PATCH 051/128] phy: sparx5-serdes: make it selectable for ARCH_LAN969X LAN969x uses the SparX-5 SERDES driver, so make it selectable for diff --git a/patches/linux/6.18.57/0052-net-sparx5-fix-wrong-chip-ids-for-TSN-SKUs.patch b/patches/linux/6.18.57/0052-net-sparx5-fix-wrong-chip-ids-for-TSN-SKUs.patch index 6227400a2e..365139426a 100644 --- a/patches/linux/6.18.57/0052-net-sparx5-fix-wrong-chip-ids-for-TSN-SKUs.patch +++ b/patches/linux/6.18.57/0052-net-sparx5-fix-wrong-chip-ids-for-TSN-SKUs.patch @@ -1,7 +1,7 @@ From 97735a9198026c9ffa6c991d25eab0f5bc0d9698 Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Wed, 6 May 2026 09:25:38 +0200 -Subject: [PATCH 052/102] net: sparx5: fix wrong chip ids for TSN SKUs +Subject: [PATCH 052/128] net: sparx5: fix wrong chip ids for TSN SKUs The TSN SKUs in enum spx5_target_chiptype have incorrect IDs: diff --git a/patches/linux/6.18.57/0053-net-sparx5-configure-serdes-for-1000BASE-X-in-sparx5.patch b/patches/linux/6.18.57/0053-net-sparx5-configure-serdes-for-1000BASE-X-in-sparx5.patch index 3ce78d44df..73aea4f5ed 100644 --- a/patches/linux/6.18.57/0053-net-sparx5-configure-serdes-for-1000BASE-X-in-sparx5.patch +++ b/patches/linux/6.18.57/0053-net-sparx5-configure-serdes-for-1000BASE-X-in-sparx5.patch @@ -1,7 +1,7 @@ From c3ea33818e99e769ca3b33323df09cae87b18800 Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Wed, 6 May 2026 09:25:39 +0200 -Subject: [PATCH 053/102] net: sparx5: configure serdes for 1000BASE-X in +Subject: [PATCH 053/128] net: sparx5: configure serdes for 1000BASE-X in sparx5_port_init() sparx5_port_init() only invokes sparx5_serdes_set() and the associated diff --git a/patches/linux/6.18.57/0054-dt-bindings-mmc-atmel-sama5d2-sdhci-add-microchip-la.patch b/patches/linux/6.18.57/0054-dt-bindings-mmc-atmel-sama5d2-sdhci-add-microchip-la.patch index 1af7983ce8..ec578b05b2 100644 --- a/patches/linux/6.18.57/0054-dt-bindings-mmc-atmel-sama5d2-sdhci-add-microchip-la.patch +++ b/patches/linux/6.18.57/0054-dt-bindings-mmc-atmel-sama5d2-sdhci-add-microchip-la.patch @@ -1,7 +1,7 @@ From f7003e2bb12d95583101d4ef55eab6f21eb9cd6f Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:35:29 +0200 -Subject: [PATCH 054/102] dt-bindings: mmc: atmel,sama5d2-sdhci: add +Subject: [PATCH 054/128] dt-bindings: mmc: atmel,sama5d2-sdhci: add microchip,lan969x-sdhci The LAN969x SDMMC controller has its own base clock divider and, unlike diff --git a/patches/linux/6.18.57/0055-mmc-sdhci-of-at91-add-LAN969x-support.patch b/patches/linux/6.18.57/0055-mmc-sdhci-of-at91-add-LAN969x-support.patch index 5de2180916..f6b9c3ffba 100644 --- a/patches/linux/6.18.57/0055-mmc-sdhci-of-at91-add-LAN969x-support.patch +++ b/patches/linux/6.18.57/0055-mmc-sdhci-of-at91-add-LAN969x-support.patch @@ -1,7 +1,7 @@ From cac6b74294ffda4f256e520bc9d3d0e6849ab9a7 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:35:44 +0200 -Subject: [PATCH 055/102] mmc: sdhci-of-at91: add LAN969x support +Subject: [PATCH 055/128] mmc: sdhci-of-at91: add LAN969x support The LAN969x SDMMC controller is an Atmel SDMMC IP block, but the driver has no compatible for it, so the eMMC on LAN969x boards never probes. diff --git a/patches/linux/6.18.57/0056-mmc-sdhci-of-at91-stop-SDCLK-on-reset-and-add-eMMC-h.patch b/patches/linux/6.18.57/0056-mmc-sdhci-of-at91-stop-SDCLK-on-reset-and-add-eMMC-h.patch index cad2ad8edd..3604e7c7b1 100644 --- a/patches/linux/6.18.57/0056-mmc-sdhci-of-at91-stop-SDCLK-on-reset-and-add-eMMC-h.patch +++ b/patches/linux/6.18.57/0056-mmc-sdhci-of-at91-stop-SDCLK-on-reset-and-add-eMMC-h.patch @@ -1,7 +1,7 @@ From dd296a20b251c66bbb82a71e746b1a59a43a4b0f Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:37:48 +0200 -Subject: [PATCH 056/102] mmc: sdhci-of-at91: stop SDCLK on reset and add eMMC +Subject: [PATCH 056/128] mmc: sdhci-of-at91: stop SDCLK on reset and add eMMC hardware reset The controller is reset, and its signaling mode changed, with SDCLK diff --git a/patches/linux/6.18.57/0057-net-sparx5-lan969x-populate-netdev-of_node.patch b/patches/linux/6.18.57/0057-net-sparx5-lan969x-populate-netdev-of_node.patch index 266f97e81e..99d8c5569c 100644 --- a/patches/linux/6.18.57/0057-net-sparx5-lan969x-populate-netdev-of_node.patch +++ b/patches/linux/6.18.57/0057-net-sparx5-lan969x-populate-netdev-of_node.patch @@ -1,7 +1,7 @@ From 104588391db5e1d6506fc02b8dae79e01b95656a Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 10 Nov 2025 13:42:53 +0100 -Subject: [PATCH 057/102] net: sparx5/lan969x: populate netdev of_node +Subject: [PATCH 057/128] net: sparx5/lan969x: populate netdev of_node Populate of_node for the port netdevs, to make the individual ports of_nodes available in sysfs. diff --git a/patches/linux/6.18.57/0058-arm64-dts-microchip-add-LAN969x-clock-header-file.patch b/patches/linux/6.18.57/0058-arm64-dts-microchip-add-LAN969x-clock-header-file.patch index 2957194506..d58f2006a1 100644 --- a/patches/linux/6.18.57/0058-arm64-dts-microchip-add-LAN969x-clock-header-file.patch +++ b/patches/linux/6.18.57/0058-arm64-dts-microchip-add-LAN969x-clock-header-file.patch @@ -1,7 +1,7 @@ From 684a31e6deb1e1037ffaba5af86a7a1298fb6256 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 2 Mar 2026 12:20:11 +0100 -Subject: [PATCH 058/102] arm64: dts: microchip: add LAN969x clock header file +Subject: [PATCH 058/128] arm64: dts: microchip: add LAN969x clock header file LAN969x uses hardware clock indexes, so document theses in a header to make them humanly readable. diff --git a/patches/linux/6.18.57/0059-arm64-dts-microchip-add-LAN969x-support.patch b/patches/linux/6.18.57/0059-arm64-dts-microchip-add-LAN969x-support.patch index e6f53851ed..cb3be8fc4e 100644 --- a/patches/linux/6.18.57/0059-arm64-dts-microchip-add-LAN969x-support.patch +++ b/patches/linux/6.18.57/0059-arm64-dts-microchip-add-LAN969x-support.patch @@ -1,7 +1,7 @@ From dc944c2dac81be7148374122391cb1390224d329 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 2 Mar 2026 12:20:12 +0100 -Subject: [PATCH 059/102] arm64: dts: microchip: add LAN969x support +Subject: [PATCH 059/128] arm64: dts: microchip: add LAN969x support Add support for Microchip LAN969x switch SoC series by adding the SoC DTSI. diff --git a/patches/linux/6.18.57/0060-arm64-dts-microchip-add-EV23X71A-board.patch b/patches/linux/6.18.57/0060-arm64-dts-microchip-add-EV23X71A-board.patch index 116cde43d3..684c553200 100644 --- a/patches/linux/6.18.57/0060-arm64-dts-microchip-add-EV23X71A-board.patch +++ b/patches/linux/6.18.57/0060-arm64-dts-microchip-add-EV23X71A-board.patch @@ -1,7 +1,7 @@ From e1d6da07c42ab6d9e2765a16afba208898abb8b3 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 2 Mar 2026 12:20:14 +0100 -Subject: [PATCH 060/102] arm64: dts: microchip: add EV23X71A board +Subject: [PATCH 060/128] arm64: dts: microchip: add EV23X71A board Microchip EV23X71A is an LAN9696 based evaluation board. diff --git a/patches/linux/6.18.57/0061-arm64-dts-microchip-lan969x-add-OTP-node.patch b/patches/linux/6.18.57/0061-arm64-dts-microchip-lan969x-add-OTP-node.patch index 70a9fff1c4..1c41a42149 100644 --- a/patches/linux/6.18.57/0061-arm64-dts-microchip-lan969x-add-OTP-node.patch +++ b/patches/linux/6.18.57/0061-arm64-dts-microchip-lan969x-add-OTP-node.patch @@ -1,7 +1,7 @@ From a707e5cdc92248cae91c53b3d1f9b41366f52b05 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 15 May 2026 13:59:09 +0200 -Subject: [PATCH 061/102] arm64: dts: microchip: lan969x: add OTP node +Subject: [PATCH 061/128] arm64: dts: microchip: lan969x: add OTP node Add the required OTP on LAN969x. diff --git a/patches/linux/6.18.57/0062-arm64-dts-microchip-lan969x-add-SDMMC-nodes.patch b/patches/linux/6.18.57/0062-arm64-dts-microchip-lan969x-add-SDMMC-nodes.patch index e5cdc7bb58..0078fd38f7 100644 --- a/patches/linux/6.18.57/0062-arm64-dts-microchip-lan969x-add-SDMMC-nodes.patch +++ b/patches/linux/6.18.57/0062-arm64-dts-microchip-lan969x-add-SDMMC-nodes.patch @@ -1,7 +1,7 @@ From 0b6e2ad8e9c69ea465dfcaf87dd6878a96a8f464 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:38:37 +0200 -Subject: [PATCH 062/102] arm64: dts: microchip: lan969x: add SDMMC nodes +Subject: [PATCH 062/128] arm64: dts: microchip: lan969x: add SDMMC nodes The SoC has two SDMMC controllers, neither of which is described, so boards with eMMC have no way to enable it. Add both, disabled by diff --git a/patches/linux/6.18.57/0063-arm64-dts-microchip-ev23x71a-enable-eMMC.patch b/patches/linux/6.18.57/0063-arm64-dts-microchip-ev23x71a-enable-eMMC.patch index 85d49d7414..20d7497d11 100644 --- a/patches/linux/6.18.57/0063-arm64-dts-microchip-ev23x71a-enable-eMMC.patch +++ b/patches/linux/6.18.57/0063-arm64-dts-microchip-ev23x71a-enable-eMMC.patch @@ -1,7 +1,7 @@ From 8f5529fc1894b4fa292d10191a3b7267af126c81 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:38:37 +0200 -Subject: [PATCH 063/102] arm64: dts: microchip: ev23x71a: enable eMMC +Subject: [PATCH 063/128] arm64: dts: microchip: ev23x71a: enable eMMC The board has an 8-bit eMMC on SDMMC0, and defines the emmc_sd pinctrl group for it, but nothing enables the controller. diff --git a/patches/linux/6.18.57/0064-wifi-brcmfmac-survey-the-requested-interface-not-the.patch b/patches/linux/6.18.57/0064-wifi-brcmfmac-survey-the-requested-interface-not-the.patch index 7d9d026b85..ebb7962007 100644 --- a/patches/linux/6.18.57/0064-wifi-brcmfmac-survey-the-requested-interface-not-the.patch +++ b/patches/linux/6.18.57/0064-wifi-brcmfmac-survey-the-requested-interface-not-the.patch @@ -1,7 +1,7 @@ From 3f11119521883f53a0fc2141820e756192245044 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 30 Jun 2026 15:42:48 +0200 -Subject: [PATCH 064/102] wifi: brcmfmac: survey the requested interface, not +Subject: [PATCH 064/128] wifi: brcmfmac: survey the requested interface, not the primary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0065-wifi-brcmfmac-honor-caller-s-rtnl-lock-when-stopping.patch b/patches/linux/6.18.57/0065-wifi-brcmfmac-honor-caller-s-rtnl-lock-when-stopping.patch index e8b43e0d7b..426a81d802 100644 --- a/patches/linux/6.18.57/0065-wifi-brcmfmac-honor-caller-s-rtnl-lock-when-stopping.patch +++ b/patches/linux/6.18.57/0065-wifi-brcmfmac-honor-caller-s-rtnl-lock-when-stopping.patch @@ -1,7 +1,7 @@ From 54f5351143c719f752d94e0e393f21a4fe7e35a3 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 30 Jun 2026 15:43:14 +0200 -Subject: [PATCH 065/102] wifi: brcmfmac: honor caller's rtnl lock when +Subject: [PATCH 065/128] wifi: brcmfmac: honor caller's rtnl lock when stopping primary interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0066-wifi-brcmfmac-let-cfg_to_ndev-return-NULL-and-harden.patch b/patches/linux/6.18.57/0066-wifi-brcmfmac-let-cfg_to_ndev-return-NULL-and-harden.patch index 56d1ad0d90..4dc82d7316 100644 --- a/patches/linux/6.18.57/0066-wifi-brcmfmac-let-cfg_to_ndev-return-NULL-and-harden.patch +++ b/patches/linux/6.18.57/0066-wifi-brcmfmac-let-cfg_to_ndev-return-NULL-and-harden.patch @@ -1,7 +1,7 @@ From 158bd44aa0b69481a405d90e238d95bdd017b989 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 30 Jun 2026 15:46:25 +0200 -Subject: [PATCH 066/102] wifi: brcmfmac: let cfg_to_ndev() return NULL and +Subject: [PATCH 066/128] wifi: brcmfmac: let cfg_to_ndev() return NULL and harden its callers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0067-wifi-brcmfmac-support-deletion-and-recreation-of-the.patch b/patches/linux/6.18.57/0067-wifi-brcmfmac-support-deletion-and-recreation-of-the.patch index 38d5ef6c7b..3c777e6fee 100644 --- a/patches/linux/6.18.57/0067-wifi-brcmfmac-support-deletion-and-recreation-of-the.patch +++ b/patches/linux/6.18.57/0067-wifi-brcmfmac-support-deletion-and-recreation-of-the.patch @@ -1,7 +1,7 @@ From 4d4beb465e67a38707ee515719f2acd93734ce1d Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 30 Jun 2026 16:05:17 +0200 -Subject: [PATCH 067/102] wifi: brcmfmac: support deletion and recreation of +Subject: [PATCH 067/128] wifi: brcmfmac: support deletion and recreation of the primary interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0068-wifi-brcmfmac-report-port-authorized-after-offloaded.patch b/patches/linux/6.18.57/0068-wifi-brcmfmac-report-port-authorized-after-offloaded.patch index 5979cd5b1f..6e915c1c62 100644 --- a/patches/linux/6.18.57/0068-wifi-brcmfmac-report-port-authorized-after-offloaded.patch +++ b/patches/linux/6.18.57/0068-wifi-brcmfmac-report-port-authorized-after-offloaded.patch @@ -1,7 +1,7 @@ From f73a221a67e6e43fb76ce294773e037d669f44b0 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Thu, 10 Sep 2026 08:29:47 +0200 -Subject: [PATCH 068/102] wifi: brcmfmac: report port authorized after +Subject: [PATCH 068/128] wifi: brcmfmac: report port authorized after offloaded PSK/SAE handshake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0069-dt-bindings-arm-AT91-document-Novarq-Tactical-1000.patch b/patches/linux/6.18.57/0069-dt-bindings-arm-AT91-document-Novarq-Tactical-1000.patch index 5d0b8141cd..31e3a6b38c 100644 --- a/patches/linux/6.18.57/0069-dt-bindings-arm-AT91-document-Novarq-Tactical-1000.patch +++ b/patches/linux/6.18.57/0069-dt-bindings-arm-AT91-document-Novarq-Tactical-1000.patch @@ -1,7 +1,7 @@ From 0efbdf46b65578f1432ab62b282242fa7b6c2cd8 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 9 Jan 2026 13:27:00 +0100 -Subject: [PATCH 069/102] dt-bindings: arm: AT91: document Novarq Tactical 1000 +Subject: [PATCH 069/128] dt-bindings: arm: AT91: document Novarq Tactical 1000 Novarq Tactical 1000 is a Microchip LAN9696 based 24x1G + 4x10G SFP switch. diff --git a/patches/linux/6.18.57/0070-arm64-dts-microchip-add-Novarq-Tactical-1000.patch b/patches/linux/6.18.57/0070-arm64-dts-microchip-add-Novarq-Tactical-1000.patch index 420fa1230f..73e7f357c2 100644 --- a/patches/linux/6.18.57/0070-arm64-dts-microchip-add-Novarq-Tactical-1000.patch +++ b/patches/linux/6.18.57/0070-arm64-dts-microchip-add-Novarq-Tactical-1000.patch @@ -1,7 +1,7 @@ From 4ea0c290a941df0d055be5974db76cd050010205 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 30 Sep 2025 13:37:49 +0200 -Subject: [PATCH 070/102] arm64: dts: microchip: add Novarq Tactical 1000 +Subject: [PATCH 070/128] arm64: dts: microchip: add Novarq Tactical 1000 Novarq Tactical 1000 is a LAN9696 based switch featuring 24x1G and 4x10G SFP ports. diff --git a/patches/linux/6.18.57/0071-arm64-dts-microchip-tactical-1000-add-port-names.patch b/patches/linux/6.18.57/0071-arm64-dts-microchip-tactical-1000-add-port-names.patch index 544ca36119..dfe6bec34c 100644 --- a/patches/linux/6.18.57/0071-arm64-dts-microchip-tactical-1000-add-port-names.patch +++ b/patches/linux/6.18.57/0071-arm64-dts-microchip-tactical-1000-add-port-names.patch @@ -1,7 +1,7 @@ From 1a800417bef77bbce9724da04d54f1b34459e0e5 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 30 Jun 2026 11:23:47 +0200 -Subject: [PATCH 071/102] arm64: dts: microchip: tactical-1000: add port names +Subject: [PATCH 071/128] arm64: dts: microchip: tactical-1000: add port names Now that driver supports parsing the "label" property, populate port names as they are physically wired up. diff --git a/patches/linux/6.18.57/0072-arm64-dts-microchip-tactical-1000-adapt-to-6.18.patch b/patches/linux/6.18.57/0072-arm64-dts-microchip-tactical-1000-adapt-to-6.18.patch index 7c81daa8fa..a9d7c0912d 100644 --- a/patches/linux/6.18.57/0072-arm64-dts-microchip-tactical-1000-adapt-to-6.18.patch +++ b/patches/linux/6.18.57/0072-arm64-dts-microchip-tactical-1000-adapt-to-6.18.patch @@ -1,7 +1,7 @@ From 12cf129fd63364ce6aa8d1b865505493a7e4dcd2 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 20 Sep 2026 12:22:43 +0200 -Subject: [PATCH 072/102] arm64: dts: microchip: tactical-1000: adapt to 6.18 +Subject: [PATCH 072/128] arm64: dts: microchip: tactical-1000: adapt to 6.18 Novarq develop against 7.3, which has three things 6.18 does not: a QSPI controller node, a tmon that also provides the fan PWM, and hence a SoC diff --git a/patches/linux/6.18.57/0073-dt-bindings-arm-AT91-document-EV23X71A-board.patch b/patches/linux/6.18.57/0073-dt-bindings-arm-AT91-document-EV23X71A-board.patch index 9e732ba476..2a06b94491 100644 --- a/patches/linux/6.18.57/0073-dt-bindings-arm-AT91-document-EV23X71A-board.patch +++ b/patches/linux/6.18.57/0073-dt-bindings-arm-AT91-document-EV23X71A-board.patch @@ -1,7 +1,7 @@ From 097bfa1b554e425775024a0c3b5daedab5a293e4 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 2 Mar 2026 12:20:13 +0100 -Subject: [PATCH 073/102] dt-bindings: arm: AT91: document EV23X71A board +Subject: [PATCH 073/128] dt-bindings: arm: AT91: document EV23X71A board Microchip EV23X71A board is an LAN9696 based evaluation board. diff --git a/patches/linux/6.18.57/0074-net-dsa-Skip-DCB-default-priority-init-on-unsupporte.patch b/patches/linux/6.18.57/0074-net-dsa-Skip-DCB-default-priority-init-on-unsupporte.patch index 32d72187a3..e1a20956ad 100644 --- a/patches/linux/6.18.57/0074-net-dsa-Skip-DCB-default-priority-init-on-unsupporte.patch +++ b/patches/linux/6.18.57/0074-net-dsa-Skip-DCB-default-priority-init-on-unsupporte.patch @@ -1,7 +1,7 @@ From 46334fce5a274b6640c1801b144e721f9a89970d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 9 Sep 2026 17:04:21 +0200 -Subject: [PATCH 074/102] net: dsa: Skip DCB default priority init on +Subject: [PATCH 074/128] net: dsa: Skip DCB default priority init on unsupported switches A driver serving several chip generations has one dsa_switch_ops for diff --git a/patches/linux/6.18.57/0075-net-dsa-Generalise-the-global-DCB-APP-mirroring-help.patch b/patches/linux/6.18.57/0075-net-dsa-Generalise-the-global-DCB-APP-mirroring-help.patch index 63fb355d1d..41c0c8fded 100644 --- a/patches/linux/6.18.57/0075-net-dsa-Generalise-the-global-DCB-APP-mirroring-help.patch +++ b/patches/linux/6.18.57/0075-net-dsa-Generalise-the-global-DCB-APP-mirroring-help.patch @@ -1,7 +1,7 @@ From b7aab315dfe05f936a0344b10e0f998002bcb43e Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 22 Sep 2026 13:51:07 +0200 -Subject: [PATCH 075/102] net: dsa: Generalise the global DCB APP mirroring +Subject: [PATCH 075/128] net: dsa: Generalise the global DCB APP mirroring helper A switch with one classification table for all its ports programs it diff --git a/patches/linux/6.18.57/0076-net-dsa-Support-the-PCP-APP-selector.patch b/patches/linux/6.18.57/0076-net-dsa-Support-the-PCP-APP-selector.patch index da78633075..fc77a011d6 100644 --- a/patches/linux/6.18.57/0076-net-dsa-Support-the-PCP-APP-selector.patch +++ b/patches/linux/6.18.57/0076-net-dsa-Support-the-PCP-APP-selector.patch @@ -1,7 +1,7 @@ From c9576e0f132ba51462c0a088451cf183c975fbe9 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 9 Sep 2026 17:04:22 +0200 -Subject: [PATCH 076/102] net: dsa: Support the PCP APP selector +Subject: [PATCH 076/128] net: dsa: Support the PCP APP selector Add port_add_pcp_prio, port_del_pcp_prio and port_get_pcp_prio switch ops and route the DCB_APP_SEL_PCP selector to them, mirroring the DSCP diff --git a/patches/linux/6.18.57/0077-net-dsa-Support-DCB-priority-rewrite.patch b/patches/linux/6.18.57/0077-net-dsa-Support-DCB-priority-rewrite.patch index 28ecc15b58..32770f971b 100644 --- a/patches/linux/6.18.57/0077-net-dsa-Support-DCB-priority-rewrite.patch +++ b/patches/linux/6.18.57/0077-net-dsa-Support-DCB-priority-rewrite.patch @@ -1,7 +1,7 @@ From d78d08175f4f8a37b3ef3b67e6fb526f6fee43aa Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 9 Sep 2026 17:10:32 +0200 -Subject: [PATCH 077/102] net: dsa: Support DCB priority rewrite +Subject: [PATCH 077/128] net: dsa: Support DCB priority rewrite The DCB rewrite table maps a priority back to the PCP and DEI, or the DSCP, that frames are remarked with on egress. DSA has no diff --git a/patches/linux/6.18.57/0078-net-dsa-Support-the-IEEE-ETS-managed-object.patch b/patches/linux/6.18.57/0078-net-dsa-Support-the-IEEE-ETS-managed-object.patch index 3d4729f413..242d9713a8 100644 --- a/patches/linux/6.18.57/0078-net-dsa-Support-the-IEEE-ETS-managed-object.patch +++ b/patches/linux/6.18.57/0078-net-dsa-Support-the-IEEE-ETS-managed-object.patch @@ -1,7 +1,7 @@ From 65c40a578d8d76adbcc7d7bb2b98ad43c5fd7171 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 22 Sep 2026 13:53:23 +0200 -Subject: [PATCH 078/102] net: dsa: Support the IEEE ETS managed object +Subject: [PATCH 078/128] net: dsa: Support the IEEE ETS managed object A switch port's transmission selection is configured today through the ets queuing discipline, which numbers its bands the other way round diff --git a/patches/linux/6.18.57/0079-net-sparx5-fix-sleep-in-atomic-context-in-MAC-table-.patch b/patches/linux/6.18.57/0079-net-sparx5-fix-sleep-in-atomic-context-in-MAC-table-.patch index dbd0fd6d34..824972ff32 100644 --- a/patches/linux/6.18.57/0079-net-sparx5-fix-sleep-in-atomic-context-in-MAC-table-.patch +++ b/patches/linux/6.18.57/0079-net-sparx5-fix-sleep-in-atomic-context-in-MAC-table-.patch @@ -1,7 +1,7 @@ From 5b9f6fe0ad79285bff06dddb7eef60c9377bab67 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 7 Oct 2026 11:42:12 +0200 -Subject: [PATCH 079/102] net: sparx5: fix sleep in atomic context in MAC table +Subject: [PATCH 079/128] net: sparx5: fix sleep in atomic context in MAC table access Adding or removing a multicast address on an unbridged switch port diff --git a/patches/linux/6.18.57/0080-misc-sparx5-symreg-symreg-debugfs-driver-DBB-1045.patch b/patches/linux/6.18.57/0080-misc-sparx5-symreg-symreg-debugfs-driver-DBB-1045.patch index ecd76faede..357eeb882d 100644 --- a/patches/linux/6.18.57/0080-misc-sparx5-symreg-symreg-debugfs-driver-DBB-1045.patch +++ b/patches/linux/6.18.57/0080-misc-sparx5-symreg-symreg-debugfs-driver-DBB-1045.patch @@ -1,7 +1,7 @@ From 9eb7532602b5ad1c766c0cb8b4f0c43272da1ddd Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 21 Jan 2026 10:09:45 +0100 -Subject: [PATCH 080/102] misc: sparx5-symreg; symreg debugfs driver (DBB-1045) +Subject: [PATCH 080/128] misc: sparx5-symreg; symreg debugfs driver (DBB-1045) Expose the switch register space of Microchip Sparx5 family SoCs through /sys/kernel/debug/symreg/mem, for the symreg tool, which diff --git a/patches/linux/6.18.57/0081-net-ethernet-mtk_wed-map-WO-memory-regions-without-r.patch b/patches/linux/6.18.57/0081-net-ethernet-mtk_wed-map-WO-memory-regions-without-r.patch index 0157b6b55c..49db7e0415 100644 --- a/patches/linux/6.18.57/0081-net-ethernet-mtk_wed-map-WO-memory-regions-without-r.patch +++ b/patches/linux/6.18.57/0081-net-ethernet-mtk_wed-map-WO-memory-regions-without-r.patch @@ -1,7 +1,7 @@ From 17958613375144f4d059777987e6ab034acf2ab1 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Mon, 5 Oct 2026 14:37:56 +0200 -Subject: [PATCH 081/102] net: ethernet: mtk_wed: map WO memory regions without +Subject: [PATCH 081/128] net: ethernet: mtk_wed: map WO memory regions without requesting them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0082-wifi-mt76-wed-fix-kernel-panic-on-non-DBDC-MT7986.patch b/patches/linux/6.18.57/0082-wifi-mt76-wed-fix-kernel-panic-on-non-DBDC-MT7986.patch index 61593ac9ac..8263cb8ff6 100644 --- a/patches/linux/6.18.57/0082-wifi-mt76-wed-fix-kernel-panic-on-non-DBDC-MT7986.patch +++ b/patches/linux/6.18.57/0082-wifi-mt76-wed-fix-kernel-panic-on-non-DBDC-MT7986.patch @@ -1,7 +1,7 @@ From 3144fdeb67f73640c6194435bdfe1d011d318d67 Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Wed, 15 Jul 2026 23:21:12 +0800 -Subject: [PATCH 082/102] wifi: mt76: wed: fix kernel panic on non-DBDC MT7986 +Subject: [PATCH 082/128] wifi: mt76: wed: fix kernel panic on non-DBDC MT7986 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/patches/linux/6.18.57/0083-wifi-mt76-mt7915-set-up-WPDMA-RX-ring-0-for-WED-on-a.patch b/patches/linux/6.18.57/0083-wifi-mt76-mt7915-set-up-WPDMA-RX-ring-0-for-WED-on-a.patch index 6dc6019f27..8f2a1d73ab 100644 --- a/patches/linux/6.18.57/0083-wifi-mt76-mt7915-set-up-WPDMA-RX-ring-0-for-WED-on-a.patch +++ b/patches/linux/6.18.57/0083-wifi-mt76-mt7915-set-up-WPDMA-RX-ring-0-for-WED-on-a.patch @@ -1,7 +1,7 @@ From 78214f7ced0ae7022257f880621d85865523660d Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 7 Oct 2026 22:20:00 +0200 -Subject: [PATCH 083/102] wifi: mt76: mt7915: set up WPDMA RX ring 0 for WED on +Subject: [PATCH 083/128] wifi: mt76: mt7915: set up WPDMA RX ring 0 for WED on a band1-only device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0084-wifi-mt76-mt7915-add-WDS-support-when-WED-is-enabled.patch b/patches/linux/6.18.57/0084-wifi-mt76-mt7915-add-WDS-support-when-WED-is-enabled.patch index a8822d998c..188d351190 100644 --- a/patches/linux/6.18.57/0084-wifi-mt76-mt7915-add-WDS-support-when-WED-is-enabled.patch +++ b/patches/linux/6.18.57/0084-wifi-mt76-mt7915-add-WDS-support-when-WED-is-enabled.patch @@ -1,7 +1,7 @@ From 08357c0c00268d987d3f615735b1e6c6056547e9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 20 Oct 2025 15:59:12 +0200 -Subject: [PATCH 084/102] wifi: mt76: mt7915: add WDS support when WED is +Subject: [PATCH 084/128] wifi: mt76: mt7915: add WDS support when WED is enabled The current WED only supports 256 wcid, whereas mt7986 can support up to diff --git a/patches/linux/6.18.57/0085-net-ethernet-mtk_wed-increase-WED-v2-WDMA-RESV_BUFF-.patch b/patches/linux/6.18.57/0085-net-ethernet-mtk_wed-increase-WED-v2-WDMA-RESV_BUFF-.patch index fd21159893..d5b278a3e0 100644 --- a/patches/linux/6.18.57/0085-net-ethernet-mtk_wed-increase-WED-v2-WDMA-RESV_BUFF-.patch +++ b/patches/linux/6.18.57/0085-net-ethernet-mtk_wed-increase-WED-v2-WDMA-RESV_BUFF-.patch @@ -1,7 +1,7 @@ From 59f60ef5023bee3c659ed50a34e0a6a4d5a18a79 Mon Sep 17 00:00:00 2001 From: Shiji Yang Date: Wed, 19 Aug 2026 21:07:07 +0800 -Subject: [PATCH 085/102] net: ethernet: mtk_wed: increase WED v2 WDMA +Subject: [PATCH 085/128] net: ethernet: mtk_wed: increase WED v2 WDMA RESV_BUFF to 0x80 Change WDMA RESV_BUFF from 0x40 to 0x80 to avoid CDM TX FIFO overflow. diff --git a/patches/linux/6.18.57/0086-wifi-mt76-mt7915-publish-wcid-before-MCU-add-command.patch b/patches/linux/6.18.57/0086-wifi-mt76-mt7915-publish-wcid-before-MCU-add-command.patch index c54ea48ccc..dbfef594ea 100644 --- a/patches/linux/6.18.57/0086-wifi-mt76-mt7915-publish-wcid-before-MCU-add-command.patch +++ b/patches/linux/6.18.57/0086-wifi-mt76-mt7915-publish-wcid-before-MCU-add-command.patch @@ -1,7 +1,7 @@ From 0f22a4d31ffe1ec1ee02192fe2007d4fdab7e6e0 Mon Sep 17 00:00:00 2001 From: Ryan Leung Date: Fri, 21 Aug 2026 09:00:01 +0000 -Subject: [PATCH 086/102] wifi: mt76: mt7915: publish wcid before MCU add +Subject: [PATCH 086/128] wifi: mt76: mt7915: publish wcid before MCU add commands mt7915_add_interface() enables the BSS/STA in firmware via diff --git a/patches/linux/6.18.57/0087-riscv-dts-spacemit-add-UART-pinctrl-combinations.patch b/patches/linux/6.18.57/0087-riscv-dts-spacemit-add-UART-pinctrl-combinations.patch index f8306de6b3..69f1aa60e5 100644 --- a/patches/linux/6.18.57/0087-riscv-dts-spacemit-add-UART-pinctrl-combinations.patch +++ b/patches/linux/6.18.57/0087-riscv-dts-spacemit-add-UART-pinctrl-combinations.patch @@ -1,7 +1,7 @@ From d8ac79ff39f12f88c497bcb6c9644877b8a63642 Mon Sep 17 00:00:00 2001 From: Hendrik Hamerlinck Date: Wed, 17 Sep 2025 08:59:07 +0200 -Subject: [PATCH 087/102] riscv: dts: spacemit: add UART pinctrl combinations +Subject: [PATCH 087/128] riscv: dts: spacemit: add UART pinctrl combinations Add UART pinctrl configurations based on the SoC datasheet and the downstream Bianbu Linux tree. The drive strength values were taken from diff --git a/patches/linux/6.18.57/0088-riscv-dts-spacemit-enable-the-i2c8-adapter.patch b/patches/linux/6.18.57/0088-riscv-dts-spacemit-enable-the-i2c8-adapter.patch index 69f2bcbe98..bd5ac5dad6 100644 --- a/patches/linux/6.18.57/0088-riscv-dts-spacemit-enable-the-i2c8-adapter.patch +++ b/patches/linux/6.18.57/0088-riscv-dts-spacemit-enable-the-i2c8-adapter.patch @@ -1,7 +1,7 @@ From 7e3084b4fbbefb845e30e5068e58bcae956d6e8c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 25 Aug 2025 12:20:54 -0500 -Subject: [PATCH 088/102] riscv: dts: spacemit: enable the i2c8 adapter +Subject: [PATCH 088/128] riscv: dts: spacemit: enable the i2c8 adapter Define properties for the I2C adapter that provides access to the SpacemiT P1 PMIC. Enable this adapter on the Banana Pi BPI-F3. diff --git a/patches/linux/6.18.57/0089-riscv-dts-spacemit-define-fixed-regulators.patch b/patches/linux/6.18.57/0089-riscv-dts-spacemit-define-fixed-regulators.patch index 94313170f1..1826f1b9d7 100644 --- a/patches/linux/6.18.57/0089-riscv-dts-spacemit-define-fixed-regulators.patch +++ b/patches/linux/6.18.57/0089-riscv-dts-spacemit-define-fixed-regulators.patch @@ -1,7 +1,7 @@ From 7f01d556a1e42225f97f0153fa76a7f2b7387251 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 25 Aug 2025 12:20:55 -0500 -Subject: [PATCH 089/102] riscv: dts: spacemit: define fixed regulators +Subject: [PATCH 089/128] riscv: dts: spacemit: define fixed regulators Define the DC power input and the 4v power as fixed supplies in the Banana Pi BPI-F3. diff --git a/patches/linux/6.18.57/0090-riscv-dts-spacemit-define-regulator-constraints.patch b/patches/linux/6.18.57/0090-riscv-dts-spacemit-define-regulator-constraints.patch index 75400aac49..0704805715 100644 --- a/patches/linux/6.18.57/0090-riscv-dts-spacemit-define-regulator-constraints.patch +++ b/patches/linux/6.18.57/0090-riscv-dts-spacemit-define-regulator-constraints.patch @@ -1,7 +1,7 @@ From f2b9631efd5b8e96a484f5bbfdd23154f6fd784a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 25 Aug 2025 12:20:56 -0500 -Subject: [PATCH 090/102] riscv: dts: spacemit: define regulator constraints +Subject: [PATCH 090/128] riscv: dts: spacemit: define regulator constraints Define basic constraints for the regulators in the SpacemiT P1 PMIC, as implemented in the Banana Pi BPI-F3. diff --git a/patches/linux/6.18.57/0091-riscv-dts-spacemit-pinctrl-update-register-and-IO-po.patch b/patches/linux/6.18.57/0091-riscv-dts-spacemit-pinctrl-update-register-and-IO-po.patch index 357ab74b64..573513287f 100644 --- a/patches/linux/6.18.57/0091-riscv-dts-spacemit-pinctrl-update-register-and-IO-po.patch +++ b/patches/linux/6.18.57/0091-riscv-dts-spacemit-pinctrl-update-register-and-IO-po.patch @@ -1,7 +1,7 @@ From bbff952346fd57b379625607a0c60108200153b4 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Thu, 8 Jan 2026 14:42:40 +0800 -Subject: [PATCH 091/102] riscv: dts: spacemit: pinctrl: update register and IO +Subject: [PATCH 091/128] riscv: dts: spacemit: pinctrl: update register and IO power Change the size of the reg register to 0x1000 to match the hardware. diff --git a/patches/linux/6.18.57/0092-riscv-dts-spacemit-Update-PMIC-supply-properties-for.patch b/patches/linux/6.18.57/0092-riscv-dts-spacemit-Update-PMIC-supply-properties-for.patch index e6c7ee3872..4b390e1a45 100644 --- a/patches/linux/6.18.57/0092-riscv-dts-spacemit-Update-PMIC-supply-properties-for.patch +++ b/patches/linux/6.18.57/0092-riscv-dts-spacemit-Update-PMIC-supply-properties-for.patch @@ -1,7 +1,7 @@ From 020a7249aaf089217f93a40c6fdacb4a995c28e4 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 6 Feb 2026 10:32:04 +0800 -Subject: [PATCH 092/102] riscv: dts: spacemit: Update PMIC supply properties +Subject: [PATCH 092/128] riscv: dts: spacemit: Update PMIC supply properties for BPI-F3 and Jupiter Use per-regulator supply names in pmic "spacemit,p1" node to specify diff --git a/patches/linux/6.18.57/0093-riscv-dts-spacemit-k1-add-SD-card-controller-and-pin.patch b/patches/linux/6.18.57/0093-riscv-dts-spacemit-k1-add-SD-card-controller-and-pin.patch index 4f3308d2c0..bdde41efd0 100644 --- a/patches/linux/6.18.57/0093-riscv-dts-spacemit-k1-add-SD-card-controller-and-pin.patch +++ b/patches/linux/6.18.57/0093-riscv-dts-spacemit-k1-add-SD-card-controller-and-pin.patch @@ -1,7 +1,7 @@ From 83bd92bb4d97b92241804ced2967f5b0bf1d01e6 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 15 May 2026 12:48:59 +0200 -Subject: [PATCH 093/102] riscv: dts: spacemit: k1: add SD card controller and +Subject: [PATCH 093/128] riscv: dts: spacemit: k1: add SD card controller and pinctrl support Add SD card controller infrastructure for SpacemiT K1 SoC with complete diff --git a/patches/linux/6.18.57/0094-riscv-dts-spacemit-k1-bananapi-f3-add-SD-card-suppor.patch b/patches/linux/6.18.57/0094-riscv-dts-spacemit-k1-bananapi-f3-add-SD-card-suppor.patch index 053aa48d3c..2605bde4f3 100644 --- a/patches/linux/6.18.57/0094-riscv-dts-spacemit-k1-bananapi-f3-add-SD-card-suppor.patch +++ b/patches/linux/6.18.57/0094-riscv-dts-spacemit-k1-bananapi-f3-add-SD-card-suppor.patch @@ -1,7 +1,7 @@ From 18b19b95243b1e70d1ed6c2b36ad95950b9b7b8a Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 15 May 2026 12:49:01 +0200 -Subject: [PATCH 094/102] riscv: dts: spacemit: k1-bananapi-f3: add SD card +Subject: [PATCH 094/128] riscv: dts: spacemit: k1-bananapi-f3: add SD card support with UHS modes Add complete SD card controller support with UHS high-speed modes. diff --git a/patches/linux/6.18.57/0095-mmc-sdhci-of-k1-add-reset-support.patch b/patches/linux/6.18.57/0095-mmc-sdhci-of-k1-add-reset-support.patch index 7852a618df..4fc9f37be8 100644 --- a/patches/linux/6.18.57/0095-mmc-sdhci-of-k1-add-reset-support.patch +++ b/patches/linux/6.18.57/0095-mmc-sdhci-of-k1-add-reset-support.patch @@ -1,7 +1,7 @@ From faf9f545c4466ea17618388c5aaa6f8ca42bae98 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 23 Dec 2025 10:24:50 +0800 -Subject: [PATCH 095/102] mmc: sdhci-of-k1: add reset support +Subject: [PATCH 095/128] mmc: sdhci-of-k1: add reset support The SDHCI controller of SpacemiT K1 SoC requires two resets, add support to explicitly request the reset line and deassert during diff --git a/patches/linux/6.18.57/0096-mmc-sdhci-of-k1-enable-essential-clock-infrastructur.patch b/patches/linux/6.18.57/0096-mmc-sdhci-of-k1-enable-essential-clock-infrastructur.patch index c87f0d7712..62a58bfbab 100644 --- a/patches/linux/6.18.57/0096-mmc-sdhci-of-k1-enable-essential-clock-infrastructur.patch +++ b/patches/linux/6.18.57/0096-mmc-sdhci-of-k1-enable-essential-clock-infrastructur.patch @@ -1,7 +1,7 @@ From f18a38a760707550435a91643cf51d4e8739e0f0 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:57 +0200 -Subject: [PATCH 096/102] mmc: sdhci-of-k1: enable essential clock +Subject: [PATCH 096/128] mmc: sdhci-of-k1: enable essential clock infrastructure for SD operation Ensure SD card pins receive clock signals by enabling pad clock diff --git a/patches/linux/6.18.57/0097-mmc-sdhci-of-k1-add-regulator-and-pinctrl-voltage-sw.patch b/patches/linux/6.18.57/0097-mmc-sdhci-of-k1-add-regulator-and-pinctrl-voltage-sw.patch index b89026def9..8983a8bab6 100644 --- a/patches/linux/6.18.57/0097-mmc-sdhci-of-k1-add-regulator-and-pinctrl-voltage-sw.patch +++ b/patches/linux/6.18.57/0097-mmc-sdhci-of-k1-add-regulator-and-pinctrl-voltage-sw.patch @@ -1,7 +1,7 @@ From 6b6ee1f575037a3daee550a0225f8f8d10f126bc Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:58 +0200 -Subject: [PATCH 097/102] mmc: sdhci-of-k1: add regulator and pinctrl voltage +Subject: [PATCH 097/128] mmc: sdhci-of-k1: add regulator and pinctrl voltage switching support Add voltage switching infrastructure for UHS-I modes by integrating both diff --git a/patches/linux/6.18.57/0098-mmc-sdhci-of-k1-add-comprehensive-SDR-tuning-support.patch b/patches/linux/6.18.57/0098-mmc-sdhci-of-k1-add-comprehensive-SDR-tuning-support.patch index bf24dfc796..e7f813b389 100644 --- a/patches/linux/6.18.57/0098-mmc-sdhci-of-k1-add-comprehensive-SDR-tuning-support.patch +++ b/patches/linux/6.18.57/0098-mmc-sdhci-of-k1-add-comprehensive-SDR-tuning-support.patch @@ -1,7 +1,7 @@ From 658bc011b09c32a0731b9a11edfa542b955c025d Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:59 +0200 -Subject: [PATCH 098/102] mmc: sdhci-of-k1: add comprehensive SDR tuning +Subject: [PATCH 098/128] mmc: sdhci-of-k1: add comprehensive SDR tuning support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0099-driver-reset-spacemit-p1-add-driver-for-poweroff-reb.patch b/patches/linux/6.18.57/0099-driver-reset-spacemit-p1-add-driver-for-poweroff-reb.patch index 44dc4a8c16..d871d50150 100644 --- a/patches/linux/6.18.57/0099-driver-reset-spacemit-p1-add-driver-for-poweroff-reb.patch +++ b/patches/linux/6.18.57/0099-driver-reset-spacemit-p1-add-driver-for-poweroff-reb.patch @@ -1,7 +1,7 @@ From 856831f83fc0466e356b6620540eacdaa2fb6c2a Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Mon, 3 Nov 2025 00:01:59 +0100 -Subject: [PATCH 099/102] driver: reset: spacemit-p1: add driver for +Subject: [PATCH 099/128] driver: reset: spacemit-p1: add driver for poweroff/reboot This driver implements poweroff/reboot support for the SpacemiT P1 PMIC diff --git a/patches/linux/6.18.57/0100-mfd-simple-mfd-i2c-Add-a-reboot-cell-for-the-Spacemi.patch b/patches/linux/6.18.57/0100-mfd-simple-mfd-i2c-Add-a-reboot-cell-for-the-Spacemi.patch index 173f32fbf2..a398ae6c86 100644 --- a/patches/linux/6.18.57/0100-mfd-simple-mfd-i2c-Add-a-reboot-cell-for-the-Spacemi.patch +++ b/patches/linux/6.18.57/0100-mfd-simple-mfd-i2c-Add-a-reboot-cell-for-the-Spacemi.patch @@ -1,7 +1,7 @@ From 1e5a95c0a6210aeff898ef1e85873632951b23a7 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Mon, 3 Nov 2025 00:02:00 +0100 -Subject: [PATCH 100/102] mfd: simple-mfd-i2c: Add a reboot cell for the +Subject: [PATCH 100/128] mfd: simple-mfd-i2c: Add a reboot cell for the SpacemiT P1 chip Add a "spacemit-p1-reboot" cell for the SpacemiT P1 chip. diff --git a/patches/linux/6.18.57/0101-nvmem-core-do-not-defer-on-a-cell-missing-from-a-bou.patch b/patches/linux/6.18.57/0101-nvmem-core-do-not-defer-on-a-cell-missing-from-a-bou.patch index f2366ba708..ce286568ef 100644 --- a/patches/linux/6.18.57/0101-nvmem-core-do-not-defer-on-a-cell-missing-from-a-bou.patch +++ b/patches/linux/6.18.57/0101-nvmem-core-do-not-defer-on-a-cell-missing-from-a-bou.patch @@ -1,7 +1,7 @@ From e37bdae94135a99a05ac92b7869a7730709a0663 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 13 Oct 2026 10:52:54 +0200 -Subject: [PATCH 101/102] nvmem: core: do not defer on a cell missing from a +Subject: [PATCH 101/128] nvmem: core: do not defer on a cell missing from a bound layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0102-net-dsa-mt7530-lock-the-MDIO-bus-around-regmap-irq-a.patch b/patches/linux/6.18.57/0102-net-dsa-mt7530-lock-the-MDIO-bus-around-regmap-irq-a.patch index 834864ec34..ca9235405e 100644 --- a/patches/linux/6.18.57/0102-net-dsa-mt7530-lock-the-MDIO-bus-around-regmap-irq-a.patch +++ b/patches/linux/6.18.57/0102-net-dsa-mt7530-lock-the-MDIO-bus-around-regmap-irq-a.patch @@ -1,7 +1,7 @@ From e86b215ec661c443e2d50ee8bb3d4d5008e8845e Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 13 Oct 2026 15:48:00 +0200 -Subject: [PATCH 102/102] net: dsa: mt7530: lock the MDIO bus around regmap-irq +Subject: [PATCH 102/128] net: dsa: mt7530: lock the MDIO bus around regmap-irq accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.57/0103-clk-mediatek-mt7988-keep-the-PCIe-port-clocks-enable.patch b/patches/linux/6.18.57/0103-clk-mediatek-mt7988-keep-the-PCIe-port-clocks-enable.patch index 5fae580a69..a628f58c30 100644 --- a/patches/linux/6.18.57/0103-clk-mediatek-mt7988-keep-the-PCIe-port-clocks-enable.patch +++ b/patches/linux/6.18.57/0103-clk-mediatek-mt7988-keep-the-PCIe-port-clocks-enable.patch @@ -1,7 +1,7 @@ From 773e6267865af44796580064e32dfddfced46deb Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 18 Oct 2026 11:31:40 +0200 -Subject: [PATCH 103/103] clk: mediatek: mt7988: keep the PCIe port clocks +Subject: [PATCH 103/128] clk: mediatek: mt7988: keep the PCIe port clocks enabled The PCIe links on the Banana Pi BPI-R4 never come up when the PCIe diff --git a/patches/linux/6.18.57/0104-wifi-mt76-mt7615-add-a-force_dbdc-module-parameter.patch b/patches/linux/6.18.57/0104-wifi-mt76-mt7615-add-a-force_dbdc-module-parameter.patch index a8d4e820af..b7dbd35cd2 100644 --- a/patches/linux/6.18.57/0104-wifi-mt76-mt7615-add-a-force_dbdc-module-parameter.patch +++ b/patches/linux/6.18.57/0104-wifi-mt76-mt7615-add-a-force_dbdc-module-parameter.patch @@ -1,7 +1,7 @@ From 75d4190394037af38822d7d0cf388d8690f33ef2 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 18 Oct 2026 13:47:49 +0200 -Subject: [PATCH 104/104] wifi: mt76: mt7615: add a force_dbdc module parameter +Subject: [PATCH 104/128] wifi: mt76: mt7615: add a force_dbdc module parameter An MT7615D card with an unprogrammed EEPROM selects no band, so the driver registers one radio that runs on either 2.4 or 5 GHz instead of diff --git a/patches/linux/6.18.57/0105-net-sparx5-fix-multicast-delivery-to-the-CPU-on-stan.patch b/patches/linux/6.18.57/0105-net-sparx5-fix-multicast-delivery-to-the-CPU-on-stan.patch new file mode 100644 index 0000000000..afa600917d --- /dev/null +++ b/patches/linux/6.18.57/0105-net-sparx5-fix-multicast-delivery-to-the-CPU-on-stan.patch @@ -0,0 +1,156 @@ +From 921d81c15a6e2d7dc15b4d189646e79183056b88 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 7 Oct 2026 17:30:17 +0200 +Subject: [PATCH 105/128] net: sparx5: fix multicast delivery to the CPU on + standalone ports + +A standalone port stops receiving a multicast group as soon as any +other standalone port leaves it. IPv6 neighbour discovery and mDNS +are the usual victims, so the port looks dead to the LAN until it is +taken down and up again, which re-joins its groups. + +Standalone ports all use pvid NULL_VID, and sparx5_mc_sync() delivers +a group to the CPU with a MAC table entry keyed on that address and +VID. The entry is therefore shared by every standalone port that is +a member of the group, but sparx5_mc_unsync() forgets it on behalf of +a single port, and the flood PGIDs have no CPU copy to fall back on. + +Reference count the shared entries. Learn on the first subscriber, +forget on the last. + +The same applies to a unicast address learned by sparx5_port_add_addr() +when two standalone ports share a MAC address, which is left as is. + +Fixes: d6fce5141929 ("net: sparx5: add switching support") +Signed-off-by: Joachim Wiberg +--- + .../microchip/sparx5/sparx5_mactable.c | 73 ++++++++++++++++++- + .../ethernet/microchip/sparx5/sparx5_main.h | 4 + + 2 files changed, 74 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +index 30acb2127797..6f8d41a879b5 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +@@ -116,21 +116,79 @@ int sparx5_mact_learn(struct sparx5 *sparx5, int pgid, + return ret; + } + ++/* Standalone ports share pvid, hence the CPU multicast MAC entry too */ ++struct sparx5_mc_cpu_entry { ++ struct list_head list; ++ unsigned char addr[ETH_ALEN]; ++ u16 vid; ++ int refcnt; ++}; ++ ++static struct sparx5_mc_cpu_entry * ++sparx5_mc_cpu_find(struct sparx5 *sparx5, const unsigned char *addr, u16 vid) ++{ ++ struct sparx5_mc_cpu_entry *e; ++ ++ list_for_each_entry(e, &sparx5->mc_cpu_entries, list) ++ if (e->vid == vid && ether_addr_equal(e->addr, addr)) ++ return e; ++ ++ return NULL; ++} ++ + int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr) + { + struct sparx5_port *port = netdev_priv(dev); + struct sparx5 *sparx5 = port->sparx5; ++ struct sparx5_mc_cpu_entry *e; ++ int ret = 0; ++ ++ spin_lock(&sparx5->mc_cpu_lock); ++ e = sparx5_mc_cpu_find(sparx5, addr, port->pvid); ++ if (e && --e->refcnt == 0) { ++ list_del(&e->list); ++ kfree(e); ++ ret = sparx5_mact_forget(sparx5, addr, port->pvid); ++ } ++ spin_unlock(&sparx5->mc_cpu_lock); + +- return sparx5_mact_forget(sparx5, addr, port->pvid); ++ return ret; + } + + int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr) + { + struct sparx5_port *port = netdev_priv(dev); + struct sparx5 *sparx5 = port->sparx5; ++ struct sparx5_mc_cpu_entry *e; ++ int ret = 0; ++ ++ spin_lock(&sparx5->mc_cpu_lock); ++ e = sparx5_mc_cpu_find(sparx5, addr, port->pvid); ++ if (!e) { ++ e = kmalloc(sizeof(*e), GFP_ATOMIC); ++ if (!e) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = sparx5_mact_learn(sparx5, ++ sparx5_get_pgid(sparx5, PGID_CPU), ++ addr, port->pvid); ++ if (ret) { ++ kfree(e); ++ goto out; ++ } ++ ++ ether_addr_copy(e->addr, addr); ++ e->vid = port->pvid; ++ e->refcnt = 0; ++ list_add_tail(&e->list, &sparx5->mc_cpu_entries); ++ } ++ e->refcnt++; ++out: ++ spin_unlock(&sparx5->mc_cpu_lock); + +- return sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), +- addr, port->pvid); ++ return ret; + } + + static int sparx5_mact_get(struct sparx5 *sparx5, +@@ -494,6 +552,8 @@ int sparx5_mact_init(struct sparx5 *sparx5) + char queue_name[32]; + + spin_lock_init(&sparx5->lock); ++ spin_lock_init(&sparx5->mc_cpu_lock); ++ INIT_LIST_HEAD(&sparx5->mc_cpu_entries); + + /* Flush MAC table */ + spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) | +@@ -529,7 +589,14 @@ int sparx5_mact_init(struct sparx5 *sparx5) + + void sparx5_mact_deinit(struct sparx5 *sparx5) + { ++ struct sparx5_mc_cpu_entry *e, *tmp; ++ + cancel_delayed_work_sync(&sparx5->mact_work); + destroy_workqueue(sparx5->mact_queue); + mutex_destroy(&sparx5->mact_lock); ++ ++ list_for_each_entry_safe(e, tmp, &sparx5->mc_cpu_entries, list) { ++ list_del(&e->list); ++ kfree(e); ++ } + } +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +index 317c4ca0d29b..c6fc9ee6be42 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +@@ -408,6 +408,10 @@ struct sparx5 { + struct list_head mdb_entries; + /* mdb list mutex */ + struct mutex mdb_lock; ++ /* CPU multicast entries, shared by the standalone ports */ ++ struct list_head mc_cpu_entries; ++ /* mc_cpu_entries lock */ ++ spinlock_t mc_cpu_lock; + struct delayed_work mact_work; + struct workqueue_struct *mact_queue; + /* Board specifics */ diff --git a/patches/linux/6.18.57/0106-net-sparx5-fix-broadcast-to-the-CPU-on-bridge-ports-.patch b/patches/linux/6.18.57/0106-net-sparx5-fix-broadcast-to-the-CPU-on-bridge-ports-.patch new file mode 100644 index 0000000000..f179a1203d --- /dev/null +++ b/patches/linux/6.18.57/0106-net-sparx5-fix-broadcast-to-the-CPU-on-bridge-ports-.patch @@ -0,0 +1,99 @@ +From 2bef1d5d89b588d4888ecda3dc5f3984c8079a27 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 8 Oct 2026 13:48:05 +0200 +Subject: [PATCH 106/128] net: sparx5: fix broadcast to the CPU on bridge ports + without a PVID + +Ports of a bridge created with vlan_default_pvid 0 never deliver +broadcast to the CPU. ARP requests from the ports go unanswered and +the bridge IP address is unreachable, while unicast and forwarding +between the ports work: + + ip link add br0 address $mac type bridge vlan_default_pvid 0 + ip link set eth0 master br0 + ip addr add 10.0.0.1/24 dev br0 + +The driver classifies bridged ports without a PVID to VID 1, to keep +them apart from the standalone ports on VID 0. Broadcast on a VID +only reaches the CPU through a MAC table entry pointing to PGID_BCAST, +which the driver learns when the bridge adds the VLAN and forgets when +the bridge removes it. A new bridge starts with VLAN 1, so the entry +for VID 1 is learned, and setting vlan_default_pvid 0 removes VLAN 1 +again, which forgets the entry the PVID-less ports depend on. + +Learn the VID 1 broadcast entry at probe, like the VID 0 entry for +standalone ports, and keep it when VLAN 1 is removed from the bridge. + +Fixes: d6fce5141929 ("net: sparx5: add switching support") +Signed-off-by: Joachim Wiberg +--- + .../net/ethernet/microchip/sparx5/sparx5_mactable.c | 5 +++++ + drivers/net/ethernet/microchip/sparx5/sparx5_main.h | 1 + + .../net/ethernet/microchip/sparx5/sparx5_switchdev.c | 12 +++++++----- + 3 files changed, 13 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +index 6f8d41a879b5..58b9436b1dd4 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +@@ -570,6 +570,11 @@ int sparx5_mact_init(struct sparx5 *sparx5) + (unsigned char[]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + NULL_VID); + ++ /* Flood BC of bridged ports without a PVID, to the ports and CPU */ ++ sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_BCAST), ++ (unsigned char[]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, ++ UNAWARE_VID); ++ + mutex_init(&sparx5->mdb_lock); + INIT_LIST_HEAD(&sparx5->mdb_entries); + mutex_init(&sparx5->mact_lock); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +index c6fc9ee6be42..ce89fd005a55 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +@@ -103,6 +103,7 @@ enum sparx5_feature { + + #define IFH_LEN 9 /* 36 bytes */ + #define NULL_VID 0 ++#define UNAWARE_VID 1 /* Bridged ports without a PVID */ + #define SPX5_MACT_PULL_DELAY (2 * HZ) + #define SPX5_STATS_CHECK_DELAY (1 * HZ) + #define SPX5_PRIOS 8 /* Number of priority queues */ +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +index 391ce604e661..1c81efd7fbba 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +@@ -155,8 +155,8 @@ static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, + /* Used PVID 1 when default_pvid is 0, to avoid + * collision with non-bridged ports. + */ +- if (port->pvid == 0) +- port->pvid = 1; ++ if (port->pvid == NULL_VID) ++ port->pvid = UNAWARE_VID; + port->vlan_aware = attr->u.vlan_filtering; + sparx5_vlan_port_apply(port->sparx5, port); + break; +@@ -346,8 +346,8 @@ static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work) + /* Used PVID 1 when default_pvid is 0, to avoid + * collision with non-bridged ports. + */ +- if (fdb_info->vid == 0) +- vid = 1; ++ if (fdb_info->vid == NULL_VID) ++ vid = UNAWARE_VID; + else + vid = fdb_info->vid; + +@@ -679,7 +679,9 @@ static int sparx5_handle_port_vlan_del(struct net_device *dev, + container_of(nb, struct sparx5, + switchdev_blocking_nb); + +- sparx5_mact_forget(sparx5, dev->broadcast, vid); ++ /* Kept for the bridged ports without a PVID */ ++ if (vid != UNAWARE_VID) ++ sparx5_mact_forget(sparx5, dev->broadcast, vid); + return 0; + } + diff --git a/patches/linux/6.18.57/0107-net-sparx5-fix-MAC-table-entry-shared-by-bridge-VID-.patch b/patches/linux/6.18.57/0107-net-sparx5-fix-MAC-table-entry-shared-by-bridge-VID-.patch new file mode 100644 index 0000000000..265ec85bd4 --- /dev/null +++ b/patches/linux/6.18.57/0107-net-sparx5-fix-MAC-table-entry-shared-by-bridge-VID-.patch @@ -0,0 +1,156 @@ +From 85fc4728952f3640d794522e4a581b70dd7ba13c Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 8 Oct 2026 13:55:02 +0200 +Subject: [PATCH 107/128] net: sparx5: fix MAC table entry shared by bridge VID + 0 and VID 1 + +The MAC address of a bridge created with vlan_default_pvid 0 is +missing from the MAC table, so unicast to the bridge IP address is +flooded to the ports instead of reaching the CPU: + + ip link add br0 address $mac type bridge vlan_default_pvid 0 + +The bridge notifies its own address as a local FDB entry on VID 0 and +on every VLAN of the bridge, VLAN 1 included until vlan_default_pvid +0 removes it. The driver maps VID 0 to UNAWARE_VID, so both entries +are learned as the same MAC table entry, and the delete of the VLAN 1 +entry forgets it while the VID 0 entry is still in use. Removing +VLAN 1 from a bridge with the default PVID loses the entry the same +way. + +Track the bridge VIDs using a locked MAC table entry and forget it +only when the last one is deleted. + +Fixes: d6fce5141929 ("net: sparx5: add switching support") +Signed-off-by: Joachim Wiberg +--- + .../microchip/sparx5/sparx5_mactable.c | 35 +++++++++++++++++-- + .../microchip/sparx5/sparx5_switchdev.c | 15 ++------ + 2 files changed, 36 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +index 58b9436b1dd4..2c0d957ff52f 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +@@ -39,8 +39,25 @@ struct sparx5_mact_entry { + #define MAC_ENT_LOCK BIT(2) + u16 vid; + u16 port; ++ u8 refs; /* Bridge VIDs using a locked entry */ ++#define MAC_ENT_REF_VID BIT(0) /* The entry's own VID */ ++#define MAC_ENT_REF_UNAWARE BIT(1) /* Bridge VID 0, see UNAWARE_VID */ + }; + ++/* Bridge VID 0 is mapped to UNAWARE_VID, to keep clear of the standalone ++ * ports on NULL_VID, so it shares the MAC table entry with that VID. ++ */ ++static u16 sparx5_mact_bridge_vid(u16 vid, u8 *ref) ++{ ++ if (vid == NULL_VID) { ++ *ref = MAC_ENT_REF_UNAWARE; ++ return UNAWARE_VID; ++ } ++ ++ *ref = MAC_ENT_REF_VID; ++ return vid; ++} ++ + static int sparx5_mact_get_status(struct sparx5 *sparx5) + { + return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL); +@@ -354,6 +371,13 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, + struct sparx5_mact_entry *mact_entry; + int ret; + u32 cfg2; ++ u8 ref; ++ ++ vid = sparx5_mact_bridge_vid(vid, &ref); ++ ++ mact_entry = find_mact_entry(sparx5, addr, vid, portno); ++ if (mact_entry && mact_entry->flags & MAC_ENT_LOCK) ++ mact_entry->refs |= ref; + + ret = sparx5_mact_find(sparx5, addr, vid, &cfg2); + if (!ret) +@@ -365,7 +389,6 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, + * mact thread to start the frame will reach CPU and the CPU will + * add the entry but without the extern_learn flag. + */ +- mact_entry = find_mact_entry(sparx5, addr, vid, portno); + if (mact_entry) + goto update_hw; + +@@ -386,6 +409,7 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, + /* New entry? */ + if (mact_entry->flags == 0) { + mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */ ++ mact_entry->refs = ref; + sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid, + dev, true); + } +@@ -398,6 +422,9 @@ int sparx5_del_mact_entry(struct sparx5 *sparx5, + u16 vid) + { + struct sparx5_mact_entry *mact_entry, *tmp; ++ u8 ref; ++ ++ vid = sparx5_mact_bridge_vid(vid, &ref); + + /* Delete the entry in SW MAC table not to get the notification when + * SW is pulling again +@@ -405,8 +432,12 @@ int sparx5_del_mact_entry(struct sparx5 *sparx5, + mutex_lock(&sparx5->mact_lock); + list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries, + list) { +- if ((vid == 0 || mact_entry->vid == vid) && ++ if (mact_entry->vid == vid && + ether_addr_equal(addr, mact_entry->mac)) { ++ mact_entry->refs &= ~ref; ++ if (mact_entry->refs) ++ continue; ++ + sparx5_mact_forget(sparx5, addr, mact_entry->vid); + + list_del(&mact_entry->list); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +index 1c81efd7fbba..973d3b0f26e7 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +@@ -329,7 +329,6 @@ static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work) + struct sparx5_port *port; + struct sparx5 *sparx5; + bool host_addr; +- u16 vid; + + rtnl_lock(); + if (!sparx5_netdevice_check(dev)) { +@@ -343,26 +342,18 @@ static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work) + + fdb_info = &switchdev_work->fdb_info; + +- /* Used PVID 1 when default_pvid is 0, to avoid +- * collision with non-bridged ports. +- */ +- if (fdb_info->vid == NULL_VID) +- vid = UNAWARE_VID; +- else +- vid = fdb_info->vid; +- + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + if (host_addr) + sparx5_add_mact_entry(sparx5, dev, + sparx5_get_pgid(sparx5, PGID_CPU), +- fdb_info->addr, vid); ++ fdb_info->addr, fdb_info->vid); + else + sparx5_add_mact_entry(sparx5, port->ndev, port->portno, +- fdb_info->addr, vid); ++ fdb_info->addr, fdb_info->vid); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: +- sparx5_del_mact_entry(sparx5, fdb_info->addr, vid); ++ sparx5_del_mact_entry(sparx5, fdb_info->addr, fdb_info->vid); + break; + } + diff --git a/patches/linux/6.18.57/0108-net-sparx5-fix-loss-of-a-bridged-port-s-MAC-entry-on.patch b/patches/linux/6.18.57/0108-net-sparx5-fix-loss-of-a-bridged-port-s-MAC-entry-on.patch new file mode 100644 index 0000000000..0dc990c51a --- /dev/null +++ b/patches/linux/6.18.57/0108-net-sparx5-fix-loss-of-a-bridged-port-s-MAC-entry-on.patch @@ -0,0 +1,73 @@ +From e5757905891a66192a17276407db751af8246b32 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 8 Oct 2026 13:55:20 +0200 +Subject: [PATCH 108/128] net: sparx5: fix loss of a bridged port's MAC entry + on link down + +Taking a bridged port down forgets its MAC address on the bridge VID, +and changing the address forgets the old one the same way. When the +bridge uses the address of that port, as it does by default with its +first port, the bridge IP address is unreachable until the port comes +up again: + + ip link set eth0 down # bridge MAC entry gone + ip link set eth0 up # back again + +The driver learns the port address to the CPU on NETDEV_PRE_UP and +forgets it on NETDEV_DOWN and on address change, at the port's current +PVID. For a standalone port that is its own VID, but once bridged the +port shares the VID with the bridge, whose local FDB entries the driver +already tracks. The bridge keeps them when a port goes down and moves +them when its address changes, so the driver must not touch them. + +Leave the MAC table alone on up, down and address change of a bridged +port. + +Fixes: d6fce5141929 ("net: sparx5: add switching support") +Signed-off-by: Joachim Wiberg +--- + .../net/ethernet/microchip/sparx5/sparx5_netdev.c | 15 +++++++++------ + .../ethernet/microchip/sparx5/sparx5_switchdev.c | 4 ++++ + 2 files changed, 13 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +index ae84ab737882..1d597c5a2057 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +@@ -193,12 +193,15 @@ static int sparx5_set_mac_address(struct net_device *dev, void *p) + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + +- /* Remove current */ +- sparx5_mact_forget(sparx5, dev->dev_addr, port->pvid); +- +- /* Add new */ +- sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), +- addr->sa_data, port->pvid); ++ /* The bridge keeps the entries of its ports */ ++ if (!test_bit(port->portno, sparx5->bridge_mask)) { ++ /* Remove current */ ++ sparx5_mact_forget(sparx5, dev->dev_addr, port->pvid); ++ ++ /* Add new */ ++ sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), ++ addr->sa_data, port->pvid); ++ } + + /* Record the address */ + eth_hw_addr_set(dev, addr->sa_data); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +index 973d3b0f26e7..a9726944fafd 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +@@ -276,6 +276,10 @@ static int sparx5_port_add_addr(struct net_device *dev, bool up) + struct sparx5 *sparx5 = port->sparx5; + u16 vid = port->pvid; + ++ /* The bridge keeps the entries of its ports */ ++ if (test_bit(port->portno, sparx5->bridge_mask)) ++ return 0; ++ + if (up) + sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), + port->ndev->dev_addr, vid); diff --git a/patches/linux/6.18.57/0109-net-sparx5-share-the-CPU-MAC-entry-of-standalone-por.patch b/patches/linux/6.18.57/0109-net-sparx5-share-the-CPU-MAC-entry-of-standalone-por.patch new file mode 100644 index 0000000000..7a1626fdb1 --- /dev/null +++ b/patches/linux/6.18.57/0109-net-sparx5-share-the-CPU-MAC-entry-of-standalone-por.patch @@ -0,0 +1,278 @@ +From b73e32eec0d6cdd0aa906c40504eb9f89c3d864d Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Fri, 9 Oct 2026 10:12:29 +0200 +Subject: [PATCH 109/128] net: sparx5: share the CPU MAC entry of standalone + ports with one address + +Taking one port of a LACP aggregate down stops unicast to the aggregate +on the remaining ports, and deleting the aggregate leaves the port whose +address the bond used unreachable until it is taken down and up again: + + ip link add lag0 type bond mode 802.3ad + ip link set e6 master lag0 # bond and both ports now use e6's MAC + ip link set e8 master lag0 + ip link set e6 down # unicast to the bond MAC stops on e8 + ip link del lag0 # e6 keeps its MAC, loses its CPU entry + +The bond sets the address of its first port on every port. A standalone +port learns its address to the CPU on NETDEV_PRE_UP and forgets it on +NETDEV_DOWN and on address change, and with ports sharing an address +the forget of one port removes the entry the others still use. The +multicast entries of standalone ports had the same problem, fixed in +commit "net: sparx5: fix multicast delivery to the CPU on standalone +ports" by sharing them through a reference-counted list. + +Keep the port addresses in that list as well. A standalone port holds +a reference on its address while up, drops it on down, on bridge join +and on address change, and takes it back on bridge leave; the entry is +forgotten with the last reference. + +Fixes: d6fce5141929 ("net: sparx5: add switching support") +Signed-off-by: Joachim Wiberg +--- + .../microchip/sparx5/sparx5_mactable.c | 70 +++++++++++-------- + .../ethernet/microchip/sparx5/sparx5_main.h | 12 ++-- + .../ethernet/microchip/sparx5/sparx5_netdev.c | 9 ++- + .../microchip/sparx5/sparx5_switchdev.c | 12 ++-- + 4 files changed, 60 insertions(+), 43 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +index 2c0d957ff52f..cf222736eff9 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +@@ -133,54 +133,54 @@ int sparx5_mact_learn(struct sparx5 *sparx5, int pgid, + return ret; + } + +-/* Standalone ports share pvid, hence the CPU multicast MAC entry too */ +-struct sparx5_mc_cpu_entry { ++/* Standalone ports share pvid, hence their CPU MAC entries too */ ++struct sparx5_cpu_entry { + struct list_head list; + unsigned char addr[ETH_ALEN]; + u16 vid; + int refcnt; + }; + +-static struct sparx5_mc_cpu_entry * +-sparx5_mc_cpu_find(struct sparx5 *sparx5, const unsigned char *addr, u16 vid) ++static struct sparx5_cpu_entry * ++sparx5_cpu_entry_find(struct sparx5 *sparx5, const unsigned char *addr, u16 vid) + { +- struct sparx5_mc_cpu_entry *e; ++ struct sparx5_cpu_entry *e; + +- list_for_each_entry(e, &sparx5->mc_cpu_entries, list) ++ list_for_each_entry(e, &sparx5->cpu_entries, list) + if (e->vid == vid && ether_addr_equal(e->addr, addr)) + return e; + + return NULL; + } + +-int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr) ++/* Forget the CPU entry of addr on vid when its last user is gone */ ++int sparx5_cpu_entry_put(struct sparx5 *sparx5, const unsigned char *addr, ++ u16 vid) + { +- struct sparx5_port *port = netdev_priv(dev); +- struct sparx5 *sparx5 = port->sparx5; +- struct sparx5_mc_cpu_entry *e; ++ struct sparx5_cpu_entry *e; + int ret = 0; + +- spin_lock(&sparx5->mc_cpu_lock); +- e = sparx5_mc_cpu_find(sparx5, addr, port->pvid); ++ spin_lock(&sparx5->cpu_lock); ++ e = sparx5_cpu_entry_find(sparx5, addr, vid); + if (e && --e->refcnt == 0) { + list_del(&e->list); + kfree(e); +- ret = sparx5_mact_forget(sparx5, addr, port->pvid); ++ ret = sparx5_mact_forget(sparx5, addr, vid); + } +- spin_unlock(&sparx5->mc_cpu_lock); ++ spin_unlock(&sparx5->cpu_lock); + + return ret; + } + +-int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr) ++/* Learn addr on vid to the CPU for its first user */ ++int sparx5_cpu_entry_get(struct sparx5 *sparx5, const unsigned char *addr, ++ u16 vid) + { +- struct sparx5_port *port = netdev_priv(dev); +- struct sparx5 *sparx5 = port->sparx5; +- struct sparx5_mc_cpu_entry *e; ++ struct sparx5_cpu_entry *e; + int ret = 0; + +- spin_lock(&sparx5->mc_cpu_lock); +- e = sparx5_mc_cpu_find(sparx5, addr, port->pvid); ++ spin_lock(&sparx5->cpu_lock); ++ e = sparx5_cpu_entry_find(sparx5, addr, vid); + if (!e) { + e = kmalloc(sizeof(*e), GFP_ATOMIC); + if (!e) { +@@ -190,24 +190,38 @@ int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr) + + ret = sparx5_mact_learn(sparx5, + sparx5_get_pgid(sparx5, PGID_CPU), +- addr, port->pvid); ++ addr, vid); + if (ret) { + kfree(e); + goto out; + } + + ether_addr_copy(e->addr, addr); +- e->vid = port->pvid; ++ e->vid = vid; + e->refcnt = 0; +- list_add_tail(&e->list, &sparx5->mc_cpu_entries); ++ list_add_tail(&e->list, &sparx5->cpu_entries); + } + e->refcnt++; + out: +- spin_unlock(&sparx5->mc_cpu_lock); ++ spin_unlock(&sparx5->cpu_lock); + + return ret; + } + ++int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ ++ return sparx5_cpu_entry_put(port->sparx5, addr, port->pvid); ++} ++ ++int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ ++ return sparx5_cpu_entry_get(port->sparx5, addr, port->pvid); ++} ++ + static int sparx5_mact_get(struct sparx5 *sparx5, + unsigned char mac[ETH_ALEN], + u16 *vid, u32 *pcfg2) +@@ -583,8 +597,8 @@ int sparx5_mact_init(struct sparx5 *sparx5) + char queue_name[32]; + + spin_lock_init(&sparx5->lock); +- spin_lock_init(&sparx5->mc_cpu_lock); +- INIT_LIST_HEAD(&sparx5->mc_cpu_entries); ++ spin_lock_init(&sparx5->cpu_lock); ++ INIT_LIST_HEAD(&sparx5->cpu_entries); + + /* Flush MAC table */ + spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) | +@@ -625,13 +639,13 @@ int sparx5_mact_init(struct sparx5 *sparx5) + + void sparx5_mact_deinit(struct sparx5 *sparx5) + { +- struct sparx5_mc_cpu_entry *e, *tmp; ++ struct sparx5_cpu_entry *e, *tmp; + + cancel_delayed_work_sync(&sparx5->mact_work); + destroy_workqueue(sparx5->mact_queue); + mutex_destroy(&sparx5->mact_lock); + +- list_for_each_entry_safe(e, tmp, &sparx5->mc_cpu_entries, list) { ++ list_for_each_entry_safe(e, tmp, &sparx5->cpu_entries, list) { + list_del(&e->list); + kfree(e); + } +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +index ce89fd005a55..9d2a96eccfb2 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +@@ -409,10 +409,10 @@ struct sparx5 { + struct list_head mdb_entries; + /* mdb list mutex */ + struct mutex mdb_lock; +- /* CPU multicast entries, shared by the standalone ports */ +- struct list_head mc_cpu_entries; +- /* mc_cpu_entries lock */ +- spinlock_t mc_cpu_lock; ++ /* CPU entries, shared by the standalone ports */ ++ struct list_head cpu_entries; ++ /* cpu_entries lock */ ++ spinlock_t cpu_lock; + struct delayed_work mact_work; + struct workqueue_struct *mact_queue; + /* Board specifics */ +@@ -491,6 +491,10 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, + int sparx5_del_mact_entry(struct sparx5 *sparx5, + const unsigned char *addr, + u16 vid); ++int sparx5_cpu_entry_get(struct sparx5 *sparx5, const unsigned char *addr, ++ u16 vid); ++int sparx5_cpu_entry_put(struct sparx5 *sparx5, const unsigned char *addr, ++ u16 vid); + int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr); + int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr); + void sparx5_set_ageing(struct sparx5 *sparx5, int msecs); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +index 1d597c5a2057..963957bef6b0 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +@@ -193,14 +193,13 @@ static int sparx5_set_mac_address(struct net_device *dev, void *p) + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + +- /* The bridge keeps the entries of its ports */ +- if (!test_bit(port->portno, sparx5->bridge_mask)) { ++ /* The bridge keeps the entries of its ports, a port that is down has none */ ++ if (!test_bit(port->portno, sparx5->bridge_mask) && netif_running(dev)) { + /* Remove current */ +- sparx5_mact_forget(sparx5, dev->dev_addr, port->pvid); ++ sparx5_cpu_entry_put(sparx5, dev->dev_addr, port->pvid); + + /* Add new */ +- sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), +- addr->sa_data, port->pvid); ++ sparx5_cpu_entry_get(sparx5, addr->sa_data, port->pvid); + } + + /* Record the address */ +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +index a9726944fafd..15cbc5ba2842 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +@@ -199,7 +199,8 @@ static int sparx5_port_bridge_join(struct sparx5_port *port, + goto err_switchdev_offload; + + /* Remove standalone port entry */ +- sparx5_mact_forget(sparx5, ndev->dev_addr, 0); ++ if (netif_running(ndev)) ++ sparx5_cpu_entry_put(sparx5, ndev->dev_addr, NULL_VID); + + /* Port enters in bridge mode therefore don't need to copy to CPU + * frames for multicast in case the bridge is not requesting them +@@ -236,8 +237,8 @@ static void sparx5_port_bridge_leave(struct sparx5_port *port, + port->vid = NULL_VID; + + /* Forward frames to CPU */ +- sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), +- port->ndev->dev_addr, 0); ++ if (netif_running(port->ndev)) ++ sparx5_cpu_entry_get(sparx5, port->ndev->dev_addr, NULL_VID); + + /* Port enters in host more therefore restore mc list */ + __dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync); +@@ -281,10 +282,9 @@ static int sparx5_port_add_addr(struct net_device *dev, bool up) + return 0; + + if (up) +- sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), +- port->ndev->dev_addr, vid); ++ sparx5_cpu_entry_get(sparx5, port->ndev->dev_addr, vid); + else +- sparx5_mact_forget(sparx5, port->ndev->dev_addr, vid); ++ sparx5_cpu_entry_put(sparx5, port->ndev->dev_addr, vid); + + return 0; + } diff --git a/patches/linux/6.18.57/0110-net-phy-micrel-Add-support-for-non-PTP-SKUs-for-lan8.patch b/patches/linux/6.18.57/0110-net-phy-micrel-Add-support-for-non-PTP-SKUs-for-lan8.patch new file mode 100644 index 0000000000..67293a9720 --- /dev/null +++ b/patches/linux/6.18.57/0110-net-phy-micrel-Add-support-for-non-PTP-SKUs-for-lan8.patch @@ -0,0 +1,121 @@ +From 11b5cee9b9fe17f9736aa7dfa126fde08807980d Mon Sep 17 00:00:00 2001 +From: Horatiu Vultur +Date: Tue, 21 Oct 2025 09:07:26 +0200 +Subject: [PATCH 110/128] net: phy: micrel: Add support for non PTP SKUs for + lan8814 + +The lan8814 has 4 different SKUs and for 2 of these SKUs the PTP is +disabled. All these SKUs have the same value in the register 2 and 3. +Meaning that we can't differentiate them based on device id, therefore +check the SKU register and based on this allow or not to create a PTP +device. + +Signed-off-by: Horatiu Vultur +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251021070726.3690685-1-horatiu.vultur@microchip.com +Signed-off-by: Paolo Abeni + +(cherry picked from commit 61b7ade9ba8c3b16867e25411b5f7cf1abe35879) +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 38 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index d79004dcb768..2007c9d03fb8 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -101,6 +101,8 @@ + #define LAN8814_CABLE_DIAG_VCT_DATA_MASK GENMASK(7, 0) + #define LAN8814_PAIR_BIT_SHIFT 12 + ++#define LAN8814_SKUS 0xB ++ + #define LAN8814_WIRE_PAIR_MASK 0xF + + /* Lan8814 general Interrupt control/status reg in GPHY specific block. */ +@@ -367,6 +369,9 @@ + + #define LAN8842_REV_8832 0x8832 + ++#define LAN8814_REV_LAN8814 0x8814 ++#define LAN8814_REV_LAN8818 0x8818 ++ + struct kszphy_hw_stat { + const char *string; + u8 reg; +@@ -449,6 +454,7 @@ struct kszphy_priv { + bool rmii_ref_clk_sel; + bool rmii_ref_clk_sel_val; + bool clk_enable; ++ bool is_ptp_available; + u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; + struct kszphy_phy_stats phy_stats; + }; +@@ -4159,6 +4165,17 @@ static int lan8804_config_intr(struct phy_device *phydev) + return 0; + } + ++/* Check if the PHY has 1588 support. There are multiple skus of the PHY and ++ * some of them support PTP while others don't support it. This function will ++ * return true is the sku supports it, otherwise will return false. ++ */ ++static bool lan8814_has_ptp(struct phy_device *phydev) ++{ ++ struct kszphy_priv *priv = phydev->priv; ++ ++ return priv->is_ptp_available; ++} ++ + static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev) + { + int ret = IRQ_NONE; +@@ -4175,6 +4192,9 @@ static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev) + ret = IRQ_HANDLED; + } + ++ if (!lan8814_has_ptp(phydev)) ++ return ret; ++ + while (true) { + irq_status = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS, + PTP_TSU_INT_STS); +@@ -4236,6 +4256,9 @@ static void lan8814_ptp_init(struct phy_device *phydev) + !IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING)) + return; + ++ if (!lan8814_has_ptp(phydev)) ++ return; ++ + lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS, + TSU_HARD_RESET, TSU_HARD_RESET_); + +@@ -4365,6 +4388,9 @@ static int __lan8814_ptp_probe_once(struct phy_device *phydev, char *pin_name, + + static int lan8814_ptp_probe_once(struct phy_device *phydev) + { ++ if (!lan8814_has_ptp(phydev)) ++ return 0; ++ + return __lan8814_ptp_probe_once(phydev, "lan8814_ptp_pin", + LAN8814_PTP_GPIO_NUM); + } +@@ -4480,6 +4506,18 @@ static int lan8814_probe(struct phy_device *phydev) + devm_phy_package_join(&phydev->mdio.dev, phydev, + addr, sizeof(struct lan8814_shared_priv)); + ++ /* There are lan8814 SKUs that don't support PTP. Make sure that for ++ * those skus no PTP device is created. Here we check if the SKU ++ * supports PTP. ++ */ ++ err = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, ++ LAN8814_SKUS); ++ if (err < 0) ++ return err; ++ ++ priv->is_ptp_available = err == LAN8814_REV_LAN8814 || ++ err == LAN8814_REV_LAN8818; ++ + if (phy_package_probe_once(phydev)) { + err = lan8814_release_coma_mode(phydev); + if (err) diff --git a/patches/linux/6.18.57/0111-net-phy-micrel-move-lan8842-errata-tables-before-lan.patch b/patches/linux/6.18.57/0111-net-phy-micrel-move-lan8842-errata-tables-before-lan.patch new file mode 100644 index 0000000000..ee45c9e934 --- /dev/null +++ b/patches/linux/6.18.57/0111-net-phy-micrel-move-lan8842-errata-tables-before-lan.patch @@ -0,0 +1,287 @@ +From 174b83efea6136c46e010956a8495c1ae3b887df Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 15:34:01 +0200 +Subject: [PATCH 111/128] net: phy: micrel: move lan8842 errata tables before + lan8814 + +The LAN8814 has the same short center tap and EEE wake timer errata as +the LAN8842. Move the register tables and lanphy_write_reg_data() above +lan8814_config_init() so the LAN8814 can use them too. + +No functional change. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 252 +++++++++++++++++++-------------------- + 1 file changed, 126 insertions(+), 126 deletions(-) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 2007c9d03fb8..494e1c0ed610 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -4411,6 +4411,132 @@ static void lan8814_setup_led(struct phy_device *phydev, int val) + LAN8814_LED_CTRL_1, temp); + } + ++#define LAN8814_POWER_MGMT_MODE_3_ANEG_MDI 0x13 ++#define LAN8814_POWER_MGMT_MODE_4_ANEG_MDIX 0x14 ++#define LAN8814_POWER_MGMT_MODE_5_10BT_MDI 0x15 ++#define LAN8814_POWER_MGMT_MODE_6_10BT_MDIX 0x16 ++#define LAN8814_POWER_MGMT_MODE_7_100BT_TRAIN 0x17 ++#define LAN8814_POWER_MGMT_MODE_8_100BT_MDI 0x18 ++#define LAN8814_POWER_MGMT_MODE_9_100BT_EEE_MDI_TX 0x19 ++#define LAN8814_POWER_MGMT_MODE_10_100BT_EEE_MDI_RX 0x1a ++#define LAN8814_POWER_MGMT_MODE_11_100BT_MDIX 0x1b ++#define LAN8814_POWER_MGMT_MODE_12_100BT_EEE_MDIX_TX 0x1c ++#define LAN8814_POWER_MGMT_MODE_13_100BT_EEE_MDIX_RX 0x1d ++#define LAN8814_POWER_MGMT_MODE_14_100BTX_EEE_TX_RX 0x1e ++ ++#define LAN8814_POWER_MGMT_DLLPD_D BIT(0) ++#define LAN8814_POWER_MGMT_ADCPD_D BIT(1) ++#define LAN8814_POWER_MGMT_PGAPD_D BIT(2) ++#define LAN8814_POWER_MGMT_TXPD_D BIT(3) ++#define LAN8814_POWER_MGMT_DLLPD_C BIT(4) ++#define LAN8814_POWER_MGMT_ADCPD_C BIT(5) ++#define LAN8814_POWER_MGMT_PGAPD_C BIT(6) ++#define LAN8814_POWER_MGMT_TXPD_C BIT(7) ++#define LAN8814_POWER_MGMT_DLLPD_B BIT(8) ++#define LAN8814_POWER_MGMT_ADCPD_B BIT(9) ++#define LAN8814_POWER_MGMT_PGAPD_B BIT(10) ++#define LAN8814_POWER_MGMT_TXPD_B BIT(11) ++#define LAN8814_POWER_MGMT_DLLPD_A BIT(12) ++#define LAN8814_POWER_MGMT_ADCPD_A BIT(13) ++#define LAN8814_POWER_MGMT_PGAPD_A BIT(14) ++#define LAN8814_POWER_MGMT_TXPD_A BIT(15) ++ ++#define LAN8814_POWER_MGMT_C_D (LAN8814_POWER_MGMT_DLLPD_D | \ ++ LAN8814_POWER_MGMT_ADCPD_D | \ ++ LAN8814_POWER_MGMT_PGAPD_D | \ ++ LAN8814_POWER_MGMT_DLLPD_C | \ ++ LAN8814_POWER_MGMT_ADCPD_C | \ ++ LAN8814_POWER_MGMT_PGAPD_C) ++ ++#define LAN8814_POWER_MGMT_B_C_D (LAN8814_POWER_MGMT_C_D | \ ++ LAN8814_POWER_MGMT_DLLPD_B | \ ++ LAN8814_POWER_MGMT_ADCPD_B | \ ++ LAN8814_POWER_MGMT_PGAPD_B) ++ ++#define LAN8814_POWER_MGMT_VAL1 (LAN8814_POWER_MGMT_C_D | \ ++ LAN8814_POWER_MGMT_ADCPD_B | \ ++ LAN8814_POWER_MGMT_PGAPD_B | \ ++ LAN8814_POWER_MGMT_ADCPD_A | \ ++ LAN8814_POWER_MGMT_PGAPD_A) ++ ++#define LAN8814_POWER_MGMT_VAL2 LAN8814_POWER_MGMT_C_D ++ ++#define LAN8814_POWER_MGMT_VAL3 (LAN8814_POWER_MGMT_C_D | \ ++ LAN8814_POWER_MGMT_DLLPD_B | \ ++ LAN8814_POWER_MGMT_ADCPD_B | \ ++ LAN8814_POWER_MGMT_PGAPD_A) ++ ++#define LAN8814_POWER_MGMT_VAL4 (LAN8814_POWER_MGMT_B_C_D | \ ++ LAN8814_POWER_MGMT_ADCPD_A | \ ++ LAN8814_POWER_MGMT_PGAPD_A) ++ ++#define LAN8814_POWER_MGMT_VAL5 LAN8814_POWER_MGMT_B_C_D ++ ++#define LAN8814_EEE_WAKE_TX_TIMER 0x0e ++#define LAN8814_EEE_WAKE_TX_TIMER_MAX_VAL 0x1f ++ ++static const struct lanphy_reg_data short_center_tap_errata[] = { ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_3_ANEG_MDI, ++ LAN8814_POWER_MGMT_VAL1 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_4_ANEG_MDIX, ++ LAN8814_POWER_MGMT_VAL1 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_5_10BT_MDI, ++ LAN8814_POWER_MGMT_VAL1 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_6_10BT_MDIX, ++ LAN8814_POWER_MGMT_VAL1 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_7_100BT_TRAIN, ++ LAN8814_POWER_MGMT_VAL2 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_8_100BT_MDI, ++ LAN8814_POWER_MGMT_VAL3 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_9_100BT_EEE_MDI_TX, ++ LAN8814_POWER_MGMT_VAL3 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_10_100BT_EEE_MDI_RX, ++ LAN8814_POWER_MGMT_VAL4 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_11_100BT_MDIX, ++ LAN8814_POWER_MGMT_VAL5 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_12_100BT_EEE_MDIX_TX, ++ LAN8814_POWER_MGMT_VAL5 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_13_100BT_EEE_MDIX_RX, ++ LAN8814_POWER_MGMT_VAL4 }, ++ { LAN8814_PAGE_POWER_REGS, ++ LAN8814_POWER_MGMT_MODE_14_100BTX_EEE_TX_RX, ++ LAN8814_POWER_MGMT_VAL4 }, ++}; ++ ++static const struct lanphy_reg_data waketx_timer_errata[] = { ++ { LAN8814_PAGE_EEE, ++ LAN8814_EEE_WAKE_TX_TIMER, ++ LAN8814_EEE_WAKE_TX_TIMER_MAX_VAL }, ++}; ++ ++static int lanphy_write_reg_data(struct phy_device *phydev, ++ const struct lanphy_reg_data *data, ++ size_t num) ++{ ++ int ret = 0; ++ ++ while (num--) { ++ ret = lanphy_write_page_reg(phydev, data->page, data->addr, ++ data->val); ++ if (ret) ++ break; ++ data++; ++ } ++ ++ return ret; ++} ++ + static int lan8814_config_init(struct phy_device *phydev) + { + struct kszphy_priv *lan8814 = phydev->priv; +@@ -5952,132 +6078,6 @@ static int lan8842_probe(struct phy_device *phydev) + return 0; + } + +-#define LAN8814_POWER_MGMT_MODE_3_ANEG_MDI 0x13 +-#define LAN8814_POWER_MGMT_MODE_4_ANEG_MDIX 0x14 +-#define LAN8814_POWER_MGMT_MODE_5_10BT_MDI 0x15 +-#define LAN8814_POWER_MGMT_MODE_6_10BT_MDIX 0x16 +-#define LAN8814_POWER_MGMT_MODE_7_100BT_TRAIN 0x17 +-#define LAN8814_POWER_MGMT_MODE_8_100BT_MDI 0x18 +-#define LAN8814_POWER_MGMT_MODE_9_100BT_EEE_MDI_TX 0x19 +-#define LAN8814_POWER_MGMT_MODE_10_100BT_EEE_MDI_RX 0x1a +-#define LAN8814_POWER_MGMT_MODE_11_100BT_MDIX 0x1b +-#define LAN8814_POWER_MGMT_MODE_12_100BT_EEE_MDIX_TX 0x1c +-#define LAN8814_POWER_MGMT_MODE_13_100BT_EEE_MDIX_RX 0x1d +-#define LAN8814_POWER_MGMT_MODE_14_100BTX_EEE_TX_RX 0x1e +- +-#define LAN8814_POWER_MGMT_DLLPD_D BIT(0) +-#define LAN8814_POWER_MGMT_ADCPD_D BIT(1) +-#define LAN8814_POWER_MGMT_PGAPD_D BIT(2) +-#define LAN8814_POWER_MGMT_TXPD_D BIT(3) +-#define LAN8814_POWER_MGMT_DLLPD_C BIT(4) +-#define LAN8814_POWER_MGMT_ADCPD_C BIT(5) +-#define LAN8814_POWER_MGMT_PGAPD_C BIT(6) +-#define LAN8814_POWER_MGMT_TXPD_C BIT(7) +-#define LAN8814_POWER_MGMT_DLLPD_B BIT(8) +-#define LAN8814_POWER_MGMT_ADCPD_B BIT(9) +-#define LAN8814_POWER_MGMT_PGAPD_B BIT(10) +-#define LAN8814_POWER_MGMT_TXPD_B BIT(11) +-#define LAN8814_POWER_MGMT_DLLPD_A BIT(12) +-#define LAN8814_POWER_MGMT_ADCPD_A BIT(13) +-#define LAN8814_POWER_MGMT_PGAPD_A BIT(14) +-#define LAN8814_POWER_MGMT_TXPD_A BIT(15) +- +-#define LAN8814_POWER_MGMT_C_D (LAN8814_POWER_MGMT_DLLPD_D | \ +- LAN8814_POWER_MGMT_ADCPD_D | \ +- LAN8814_POWER_MGMT_PGAPD_D | \ +- LAN8814_POWER_MGMT_DLLPD_C | \ +- LAN8814_POWER_MGMT_ADCPD_C | \ +- LAN8814_POWER_MGMT_PGAPD_C) +- +-#define LAN8814_POWER_MGMT_B_C_D (LAN8814_POWER_MGMT_C_D | \ +- LAN8814_POWER_MGMT_DLLPD_B | \ +- LAN8814_POWER_MGMT_ADCPD_B | \ +- LAN8814_POWER_MGMT_PGAPD_B) +- +-#define LAN8814_POWER_MGMT_VAL1 (LAN8814_POWER_MGMT_C_D | \ +- LAN8814_POWER_MGMT_ADCPD_B | \ +- LAN8814_POWER_MGMT_PGAPD_B | \ +- LAN8814_POWER_MGMT_ADCPD_A | \ +- LAN8814_POWER_MGMT_PGAPD_A) +- +-#define LAN8814_POWER_MGMT_VAL2 LAN8814_POWER_MGMT_C_D +- +-#define LAN8814_POWER_MGMT_VAL3 (LAN8814_POWER_MGMT_C_D | \ +- LAN8814_POWER_MGMT_DLLPD_B | \ +- LAN8814_POWER_MGMT_ADCPD_B | \ +- LAN8814_POWER_MGMT_PGAPD_A) +- +-#define LAN8814_POWER_MGMT_VAL4 (LAN8814_POWER_MGMT_B_C_D | \ +- LAN8814_POWER_MGMT_ADCPD_A | \ +- LAN8814_POWER_MGMT_PGAPD_A) +- +-#define LAN8814_POWER_MGMT_VAL5 LAN8814_POWER_MGMT_B_C_D +- +-#define LAN8814_EEE_WAKE_TX_TIMER 0x0e +-#define LAN8814_EEE_WAKE_TX_TIMER_MAX_VAL 0x1f +- +-static const struct lanphy_reg_data short_center_tap_errata[] = { +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_3_ANEG_MDI, +- LAN8814_POWER_MGMT_VAL1 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_4_ANEG_MDIX, +- LAN8814_POWER_MGMT_VAL1 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_5_10BT_MDI, +- LAN8814_POWER_MGMT_VAL1 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_6_10BT_MDIX, +- LAN8814_POWER_MGMT_VAL1 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_7_100BT_TRAIN, +- LAN8814_POWER_MGMT_VAL2 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_8_100BT_MDI, +- LAN8814_POWER_MGMT_VAL3 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_9_100BT_EEE_MDI_TX, +- LAN8814_POWER_MGMT_VAL3 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_10_100BT_EEE_MDI_RX, +- LAN8814_POWER_MGMT_VAL4 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_11_100BT_MDIX, +- LAN8814_POWER_MGMT_VAL5 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_12_100BT_EEE_MDIX_TX, +- LAN8814_POWER_MGMT_VAL5 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_13_100BT_EEE_MDIX_RX, +- LAN8814_POWER_MGMT_VAL4 }, +- { LAN8814_PAGE_POWER_REGS, +- LAN8814_POWER_MGMT_MODE_14_100BTX_EEE_TX_RX, +- LAN8814_POWER_MGMT_VAL4 }, +-}; +- +-static const struct lanphy_reg_data waketx_timer_errata[] = { +- { LAN8814_PAGE_EEE, +- LAN8814_EEE_WAKE_TX_TIMER, +- LAN8814_EEE_WAKE_TX_TIMER_MAX_VAL }, +-}; +- +-static int lanphy_write_reg_data(struct phy_device *phydev, +- const struct lanphy_reg_data *data, +- size_t num) +-{ +- int ret = 0; +- +- while (num--) { +- ret = lanphy_write_page_reg(phydev, data->page, data->addr, +- data->val); +- if (ret) +- break; +- data++; +- } +- +- return ret; +-} +- + static int lan8842_erratas(struct phy_device *phydev) + { + int ret; diff --git a/patches/linux/6.18.57/0112-net-phy-micrel-lan8814-errata.patch b/patches/linux/6.18.57/0112-net-phy-micrel-lan8814-errata.patch new file mode 100644 index 0000000000..7b54f4e87a --- /dev/null +++ b/patches/linux/6.18.57/0112-net-phy-micrel-lan8814-errata.patch @@ -0,0 +1,80 @@ +From 316ae725e11b8a789afcbe8abc19a9bf7f845b9f Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 15:34:03 +0200 +Subject: [PATCH 112/128] net: phy: micrel: lan8814 errata + +The LAN8814 runs with the power-on defaults for several registers that +the silicon errata [1] and Microchip's PHY API require to be changed on +every port: the power management modes for PHY-side shorted center taps +(module 4), the 1000BASE-T EEE TX wake timer (module 9), and the PLL +trim. The driver applies the first two to the LAN8842 only, and the PLL +trim to neither. + +Apply them from config_init, after the soft reset that clears them, +using the same tables as the LAN8842. The values match what the LAN8814 +support in Microchip's PHY API writes after reset. + +[1] https://ww1.microchip.com/downloads/aemDocuments/documents/UNG/ProductDocuments/Errata/LAN8814-Errata-DS80001002.pdf + +Fixes: 7467d716583e ("net: phy: micrel: Add config_init for LAN8814") +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 38 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 37 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 494e1c0ed610..6aa27b651e81 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -4537,6 +4537,42 @@ static int lanphy_write_reg_data(struct phy_device *phydev, + return ret; + } + ++#define LAN8814_PAGE_ANALOG_CTRL 29 ++#define LAN8814_ANALOG_CONTROL_1 0x01 ++#define LAN8814_ANALOG_CONTROL_1_PLL_TRIM_MASK GENMASK(6, 5) ++#define LAN8814_ANALOG_CONTROL_1_PLL_TRIM FIELD_PREP_CONST(GENMASK(6, 5), 2) ++#define LAN8814_ANALOG_CONTROL_10 0x0d ++#define LAN8814_ANALOG_CONTROL_10_PLL_DIV_MASK GENMASK(1, 0) ++#define LAN8814_ANALOG_CONTROL_10_PLL_DIV 1 ++ ++static int lan8814_erratas(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = lanphy_write_reg_data(phydev, short_center_tap_errata, ++ ARRAY_SIZE(short_center_tap_errata)); ++ if (ret) ++ return ret; ++ ++ ret = lanphy_write_reg_data(phydev, waketx_timer_errata, ++ ARRAY_SIZE(waketx_timer_errata)); ++ if (ret) ++ return ret; ++ ++ ret = lanphy_modify_page_reg(phydev, LAN8814_PAGE_ANALOG_CTRL, ++ LAN8814_ANALOG_CONTROL_1, ++ LAN8814_ANALOG_CONTROL_1_PLL_TRIM_MASK, ++ LAN8814_ANALOG_CONTROL_1_PLL_TRIM); ++ if (ret < 0) ++ return ret; ++ ++ ret = lanphy_modify_page_reg(phydev, LAN8814_PAGE_ANALOG_CTRL, ++ LAN8814_ANALOG_CONTROL_10, ++ LAN8814_ANALOG_CONTROL_10_PLL_DIV_MASK, ++ LAN8814_ANALOG_CONTROL_10_PLL_DIV); ++ return ret < 0 ? ret : 0; ++} ++ + static int lan8814_config_init(struct phy_device *phydev) + { + struct kszphy_priv *lan8814 = phydev->priv; +@@ -4562,7 +4598,7 @@ static int lan8814_config_init(struct phy_device *phydev) + if (lan8814->led_mode >= 0) + lan8814_setup_led(phydev, lan8814->led_mode); + +- return 0; ++ return lan8814_erratas(phydev); + } + + /* It is expected that there will not be any 'lan8814_take_coma_mode' diff --git a/patches/linux/6.18.57/0113-net-phy-micrel-lan8814-resync-QSGMII-after-MAC-setup.patch b/patches/linux/6.18.57/0113-net-phy-micrel-lan8814-resync-QSGMII-after-MAC-setup.patch new file mode 100644 index 0000000000..70ae8a1a0b --- /dev/null +++ b/patches/linux/6.18.57/0113-net-phy-micrel-lan8814-resync-QSGMII-after-MAC-setup.patch @@ -0,0 +1,111 @@ +From bf7ac1ba6b31449885bf3e639e97e99f86c2bfe8 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 15:42:34 +0200 +Subject: [PATCH 113/128] net: phy: micrel: lan8814: resync QSGMII after MAC + setup + +On a LAN969x switch with LAN8804 quad PHYs, a copper port at times comes +up with one direction dead: the switch counts the frames it sends, the +link partner receives none of them, and the link stays up at 1000 Mbps. +A QSGMII soft reset of the PHY package followed by a new autonegotiation +on the port brings it back. + +Microchip's PHY API runs that sequence on every LAN8814 port: a QSGMII +soft reset once the MAC has configured its end of the QSGMII, and one +more autonegotiation after the first 1000BASE-T link, as a workaround for +CRC errors. The driver resets the QSGMII only from config_init, which +runs before phylink configures the MAC side. + +Reset the package QSGMII once from config_aneg, which runs after the MAC +side is set up, and restart autonegotiation once per port when it first +links at 1000 Mbps with both receivers OK. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 52 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 51 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 6aa27b651e81..7e20ccfec770 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -405,6 +405,9 @@ struct lan8814_shared_priv { + + /* Lock for ptp_clock */ + struct mutex shared_lock; ++ ++ unsigned long flags; ++#define LAN8814_SHARED_F_QSGMII_RESYNCED 0 + }; + + struct lan8814_ptp_rx_ts { +@@ -455,6 +458,7 @@ struct kszphy_priv { + bool rmii_ref_clk_sel_val; + bool clk_enable; + bool is_ptp_available; ++ bool aneg_restarted; + u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; + struct kszphy_phy_stats phy_stats; + }; +@@ -4601,6 +4605,51 @@ static int lan8814_config_init(struct phy_device *phydev) + return lan8814_erratas(phydev); + } + ++/* Microchip's PHY API resets the package QSGMII once the MAC has configured ++ * its end, and renegotiates each port once after its first 1000BASE-T link, ++ * to avoid CRC errors. Under phylink the MAC side is set up before ++ * config_aneg runs. ++ */ ++static int lan8814_config_aneg(struct phy_device *phydev) ++{ ++ struct lan8814_shared_priv *shared = phy_package_get_priv(phydev); ++ int err; ++ ++ if (!test_and_set_bit(LAN8814_SHARED_F_QSGMII_RESYNCED, &shared->flags)) { ++ err = lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, ++ LAN8814_QSGMII_SOFT_RESET, ++ LAN8814_QSGMII_SOFT_RESET_BIT, ++ LAN8814_QSGMII_SOFT_RESET_BIT); ++ if (err < 0) ++ return err; ++ } ++ ++ return genphy_config_aneg(phydev); ++} ++ ++static int lan8814_read_status(struct phy_device *phydev) ++{ ++ struct kszphy_priv *priv = phydev->priv; ++ int err, val; ++ ++ err = ksz9031_read_status(phydev); ++ if (err || priv->aneg_restarted || !phydev->link || ++ phydev->speed != SPEED_1000) ++ return err; ++ ++ val = phy_read(phydev, MII_STAT1000); ++ if (val < 0) ++ return val; ++ if ((val & (LPA_1000LOCALRXOK | LPA_1000REMRXOK)) != ++ (LPA_1000LOCALRXOK | LPA_1000REMRXOK)) ++ return 0; ++ ++ priv->aneg_restarted = true; ++ phydev->link = 0; ++ ++ return genphy_restart_aneg(phydev); ++} ++ + /* It is expected that there will not be any 'lan8814_take_coma_mode' + * function called in suspend. Because the GPIO line can be shared, so if one of + * the phys goes back in coma mode, then all the other PHYs will go, which is +@@ -6584,7 +6633,8 @@ static struct phy_driver ksphy_driver[] = { + .driver_data = &lan8814_type, + .probe = lan8814_probe, + .soft_reset = genphy_soft_reset, +- .read_status = ksz9031_read_status, ++ .config_aneg = lan8814_config_aneg, ++ .read_status = lan8814_read_status, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, diff --git a/patches/linux/6.18.57/0114-net-phy-micrel-lan8814-disable-EEE.patch b/patches/linux/6.18.57/0114-net-phy-micrel-lan8814-disable-EEE.patch new file mode 100644 index 0000000000..68f1e70094 --- /dev/null +++ b/patches/linux/6.18.57/0114-net-phy-micrel-lan8814-disable-EEE.patch @@ -0,0 +1,33 @@ +From 4dc970c9d12c68a0168e895c03c201a256c05f4d Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:27:48 +0200 +Subject: [PATCH 114/128] net: phy: micrel: lan8814: disable EEE + +The LAN8814 can enter 100BASE-TX EEE low power idle spuriously after the +cable is unplugged (errata module 24). The PHY advertises EEE out of +reset, so two LAN8814s negotiate it even when neither MAC supports LPI, +since phylink only disables PHY EEE for MACs that implement the LPI ops. + +Disable EEE at probe, the default in Microchip's PHY API. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 7e20ccfec770..92daa60411d1 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -4745,6 +4745,11 @@ static int lan8814_probe(struct phy_device *phydev) + lan8814_clear_2psp_bit(phydev); + lan8814_update_meas_time(phydev); + ++ /* Errata module 24: spurious LPI entry on cable unplug. Keep EEE ++ * off, as Microchip's PHY API does by default. ++ */ ++ phy_disable_eee(phydev); ++ + return 0; + } + diff --git a/patches/linux/6.18.57/0115-net-phy-micrel-lan8814-restart-stuck-autonegotiation.patch b/patches/linux/6.18.57/0115-net-phy-micrel-lan8814-restart-stuck-autonegotiation.patch new file mode 100644 index 0000000000..882a6ab1e0 --- /dev/null +++ b/patches/linux/6.18.57/0115-net-phy-micrel-lan8814-restart-stuck-autonegotiation.patch @@ -0,0 +1,147 @@ +From 2d1ed7318aebca1e2380b4d2c2bd53837b83dfe5 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:27:50 +0200 +Subject: [PATCH 115/128] net: phy: micrel: lan8814: restart stuck + autonegotiation + +The LAN8814 autonegotiation state machine can get stuck decoding valid +pages from the link partner, and the link never comes up (errata module +20). The errata asks software to watch for this and restart +autonegotiation. + +While the link is down with autonegotiation enabled, check once a second +for a signal from the partner. After three seconds of signal without a +link or completed autonegotiation, restart it once, as Microchip's PHY +API does. Without a signal, check every five seconds. The PHY raises no +interrupt in this state, so this runs as delayed work. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 80 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 80 insertions(+) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 92daa60411d1..9421f0fe2fe4 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -459,6 +459,9 @@ struct kszphy_priv { + bool clk_enable; + bool is_ptp_available; + bool aneg_restarted; ++ struct phy_device *phydev; ++ struct delayed_work aneg_work; ++ unsigned int aneg_checks; + u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; + struct kszphy_phy_stats phy_stats; + }; +@@ -4605,6 +4608,69 @@ static int lan8814_config_init(struct phy_device *phydev) + return lan8814_erratas(phydev); + } + ++#define LAN8814_AX_AN_STATUS 0x14 ++#define LAN8814_AX_AN_STATUS_SIG_DET BIT(13) ++#define LAN8814_ANEG_CHECKS 3 ++ ++static void lan8814_aneg_queue(struct kszphy_priv *priv, unsigned int secs) ++{ ++ mod_delayed_work(system_power_efficient_wq, &priv->aneg_work, ++ round_jiffies_relative(secs * HZ)); ++} ++ ++/* Errata module 20: autonegotiation can get stuck while the link partner is ++ * present. Restart it once if a signal has been detected for three seconds ++ * without a link, as Microchip's PHY API does. ++ */ ++static void lan8814_aneg_work(struct work_struct *work) ++{ ++ struct kszphy_priv *priv = container_of(work, struct kszphy_priv, ++ aneg_work.work); ++ struct phy_device *phydev = priv->phydev; ++ int val; ++ ++ mutex_lock(&phydev->lock); ++ ++ if (!phy_is_started(phydev) || phydev->link || ++ phydev->autoneg != AUTONEG_ENABLE) ++ goto out; ++ ++ val = phy_read(phydev, LAN8814_AX_AN_STATUS); ++ if (val < 0) ++ goto out; ++ ++ if (!(val & LAN8814_AX_AN_STATUS_SIG_DET)) { ++ priv->aneg_checks = 0; ++ lan8814_aneg_queue(priv, 5); ++ goto out; ++ } ++ ++ val = phy_read(phydev, MII_BMSR); ++ if (val < 0 || (val & (BMSR_LSTATUS | BMSR_ANEGCOMPLETE))) ++ goto out; ++ ++ if (++priv->aneg_checks < LAN8814_ANEG_CHECKS) { ++ lan8814_aneg_queue(priv, 1); ++ goto out; ++ } ++ ++ phydev_dbg(phydev, "autonegotiation stuck, restarting\n"); ++ genphy_restart_aneg(phydev); ++out: ++ mutex_unlock(&phydev->lock); ++} ++ ++static void lan8814_link_change_notify(struct phy_device *phydev) ++{ ++ struct kszphy_priv *priv = phydev->priv; ++ ++ priv->aneg_checks = 0; ++ if (phydev->link || phydev->autoneg != AUTONEG_ENABLE) ++ cancel_delayed_work(&priv->aneg_work); ++ else ++ lan8814_aneg_queue(priv, 1); ++} ++ + /* Microchip's PHY API resets the package QSGMII once the MAC has configured + * its end, and renegotiates each port once after its first 1000BASE-T link, + * to avoid CRC errors. Under phylink the MAC side is set up before +@@ -4624,6 +4690,8 @@ static int lan8814_config_aneg(struct phy_device *phydev) + return err; + } + ++ lan8814_link_change_notify(phydev); ++ + return genphy_config_aneg(phydev); + } + +@@ -4750,9 +4818,19 @@ static int lan8814_probe(struct phy_device *phydev) + */ + phy_disable_eee(phydev); + ++ priv->phydev = phydev; ++ INIT_DELAYED_WORK(&priv->aneg_work, lan8814_aneg_work); ++ + return 0; + } + ++static void lan8814_remove(struct phy_device *phydev) ++{ ++ struct kszphy_priv *priv = phydev->priv; ++ ++ cancel_delayed_work_sync(&priv->aneg_work); ++} ++ + #define LAN8841_MMD_TIMER_REG 0 + #define LAN8841_MMD0_REGISTER_17 17 + #define LAN8841_MMD0_REGISTER_17_DROP_OPT(x) ((x) & 0x3) +@@ -6637,9 +6715,11 @@ static struct phy_driver ksphy_driver[] = { + .config_init = lan8814_config_init, + .driver_data = &lan8814_type, + .probe = lan8814_probe, ++ .remove = lan8814_remove, + .soft_reset = genphy_soft_reset, + .config_aneg = lan8814_config_aneg, + .read_status = lan8814_read_status, ++ .link_change_notify = lan8814_link_change_notify, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, diff --git a/patches/linux/6.18.57/0116-net-phy-micrel-lan8814-10BASE-T-transmitter-errata.patch b/patches/linux/6.18.57/0116-net-phy-micrel-lan8814-10BASE-T-transmitter-errata.patch new file mode 100644 index 0000000000..466676f539 --- /dev/null +++ b/patches/linux/6.18.57/0116-net-phy-micrel-lan8814-10BASE-T-transmitter-errata.patch @@ -0,0 +1,54 @@ +From bf204b62d4a73005f6662ff4e5ff4983270ec5aa Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:39:53 +0200 +Subject: [PATCH 116/128] net: phy: micrel: lan8814: 10BASE-T transmitter + errata + +The default 10BASE-T transmitter settings of the LAN8814 may fail the +IEEE Clause 14 MAU compliance tests (errata module 13). The errata +calls for an initialization script on every port. + +Apply the two analog settings Microchip's PHY API writes after reset. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 9421f0fe2fe4..49feee9479d0 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -4430,6 +4430,8 @@ static void lan8814_setup_led(struct phy_device *phydev, int val) + #define LAN8814_POWER_MGMT_MODE_12_100BT_EEE_MDIX_TX 0x1c + #define LAN8814_POWER_MGMT_MODE_13_100BT_EEE_MDIX_RX 0x1d + #define LAN8814_POWER_MGMT_MODE_14_100BTX_EEE_TX_RX 0x1e ++#define LAN8814_ANALOG_CONTROL_4 0x04 ++#define LAN8814_AFED_CONTROL 0x09 + + #define LAN8814_POWER_MGMT_DLLPD_D BIT(0) + #define LAN8814_POWER_MGMT_ADCPD_D BIT(1) +@@ -4552,6 +4554,12 @@ static int lanphy_write_reg_data(struct phy_device *phydev, + #define LAN8814_ANALOG_CONTROL_10_PLL_DIV_MASK GENMASK(1, 0) + #define LAN8814_ANALOG_CONTROL_10_PLL_DIV 1 + ++/* Errata module 13: 10BASE-T transmitter settings, from Microchip's PHY API */ ++static const struct lanphy_reg_data mau_errata[] = { ++ { LAN8814_PAGE_POWER_REGS, LAN8814_ANALOG_CONTROL_4, 0x81e0 }, ++ { LAN8814_PAGE_POWER_REGS, LAN8814_AFED_CONTROL, 0xe214 }, ++}; ++ + static int lan8814_erratas(struct phy_device *phydev) + { + int ret; +@@ -4566,6 +4574,10 @@ static int lan8814_erratas(struct phy_device *phydev) + if (ret) + return ret; + ++ ret = lanphy_write_reg_data(phydev, mau_errata, ARRAY_SIZE(mau_errata)); ++ if (ret) ++ return ret; ++ + ret = lanphy_modify_page_reg(phydev, LAN8814_PAGE_ANALOG_CTRL, + LAN8814_ANALOG_CONTROL_1, + LAN8814_ANALOG_CONTROL_1_PLL_TRIM_MASK, diff --git a/patches/linux/6.18.57/0117-net-phy-micrel-lan8814-half-duplex-FIFO-workaround.patch b/patches/linux/6.18.57/0117-net-phy-micrel-lan8814-half-duplex-FIFO-workaround.patch new file mode 100644 index 0000000000..edbac7852f --- /dev/null +++ b/patches/linux/6.18.57/0117-net-phy-micrel-lan8814-half-duplex-FIFO-workaround.patch @@ -0,0 +1,99 @@ +From d9ae860623e560a166a4755ae5af973da7d38f55 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:40:31 +0200 +Subject: [PATCH 117/128] net: phy: micrel: lan8814: half-duplex FIFO + workaround + +The LAN8814 may incur late collisions on half-duplex links (errata +module 11). The errata lists the issue as fixed in revision C1 silicon, +but Microchip's PHY API applies a workaround on every revision: lower +the rate adaptation FIFO thresholds when a 10 or 100 Mbps link comes up +in half duplex, and reset the FIFOs, which restores the defaults, when +the link goes down. + +Do the same from link_change_notify, on link transitions only. +--- + drivers/net/phy/micrel.c | 49 ++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 47 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 49feee9479d0..969d4590978d 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -462,6 +462,7 @@ struct kszphy_priv { + struct phy_device *phydev; + struct delayed_work aneg_work; + unsigned int aneg_checks; ++ bool link_up; + u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; + struct kszphy_phy_stats phy_stats; + }; +@@ -4672,7 +4673,7 @@ static void lan8814_aneg_work(struct work_struct *work) + mutex_unlock(&phydev->lock); + } + +-static void lan8814_link_change_notify(struct phy_device *phydev) ++static void lan8814_aneg_arm(struct phy_device *phydev) + { + struct kszphy_priv *priv = phydev->priv; + +@@ -4683,6 +4684,50 @@ static void lan8814_link_change_notify(struct phy_device *phydev) + lan8814_aneg_queue(priv, 1); + } + ++#define LAN8814_RX_RA_FIFO_THRESHOLDS_1 0x20 ++#define LAN8814_RX_RA_FIFO_THRESHOLDS_2 0x21 ++#define LAN8814_RX_RA_FIFO_THRESHOLDS_3 0x22 ++#define LAN8814_TX_RA_FIFO_THRESHOLDS_1 0x26 ++#define LAN8814_TX_RA_FIFO_THRESHOLDS_2 0x27 ++#define LAN8814_TX_RA_FIFO_THRESHOLDS_3 0x28 ++#define LAN8814_TX_RA_FIFO_RESET 0x2c ++#define LAN8814_RX_RA_FIFO_RESET 0x2d ++ ++/* Errata module 11: late collisions in half duplex. Microchip's PHY API ++ * lowers the rate adaptation FIFO thresholds on 10/100 half-duplex links, ++ * and resets the FIFOs to their defaults when the link goes down. ++ */ ++static const struct lanphy_reg_data half_duplex_errata[] = { ++ { LAN8814_PAGE_PORT_REGS, LAN8814_RX_RA_FIFO_THRESHOLDS_1, 0x200 }, ++ { LAN8814_PAGE_PORT_REGS, LAN8814_RX_RA_FIFO_THRESHOLDS_2, 0x404 }, ++ { LAN8814_PAGE_PORT_REGS, LAN8814_RX_RA_FIFO_THRESHOLDS_3, 0x608 }, ++ { LAN8814_PAGE_PORT_REGS, LAN8814_TX_RA_FIFO_THRESHOLDS_1, 0x200 }, ++ { LAN8814_PAGE_PORT_REGS, LAN8814_TX_RA_FIFO_THRESHOLDS_2, 0x404 }, ++ { LAN8814_PAGE_PORT_REGS, LAN8814_TX_RA_FIFO_THRESHOLDS_3, 0x608 }, ++}; ++ ++static const struct lanphy_reg_data ra_fifo_reset[] = { ++ { LAN8814_PAGE_PORT_REGS, LAN8814_TX_RA_FIFO_RESET, 1 }, ++ { LAN8814_PAGE_PORT_REGS, LAN8814_RX_RA_FIFO_RESET, 1 }, ++}; ++ ++static void lan8814_link_change_notify(struct phy_device *phydev) ++{ ++ struct kszphy_priv *priv = phydev->priv; ++ ++ if (phydev->link != priv->link_up) { ++ priv->link_up = phydev->link; ++ if (!phydev->link) ++ lanphy_write_reg_data(phydev, ra_fifo_reset, ++ ARRAY_SIZE(ra_fifo_reset)); ++ else if (phydev->duplex == DUPLEX_HALF) ++ lanphy_write_reg_data(phydev, half_duplex_errata, ++ ARRAY_SIZE(half_duplex_errata)); ++ } ++ ++ lan8814_aneg_arm(phydev); ++} ++ + /* Microchip's PHY API resets the package QSGMII once the MAC has configured + * its end, and renegotiates each port once after its first 1000BASE-T link, + * to avoid CRC errors. Under phylink the MAC side is set up before +@@ -4702,7 +4747,7 @@ static int lan8814_config_aneg(struct phy_device *phydev) + return err; + } + +- lan8814_link_change_notify(phydev); ++ lan8814_aneg_arm(phydev); + + return genphy_config_aneg(phydev); + } diff --git a/patches/linux/6.18.57/0118-net-phy-micrel-lan8814-reset-DSP-on-stuck-forced-100.patch b/patches/linux/6.18.57/0118-net-phy-micrel-lan8814-reset-DSP-on-stuck-forced-100.patch new file mode 100644 index 0000000000..9deff6163a --- /dev/null +++ b/patches/linux/6.18.57/0118-net-phy-micrel-lan8814-reset-DSP-on-stuck-forced-100.patch @@ -0,0 +1,209 @@ +From b451a9a0858a5c9cff2949b41d2e4a5b417ccf31 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:42:51 +0200 +Subject: [PATCH 118/128] net: phy: micrel: lan8814: reset DSP on stuck forced + 100BASE-TX + +With the speed forced to 100 Mbps, the LAN8814 DSP can fail to train on +the received signal, and the link never comes up (errata module 22). +The errata calls for periodic poll and restart. + +Extend the autonegotiation watchdog: with autonegotiation off at 100 +Mbps, soft reset the DSP every three seconds while the PHY detects a +link partner but has no link, as Microchip's PHY API does. The soft +reset keeps the register settings. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 115 ++++++++++++++++++++++++++------------- + 1 file changed, 77 insertions(+), 38 deletions(-) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 969d4590978d..af358c30cd75 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -460,8 +460,8 @@ struct kszphy_priv { + bool is_ptp_available; + bool aneg_restarted; + struct phy_device *phydev; +- struct delayed_work aneg_work; +- unsigned int aneg_checks; ++ struct delayed_work link_work; ++ unsigned int link_checks; + bool link_up; + u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; + struct kszphy_phy_stats phy_stats; +@@ -4622,66 +4622,105 @@ static int lan8814_config_init(struct phy_device *phydev) + } + + #define LAN8814_AX_AN_STATUS 0x14 ++#define LAN8814_AX_AN_STATUS_LINK_DET BIT(14) + #define LAN8814_AX_AN_STATUS_SIG_DET BIT(13) +-#define LAN8814_ANEG_CHECKS 3 ++#define LAN8814_CTRL_SOFT_RESET BIT(1) ++#define LAN8814_LINK_CHECKS 3 + +-static void lan8814_aneg_queue(struct kszphy_priv *priv, unsigned int secs) ++static void lan8814_link_queue(struct kszphy_priv *priv, unsigned int secs) + { +- mod_delayed_work(system_power_efficient_wq, &priv->aneg_work, ++ mod_delayed_work(system_power_efficient_wq, &priv->link_work, + round_jiffies_relative(secs * HZ)); + } + +-/* Errata module 20: autonegotiation can get stuck while the link partner is +- * present. Restart it once if a signal has been detected for three seconds +- * without a link, as Microchip's PHY API does. ++/* Count the seconds the PHY reports @det in LAN8814_AX_AN_STATUS while BMSR ++ * shows none of @bmsr, and return true once that reaches LAN8814_LINK_CHECKS. + */ +-static void lan8814_aneg_work(struct work_struct *work) ++static bool lan8814_link_stuck(struct phy_device *phydev, u16 det, u16 bmsr) + { +- struct kszphy_priv *priv = container_of(work, struct kszphy_priv, +- aneg_work.work); +- struct phy_device *phydev = priv->phydev; ++ struct kszphy_priv *priv = phydev->priv; + int val; + +- mutex_lock(&phydev->lock); +- +- if (!phy_is_started(phydev) || phydev->link || +- phydev->autoneg != AUTONEG_ENABLE) +- goto out; +- + val = phy_read(phydev, LAN8814_AX_AN_STATUS); + if (val < 0) +- goto out; ++ return false; + +- if (!(val & LAN8814_AX_AN_STATUS_SIG_DET)) { +- priv->aneg_checks = 0; +- lan8814_aneg_queue(priv, 5); +- goto out; ++ if (!(val & det)) { ++ priv->link_checks = 0; ++ lan8814_link_queue(priv, 5); ++ return false; + } + + val = phy_read(phydev, MII_BMSR); +- if (val < 0 || (val & (BMSR_LSTATUS | BMSR_ANEGCOMPLETE))) +- goto out; ++ if (val < 0 || (val & bmsr)) ++ return false; + +- if (++priv->aneg_checks < LAN8814_ANEG_CHECKS) { +- lan8814_aneg_queue(priv, 1); +- goto out; ++ if (++priv->link_checks < LAN8814_LINK_CHECKS) { ++ lan8814_link_queue(priv, 1); ++ return false; + } + ++ priv->link_checks = 0; ++ return true; ++} ++ ++/* Errata module 20: autonegotiation can get stuck while the link partner is ++ * present. Restart it once if a signal has been detected for three seconds ++ * without a link, as Microchip's PHY API does. ++ */ ++static void lan8814_aneg_check(struct phy_device *phydev) ++{ ++ if (!lan8814_link_stuck(phydev, LAN8814_AX_AN_STATUS_SIG_DET, ++ BMSR_LSTATUS | BMSR_ANEGCOMPLETE)) ++ return; ++ + phydev_dbg(phydev, "autonegotiation stuck, restarting\n"); + genphy_restart_aneg(phydev); +-out: ++} ++ ++/* Errata module 22: a forced 100BASE-TX link can fail to train. Soft reset ++ * the DSP every three seconds while the PHY detects a link partner but has ++ * no link, as Microchip's PHY API does. ++ */ ++static void lan8814_100tx_check(struct phy_device *phydev) ++{ ++ if (!lan8814_link_stuck(phydev, LAN8814_AX_AN_STATUS_LINK_DET, ++ BMSR_LSTATUS)) ++ return; ++ ++ phydev_dbg(phydev, "no 100BASE-TX link, resetting DSP\n"); ++ phy_set_bits(phydev, MII_KSZPHY_CTRL, LAN8814_CTRL_SOFT_RESET); ++ lan8814_link_queue(phydev->priv, 1); ++} ++ ++static void lan8814_link_work(struct work_struct *work) ++{ ++ struct kszphy_priv *priv = container_of(work, struct kszphy_priv, ++ link_work.work); ++ struct phy_device *phydev = priv->phydev; ++ ++ mutex_lock(&phydev->lock); ++ ++ if (phy_is_started(phydev) && !phydev->link) { ++ if (phydev->autoneg == AUTONEG_ENABLE) ++ lan8814_aneg_check(phydev); ++ else if (phydev->speed == SPEED_100) ++ lan8814_100tx_check(phydev); ++ } ++ + mutex_unlock(&phydev->lock); + } + +-static void lan8814_aneg_arm(struct phy_device *phydev) ++static void lan8814_link_watch(struct phy_device *phydev) + { + struct kszphy_priv *priv = phydev->priv; + +- priv->aneg_checks = 0; +- if (phydev->link || phydev->autoneg != AUTONEG_ENABLE) +- cancel_delayed_work(&priv->aneg_work); ++ priv->link_checks = 0; ++ if (phydev->link || (phydev->autoneg != AUTONEG_ENABLE && ++ phydev->speed != SPEED_100)) ++ cancel_delayed_work(&priv->link_work); + else +- lan8814_aneg_queue(priv, 1); ++ lan8814_link_queue(priv, 1); + } + + #define LAN8814_RX_RA_FIFO_THRESHOLDS_1 0x20 +@@ -4725,7 +4764,7 @@ static void lan8814_link_change_notify(struct phy_device *phydev) + ARRAY_SIZE(half_duplex_errata)); + } + +- lan8814_aneg_arm(phydev); ++ lan8814_link_watch(phydev); + } + + /* Microchip's PHY API resets the package QSGMII once the MAC has configured +@@ -4747,7 +4786,7 @@ static int lan8814_config_aneg(struct phy_device *phydev) + return err; + } + +- lan8814_aneg_arm(phydev); ++ lan8814_link_watch(phydev); + + return genphy_config_aneg(phydev); + } +@@ -4876,7 +4915,7 @@ static int lan8814_probe(struct phy_device *phydev) + phy_disable_eee(phydev); + + priv->phydev = phydev; +- INIT_DELAYED_WORK(&priv->aneg_work, lan8814_aneg_work); ++ INIT_DELAYED_WORK(&priv->link_work, lan8814_link_work); + + return 0; + } +@@ -4885,7 +4924,7 @@ static void lan8814_remove(struct phy_device *phydev) + { + struct kszphy_priv *priv = phydev->priv; + +- cancel_delayed_work_sync(&priv->aneg_work); ++ cancel_delayed_work_sync(&priv->link_work); + } + + #define LAN8841_MMD_TIMER_REG 0 diff --git a/patches/linux/6.18.57/0119-net-sparx5-take-a-port-s-MAC-address-from-the-device.patch b/patches/linux/6.18.57/0119-net-sparx5-take-a-port-s-MAC-address-from-the-device.patch new file mode 100644 index 0000000000..71da547834 --- /dev/null +++ b/patches/linux/6.18.57/0119-net-sparx5-take-a-port-s-MAC-address-from-the-device.patch @@ -0,0 +1,37 @@ +From d15c1ee7ecddb25b0d0fb9e5eab0bad4ad526c3f Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 14 Oct 2026 11:01:01 +0200 +Subject: [PATCH 119/128] net: sparx5: take a port's MAC address from the + device tree + +Every port gets the switch base address plus its port number plus one, +so a board cannot give its ports addresses in front panel order, or take +them from per-port nvmem cells, the way other switch and Ethernet drivers +allow. + +Use the address in the port's device tree node when there is one, and +mark it permanent. Ports without one keep the base address plus port. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +index 787dcdc996cd..af63b982b6f5 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +@@ -342,6 +342,13 @@ static int sparx5_create_port(struct sparx5 *sparx5, + INIT_LIST_HEAD(&spx5_port->tc_templates); + sparx5->ports[config->portno] = spx5_port; + ++ /* An address given for the port overrides the base address + port */ ++ err = of_get_ethdev_address(config->node, ndev); ++ if (err == -EPROBE_DEFER) ++ return err; ++ if (!err) ++ ndev->addr_assign_type = NET_ADDR_PERM; ++ + err = sparx5_port_init(sparx5, spx5_port, &config->conf); + if (err) { + dev_err(sparx5->dev, "port init failed\n"); diff --git a/patches/linux/6.18.57/0120-net-sparx5-forward-in-a-VLAN-unaware-bridge-without-.patch b/patches/linux/6.18.57/0120-net-sparx5-forward-in-a-VLAN-unaware-bridge-without-.patch new file mode 100644 index 0000000000..2b675b2bc3 --- /dev/null +++ b/patches/linux/6.18.57/0120-net-sparx5-forward-in-a-VLAN-unaware-bridge-without-.patch @@ -0,0 +1,66 @@ +From 221f1b44d9bc2b3bfcddc533556c7a1d78f46fce Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 14 Oct 2026 14:46:53 +0200 +Subject: [PATCH 120/128] net: sparx5: forward in a VLAN-unaware bridge without + VLAN 1 + +A VLAN-unaware bridge created with vlan_default_pvid 0 forwards nothing +between its ports: + + ip link add br0 type bridge vlan_filtering 0 vlan_default_pvid 0 + +Bridged ports without a PVID classify to UNAWARE_VID, but a port only +becomes a member of that VLAN when the bridge adds VID 1 to it, which it +never does without a default PVID. The VLAN mask is empty and every +frame is dropped. + +Make every port in a VLAN-unaware bridge a member of UNAWARE_VID, and +update the mask whenever a port's bridge membership or VLAN awareness +changes. + +Signed-off-by: Joachim Wiberg +--- + .../ethernet/microchip/sparx5/sparx5_vlan.c | 20 ++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c +index 494782871903..c12e90f7a4ee 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c +@@ -9,10 +9,25 @@ + + static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid) + { ++ DECLARE_BITMAP(ports, SPX5_PORTS); + u32 mask[3]; ++ int portno; ++ ++ bitmap_copy(ports, sparx5->vlan_mask[vid], SPX5_PORTS); ++ ++ /* Ports in a VLAN-unaware bridge classify to UNAWARE_VID, whether or ++ * not the bridge has added that VLAN to them. ++ */ ++ if (vid == UNAWARE_VID) { ++ for_each_set_bit(portno, sparx5->bridge_mask, SPX5_PORTS) { ++ if (sparx5->ports[portno] && ++ !sparx5->ports[portno]->vlan_aware) ++ __set_bit(portno, ports); ++ } ++ } + + /* Divide up mask in 32 bit words */ +- bitmap_to_arr32(mask, sparx5->vlan_mask[vid], SPX5_PORTS); ++ bitmap_to_arr32(mask, ports, SPX5_PORTS); + + /* Output mask to respective registers */ + spx5_wr(mask[0], sparx5, ANA_L3_VLAN_MASK_CFG(vid)); +@@ -207,6 +222,9 @@ void sparx5_vlan_port_apply(struct sparx5 *sparx5, + { + u32 val; + ++ /* Bridge membership or VLAN awareness may have changed */ ++ sparx5_vlant_set_mask(sparx5, UNAWARE_VID); ++ + /* Configure PVID, vlan aware */ + val = ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(port->vlan_aware) | + ANA_CL_VLAN_CTRL_VLAN_POP_CNT_SET(port->vlan_aware) | diff --git a/patches/linux/6.18.57/0121-net-sparx5-terminate-VLAN-uppers-of-bridged-ports.patch b/patches/linux/6.18.57/0121-net-sparx5-terminate-VLAN-uppers-of-bridged-ports.patch new file mode 100644 index 0000000000..68e2fec632 --- /dev/null +++ b/patches/linux/6.18.57/0121-net-sparx5-terminate-VLAN-uppers-of-bridged-ports.patch @@ -0,0 +1,193 @@ +From e94e5c8a1c51712739a424534fa0b1bfedc3bb6f Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 14 Oct 2026 15:18:34 +0200 +Subject: [PATCH 121/128] net: sparx5: terminate VLAN uppers of bridged ports + +A VLAN interface on top of a port in a VLAN-aware bridge never receives +anything, unless the bridge also has that VLAN on the port: + + ip link add link eth0 name eth0.10 type vlan id 10 + +The switch classifies the tagged frames to VID 10, where nothing sends +the port's address or broadcast to the CPU, so they are dropped. The +driver has no ndo_vlan_rx_add_vid and never learns about the upper. + +Implement ndo_vlan_rx_add_vid and ndo_vlan_rx_kill_vid: add CPU entries +for the port address and broadcast in the VID, and move them along when +the port address changes. The port does not join the VLAN, so the VID +stays local to the port and is not bridged, as in software. Broadcast +goes to the flood PGID, so it still reaches ports that are members of +the VID in the bridge. + +The bridge adds the same broadcast entry for each of its own VLANs, so +it now takes a reference on the shared entry too, instead of removing +it from under a VLAN upper. + +Signed-off-by: Joachim Wiberg +--- + .../microchip/sparx5/sparx5_mactable.c | 5 +- + .../ethernet/microchip/sparx5/sparx5_netdev.c | 70 ++++++++++++++++++- + .../microchip/sparx5/sparx5_switchdev.c | 11 +-- + 3 files changed, 79 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +index cf222736eff9..c90595cb481e 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +@@ -188,8 +188,11 @@ int sparx5_cpu_entry_get(struct sparx5 *sparx5, const unsigned char *addr, + goto out; + } + ++ /* Broadcast also floods to the VLAN's member ports */ + ret = sparx5_mact_learn(sparx5, +- sparx5_get_pgid(sparx5, PGID_CPU), ++ sparx5_get_pgid(sparx5, ++ is_broadcast_ether_addr(addr) ? ++ PGID_BCAST : PGID_CPU), + addr, vid); + if (ret) { + kfree(e); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +index 963957bef6b0..c1c4b027eb1c 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +@@ -9,6 +9,8 @@ + #include "sparx5_port.h" + #include "sparx5_tc.h" + ++#include ++ + /* The IFH bit position of the first VSTAX bit. This is because the + * VSTAX bit positions in Data sheet is starting from zero. + */ +@@ -184,6 +186,25 @@ static int sparx5_port_get_phys_port_name(struct net_device *dev, + return 0; + } + ++struct sparx5_addr_move { ++ struct sparx5 *sparx5; ++ const unsigned char *from; ++ const unsigned char *to; ++}; ++ ++/* Called for every VID in the filter, also those without a VLAN device */ ++static int sparx5_vlan_move_addr(struct net_device *vlan_dev, int vid, ++ void *arg) ++{ ++ struct sparx5_addr_move *move = arg; ++ ++ if (!vid) ++ return 0; ++ ++ sparx5_cpu_entry_put(move->sparx5, move->from, vid); ++ return sparx5_cpu_entry_get(move->sparx5, move->to, vid); ++} ++ + static int sparx5_set_mac_address(struct net_device *dev, void *p) + { + struct sparx5_port *port = netdev_priv(dev); +@@ -202,12 +223,57 @@ static int sparx5_set_mac_address(struct net_device *dev, void *p) + sparx5_cpu_entry_get(sparx5, addr->sa_data, port->pvid); + } + ++ /* Frames to VLAN uppers are delivered by address and VID */ ++ vlan_for_each(dev, sparx5_vlan_move_addr, ++ &(struct sparx5_addr_move){ sparx5, dev->dev_addr, ++ addr->sa_data }); ++ + /* Record the address */ + eth_hw_addr_set(dev, addr->sa_data); + + return 0; + } + ++/* A VLAN upper terminates its VID locally, also on a bridged port: its ++ * traffic goes to the CPU without the port joining the VLAN. ++ */ ++static int sparx5_vlan_rx_add_vid(struct net_device *dev, __be16 proto, ++ u16 vid) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ struct sparx5 *sparx5 = port->sparx5; ++ int err; ++ ++ if (!vid) ++ return 0; ++ ++ err = sparx5_cpu_entry_get(sparx5, dev->dev_addr, vid); ++ if (err || vid == UNAWARE_VID) ++ return err; ++ ++ err = sparx5_cpu_entry_get(sparx5, dev->broadcast, vid); ++ if (err) ++ sparx5_cpu_entry_put(sparx5, dev->dev_addr, vid); ++ ++ return err; ++} ++ ++static int sparx5_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, ++ u16 vid) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ struct sparx5 *sparx5 = port->sparx5; ++ ++ if (!vid) ++ return 0; ++ ++ if (vid != UNAWARE_VID) ++ sparx5_cpu_entry_put(sparx5, dev->broadcast, vid); ++ sparx5_cpu_entry_put(sparx5, dev->dev_addr, vid); ++ ++ return 0; ++} ++ + static int sparx5_get_port_parent_id(struct net_device *dev, + struct netdev_phys_item_id *ppid) + { +@@ -254,6 +320,8 @@ static const struct net_device_ops sparx5_port_netdev_ops = { + .ndo_set_rx_mode = sparx5_set_rx_mode, + .ndo_get_phys_port_name = sparx5_port_get_phys_port_name, + .ndo_set_mac_address = sparx5_set_mac_address, ++ .ndo_vlan_rx_add_vid = sparx5_vlan_rx_add_vid, ++ .ndo_vlan_rx_kill_vid = sparx5_vlan_rx_kill_vid, + .ndo_validate_addr = eth_validate_addr, + .ndo_get_stats64 = sparx5_get_stats64, + .ndo_get_port_parent_id = sparx5_get_port_parent_id, +@@ -279,7 +347,7 @@ struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno) + return ERR_PTR(-ENOMEM); + + ndev->hw_features |= NETIF_F_HW_TC; +- ndev->features |= NETIF_F_HW_TC; ++ ndev->features |= NETIF_F_HW_TC | NETIF_F_HW_VLAN_CTAG_FILTER; + + SET_NETDEV_DEV(ndev, sparx5->dev); + spx5_port = netdev_priv(ndev); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +index 15cbc5ba2842..e87a343e150c 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +@@ -437,10 +437,11 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev, + container_of(nb, struct sparx5, + switchdev_blocking_nb); + +- /* Flood broadcast to CPU */ +- sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_BCAST), +- dev->broadcast, v->vid); +- return 0; ++ /* Flood broadcast to CPU, UNAWARE_VID has it already */ ++ if (v->vid == UNAWARE_VID) ++ return 0; ++ ++ return sparx5_cpu_entry_get(sparx5, dev->broadcast, v->vid); + } + + if (!sparx5_netdevice_check(dev)) +@@ -676,7 +677,7 @@ static int sparx5_handle_port_vlan_del(struct net_device *dev, + + /* Kept for the bridged ports without a PVID */ + if (vid != UNAWARE_VID) +- sparx5_mact_forget(sparx5, dev->broadcast, vid); ++ sparx5_cpu_entry_put(sparx5, dev->broadcast, vid); + return 0; + } + diff --git a/patches/linux/6.18.57/0122-net-sparx5-do-not-double-tag-frames-from-the-CPU-on-.patch b/patches/linux/6.18.57/0122-net-sparx5-do-not-double-tag-frames-from-the-CPU-on-.patch new file mode 100644 index 0000000000..c15d5d34cc --- /dev/null +++ b/patches/linux/6.18.57/0122-net-sparx5-do-not-double-tag-frames-from-the-CPU-on-.patch @@ -0,0 +1,42 @@ +From d76a808dc7ec91ef6715fc6cc750f4c7f580b926 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 14 Oct 2026 15:21:35 +0200 +Subject: [PATCH 122/128] net: sparx5: do not double tag frames from the CPU on + trunk ports + +A frame the CPU sends out a VLAN-aware bridge port without an untagged +VLAN leaves the switch with two tags, an outer one with VID 0 around the +frame's own: + + 802.1Q vlan 0, 802.1Q vlan 55, ARP who-has 10.0.1.2 + +The CPU injects frames with their VLAN tag in the frame and VID 0 in +the frame header, and the rewriter on such a port tags every frame, +VID 0 included. A VLAN interface on the bridge cannot reach anything +over a trunk. + +Tag all frames except VID 0 instead. Frames forwarded by the switch +are always classified to a VLAN in a VLAN-aware bridge, so they are +still tagged. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c +index c12e90f7a4ee..55dd02b53d01 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c +@@ -249,7 +249,10 @@ void sparx5_vlan_port_apply(struct sparx5 *sparx5, + /* Tag all frames except when VID == DEFAULT_VLAN */ + val |= REW_TAG_CTRL_TAG_CFG_SET(1); + else +- val |= REW_TAG_CTRL_TAG_CFG_SET(3); ++ /* Tag all frames except when VID == 0, frames from ++ * the CPU carry their own tag and have VID 0. ++ */ ++ val |= REW_TAG_CTRL_TAG_CFG_SET(2); + } + spx5_wr(val, sparx5, REW_TAG_CTRL(port->portno)); + diff --git a/patches/linux/6.18.57/0123-net-sparx5-ignore-switchdev-attributes-of-uppers-and.patch b/patches/linux/6.18.57/0123-net-sparx5-ignore-switchdev-attributes-of-uppers-and.patch new file mode 100644 index 0000000000..6def3c64bf --- /dev/null +++ b/patches/linux/6.18.57/0123-net-sparx5-ignore-switchdev-attributes-of-uppers-and.patch @@ -0,0 +1,43 @@ +From 0857b4c1d3bc1a2c7256e54966b4ed3064c8303e Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 15 Oct 2026 11:15:52 +0200 +Subject: [PATCH 123/128] net: sparx5: ignore switchdev attributes of uppers + and other bridges + +Bridging a VLAN interface on top of a standalone port fills the log with + + sparx5-switch e00c0000.switch e5: Controlling non-bridged port 7? + +The switchdev helper passes the attributes of a bridge port down to the +lower sparx5 port, so the bridge settings of the VLAN interface are +applied to a port that is in no bridge: its STP state, flood flags, +VLAN filtering and ageing time. + +Only handle an attribute when it is for the port itself, or for the +bridge the port is offloaded to. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +index e87a343e150c..a56b5a83f814 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +@@ -137,6 +137,15 @@ static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, + struct netlink_ext_ack *extack) + { + struct sparx5_port *port = netdev_priv(dev); ++ struct sparx5 *sparx5 = port->sparx5; ++ ++ /* Attributes of an upper in a bridge, e.g. a VLAN interface on the ++ * port, or of a bridge the port is not in, are not ours. ++ */ ++ if (attr->orig_dev != dev && ++ (attr->orig_dev != sparx5->hw_bridge_dev || ++ !test_bit(port->portno, sparx5->bridge_mask))) ++ return -EOPNOTSUPP; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: diff --git a/patches/linux/6.18.57/0124-net-sparx5-send-reserved-link-local-multicast-to-the.patch b/patches/linux/6.18.57/0124-net-sparx5-send-reserved-link-local-multicast-to-the.patch new file mode 100644 index 0000000000..a7c64eaae8 --- /dev/null +++ b/patches/linux/6.18.57/0124-net-sparx5-send-reserved-link-local-multicast-to-the.patch @@ -0,0 +1,53 @@ +From 236ab19b0c210fe936f7ab9694932bc199b6c888 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 15 Oct 2026 11:19:46 +0200 +Subject: [PATCH 124/128] net: sparx5: send reserved link-local multicast to + the CPU + +STP does not converge on a ring of LAN969x switches, and LACP fails +over aggregated links. BPDUs and LACPDUs on a bridged port are flooded +to the other ports by the switch, so a bridge sees BPDUs relayed from +bridges that are not its neighbours. + +ANA_CL_CAPTURE_BPDU_CFG only discards pause frames and leaves every +other address in 01-80-C2-00-00-0X to normal forwarding. A bridge never +forwards these, they are for the link they arrived on. + +Redirect them all to the CPU, and keep discarding pause frames. The +bridge still forwards the ones its group_fwd_mask asks for, in software. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/ethernet/microchip/sparx5/sparx5_port.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +index 62c49893de3c..bfdbfed1c929 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +@@ -23,7 +23,12 @@ enum port_error { + SPX5_PERR_IFTYPE, + }; + +-#define PAUSE_DISCARD 0xC ++/* ANA_CL_CAPTURE_BPDU_CFG, two bits per address 01-80-C2-00-00-0X: ++ * redirect them all to the CPU, a bridge never forwards them, but ++ * discard pause frames, 01-80-C2-00-00-01. ++ */ ++#define BPDU_REDIR_ALL 0x55555555 ++#define BPDU_PAUSE_DISCARD 0xC + #define ETH_MAXLEN (ETH_DATA_LEN + ETH_HLEN + ETH_FCS_LEN) + + static void decode_sgmii_word(u16 lp_abil, struct sparx5_port_status *status) +@@ -1098,8 +1103,9 @@ int sparx5_port_init(struct sparx5 *sparx5, + sparx5, + QSYS_ATOP(port->portno)); + +- /* Discard pause frame 01-80-C2-00-00-01 */ +- spx5_wr(PAUSE_DISCARD, sparx5, ANA_CL_CAPTURE_BPDU_CFG(port->portno)); ++ /* Reserved link-local multicast to the CPU, pause frames dropped */ ++ spx5_wr(BPDU_REDIR_ALL | BPDU_PAUSE_DISCARD, sparx5, ++ ANA_CL_CAPTURE_BPDU_CFG(port->portno)); + + /* Discard SMAC multicast */ + spx5_rmw(ANA_CL_FILTER_CTRL_FILTER_SMAC_MC_DIS_SET(0), diff --git a/patches/linux/6.18.57/0125-net-sparx5-add-LAG-offload.patch b/patches/linux/6.18.57/0125-net-sparx5-add-LAG-offload.patch new file mode 100644 index 0000000000..bb0e592468 --- /dev/null +++ b/patches/linux/6.18.57/0125-net-sparx5-add-LAG-offload.patch @@ -0,0 +1,1562 @@ +From 1636af47a97c3fbabb86d7b0ccab9cad08d6705f Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Fri, 16 Oct 2026 10:14:44 +0200 +Subject: [PATCH 125/128] net: sparx5: add LAG offload + +A bond in a bridge does not forward on LAN969x. The driver does not +offload link aggregation, so the bond is a software port in an +offloaded bridge, and the switch never hands its traffic to the CPU: +unknown unicast from the other bridge ports stays in hardware, and +frames for the bridge arriving on the bond members are dropped. + +Offload the bond instead. Ports enslaved to a bond join the bridge +through it, the bond's members share an aggregation mask and a logical +port, MAC table entries for the bond point to its lowest numbered +member, and frames are not sent back out the aggregate they came in on. +MDB entries and VLANs on the bond are applied to each member, and FDB +entries for foreign bridge ports are installed towards the CPU. + +Based on the LAG support by Daniel Machon in Microchip's BSP kernel, +branch bsp-6.12-2025: + + commit 5fd9c90023a0 ("net: sparx5/lan969x: add registers required to configure LAG") + commit 684893932384 ("net: sparx5/lan969x: expose sparx5_mac_entry struct") + commit 9b7df456732a ("net: sparx5/lan969x: pass sparx5_port in sparx5_fdb_call_notifiers()") + commit 5e7ca258ec39 ("net: sparx5/lan969x: add function to set the destination PGID's") + commit 33d523ead729 ("net: sparx5/lan969x: use switchdev_handle_fdb_event_to_device helper") + commit 94f4b8667c48 ("net: sparx5/lan969x: move unoffloading to PRECHANGEUPPER") + commit 5baa99df9a9d ("net: sparx5/lan969x: add new brport_dev argument") + commit 9cfbe0dee25d ("net: sparx5/lan969x: add LAG support") + commit d17f1c4c4bce ("net: sparx5/lan969x: do not send frames back on LAG ports") + commit 64054ae42e82 ("net: sparx5: handle foreign device FDB events") + commit c92187b2b2a5 ("net: sparx5: allow foreign devices in bridge") + +and the MDB fix for LAG interfaces by Horatiu Vultur: + + commit 7beaccf08da5 ("net: sparx5: Fix for MDB") + +Adapted to the mainline 6.18 driver: registers in its regs->gaddr form, +with ANA_CL_PORT_ID_CFG added, the source and destination masks folded +into sparx5_update_fwd(), the MDB fix applied to the MDB code in +sparx5_switchdev.c, and the HSR, MRP, XDP and QoS parts of those +commits left out. Switchdev attributes of the bond are accepted on its +member ports, and the bridge's own addresses are still installed for the +CPU when the bridge gets them before any port joins, as before the +switch to switchdev_handle_fdb_event_to_device(). + +Signed-off-by: Daniel Machon +Signed-off-by: Horatiu Vultur +Signed-off-by: Joachim Wiberg +--- + .../net/ethernet/microchip/sparx5/Makefile | 2 +- + .../microchip/sparx5/lan969x/lan969x_regs.c | 2 + + .../ethernet/microchip/sparx5/sparx5_lag.c | 418 ++++++++++++++++++ + .../microchip/sparx5/sparx5_mactable.c | 51 ++- + .../ethernet/microchip/sparx5/sparx5_main.h | 40 ++ + .../microchip/sparx5/sparx5_main_regs.h | 124 ++++++ + .../ethernet/microchip/sparx5/sparx5_regs.c | 2 + + .../ethernet/microchip/sparx5/sparx5_regs.h | 2 + + .../microchip/sparx5/sparx5_switchdev.c | 396 +++++++++++++++-- + .../ethernet/microchip/sparx5/sparx5_vlan.c | 43 ++ + 10 files changed, 1015 insertions(+), 65 deletions(-) + create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_lag.c + +diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile +index d447f9e84d92..03313e51f79d 100644 +--- a/drivers/net/ethernet/microchip/sparx5/Makefile ++++ b/drivers/net/ethernet/microchip/sparx5/Makefile +@@ -11,7 +11,7 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \ + sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \ + sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o \ + sparx5_tc_matchall.o sparx5_pool.o sparx5_sdlb.o sparx5_police.o \ +- sparx5_psfp.o sparx5_mirror.o sparx5_regs.o ++ sparx5_psfp.o sparx5_mirror.o sparx5_regs.o sparx5_lag.o + + sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o + sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o +diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c +index ace4ba21eec4..8722aec120f7 100644 +--- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c ++++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c +@@ -53,6 +53,7 @@ const unsigned int lan969x_gaddr[GADDR_LAST] = { + [GA_ANA_AC_RAM_CTRL] = 202000, + [GA_ANA_AC_PS_COMMON] = 202880, + [GA_ANA_AC_MIRROR_PROBE] = 203232, ++ [GA_ANA_AC_AGGR] = 202240, + [GA_ANA_AC_SRC] = 201728, + [GA_ANA_AC_PGID] = 131072, + [GA_ANA_AC_TSN_SF] = 202028, +@@ -219,4 +220,5 @@ const unsigned int lan969x_fsize[FSIZE_LAST] = { + [FW_XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP] = 14, + [FW_XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP] = 14, + [FW_XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM] = 14, ++ [FW_ANA_CL_PORT_ID_CFG_LPORT_NUM] = 6, + }; +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_lag.c b/drivers/net/ethernet/microchip/sparx5/sparx5_lag.c +new file mode 100644 +index 000000000000..c574a28d6307 +--- /dev/null ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_lag.c +@@ -0,0 +1,418 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* Microchip Sparx5 Switch driver ++ * ++ * Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries. ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "sparx5_main.h" ++ ++#define LAG_MASKS_CNT 16 ++ ++static void sparx5_lag_add(struct sparx5_port *port, ++ struct net_device *lag_master) ++{ ++ lockdep_assert_held(&port->sparx5->lock); ++ ++ port->lag_master = lag_master; ++} ++ ++static void sparx5_lag_del(struct sparx5_port *port) ++{ ++ lockdep_assert_held(&port->sparx5->lock); ++ ++ port->lag_master = NULL; ++} ++ ++static void sparx5_lag_add_locked(struct sparx5_port *port, ++ struct net_device *lag_master) ++{ ++ struct sparx5 *sparx5 = port->sparx5; ++ ++ spin_lock(&sparx5->lock); ++ sparx5_lag_add(port, lag_master); ++ spin_unlock(&sparx5->lock); ++} ++ ++static void sparx5_lag_del_locked(struct sparx5_port *port) ++{ ++ struct sparx5 *sparx5 = port->sparx5; ++ ++ spin_lock(&sparx5->lock); ++ sparx5_lag_del(port); ++ spin_unlock(&sparx5->lock); ++} ++ ++void sparx5_lag_mask_get(struct sparx5 *sparx5, struct net_device *lag_master, ++ unsigned long *lag_mask) ++{ ++ struct sparx5_port *port; ++ ++ bitmap_zero(lag_mask, SPX5_PORTS); ++ ++ if (!lag_master) ++ return; ++ ++ for (int i = 0; i < sparx5->data->consts->n_ports; i++) { ++ port = sparx5->ports[i]; ++ if (!port) ++ continue; ++ ++ if (port->lag_master == lag_master) ++ set_bit(i, lag_mask); ++ } ++} ++ ++bool sparx5_lag_is_first(struct net_device *lag_master, struct net_device *dev) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ struct sparx5 *sparx5 = port->sparx5; ++ DECLARE_BITMAP(lag_mask, SPX5_PORTS); ++ ++ if (port->lag_master != lag_master) ++ return false; ++ ++ sparx5_lag_mask_get(sparx5, lag_master, lag_mask); ++ ++ return port->portno == find_first_bit(lag_mask, SPX5_PORTS); ++} ++ ++/* The LAG id is the lowest physical port number within the LAG. */ ++static int sparx5_lag_id_get(struct sparx5 *sparx5, ++ struct net_device *lag_master) ++{ ++ DECLARE_BITMAP(lag_mask, SPX5_PORTS); ++ ++ sparx5_lag_mask_get(sparx5, lag_master, lag_mask); ++ ++ if (bitmap_empty(lag_mask, SPX5_PORTS)) ++ return -EINVAL; ++ ++ return find_first_bit(lag_mask, SPX5_PORTS); ++} ++ ++static bool sparx5_lag_hash_type_check(struct sparx5 *sparx5, ++ enum netdev_lag_hash hash_type) ++{ ++ for (int i = 0; i < sparx5->data->consts->n_ports; i++) { ++ struct sparx5_port *port = sparx5->ports[i]; ++ ++ if (!port || !port->lag_master) ++ continue; ++ ++ if (port->lag_hash_type != hash_type) ++ return false; ++ } ++ ++ return true; ++} ++ ++int sparx5_lag_aggr_code_set(struct net_device *dev, ++ struct netdev_notifier_changeupper_info *info) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ struct netdev_lag_upper_info *upper_info; ++ struct sparx5 *sparx5 = port->sparx5; ++ struct netlink_ext_ack *extack; ++ ++ extack = netdev_notifier_info_to_extack(&info->info); ++ ++ upper_info = info->upper_info; ++ if (!upper_info) { ++ port->lag_hash_type = NETDEV_LAG_HASH_NONE; ++ return NOTIFY_DONE; ++ } ++ ++ if (upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "LAG device using unsupported TX type"); ++ return -EINVAL; ++ } ++ ++ if (!sparx5_lag_hash_type_check(sparx5, upper_info->hash_type)) { ++ NL_SET_ERR_MSG_MOD(extack, "LAG devices must have the same hash_type"); ++ return -EINVAL; ++ } ++ ++ switch (upper_info->hash_type) { ++ case NETDEV_LAG_HASH_L2: ++ /* L2 */ ++ spx5_wr(ANA_CL_AGGR_CFG_AGGR_DMAC_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_SMAC_ENA_SET(1), ++ sparx5, ANA_CL_AGGR_CFG); ++ break; ++ case NETDEV_LAG_HASH_L34: ++ /* L3 */ ++ spx5_wr(ANA_CL_AGGR_CFG_AGGR_IP4_SIP_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_IP4_DIP_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_IP6_SIP_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_IP6_DIP_ENA_SET(1) | ++ /* L4 */ ++ ANA_CL_AGGR_CFG_AGGR_IP4_TCPUDP_PORT_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_IP6_TCPUDP_PORT_ENA_SET(1), ++ sparx5, ANA_CL_AGGR_CFG); ++ break; ++ case NETDEV_LAG_HASH_L23: ++ /* L2 */ ++ spx5_wr(ANA_CL_AGGR_CFG_AGGR_DMAC_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_SMAC_ENA_SET(1) | ++ /* L3 */ ++ ANA_CL_AGGR_CFG_AGGR_IP4_SIP_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_IP4_DIP_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_IP6_SIP_ENA_SET(1) | ++ ANA_CL_AGGR_CFG_AGGR_IP6_DIP_ENA_SET(1), ++ sparx5, ANA_CL_AGGR_CFG); ++ break; ++ default: ++ NL_SET_ERR_MSG_MOD(extack, ++ "LAG device using unsupported hash type"); ++ return -EINVAL; ++ } ++ ++ port->lag_hash_type = upper_info->hash_type; ++ ++ return NOTIFY_OK; ++} ++ ++static void sparx5_lag_map_logical_ports(struct sparx5 *sparx5) ++{ ++ struct sparx5_port *port; ++ int logical_port; ++ ++ for (int i = 0; i < sparx5->data->consts->n_ports; i++) { ++ port = sparx5->ports[i]; ++ if (!port) ++ continue; ++ ++ if (port->lag_master) { ++ /* If this port is part of a LAG, the logical port ++ * number is the physical port number of the port with ++ * the lowest port number within the LAG. ++ */ ++ logical_port = sparx5_lag_id_get(sparx5, ++ port->lag_master); ++ if (logical_port < 0) ++ /* This should not happen. */ ++ logical_port = port->portno; ++ } else { ++ /* By default, the logical port number is the same as ++ * the physical port number. ++ */ ++ logical_port = port->portno; ++ } ++ ++ spx5_rmw(ANA_CL_PORT_ID_CFG_LPORT_NUM_SET(logical_port), ++ ANA_CL_PORT_ID_CFG_LPORT_NUM, sparx5, ++ ANA_CL_PORT_ID_CFG(i)); ++ } ++} ++ ++/* Set the aggregation masks. ++ * ++ * By default all aggregation masks will be all ones, corresponding to no ++ * forwarding restrictions wrt. link aggregation. For port devices under a LAG, ++ * only one port per LAG must be set in each aggregation mask. Effectively, this ++ * means that a frame will only get forwarded to one port per LAG. ++ * ++ * To enforce this, we get a mask of each port member of a LAG, and distribute ++ * each port across all aggregation masks. ++ */ ++int sparx5_lag_aggr_masks_set(struct sparx5_port *port, bool leaving) ++{ ++ const struct sparx5_consts *consts = port->sparx5->data->consts; ++ struct sparx5 *sparx5 = port->sparx5; ++ DECLARE_BITMAP(lag_mask, SPX5_PORTS); ++ int n_active_ports = 0, portno = 0; ++ u8 active_idx[LAG_MASKS_CNT]; ++ u32 mask; ++ ++ sparx5_lag_mask_get(sparx5, port->lag_master, lag_mask); ++ ++ for_each_set_bit(portno, lag_mask, consts->n_ports) ++ if (sparx5->ports[portno]->lag_tx_active) ++ active_idx[n_active_ports++] = portno; ++ ++ if (!leaving && n_active_ports > LAG_MASKS_CNT) ++ return -EOPNOTSUPP; ++ ++ for (int i = 0; i < LAG_MASKS_CNT; i++) { ++ mask = leaving ? BIT(port->portno) : 0; ++ ++ if (n_active_ports) ++ mask |= BIT(active_idx[i % n_active_ports]); ++ ++ spx5_rmw(mask, lag_mask[0] | BIT(port->portno), sparx5, ++ ANA_AC_AGGR_CFG(i)); ++ ++ if (is_sparx5(sparx5)) ++ spx5_rmw(mask, lag_mask[1] | BIT(port->portno), sparx5, ++ ANA_AC_AGGR_CFG(i)); ++ } ++ ++ return 0; ++} ++ ++static int sparx5_lag_replace_mact_entries(struct sparx5_port *src_port, ++ struct sparx5_port *dst_port) ++{ ++ struct sparx5_mact_entry *mact_entry, *tmp; ++ struct sparx5 *sparx5 = src_port->sparx5; ++ ++ mutex_lock(&sparx5->mact_lock); ++ list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries, list) { ++ if (mact_entry->port == src_port->portno && mact_entry->lag) { ++ sparx5_mact_forget(sparx5, mact_entry->mac, ++ mact_entry->vid); ++ ++ sparx5_mact_learn(sparx5, dst_port->portno, ++ mact_entry->mac, mact_entry->vid); ++ ++ mact_entry->port = dst_port->portno; ++ } ++ } ++ mutex_unlock(&sparx5->mact_lock); ++ ++ return 0; ++} ++ ++static int sparx5_lag_remove_mact_entries(struct sparx5_port *src_port) ++{ ++ struct sparx5_mact_entry *mact_entry, *tmp; ++ struct sparx5 *sparx5 = src_port->sparx5; ++ ++ mutex_lock(&sparx5->mact_lock); ++ list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries, list) { ++ if (mact_entry->port == src_port->portno && mact_entry->lag) { ++ sparx5_mact_forget(sparx5, mact_entry->mac, ++ mact_entry->vid); ++ ++ list_del(&mact_entry->list); ++ devm_kfree(sparx5->dev, mact_entry); ++ } ++ } ++ mutex_unlock(&sparx5->mact_lock); ++ ++ return 0; ++} ++ ++int sparx5_lag_join(struct sparx5_port *port, struct net_device *brport_dev, ++ struct net_device *lag_master, ++ struct netlink_ext_ack *extack) ++{ ++ struct sparx5 *sparx5 = port->sparx5; ++ struct net_device *dev = port->ndev; ++ struct net_device *br; ++ int lag_id, err; ++ ++ br = netdev_master_upper_dev_get(lag_master); ++ if (br && netif_is_bridge_master(br)) { ++ /* If a port is joining a LAG, which in turn is under a bridge, ++ * we have to make sure the port joining the LAG, is also ++ * properly joining the bridge. The easiest way to do this, is ++ * to generate an event for the joining port, with the upper_dev ++ * being the bridge, instead of the LAG. ++ */ ++ ++ struct netdev_notifier_changeupper_info info = { ++ .linking = true, ++ .upper_dev = br, ++ }; ++ ++ /* Port PVID is not set in case the netdevice event was for a ++ * lag device. Set it here. Is reset on bridge_leave(); ++ */ ++ port->pvid = 1; ++ ++ sparx5_port_prechangeupper(port->ndev, port->ndev, &info); ++ sparx5_port_changeupper(port->ndev, port->ndev, &info); ++ ++ /* Must be called _after_ the port joins the bridge */ ++ sparx5_attr_stp_state_set(port, ++ br_port_get_stp_state(brport_dev)); ++ } ++ ++ /* Get the lagid before adding the new port. */ ++ lag_id = sparx5_lag_id_get(sparx5, lag_master); ++ ++ sparx5_lag_add_locked(port, lag_master); ++ ++ /* MAC table entries for LAG ports, are added for the LAG port ++ * with the lowest physical port number (lagid). If a new port, with a ++ * lower physical port number than the current lagid joins the lag, all ++ * LAG entries have to be updated. ++ */ ++ if (lag_id >= 0 && sparx5_lag_is_first(lag_master, dev)) ++ sparx5_lag_replace_mact_entries(sparx5->ports[lag_id], port); ++ ++ sparx5_update_dst_fwd(sparx5); ++ ++ err = sparx5_lag_aggr_masks_set(port, false); ++ if (err) ++ return err; ++ ++ sparx5_lag_map_logical_ports(sparx5); ++ ++ return 0; ++} ++ ++void sparx5_lag_leave(struct sparx5_port *port, struct net_device *lag_master) ++{ ++ DECLARE_BITMAP(lag_mask, SPX5_PORTS); ++ struct sparx5 *sparx5 = port->sparx5; ++ struct net_device *br; ++ int lag_id; ++ ++ br = netdev_master_upper_dev_get(lag_master); ++ if (br && netif_is_bridge_master(br)) { ++ /* If a port is leaving a LAG, which in turn is under a bridge, ++ * we have to make sure the port leaving the LAG, is also ++ * properly leaving the bridge. The easiest way to do this, is ++ * to generate an event for the leaving port, with the upper_dev ++ * being the bridge, instead og the LAG. ++ */ ++ ++ struct netdev_notifier_changeupper_info info = { ++ .linking = false, ++ .upper_dev = br, ++ }; ++ ++ /* Must be called _before_ the port leaves the bridge */ ++ sparx5_attr_stp_state_set(port, ++ br_port_get_stp_state(port->ndev)); ++ ++ sparx5_port_prechangeupper(port->ndev, port->ndev, &info); ++ sparx5_port_changeupper(port->ndev, port->ndev, &info); ++ } ++ ++ if (sparx5_lag_is_first(lag_master, port->ndev)) { ++ /* MAC table entries for LAG ports, are added for the LAG port ++ * with the lowest physical port number (lagid). If that port ++ * leaves the lag, all LAG entries have to be either updated to ++ * the new lagid, or removed entirely. ++ */ ++ ++ sparx5_lag_del_locked(port); ++ ++ sparx5_lag_mask_get(sparx5, lag_master, lag_mask); ++ ++ if (!bitmap_empty(lag_mask, SPX5_PORTS)) { ++ lag_id = sparx5_lag_id_get(sparx5, lag_master); ++ ++ sparx5_lag_replace_mact_entries(port, ++ sparx5->ports[lag_id]); ++ } else { ++ sparx5_lag_remove_mact_entries(port); ++ } ++ } else { ++ sparx5_lag_del_locked(port); ++ } ++ ++ sparx5_update_dst_fwd(sparx5); ++ /* Ignore return value */ ++ sparx5_lag_aggr_masks_set(port, true); ++ sparx5_lag_map_logical_ports(sparx5); ++} +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +index c90595cb481e..359555386e13 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +@@ -30,20 +30,6 @@ + #define TABLE_UPDATE_SLEEP_US 10 + #define TABLE_UPDATE_TIMEOUT_US 100000 + +-struct sparx5_mact_entry { +- struct list_head list; +- unsigned char mac[ETH_ALEN]; +- u32 flags; +-#define MAC_ENT_ALIVE BIT(0) +-#define MAC_ENT_MOVED BIT(1) +-#define MAC_ENT_LOCK BIT(2) +- u16 vid; +- u16 port; +- u8 refs; /* Bridge VIDs using a locked entry */ +-#define MAC_ENT_REF_VID BIT(0) /* The entry's own VID */ +-#define MAC_ENT_REF_UNAWARE BIT(1) /* Bridge VID 0, see UNAWARE_VID */ +-}; +- + /* Bridge VID 0 is mapped to UNAWARE_VID, to keep clear of the standalone + * ports on NULL_VID, so it shares the MAC table entry with that VID. + */ +@@ -334,6 +320,7 @@ static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5, + const unsigned char *mac, + u16 vid, u16 port_index) + { ++ struct sparx5_port *port = sparx5->ports[port_index]; + struct sparx5_mact_entry *mact_entry; + + mact_entry = devm_kzalloc(sparx5->dev, +@@ -344,6 +331,12 @@ static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5, + memcpy(mact_entry->mac, mac, ETH_ALEN); + mact_entry->vid = vid; + mact_entry->port = port_index; ++ ++ spin_lock(&sparx5->lock); ++ mact_entry->lag = !!(mact_entry->port < sparx5->data->consts->n_ports && ++ port->lag_master); ++ spin_unlock(&sparx5->lock); ++ + return mact_entry; + } + +@@ -370,14 +363,21 @@ static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5, + + static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type, + const char *mac, u16 vid, +- struct net_device *dev, bool offloaded) ++ struct sparx5_port *port, bool offloaded) + { + struct switchdev_notifier_fdb_info info = {}; ++ struct sparx5 *sparx5 = port->sparx5; ++ struct net_device *ndev; + + info.addr = mac; + info.vid = vid; + info.offloaded = offloaded; +- call_switchdev_notifiers(type, dev, &info.info, NULL); ++ ++ spin_lock(&sparx5->lock); ++ ndev = (port->lag_master ? port->lag_master : port->ndev); ++ spin_unlock(&sparx5->lock); ++ ++ call_switchdev_notifiers(type, ndev, &info.info, NULL); + } + + int sparx5_add_mact_entry(struct sparx5 *sparx5, +@@ -385,6 +385,7 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, + u16 portno, + const unsigned char *addr, u16 vid) + { ++ struct sparx5_port *port = netdev_priv(dev); + struct sparx5_mact_entry *mact_entry; + int ret; + u32 cfg2; +@@ -419,7 +420,6 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, + mutex_lock(&sparx5->mact_lock); + list_add_tail(&mact_entry->list, &sparx5->mact_entries); + mutex_unlock(&sparx5->mact_lock); +- + update_hw: + ret = sparx5_mact_learn(sparx5, portno, addr, vid); + +@@ -427,8 +427,14 @@ int sparx5_add_mact_entry(struct sparx5 *sparx5, + if (mact_entry->flags == 0) { + mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */ + mact_entry->refs = ref; +- sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid, +- dev, true); ++ /* Only notify bridge for entries on physical ports. ++ * Host/foreign device entries use a PGID (portno >= n_ports) ++ * and must not notify, as it would override the bridge's ++ * FDB entry pointing to the foreign device. ++ */ ++ if (portno < sparx5->data->consts->n_ports) ++ sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, ++ addr, vid, port, true); + } + + return ret; +@@ -520,9 +526,8 @@ static void sparx5_mact_handle_entry(struct sparx5 *sparx5, + } + + /* New or moved entry - notify bridge */ +- sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, +- mac, vid, sparx5->ports[port]->ndev, +- true); ++ sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, mac, vid, ++ sparx5->ports[port], true); + } + + static void sparx5_mact_pull_work(struct work_struct *work) +@@ -571,7 +576,7 @@ static void sparx5_mact_pull_work(struct work_struct *work) + + sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, + mact_entry->mac, mact_entry->vid, +- sparx5->ports[mact_entry->port]->ndev, ++ sparx5->ports[mact_entry->port], + true); + + list_del(&mact_entry->list); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +index 9d2a96eccfb2..fa16529a747f 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +@@ -237,6 +237,11 @@ struct sparx5_port { + struct sk_buff_head tx_skbs; + bool is_mrouter; + struct list_head tc_templates; /* list of TC templates on this port */ ++ ++ /* LAG */ ++ struct net_device *lag_master; ++ enum netdev_lag_hash lag_hash_type; ++ bool lag_tx_active; + }; + + enum sparx5_core_clockfreq { +@@ -449,6 +454,14 @@ bool sparx5_has_feature(struct sparx5 *sparx5, enum sparx5_feature feature); + /* sparx5_switchdev.c */ + int sparx5_register_notifier_blocks(struct sparx5 *sparx5); + void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5); ++void sparx5_attr_stp_state_set(struct sparx5_port *port, u8 state); ++int sparx5_port_prechangeupper(struct net_device *dev, ++ struct net_device *brport_dev, ++ struct netdev_notifier_changeupper_info *info); ++ ++int sparx5_port_changeupper(struct net_device *dev, ++ struct net_device *brport_dev, ++ struct netdev_notifier_changeupper_info *info); + + /* sparx5_packet.c */ + struct frame_info { +@@ -476,6 +489,21 @@ void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma); + void sparx5_fdma_injection_mode(struct sparx5 *sparx5); + + /* sparx5_mactable.c */ ++struct sparx5_mact_entry { ++ struct list_head list; ++ unsigned char mac[ETH_ALEN]; ++ u32 flags; ++#define MAC_ENT_ALIVE BIT(0) ++#define MAC_ENT_MOVED BIT(1) ++#define MAC_ENT_LOCK BIT(2) ++ u16 vid; ++ u16 port; ++ bool lag; ++ u8 refs; /* Bridge VIDs using a locked entry */ ++#define MAC_ENT_REF_VID BIT(0) /* The entry's own VID */ ++#define MAC_ENT_REF_UNAWARE BIT(1) /* Bridge VID 0, see UNAWARE_VID */ ++}; ++ + int sparx5_mact_learn(struct sparx5 *sparx5, int port, + const unsigned char mac[ETH_ALEN], u16 vid); + bool sparx5_mact_getnext(struct sparx5 *sparx5, +@@ -502,6 +530,7 @@ int sparx5_mact_init(struct sparx5 *sparx5); + void sparx5_mact_deinit(struct sparx5 *sparx5); + + /* sparx5_vlan.c */ ++void sparx5_update_dst_fwd(struct sparx5 *sparx5); + void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable); + void sparx5_pgid_clear(struct sparx5 *spx5, int pgid); + void sparx5_pgid_read_mask(struct sparx5 *sparx5, int pgid, u32 portmask[3]); +@@ -723,6 +752,17 @@ void sparx5_mirror_del(struct sparx5_mall_entry *entry); + void sparx5_mirror_stats(struct sparx5_mall_entry *entry, + struct flow_stats *fstats); + ++/* sparx5_lag.c */ ++int sparx5_lag_join(struct sparx5_port *port, struct net_device *brport_dev, ++ struct net_device *bond, struct netlink_ext_ack *extack); ++void sparx5_lag_leave(struct sparx5_port *port, struct net_device *bond); ++int sparx5_lag_aggr_code_set(struct net_device *dev, ++ struct netdev_notifier_changeupper_info *info); ++int sparx5_lag_aggr_masks_set(struct sparx5_port *port, bool leaving); ++bool sparx5_lag_is_first(struct net_device *lag_master, struct net_device *dev); ++void sparx5_lag_mask_get(struct sparx5 *sparx5, struct net_device *lag_master, ++ unsigned long *lag_mask); ++ + /* Clock period in picoseconds */ + static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock) + { +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +index d9ef4ef137b8..e9d2811b4c51 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +@@ -165,6 +165,23 @@ extern const struct sparx5_regs *regs; + #define ANA_AC_PROBE_PORT_CFG2_PROBE_PORT_MASK2_GET(x)\ + FIELD_GET(ANA_AC_PROBE_PORT_CFG2_PROBE_PORT_MASK2, x) + ++/* ANA_AC:AGGR:AGGR_CFG */ ++#define ANA_AC_AGGR_CFG(g) \ ++ __REG(TARGET_ANA_AC, 0, 1, regs->gaddr[GA_ANA_AC_AGGR], g, 16, 16, 0, 0, 1, \ ++ 4) ++ ++/* SPARX5 ONLY */ ++/* ANA_AC:AGGR:AGGR_CFG1 */ ++#define ANA_AC_AGGR_CFG1(g) \ ++ __REG(TARGET_ANA_AC, 0, 1, regs->gaddr[GA_ANA_AC_AGGR], g, 16, 16, 4, 0, 1, \ ++ 4) ++ ++/* SPARX5 ONLY */ ++/* ANA_AC:AGGR:AGGR_CFG2 */ ++#define ANA_AC_AGGR_CFG2(g) \ ++ __REG(TARGET_ANA_AC, 0, 1, regs->gaddr[GA_ANA_AC_AGGR], g, 16, 16, 8, 0, 1, \ ++ 4) ++ + /* ANA_AC:SRC:SRC_CFG */ + #define ANA_AC_SRC_CFG(g) \ + __REG(TARGET_ANA_AC, 0, 1, regs->gaddr[GA_ANA_AC_SRC], g, \ +@@ -762,6 +779,113 @@ extern const struct sparx5_regs *regs; + #define ANA_ACL_OWN_UPSID_OWN_UPSID_GET(x)\ + FIELD_GET(ANA_ACL_OWN_UPSID_OWN_UPSID, x) + ++/* ANA_CL:PORT:PORT_ID_CFG */ ++#define ANA_CL_PORT_ID_CFG(g) \ ++ __REG(TARGET_ANA_CL, 0, 1, regs->gaddr[GA_ANA_CL_PORT], g, \ ++ regs->gcnt[GC_ANA_CL_PORT], 512, 104, 0, 1, 4) ++ ++#define ANA_CL_PORT_ID_CFG_LPORT_NUM\ ++ GENMASK(regs->fsize[FW_ANA_CL_PORT_ID_CFG_LPORT_NUM] + 0 - 1, 0) ++#define ANA_CL_PORT_ID_CFG_LPORT_NUM_SET(x)\ ++ spx5_field_prep(ANA_CL_PORT_ID_CFG_LPORT_NUM, x) ++#define ANA_CL_PORT_ID_CFG_LPORT_NUM_GET(x)\ ++ spx5_field_get(ANA_CL_PORT_ID_CFG_LPORT_NUM, x) ++ ++/* ANA_CL:COMMON:AGGR_CFG */ ++#define ANA_CL_AGGR_CFG \ ++ __REG(TARGET_ANA_CL, 0, 1, regs->gaddr[GA_ANA_CL_COMMON], 0, 1, 756, 12, 0, \ ++ 1, 4) ++ ++#define ANA_CL_AGGR_CFG_RT_UPD_VSTAX_AC_ENA BIT(14) ++#define ANA_CL_AGGR_CFG_RT_UPD_VSTAX_AC_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_RT_UPD_VSTAX_AC_ENA, x) ++#define ANA_CL_AGGR_CFG_RT_UPD_VSTAX_AC_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_RT_UPD_VSTAX_AC_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_SHORT_AGGR_ENA BIT(13) ++#define ANA_CL_AGGR_CFG_SHORT_AGGR_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_SHORT_AGGR_ENA, x) ++#define ANA_CL_AGGR_CFG_SHORT_AGGR_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_SHORT_AGGR_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_ISDX_ENA BIT(12) ++#define ANA_CL_AGGR_CFG_AGGR_ISDX_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_ISDX_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_ISDX_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_ISDX_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_USE_VSTAX_AC_ENA BIT(11) ++#define ANA_CL_AGGR_CFG_AGGR_USE_VSTAX_AC_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_USE_VSTAX_AC_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_USE_VSTAX_AC_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_USE_VSTAX_AC_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_DMAC_REVERSED_ENA BIT(10) ++#define ANA_CL_AGGR_CFG_AGGR_DMAC_REVERSED_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_DMAC_REVERSED_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_DMAC_REVERSED_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_DMAC_REVERSED_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_RND_ENA BIT(9) ++#define ANA_CL_AGGR_CFG_AGGR_RND_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_RND_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_RND_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_RND_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_IP6_DIP_ENA BIT(8) ++#define ANA_CL_AGGR_CFG_AGGR_IP6_DIP_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_IP6_DIP_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_IP6_DIP_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_IP6_DIP_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_IP6_SIP_ENA BIT(7) ++#define ANA_CL_AGGR_CFG_AGGR_IP6_SIP_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_IP6_SIP_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_IP6_SIP_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_IP6_SIP_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_IP6_FLOW_LBL_ENA BIT(6) ++#define ANA_CL_AGGR_CFG_AGGR_IP6_FLOW_LBL_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_IP6_FLOW_LBL_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_IP6_FLOW_LBL_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_IP6_FLOW_LBL_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_IP6_TCPUDP_PORT_ENA BIT(5) ++#define ANA_CL_AGGR_CFG_AGGR_IP6_TCPUDP_PORT_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_IP6_TCPUDP_PORT_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_IP6_TCPUDP_PORT_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_IP6_TCPUDP_PORT_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_IP4_TCPUDP_PORT_ENA BIT(4) ++#define ANA_CL_AGGR_CFG_AGGR_IP4_TCPUDP_PORT_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_IP4_TCPUDP_PORT_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_IP4_TCPUDP_PORT_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_IP4_TCPUDP_PORT_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_IP4_DIP_ENA BIT(3) ++#define ANA_CL_AGGR_CFG_AGGR_IP4_DIP_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_IP4_DIP_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_IP4_DIP_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_IP4_DIP_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_IP4_SIP_ENA BIT(2) ++#define ANA_CL_AGGR_CFG_AGGR_IP4_SIP_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_IP4_SIP_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_IP4_SIP_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_IP4_SIP_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_DMAC_ENA BIT(1) ++#define ANA_CL_AGGR_CFG_AGGR_DMAC_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_DMAC_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_DMAC_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_DMAC_ENA, x) ++ ++#define ANA_CL_AGGR_CFG_AGGR_SMAC_ENA BIT(0) ++#define ANA_CL_AGGR_CFG_AGGR_SMAC_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_AGGR_CFG_AGGR_SMAC_ENA, x) ++#define ANA_CL_AGGR_CFG_AGGR_SMAC_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_AGGR_CFG_AGGR_SMAC_ENA, x) ++ + /* ANA_ACL:KEY_SEL:VCAP_S2_KEY_SEL */ + #define ANA_ACL_VCAP_S2_KEY_SEL(g, r) \ + __REG(TARGET_ANA_ACL, 0, 1, regs->gaddr[GA_ANA_ACL_KEY_SEL], g, \ +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c +index 220e81b714d4..d6c0dc3194fe 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c +@@ -53,6 +53,7 @@ const unsigned int sparx5_gaddr[GADDR_LAST] = { + [GA_ANA_AC_RAM_CTRL] = 839108, + [GA_ANA_AC_PS_COMMON] = 894472, + [GA_ANA_AC_MIRROR_PROBE] = 893696, ++ [GA_ANA_AC_AGGR] = 851712, + [GA_ANA_AC_SRC] = 849920, + [GA_ANA_AC_PGID] = 786432, + [GA_ANA_AC_TSN_SF] = 839136, +@@ -219,4 +220,5 @@ const unsigned int sparx5_fsize[FSIZE_LAST] = { + [FW_XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP] = 15, + [FW_XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP] = 15, + [FW_XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM] = 15, ++ [FW_ANA_CL_PORT_ID_CFG_LPORT_NUM] = 7, + }; +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h +index ea28130c2341..3e433b34b7c6 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h +@@ -62,6 +62,7 @@ enum sparx5_gaddr_enum { + GA_ANA_AC_RAM_CTRL, + GA_ANA_AC_PS_COMMON, + GA_ANA_AC_MIRROR_PROBE, ++ GA_ANA_AC_AGGR, + GA_ANA_AC_SRC, + GA_ANA_AC_PGID, + GA_ANA_AC_TSN_SF, +@@ -232,6 +233,7 @@ enum sparx5_fsize_enum { + FW_XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP, + FW_XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP, + FW_XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM, ++ FW_ANA_CL_PORT_ID_CFG_LPORT_NUM, + FSIZE_LAST, + }; + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +index a56b5a83f814..6f5a47ae0c7a 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +@@ -5,6 +5,7 @@ + */ + + #include ++#include + #include + + #include "sparx5_main_regs.h" +@@ -16,6 +17,7 @@ struct sparx5_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct net_device *dev; ++ struct net_device *orig_dev; + struct sparx5 *sparx5; + unsigned long event; + }; +@@ -62,12 +64,11 @@ static void sparx5_port_attr_bridge_flags(struct sparx5_port *port, + !!(flags.val & BR_BCAST_FLOOD)); + } + +-static void sparx5_attr_stp_state_set(struct sparx5_port *port, +- u8 state) ++void sparx5_attr_stp_state_set(struct sparx5_port *port, u8 state) + { + struct sparx5 *sparx5 = port->sparx5; + +- if (!test_bit(port->portno, sparx5->bridge_mask)) { ++ if (!test_bit(port->portno, sparx5->bridge_mask) && !port->lag_master) { + netdev_err(port->ndev, + "Controlling non-bridged port %d?\n", port->portno); + return; +@@ -140,9 +141,10 @@ static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, + struct sparx5 *sparx5 = port->sparx5; + + /* Attributes of an upper in a bridge, e.g. a VLAN interface on the +- * port, or of a bridge the port is not in, are not ours. ++ * port, or of a bridge the port is not in, are not ours. Those of ++ * the LAG the port is in are. + */ +- if (attr->orig_dev != dev && ++ if (attr->orig_dev != dev && attr->orig_dev != port->lag_master && + (attr->orig_dev != sparx5->hw_bridge_dev || + !test_bit(port->portno, sparx5->bridge_mask))) + return -EOPNOTSUPP; +@@ -182,6 +184,7 @@ static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, + } + + static int sparx5_port_bridge_join(struct sparx5_port *port, ++ struct net_device *brport_dev, + struct net_device *bridge, + struct netlink_ext_ack *extack) + { +@@ -202,7 +205,7 @@ static int sparx5_port_bridge_join(struct sparx5_port *port, + + set_bit(port->portno, sparx5->bridge_mask); + +- err = switchdev_bridge_port_offload(ndev, ndev, NULL, NULL, NULL, ++ err = switchdev_bridge_port_offload(brport_dev, ndev, NULL, NULL, NULL, + false, extack); + if (err) + goto err_switchdev_offload; +@@ -234,8 +237,6 @@ static void sparx5_port_bridge_leave(struct sparx5_port *port, + struct switchdev_brport_flags flags = {0}; + struct sparx5 *sparx5 = port->sparx5; + +- switchdev_bridge_port_unoffload(port->ndev, NULL, NULL, NULL); +- + clear_bit(port->portno, sparx5->bridge_mask); + if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS)) + sparx5->hw_bridge_dev = NULL; +@@ -258,8 +259,38 @@ static void sparx5_port_bridge_leave(struct sparx5_port *port, + sparx5_port_attr_bridge_flags(port, flags); + } + +-static int sparx5_port_changeupper(struct net_device *dev, +- struct netdev_notifier_changeupper_info *info) ++int ++sparx5_port_prechangeupper(struct net_device *dev, ++ struct net_device *brport_dev, ++ struct netdev_notifier_changeupper_info *info) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ int err = NOTIFY_DONE; ++ ++ if (netif_is_bridge_master(info->upper_dev)) { ++ if (info->linking) ++ return 0; ++ else ++ switchdev_bridge_port_unoffload(dev, port, NULL, NULL); ++ } ++ ++ if (netif_is_lag_master(info->upper_dev)) { ++ err = sparx5_lag_aggr_code_set(dev, info); ++ if (err) ++ return err; ++ ++ if (info->linking) ++ return 0; ++ ++ switchdev_bridge_port_unoffload(brport_dev, port, NULL, NULL); ++ } ++ ++ return err; ++} ++ ++int sparx5_port_changeupper(struct net_device *dev, ++ struct net_device *brport_dev, ++ struct netdev_notifier_changeupper_info *info) + { + struct sparx5_port *port = netdev_priv(dev); + struct netlink_ext_ack *extack; +@@ -269,7 +300,9 @@ static int sparx5_port_changeupper(struct net_device *dev, + + if (netif_is_bridge_master(info->upper_dev)) { + if (info->linking) +- err = sparx5_port_bridge_join(port, info->upper_dev, ++ err = sparx5_port_bridge_join(port, ++ brport_dev, ++ info->upper_dev, + extack); + else + sparx5_port_bridge_leave(port, info->upper_dev); +@@ -277,9 +310,47 @@ static int sparx5_port_changeupper(struct net_device *dev, + sparx5_vlan_port_apply(port->sparx5, port); + } + ++ if (netif_is_lag_master(info->upper_dev)) { ++ /* Upper device is a LAG master, add this device to the LAG. */ ++ if (info->linking) ++ err = sparx5_lag_join(port, ++ info->upper_dev, ++ info->upper_dev, ++ extack); ++ else ++ sparx5_lag_leave(port, info->upper_dev); ++ } ++ + return err; + } + ++static int ++sparx5_port_changelower(struct net_device *dev, ++ struct netdev_notifier_changelowerstate_info *info) ++{ ++ struct netdev_lag_lower_state_info *lag = info->lower_state_info; ++ struct sparx5_port *port = netdev_priv(dev); ++ struct sparx5 *sparx5 = port->sparx5; ++ bool is_active; ++ ++ if (netif_is_lag_port(dev)) { ++ if (!port->lag_master) ++ return NOTIFY_DONE; ++ ++ is_active = lag->link_up && lag->tx_enabled; ++ ++ if (port->lag_tx_active == is_active) ++ return NOTIFY_DONE; ++ ++ port->lag_tx_active = is_active; ++ ++ sparx5_update_dst_fwd(sparx5); ++ sparx5_lag_aggr_masks_set(port, false); ++ } ++ ++ return NOTIFY_OK; ++} ++ + static int sparx5_port_add_addr(struct net_device *dev, bool up) + { + struct sparx5_port *port = netdev_priv(dev); +@@ -308,8 +379,22 @@ static int sparx5_netdevice_port_event(struct net_device *dev, + return 0; + + switch (event) { ++ case NETDEV_PRECHANGEUPPER: ++ /* When a port is directly attached to a bridge, the brport_dev ++ * and dev are identical. ++ */ ++ ++ sparx5_port_prechangeupper(dev, dev, ptr); ++ break; + case NETDEV_CHANGEUPPER: +- err = sparx5_port_changeupper(dev, ptr); ++ /* When a port is directly attached to a bridge, the brport_dev ++ * and dev are identical. ++ */ ++ ++ err = sparx5_port_changeupper(dev, dev, ptr); ++ break; ++ case NETDEV_CHANGELOWERSTATE: ++ err = sparx5_port_changelower(dev, ptr); + break; + case NETDEV_PRE_UP: + err = sparx5_port_add_addr(dev, true); +@@ -322,13 +407,73 @@ static int sparx5_netdevice_port_event(struct net_device *dev, + return err; + } + ++static int ++sparx5_netdevice_lag_event(struct net_device *dev, struct notifier_block *nb, ++ unsigned long event, ++ struct netdev_notifier_changeupper_info *info) ++{ ++ /* Event for lag master. Walk the lower devices. */ ++ struct sparx5_port *port; ++ struct net_device *lower; ++ struct list_head *iter; ++ int err = 0; ++ ++ netdev_for_each_lower_dev(dev, lower, iter) { ++ if (!sparx5_netdevice_check(lower)) ++ continue; ++ ++ port = netdev_priv(lower); ++ if (port->lag_master != dev) ++ continue; ++ ++ switch (event) { ++ case NETDEV_PRECHANGEUPPER: ++ err = sparx5_port_prechangeupper(lower, dev, info); ++ break; ++ case NETDEV_CHANGEUPPER: ++ err = sparx5_port_changeupper(lower, dev, info); ++ break; ++ default: ++ break; ++ } ++ ++ if (err) ++ return err; ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++static int ++sparx5_netdevice_foreign_event(struct net_device *dev, ++ struct notifier_block *nb, unsigned long event, ++ struct netdev_notifier_changeupper_info *info) ++{ ++ return NOTIFY_DONE; ++} ++ + static int sparx5_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr) + { + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + int ret = 0; + +- ret = sparx5_netdevice_port_event(dev, nb, event, ptr); ++ if (netif_is_lag_master(dev)) { ++ /* This event is for a lag master device e.g bond0 */ ++ ++ ret = sparx5_netdevice_lag_event(dev, nb, event, ptr); ++ } else if (netif_is_bridge_master(dev)) { ++ /* This event is for a bridge master device e.g br0 */ ++ ++ ret = 0; ++ } else if (sparx5_netdevice_check(dev)) { ++ /* This event is for a sparx5 port device e.g eth0 */ ++ ++ ret = sparx5_netdevice_port_event(dev, nb, event, ptr); ++ } else { ++ /* Anything else */ ++ ret = sparx5_netdevice_foreign_event(dev, nb, event, ptr); ++ } + + return notifier_from_errno(ret); + } +@@ -337,20 +482,42 @@ static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work) + { + struct sparx5_switchdev_event_work *switchdev_work = + container_of(work, struct sparx5_switchdev_event_work, work); ++ struct net_device *orig_dev = switchdev_work->orig_dev; + struct net_device *dev = switchdev_work->dev; + struct switchdev_notifier_fdb_info *fdb_info; + struct sparx5_port *port; + struct sparx5 *sparx5; + bool host_addr; + ++ sparx5 = switchdev_work->sparx5; ++ + rtnl_lock(); +- if (!sparx5_netdevice_check(dev)) { +- host_addr = true; +- sparx5 = switchdev_work->sparx5; +- } else { ++ ++ if (sparx5_netdevice_check(orig_dev)) { ++ /* The notification was for a netdevice */ ++ ++ port = netdev_priv(orig_dev); + host_addr = false; +- sparx5 = switchdev_work->sparx5; ++ } else if (netif_is_bridge_master(orig_dev)) { ++ /* The notification was for a bridge master - add host addr */ ++ ++ port = netdev_priv(orig_dev); ++ host_addr = true; ++ } else if (netif_is_lag_master(orig_dev)) { ++ /* The notification was for a LAG master - add FDB for first ++ * port in LAG. LAG ports can join and leave the LAG while FDB ++ * events are being handled. ++ */ ++ ++ if (!sparx5_lag_is_first(orig_dev, dev)) ++ goto out; ++ + port = netdev_priv(dev); ++ host_addr = false; ++ } else { ++ /* Foreign device - forward to CPU for software bridging */ ++ ++ host_addr = true; + } + + fdb_info = &switchdev_work->fdb_info; +@@ -370,6 +537,7 @@ static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work) + break; + } + ++out: + rtnl_unlock(); + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); +@@ -381,41 +549,34 @@ static void sparx5_schedule_work(struct work_struct *work) + queue_work(sparx5_owq, work); + } + +-static int sparx5_switchdev_event(struct notifier_block *nb, +- unsigned long event, void *ptr) ++static int ++__sparx5_switchdev_handle_fdb(struct sparx5 *sparx5, struct net_device *dev, ++ struct net_device *orig_dev, unsigned long event, ++ const struct switchdev_notifier_fdb_info *fdb_info) + { +- struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + struct sparx5_switchdev_event_work *switchdev_work; +- struct switchdev_notifier_fdb_info *fdb_info; +- struct switchdev_notifier_info *info = ptr; +- struct sparx5 *spx5; +- int err; +- +- spx5 = container_of(nb, struct sparx5, switchdev_nb); + + switch (event) { +- case SWITCHDEV_PORT_ATTR_SET: +- err = switchdev_handle_port_attr_set(dev, ptr, +- sparx5_netdevice_check, +- sparx5_port_attr_set); +- return notifier_from_errno(err); + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fallthrough; + case SWITCHDEV_FDB_DEL_TO_DEVICE: ++ if (sparx5_netdevice_check(orig_dev) && ++ !fdb_info->added_by_user) ++ break; ++ + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (!switchdev_work) + return NOTIFY_BAD; + + switchdev_work->dev = dev; ++ switchdev_work->orig_dev = orig_dev; + switchdev_work->event = event; +- switchdev_work->sparx5 = spx5; ++ switchdev_work->sparx5 = sparx5; + +- fdb_info = container_of(info, +- struct switchdev_notifier_fdb_info, +- info); + INIT_WORK(&switchdev_work->work, + sparx5_switchdev_bridge_fdb_event_work); +- memcpy(&switchdev_work->fdb_info, ptr, ++ memcpy(&switchdev_work->fdb_info, ++ fdb_info, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + if (!switchdev_work->fdb_info.addr) +@@ -435,6 +596,80 @@ static int sparx5_switchdev_event(struct notifier_block *nb, + return NOTIFY_BAD; + } + ++static int ++sparx5_switchdev_handle_fdb(struct net_device *dev, ++ struct net_device *orig_dev, ++ unsigned long event, const void *ctx, ++ const struct switchdev_notifier_fdb_info *fdb_info) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ ++ return __sparx5_switchdev_handle_fdb(port->sparx5, dev, orig_dev, ++ event, fdb_info); ++} ++ ++static bool sparx5_foreign_device_check(const struct net_device *dev, ++ const struct net_device *foreign_dev) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ struct sparx5 *sparx5 = port->sparx5; ++ int i; ++ ++ /* Our own bridge master is not foreign. */ ++ if (netif_is_bridge_master(foreign_dev)) ++ if (sparx5->hw_bridge_dev == foreign_dev) ++ return false; ++ ++ /* A LAG master that contains one of our ports is not foreign. */ ++ if (netif_is_lag_master(foreign_dev)) ++ for (i = 0; i < sparx5->data->consts->n_ports; ++i) ++ if (sparx5->ports[i] && ++ sparx5->ports[i]->lag_master == foreign_dev) ++ return false; ++ ++ /* Everything else is a foreign device. */ ++ return true; ++} ++ ++static int sparx5_switchdev_event(struct notifier_block *nb, ++ unsigned long event, void *ptr) ++{ ++ struct net_device *dev = switchdev_notifier_info_to_dev(ptr); ++ int err; ++ ++ switch (event) { ++ case SWITCHDEV_PORT_ATTR_SET: ++ err = switchdev_handle_port_attr_set(dev, ++ ptr, ++ sparx5_netdevice_check, ++ sparx5_port_attr_set); ++ return notifier_from_errno(err); ++ case SWITCHDEV_FDB_ADD_TO_DEVICE: ++ fallthrough; ++ case SWITCHDEV_FDB_DEL_TO_DEVICE: ++ /* The bridge's own addresses, also those it gets before any ++ * port joins, which have no lower device to reach us through. ++ */ ++ if (netif_is_bridge_master(dev)) { ++ struct sparx5 *sparx5 = container_of(nb, struct sparx5, ++ switchdev_nb); ++ ++ return __sparx5_switchdev_handle_fdb(sparx5, dev, dev, ++ event, ptr); ++ } ++ ++ err = switchdev_handle_fdb_event_to_device(dev, ++ event, ++ ptr, ++ sparx5_netdevice_check, ++ sparx5_foreign_device_check, ++ sparx5_switchdev_handle_fdb); ++ return notifier_from_errno(err); ++ } ++ ++ return NOTIFY_DONE; ++} ++ + static int sparx5_handle_port_vlan_add(struct net_device *dev, + struct notifier_block *nb, + const struct switchdev_obj_port_vlan *v) +@@ -453,6 +688,33 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev, + return sparx5_cpu_entry_get(sparx5, dev->broadcast, v->vid); + } + ++ if (netif_is_lag_master(dev)) { ++ /* Walk lower devices and make each port member of the bridge ++ * VLAN. ++ */ ++ struct net_device *lower; ++ struct list_head *iter; ++ int err; ++ ++ netdev_for_each_lower_dev(dev, lower, iter) { ++ if (!sparx5_netdevice_check(lower)) ++ continue; ++ ++ port = netdev_priv(lower); ++ if (port->lag_master != dev) ++ continue; ++ ++ err = sparx5_vlan_vid_add(port, ++ v->vid, ++ v->flags & BRIDGE_VLAN_INFO_PVID, ++ v->flags & BRIDGE_VLAN_INFO_UNTAGGED); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++ } ++ + if (!sparx5_netdevice_check(dev)) + return -EOPNOTSUPP; + +@@ -645,6 +907,20 @@ static int sparx5_handle_port_mdb_del(struct net_device *dev, + return 0; + } + ++static int sparx5_handle_port_obj_add_mdb(struct net_device *dev, ++ const void *ctx, ++ const struct switchdev_obj *obj, ++ struct netlink_ext_ack *extack) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ ++ if (ctx && ctx != port) ++ return 0; ++ ++ return sparx5_handle_port_mdb_add(dev, NULL, ++ SWITCHDEV_OBJ_PORT_MDB(obj)); ++} ++ + static int sparx5_handle_port_obj_add(struct net_device *dev, + struct notifier_block *nb, + struct switchdev_notifier_port_obj_info *info) +@@ -659,8 +935,9 @@ static int sparx5_handle_port_obj_add(struct net_device *dev, + break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + case SWITCHDEV_OBJ_ID_HOST_MDB: +- err = sparx5_handle_port_mdb_add(dev, nb, +- SWITCHDEV_OBJ_PORT_MDB(obj)); ++ err = switchdev_handle_port_obj_add(dev, info, ++ sparx5_netdevice_check, ++ sparx5_handle_port_obj_add_mdb); + break; + default: + err = -EOPNOTSUPP; +@@ -690,6 +967,29 @@ static int sparx5_handle_port_vlan_del(struct net_device *dev, + return 0; + } + ++ if (netif_is_lag_master(dev)) { ++ /* Walk lower devices and make each port member of the bridge ++ * VLAN. ++ */ ++ struct net_device *lower; ++ struct list_head *iter; ++ ++ netdev_for_each_lower_dev(dev, lower, iter) { ++ if (!sparx5_netdevice_check(lower)) ++ continue; ++ ++ port = netdev_priv(lower); ++ if (port->lag_master != dev) ++ continue; ++ ++ ret = sparx5_vlan_vid_del(port, vid); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++ } ++ + if (!sparx5_netdevice_check(dev)) + return -EOPNOTSUPP; + +@@ -700,6 +1000,19 @@ static int sparx5_handle_port_vlan_del(struct net_device *dev, + return 0; + } + ++static int sparx5_handle_port_obj_del_mdb(struct net_device *dev, ++ const void *ctx, ++ const struct switchdev_obj *obj) ++{ ++ struct sparx5_port *port = netdev_priv(dev); ++ ++ if (ctx && ctx != port) ++ return 0; ++ ++ return sparx5_handle_port_mdb_del(dev, NULL, ++ SWITCHDEV_OBJ_PORT_MDB(obj)); ++} ++ + static int sparx5_handle_port_obj_del(struct net_device *dev, + struct notifier_block *nb, + struct switchdev_notifier_port_obj_info *info) +@@ -714,8 +1027,9 @@ static int sparx5_handle_port_obj_del(struct net_device *dev, + break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + case SWITCHDEV_OBJ_ID_HOST_MDB: +- err = sparx5_handle_port_mdb_del(dev, nb, +- SWITCHDEV_OBJ_PORT_MDB(obj)); ++ err = switchdev_handle_port_obj_del(dev, info, ++ sparx5_netdevice_check, ++ sparx5_handle_port_obj_del_mdb); + break; + default: + err = -EOPNOTSUPP; +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c +index 55dd02b53d01..ece42d1ed9d3 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c +@@ -176,6 +176,7 @@ void sparx5_pgid_read_mask(struct sparx5 *spx5, int pgid, u32 portmask[3]) + void sparx5_update_fwd(struct sparx5 *sparx5) + { + DECLARE_BITMAP(workmask, SPX5_PORTS); ++ DECLARE_BITMAP(lagmask, SPX5_PORTS); + u32 mask[3]; + int port; + +@@ -188,6 +189,20 @@ void sparx5_update_fwd(struct sparx5 *sparx5) + /* Allow to send to all bridged but self */ + bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS); + clear_bit(port, workmask); ++ ++ spin_lock(&sparx5->lock); ++ if (sparx5->ports[port] && sparx5->ports[port]->lag_master) { ++ /* Do not send frames back on the LAG they were ++ * received on. ++ */ ++ sparx5_lag_mask_get(sparx5, ++ sparx5->ports[port]->lag_master, ++ lagmask); ++ bitmap_andnot(workmask, workmask, lagmask, ++ SPX5_PORTS); ++ } ++ spin_unlock(&sparx5->lock); ++ + bitmap_to_arr32(mask, workmask, SPX5_PORTS); + spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port)); + if (is_sparx5(sparx5)) { +@@ -216,6 +231,34 @@ void sparx5_update_fwd(struct sparx5 *sparx5) + } + } + ++void sparx5_update_dst_fwd(struct sparx5 *sparx5) ++{ ++ DECLARE_BITMAP(dstmask, SPX5_PORTS); ++ struct sparx5_port *port; ++ u32 mask[3] = {0}; ++ ++ for (int i = 0; i < sparx5->data->consts->n_ports; i++) { ++ port = sparx5->ports[i]; ++ if (!port) ++ continue; ++ ++ bitmap_zero(dstmask, SPX5_PORTS); ++ ++ if (port->lag_master) ++ sparx5_lag_mask_get(sparx5, port->lag_master, dstmask); ++ else ++ set_bit(i, dstmask); ++ ++ bitmap_to_arr32(mask, dstmask, SPX5_PORTS); ++ ++ spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(i)); ++ if (is_sparx5(sparx5)) { ++ spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(i)); ++ spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(i)); ++ } ++ } ++} ++ + void sparx5_vlan_port_apply(struct sparx5 *sparx5, + struct sparx5_port *port) + diff --git a/patches/linux/6.18.57/0126-net-sparx5-fix-leak-of-in-flight-PTP-timestamp-reque.patch b/patches/linux/6.18.57/0126-net-sparx5-fix-leak-of-in-flight-PTP-timestamp-reque.patch new file mode 100644 index 0000000000..bafb73285d --- /dev/null +++ b/patches/linux/6.18.57/0126-net-sparx5-fix-leak-of-in-flight-PTP-timestamp-reque.patch @@ -0,0 +1,56 @@ +From c331cf45e4a8a437a4826cfe23387535f185f94c Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Sat, 17 Oct 2026 09:14:50 +0200 +Subject: [PATCH 126/128] net: sparx5: fix leak of in-flight PTP timestamp + requests + +A two-step PTP frame increments ptp_skbs when it is queued for a TX +timestamp, and the timestamp interrupt decrements it when it matches the +frame. sparx5_ptp_txtstamp_old_release() frees requests older than +SPARX5_PTP_TIMEOUT without decrementing ptp_skbs, so every timestamp +that takes longer than 10 ms leaks one slot. After SPARX5_MAX_PTP_ID +leaks, sparx5_ptp_txtstamp_request() returns -EBUSY for every two-step +frame on the switch until the driver is reloaded. + +Decrement ptp_skbs for each released request. Take ptp_ts_id_lock +after dropping the queue lock, since sparx5_ptp_txtstamp_release() takes +the two in the opposite order. + +Fixes: 70dfe25cd866 ("net: sparx5: Update extraction/injection for timestamping") +Signed-off-by: Joachim Wiberg +--- + drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c +index 2826e48af103..7290a77b1e38 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c +@@ -206,8 +206,10 @@ static void sparx5_ptp_classify(struct sparx5_port *port, struct sk_buff *skb, + + static void sparx5_ptp_txtstamp_old_release(struct sparx5_port *port) + { ++ struct sparx5 *sparx5 = port->sparx5; + struct sk_buff *skb, *skb_tmp; + unsigned long flags; ++ int released = 0; + + spin_lock_irqsave(&port->tx_skbs.lock, flags); + skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { +@@ -217,8 +219,16 @@ static void sparx5_ptp_txtstamp_old_release(struct sparx5_port *port) + + __skb_unlink(skb, &port->tx_skbs); + dev_kfree_skb_any(skb); ++ released++; + } + spin_unlock_irqrestore(&port->tx_skbs.lock, flags); ++ ++ if (!released) ++ return; ++ ++ spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags); ++ sparx5->ptp_skbs -= released; ++ spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags); + } + + int sparx5_ptp_txtstamp_request(struct sparx5_port *port, diff --git a/patches/linux/6.18.57/0127-net-phy-micrel-lan8814-resync-QSGMII-after-every-PHY.patch b/patches/linux/6.18.57/0127-net-phy-micrel-lan8814-resync-QSGMII-after-every-PHY.patch new file mode 100644 index 0000000000..d96a23d351 --- /dev/null +++ b/patches/linux/6.18.57/0127-net-phy-micrel-lan8814-resync-QSGMII-after-every-PHY.patch @@ -0,0 +1,96 @@ +From 72129141a364b09caa4cd04d909c379b644dfed6 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Sat, 17 Oct 2026 18:51:08 +0200 +Subject: [PATCH 127/128] net: phy: micrel: lan8814: resync QSGMII after every + PHY reset + +A LAN8804 copper port can come up with its transmit direction dead +after the PHY has been reattached: the switch MAC counts the frames out, +the link partner receives none, and the link stays up at 1000 Mbps. A +link flap does not recover it, a QSGMII soft reset of the PHY package +does. Taking the interface down and up twice, a few seconds apart, on +both ends of a link triggers it in about one cycle out of five. + +The QSGMII resync added to config_aneg runs once per package and boot, +but phylib soft-resets the PHY on every attach. Microchip's PHY API +resets the package QSGMII and restarts aneg after every PHY reset. + +Mark the port in config_init, which runs after each PHY reset, and do +the QSGMII soft reset and an aneg restart in the following config_aneg. +With that, the same test passes 20 cycles out of 20. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index af358c30cd75..b0df8aee081b 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -405,9 +405,6 @@ struct lan8814_shared_priv { + + /* Lock for ptp_clock */ + struct mutex shared_lock; +- +- unsigned long flags; +-#define LAN8814_SHARED_F_QSGMII_RESYNCED 0 + }; + + struct lan8814_ptp_rx_ts { +@@ -459,6 +456,7 @@ struct kszphy_priv { + bool clk_enable; + bool is_ptp_available; + bool aneg_restarted; ++ bool qsgmii_resync; + struct phy_device *phydev; + struct delayed_work link_work; + unsigned int link_checks; +@@ -4618,6 +4616,8 @@ static int lan8814_config_init(struct phy_device *phydev) + if (lan8814->led_mode >= 0) + lan8814_setup_led(phydev, lan8814->led_mode); + ++ lan8814->qsgmii_resync = true; ++ + return lan8814_erratas(phydev); + } + +@@ -4767,17 +4767,19 @@ static void lan8814_link_change_notify(struct phy_device *phydev) + lan8814_link_watch(phydev); + } + +-/* Microchip's PHY API resets the package QSGMII once the MAC has configured +- * its end, and renegotiates each port once after its first 1000BASE-T link, +- * to avoid CRC errors. Under phylink the MAC side is set up before +- * config_aneg runs. ++/* Microchip's PHY API resets the package QSGMII and restarts aneg after ++ * every PHY reset, once the MAC has configured its end, and renegotiates ++ * each port once after its first 1000BASE-T link, to avoid CRC errors. ++ * Under phylink the MAC side is set up before config_aneg runs. + */ + static int lan8814_config_aneg(struct phy_device *phydev) + { +- struct lan8814_shared_priv *shared = phy_package_get_priv(phydev); ++ struct kszphy_priv *priv = phydev->priv; ++ bool resync = priv->qsgmii_resync; + int err; + +- if (!test_and_set_bit(LAN8814_SHARED_F_QSGMII_RESYNCED, &shared->flags)) { ++ if (resync) { ++ priv->qsgmii_resync = false; + err = lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, + LAN8814_QSGMII_SOFT_RESET, + LAN8814_QSGMII_SOFT_RESET_BIT, +@@ -4788,7 +4790,11 @@ static int lan8814_config_aneg(struct phy_device *phydev) + + lan8814_link_watch(phydev); + +- return genphy_config_aneg(phydev); ++ err = genphy_config_aneg(phydev); ++ if (err || !resync || phydev->autoneg != AUTONEG_ENABLE) ++ return err; ++ ++ return genphy_restart_aneg(phydev); + } + + static int lan8814_read_status(struct phy_device *phydev) diff --git a/patches/linux/6.18.57/0128-net-sparx5-let-the-bridge-forward-IGMP.patch b/patches/linux/6.18.57/0128-net-sparx5-let-the-bridge-forward-IGMP.patch new file mode 100644 index 0000000000..b0e5123a3f --- /dev/null +++ b/patches/linux/6.18.57/0128-net-sparx5-let-the-bridge-forward-IGMP.patch @@ -0,0 +1,169 @@ +From 6da4a1ec8fb77f75573241fa3db900337100164e Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Sun, 18 Oct 2026 14:14:05 +0200 +Subject: [PATCH 128/128] net: sparx5: let the bridge forward IGMP + +IGMP never crosses a sparx5 bridge. With three bridges in a chain, the +middle one never learns a group joined behind the last one, and none of +them hears the querier's queries from the others: + + host -- br0 -- br1 -- br2 -- receiver + +The ports redirect IGMP to the CPU, CPU_IGMP_REDIR_ENA is set in +ANA_CL:PORT:CAPTURE_CFG, so the switch does not forward it. The driver +still marks every frame from a bridged port with offload_fwd_mark, so the +bridge does not forward it either. + +Leave IGMP unmarked when the port redirects it, like lan966x does. + +Signed-off-by: Joachim Wiberg +--- + .../microchip/sparx5/lan969x/lan969x_fdma.c | 4 +- + .../ethernet/microchip/sparx5/sparx5_fdma.c | 6 +-- + .../ethernet/microchip/sparx5/sparx5_main.h | 1 + + .../microchip/sparx5/sparx5_main_regs.h | 11 +++++ + .../ethernet/microchip/sparx5/sparx5_packet.c | 46 ++++++++++++++++--- + 5 files changed, 54 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c +index 1282f5c3ee6d..995bb5603239 100644 +--- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c ++++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c +@@ -132,9 +132,7 @@ static struct sk_buff *lan969x_fdma_rx_get_frame(struct sparx5 *sparx5, + + sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp); + skb->protocol = eth_type_trans(skb, skb->dev); +- +- if (test_bit(port->portno, sparx5->bridge_mask)) +- skb->offload_fwd_mark = 1; ++ sparx5_rx_fwd_mark(port, skb); + + skb->dev->stats.rx_bytes += skb->len; + skb->dev->stats.rx_packets++; +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c +index dbe86f937b21..f360ba2293be 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c +@@ -168,11 +168,7 @@ static bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx + + sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp); + skb->protocol = eth_type_trans(skb, skb->dev); +- /* Everything we see on an interface that is in the HW bridge +- * has already been forwarded +- */ +- if (test_bit(port->portno, sparx5->bridge_mask)) +- skb->offload_fwd_mark = 1; ++ sparx5_rx_fwd_mark(port, skb); + skb->dev->stats.rx_bytes += skb->len; + skb->dev->stats.rx_packets++; + rx->packets++; +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +index fa16529a747f..c02d339db858 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +@@ -471,6 +471,7 @@ struct frame_info { + + void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp); + void sparx5_ifh_parse(struct sparx5 *sparx5, u32 *ifh, struct frame_info *info); ++void sparx5_rx_fwd_mark(struct sparx5_port *port, struct sk_buff *skb); + irqreturn_t sparx5_xtr_handler(int irq, void *_priv); + netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev); + int sparx5_manual_injection_mode(struct sparx5 *sparx5); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +index e9d2811b4c51..937e85941725 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +@@ -1663,6 +1663,17 @@ extern const struct sparx5_regs *regs; + #define ANA_CL_QOS_CFG_DEFAULT_QOS_VAL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_QOS_VAL, x) + ++/* ANA_CL:PORT:CAPTURE_CFG */ ++#define ANA_CL_CAPTURE_CFG(g) \ ++ __REG(TARGET_ANA_CL, 0, 1, regs->gaddr[GA_ANA_CL_PORT], g, \ ++ regs->gcnt[GC_ANA_CL_PORT], 512, 184, 0, 1, 4) ++ ++#define ANA_CL_CAPTURE_CFG_CPU_IGMP_REDIR_ENA BIT(0) ++#define ANA_CL_CAPTURE_CFG_CPU_IGMP_REDIR_ENA_SET(x)\ ++ FIELD_PREP(ANA_CL_CAPTURE_CFG_CPU_IGMP_REDIR_ENA, x) ++#define ANA_CL_CAPTURE_CFG_CPU_IGMP_REDIR_ENA_GET(x)\ ++ FIELD_GET(ANA_CL_CAPTURE_CFG_CPU_IGMP_REDIR_ENA, x) ++ + /* ANA_CL:PORT:CAPTURE_BPDU_CFG */ + #define ANA_CL_CAPTURE_BPDU_CFG(g) \ + __REG(TARGET_ANA_CL, 0, 1, regs->gaddr[GA_ANA_CL_PORT], g, \ +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c +index f713656f1fae..748f347f4a7b 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c +@@ -4,6 +4,9 @@ + * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. + */ + ++#include ++#include ++ + #include "sparx5_main_regs.h" + #include "sparx5_main.h" + +@@ -57,6 +60,42 @@ void sparx5_ifh_parse(struct sparx5 *sparx5, u32 *ifh, struct frame_info *info) + ((u64)xtr_hdr[5] << 0); + } + ++/* Everything we see on an interface that is in the HW bridge has already ++ * been forwarded, except IGMP that the port redirects to the CPU. ++ */ ++void sparx5_rx_fwd_mark(struct sparx5_port *port, struct sk_buff *skb) ++{ ++ struct sparx5 *sparx5 = port->sparx5; ++ __be16 proto = skb->protocol; ++ const struct iphdr *iph; ++ unsigned int off = 0; ++ struct iphdr _iph; ++ ++ if (!test_bit(port->portno, sparx5->bridge_mask)) ++ return; ++ ++ skb->offload_fwd_mark = 1; ++ ++ if (eth_type_vlan(proto)) { ++ const struct vlan_hdr *vh; ++ struct vlan_hdr _vh; ++ ++ vh = skb_header_pointer(skb, 0, sizeof(_vh), &_vh); ++ if (!vh) ++ return; ++ proto = vh->h_vlan_encapsulated_proto; ++ off = VLAN_HLEN; ++ } ++ if (proto != htons(ETH_P_IP)) ++ return; ++ ++ iph = skb_header_pointer(skb, off, sizeof(_iph), &_iph); ++ if (iph && iph->protocol == IPPROTO_IGMP && ++ spx5_rd(sparx5, ANA_CL_CAPTURE_CFG(port->portno)) & ++ ANA_CL_CAPTURE_CFG_CPU_IGMP_REDIR_ENA) ++ skb->offload_fwd_mark = 0; ++} ++ + static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap) + { + bool eof_flag = false, pruned_flag = false, abort_flag = false; +@@ -148,17 +187,12 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap) + return; + } + +- /* Everything we see on an interface that is in the HW bridge +- * has already been forwarded +- */ +- if (test_bit(port->portno, sparx5->bridge_mask)) +- skb->offload_fwd_mark = 1; +- + /* Finish up skb */ + skb_put(skb, byte_cnt - ETH_FCS_LEN); + eth_skb_pad(skb); + sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp); + skb->protocol = eth_type_trans(skb, netdev); ++ sparx5_rx_fwd_mark(port, skb); + netdev->stats.rx_bytes += skb->len; + netdev->stats.rx_packets++; + netif_rx(skb); diff --git a/src/confd/bin/firewall b/src/confd/bin/firewall index 8c76f053e7..911b3b58ce 100755 --- a/src/confd/bin/firewall +++ b/src/confd/bin/firewall @@ -7,6 +7,7 @@ DEST="org.fedoraproject.FirewallD1" OBJECT="/org/fedoraproject/FirewallD1" INTERFACE="org.fedoraproject.FirewallD1" ADDRSET_DIR="/run/confd/address-sets" +DYNAMIC_DIR="/etc/firewalld/dynamic" VERBOSE=0 print() { @@ -23,8 +24,9 @@ check_firewalld() call_reload() { + # A reload takes over half a minute on a slow single-core system output=$(gdbus call --system --dest "$DEST" --object-path "$OBJECT" \ - --method "$INTERFACE.reload" 2>&1) + --timeout 300 --method "$INTERFACE.reload" 2>&1) ret=$? # Validate both return code and output @@ -118,18 +120,28 @@ ipset_call() fi } -# Dynamic address-set entries only exist in the runtime config; a reload -# rebuilds the sets from the generated ipset XML, which holds static -# entries only. Re-apply the dynamic entries tracked by confd's -# add/remove action handlers, so they survive the reload without being -# baked into the XML (which would resurrect entries removed while the -# reload was in flight). +# confd writes the dynamic address-set entries, tracked by the add and +# remove action handlers in its shadow files, into the generated ipsets so +# they survive a reload, and lists them in $DYNAMIC_DIR. Entries removed +# while the reload was in flight are back now, remove them again, and add +# the entries added meanwhile. # # An entry firewalld rejects as invalid, e.g., one now overlapping a # static entry, is dropped from the shadow file, or it could never be # removed with the remove action again. addrset_resync() { + for baked in "$DYNAMIC_DIR"/*; do + [ -f "$baked" ] || continue + name=$(basename "$baked") + + while IFS= read -r entry; do + [ -n "$entry" ] || continue + grep -qxF "$entry" "$ADDRSET_DIR/$name" 2>/dev/null && continue + ipset_call removeEntry "$name" "$entry" + done < "$baked" + done + for file in "$ADDRSET_DIR"/*; do case "$file" in *.resync) continue ;; esac [ -f "$file" ] || continue @@ -139,7 +151,8 @@ addrset_resync() while IFS= read -r entry; do [ -n "$entry" ] || continue - if ! ipset_call addEntry "$name" "$entry"; then + if ! grep -qxF "$entry" "$DYNAMIC_DIR/$name" 2>/dev/null && + ! ipset_call addEntry "$name" "$entry"; then case "$output" in *INVALID_ENTRY*) logger -t firewall -p daemon.warn "ipset $name: dropping rejected dynamic entry $entry" diff --git a/src/confd/src/firewall.c b/src/confd/src/firewall.c index ec4dc9a8e1..226c972ff3 100644 --- a/src/confd/src/firewall.c +++ b/src/confd/src/firewall.c @@ -27,6 +27,7 @@ #define FIREWALLD_POLICIES_DIR FIREWALLD_DIR_NEXT "/policies" #define FIREWALLD_IPSETS_DIR FIREWALLD_DIR_NEXT "/ipsets" #define IPSETS_ACTIVE_DIR FIREWALLD_DIR "/ipsets" +#define FIREWALLD_DYNAMIC_DIR FIREWALLD_DIR_NEXT "/dynamic" #define ADDRSET_RUNDIR "/run/confd/address-sets" #define ENTRY_STRLEN 64 /* worst-case ip-prefix + margin */ @@ -106,6 +107,29 @@ static int prefix_parse(const char *str, struct prefix *p) return -1; } +static bool prefix_overlap(const char *a, const char *b) +{ + struct prefix pa, pb; + int len, i; + + if (prefix_parse(a, &pa) || prefix_parse(b, &pb) || pa.af != pb.af) + return false; + + len = pa.len < pb.len ? pa.len : pb.len; + for (i = 0; i < len / 8; i++) { + if (pa.addr[i] != pb.addr[i]) + return false; + } + if (len % 8) { + uint8_t mask = 0xff << (8 - len % 8); + + if ((pa.addr[i] & mask) != (pb.addr[i] & mask)) + return false; + } + + return true; +} + static bool shadow_has(const char *name, const char *entry) { char line[ENTRY_STRLEN]; @@ -348,12 +372,58 @@ static int generate_zone(struct lyd_node *cfg, const char *name, char **ifaces) } /* - * Only static entries go into the generated ipset. Dynamic entries, - * added at runtime with the add action, are re-applied from the shadow - * files by 'firewall reload' after firewalld has reloaded. Baking them - * into the XML would resurrect entries removed while a reload was in - * flight -- the reload is asynchronous to the action handlers. + * Dynamic entries, added at runtime with the add action, go into the + * generated ipset so a reload, which can take half a minute on a slow + * system, does not drop them meanwhile. They are also listed in + * FIREWALLD_DYNAMIC_DIR, for 'firewall reload' to remove the ones that + * were removed while the reload was in flight. Entries that overlap + * static ones are left out, nftables refuses overlapping elements in + * interval sets. */ +static void merge_dynamic(FILE *fp, struct lyd_node *cfg, const char *name) +{ + char line[ENTRY_STRLEN]; + FILE *sf, *df; + + sf = fopenf("r", ADDRSET_RUNDIR "/%s", name); + if (!sf) + return; + + if (fmkpath(0755, FIREWALLD_DYNAMIC_DIR) && errno != EEXIST) + ERRNO("Failed creating " FIREWALLD_DYNAMIC_DIR); + df = fopenf("w", FIREWALLD_DYNAMIC_DIR "/%s", name); + + while (fgets(line, sizeof(line), sf)) { + struct lyd_node *node; + bool skip = false; + + chomp(line); + if (!line[0]) + continue; + + LYX_LIST_FOR_EACH(lyd_child(cfg), node, "entry") { + if (prefix_overlap(line, lyd_get_value(node))) { + skip = true; + break; + } + } + + if (skip) { + NOTE("address-set %s: dropping dynamic entry %s, overlaps static entry", + name, line); + continue; + } + + fprintf(fp, " %s\n", line); + if (df) + fprintf(df, "%s\n", line); + } + + if (df) + fclose(df); + fclose(sf); +} + static int generate_ipset(struct lyd_node *cfg, const char *name) { const char *family, *timeout, *desc; @@ -383,6 +453,9 @@ static int generate_ipset(struct lyd_node *cfg, const char *name) LYX_LIST_FOR_EACH(lyd_child(cfg), node, "entry") fprintf(fp, " %s\n", lyd_get_value(node)); + if (!timeout) + merge_dynamic(fp, cfg, name); + fprintf(fp, "\n"); return close_file(fp); @@ -807,7 +880,7 @@ int firewall_change(sr_session_ctx_t *session, struct lyd_node *config, struct l LYX_LIST_FOR_EACH(clist, cnode, "service") generate_service(cnode, lydx_get_cattr(cnode, "name")); - /* Regenerate all address-sets (static entries only) */ + /* Regenerate all address-sets */ clist = lydx_get_descendant(tree, "firewall", "address-set", NULL); LYX_LIST_FOR_EACH(clist, cnode, "address-set") generate_ipset(cnode, lydx_get_cattr(cnode, "name")); diff --git a/src/confd/src/interfaces.c b/src/confd/src/interfaces.c index e476928b2d..7458c134c8 100644 --- a/src/confd/src/interfaces.c +++ b/src/confd/src/interfaces.c @@ -455,6 +455,9 @@ static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net, case IFT_BRIDGE: return bridge_gen(dif, cif, ip, 0); case IFT_LAG: + /* Some bond options cannot be set on a bond that is up */ + if (!lydx_get_child(dif, "lag")) + return 0; return lag_gen(dif, cif, ip, 0); case IFT_VLAN: return vlan_gen(dif, cif, ip); @@ -524,6 +527,51 @@ static bool netdag_must_del(struct lyd_node *dif, struct lyd_node *cif) return false; } +/* + * Taking the link down renegotiates a copper link and restarts STP on a + * bridge port, so only do it for changes that need it: a new IPv6 address + * generation mode only applies on the next link up, the addresses SLAAC + * has created only go away with the link, and the radio settings cannot + * all change on a running interface. A bond takes itself down when its + * own settings change. + */ +/* Setting enabled to what it already was, e.g., true, is no change */ +static bool enabled_changed(struct lyd_node *dif) +{ + struct lyd_node *node = lydx_get_child(dif, "enabled"); + struct lydx_diff nd; + + if (!node) + return false; + + lydx_get_diff(node, &nd); + return strcmp(nd.old ?: "true", nd.new ?: "true"); +} + +static bool netdag_needs_bounce(struct lyd_node *dif, struct lyd_node *cif, + enum lydx_op op) +{ + struct lyd_node *ipv6; + + if (op == LYDX_OP_CREATE || + enabled_changed(dif) || + lydx_get_child(dif, "custom-phys-address") || + lydx_get_descendant(lyd_child(dif), "bridge-port", "bridge", NULL) || + lydx_get_descendant(lyd_child(dif), "lag-port", NULL)) + return true; + + ipv6 = lydx_get_child(dif, "ipv6"); + if (ipv6 && (lydx_get_op(ipv6) != LYDX_OP_NONE || + lydx_get_child(ipv6, "enabled") || + lydx_get_descendant(lyd_child(ipv6), "autoconf", + "create-temporary-addresses", NULL) || + lydx_get_descendant(lyd_child(ipv6), "autoconf", + "create-global-addresses", NULL))) + return true; + + return iftype_from_iface(cif) == IFT_WIFI && lydx_get_child(dif, "wifi"); +} + static int eth_gen_del(struct lyd_node *dif, FILE *ip) { const char *ifname = lydx_get_cattr(dif, "name"); @@ -738,7 +786,8 @@ static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net * and must not be bounced for a change of its settings, the * station would not notice and the port would stay down. */ wds = iftype_from_iface(cif) == IFT_WIFI && wifi_get_mode(cif) == wifi_wds; - if (pppd_owned || (wds && lydx_is_enabled(cif, "enabled"))) + if (pppd_owned || (wds && lydx_is_enabled(cif, "enabled")) || + !netdag_needs_bounce(dif, cif, op)) fprintf(ip, "link set dev %s", ifname); else fprintf(ip, "link set dev %s down", ifname); diff --git a/src/confd/src/ptp.c b/src/confd/src/ptp.c index e35e2cc85e..7968a76e40 100644 --- a/src/confd/src/ptp.c +++ b/src/confd/src/ptp.c @@ -37,10 +37,9 @@ static const char *instance_type_to_clock_type(const char *type) * * ieee802-dot1as sets the full gPTP option set as required by the standard: * transportSpecific, network_transport, delay_mechanism, multicast MACs, - * gmCapable, follow_up_info, assume_two_step, path_trace_enabled, - * and the tighter neighborPropDelayThresh. + * gmCapable, follow_up_info, assume_two_step, path_trace_enabled. */ -static bool emit_profile_globals(FILE *fp, const char *profile, bool hw_ts) +static bool emit_profile_globals(FILE *fp, const char *profile) { bool dot1as = profile && !strcmp(profile, "ieee802-dot1as"); @@ -54,22 +53,35 @@ static bool emit_profile_globals(FILE *fp, const char *profile, bool hw_ts) fprintf(fp, "follow_up_info 1\n"); fprintf(fp, "assume_two_step 1\n"); fprintf(fp, "path_trace_enabled 1\n"); - /* - * 802.1AS P2P gate: if meanLinkDelay exceeds this threshold the - * port stays asCapable=false and never leaves LISTENING. 800 ns - * is the 802.1AS default and is appropriate only for hardware - * timestamping — software timestamps (QEMU, tap interfaces) can - * easily produce peer delays in the microsecond range, which - * would keep both ports stuck in LISTENING indefinitely. - */ - if (hw_ts) - fprintf(fp, "neighborPropDelayThresh 800\n"); } else { fprintf(fp, "transportSpecific 0\n"); } return dot1as; } +/* + * 802.1AS gate: a port whose measured link delay exceeds the threshold + * never becomes asCapable and stays in LISTENING. A switch core time + * stamps behind the PHY, so the delay it sees includes the PHY latency, + * 1.5 us on a LAN8814 at 1 Gbit/s and more at 100 Mbit/s, which the + * 800 ns of the automotive profile leaves no room for. Software time + * stamps are slower still, they get ptp4l's own default of 20 ms. + */ +#define PTP_LINK_DELAY_THRESH_NS 10000 + +static void emit_link_delay_thresh(FILE *fp, struct lyd_node *port_ds, bool hw_ts) +{ + const char *v = lydx_get_cattr(port_ds, "mean-link-delay-thresh"); + long long ns = PTP_LINK_DELAY_THRESH_NS; + + if (v) + ns = (strtoll(v, NULL, 10) + 32768) / 65536; /* ns * 2^16 */ + else if (!hw_ts) + return; + + fprintf(fp, "neighborPropDelayThresh %lld\n", ns); +} + /* * Return true if ifname has hardware TX timestamping capability according * to the probed data in system.json (confd->root). If the interface is @@ -181,8 +193,12 @@ static int write_instance_conf(struct lyd_node *inst, json_t *root) ts = instance_time_stamping(idx, inst, root); fprintf(fp, "time_stamping %s\n", ts); + /* PHY timestamps arrive over MDIO, later than ptp4l's 10 ms default */ + if (!strcmp(ts, "hardware")) + fprintf(fp, "tx_timestamp_timeout 100\n"); + /* Profile — sets transportSpecific and all protocol-mandatory options */ - dot1as = emit_profile_globals(fp, profile, !strcmp(ts, "hardware")); + dot1as = emit_profile_globals(fp, profile); /* domainNumber */ v = lydx_get_cattr(default_ds, "domain-number"); @@ -200,8 +216,8 @@ static int write_instance_conf(struct lyd_node *inst, json_t *root) * boundary_clock_jbod silences ptp4l's startup PHC-mismatch check and * lets each port use its own PHC. It is a no-op when all ports share * the same PHC (single-chip board or software timestamping). - * On multi-chip hardware, phc2sys -a disciplines the secondary PHCs; - * see needs_phc2sys() / activate_phc2sys(). + * On multi-chip hardware, phc2sys -a disciplines the secondary PHCs + * of a BC; see needs_phc2sys() / activate_phc2sys(). */ if (tc || bc) fprintf(fp, "boundary_clock_jbod 1\n"); @@ -303,6 +319,9 @@ static int write_instance_conf(struct lyd_node *inst, json_t *root) if (v && strcmp(v, "0")) fprintf(fp, "delayAsymmetry %s\n", v); + if (dot1as) + emit_link_delay_thresh(fp, port_ds, !strcmp(ts, "hardware")); + if (lydx_is_enabled(port_ds, "time-transmitter-only")) fprintf(fp, "masterOnly 1\n"); @@ -315,11 +334,10 @@ static int write_instance_conf(struct lyd_node *inst, json_t *root) /* * True when a PTP instance needs a phc2sys companion to keep all its - * PHC devices in sync. Required for BC and TC instances on hardware - * with multiple switch chips, where each chip has its own /dev/ptpN. - * On single-chip hardware the function is a no-op: phc2sys -a finds - * no second PHC and exits immediately. OC has one port → one PHC, - * so no sync is ever needed. + * PHC devices in sync. Required for BC instances on hardware with + * multiple switch chips, where each chip has its own /dev/ptpN. OC + * has one port, so one PHC. TC is left out because ptp4l does not + * answer management requests in TC mode, so phc2sys -a never attaches. */ static bool needs_phc2sys(struct lyd_node *inst, json_t *root) { @@ -328,7 +346,7 @@ static bool needs_phc2sys(struct lyd_node *inst, json_t *root) if (!type) return false; - if (strcmp(type, "bc") && strcmp(type, "p2p-tc") && strcmp(type, "e2e-tc")) + if (strcmp(type, "bc")) return false; return !strcmp(instance_time_stamping(instance(inst), inst, root), "hardware"); @@ -337,8 +355,8 @@ static bool needs_phc2sys(struct lyd_node *inst, json_t *root) /* * Enable the phc2sys@ companion service for a multi-port HW instance. * phc2sys -a subscribes to ptp4l's UDS, discovers the active slave - * port via BMCA, and disciplines all other PHCs to match it. - * No config file is needed — the UDS path is passed on the command line. + * port via BMCA, and disciplines all other PHCs to match it. It reads + * the instance's ptp4l config for the UDS path, domain and transport. */ static void activate_phc2sys(uint16_t idx) { diff --git a/src/statd/python/yanger/ieee1588_ptp.py b/src/statd/python/yanger/ieee1588_ptp.py index eb2a0a0922..27577ad43d 100644 --- a/src/statd/python/yanger/ieee1588_ptp.py +++ b/src/statd/python/yanger/ieee1588_ptp.py @@ -176,10 +176,14 @@ def _time_source_identity(raw): # delay-mechanism and port-state mapping # --------------------------------------------------------------------------- +# pmc prints the numeric enum delay_mechanism; auto (0) runs E2E until a +# peer delay request switches the port to P2P _DELAY_MECH_MAP = { - "E2E": "e2e", - "P2P": "p2p", - "AUTO": "no-mechanism", + "0": "e2e", + "1": "e2e", + "2": "p2p", + "3": "common-p2p", + "254": "no-mechanism", } _PORT_STATE_MAP = { @@ -300,7 +304,10 @@ def _build_parent_ds(d): v = d.get("observedParentClockPhaseChangeRate") if v: try: - ds["observed-parent-clock-phase-change-rate"] = int(v) + rate = int(v, 16) + if rate & 0x80000000: + rate -= 1 << 32 + ds["observed-parent-clock-phase-change-rate"] = rate except ValueError: pass @@ -423,8 +430,8 @@ def _build_port_ds(d): pass v = d.get("delayMechanism") - if v: - ds["delay-mechanism"] = _DELAY_MECH_MAP.get(v, "e2e") + if v in _DELAY_MECH_MAP: + ds["delay-mechanism"] = _DELAY_MECH_MAP[v] v = d.get("logMinPdelayReqInterval") if v: diff --git a/test/case/firewall/address-set/test.py b/test/case/firewall/address-set/test.py index 70eb7bb88b..c9268d972d 100755 --- a/test/case/firewall/address-set/test.py +++ b/test/case/firewall/address-set/test.py @@ -148,7 +148,7 @@ def entries_settled(): } }) - ns.must_reach(TARGET_IP, timeout=30) + ns.must_reach(TARGET_IP, timeout=60) current = current_entries(target, "allowed") assert current.get(HOST_IP) is True, \ f"Dynamic entry {HOST_IP} lost in firewall reload" diff --git a/test/infamy/env.py b/test/infamy/env.py index fef65be2d5..d8ed8a583b 100644 --- a/test/infamy/env.py +++ b/test/infamy/env.py @@ -2,6 +2,7 @@ import os import pydot import shlex +import subprocess import sys import random import inspect @@ -64,6 +65,55 @@ def new_init(self, *args, **kw): return decorator +# Bridge port states, link events, per-port packet counts and learned +# unicast addresses of every attached DUT, logged when a test fails. +_DUT_STATE_SH = r""" +echo "uptime $(cut -d' ' -f1 /proc/uptime)" +bridge link show 2>/dev/null | awk '{ s = m = "-" + for (i = 1; i < NF; i++) { if ($i == "state") s = $(i + 1); if ($i == "master") m = $(i + 1) } + sub(":", "", $2); printf "%s:%s:%s ", $2, m, s } END { if (NR) print "" }' +for d in /sys/class/net/*; do + [ -e $d/device ] && [ "$(cat $d/operstate)" = up ] || continue + printf "%s rx=%s tx=%s txdrop=%s " ${d##*/} $(cat $d/statistics/rx_packets \ + $d/statistics/tx_packets $d/statistics/tx_dropped) +done; echo +dmesg | grep -E "Link is (Up|Down)" | tail -10 | sed 's/sparx5-switch [^ ]* //' +bridge fdb show 2>/dev/null | awk '$0 ~ / master / && $0 !~ /permanent|^33:33|^01:00:5e/ { + for (i = 1; i < NF; i++) if ($i == "dev") d[$1] = $(i + 1) + if ($0 ~ /offload/) hw[$1] = 1 } + END { for (m in d) printf "%s@%s%s ", m, d[m], (m in hw ? "" : "(sw)"); if (length(d)) print "" }' +""" +_attached = {} + + +def _dump_duts(): + if not _attached: + return + + def state(dev): + try: + if not isinstance(dev, ssh.Device): + dev = ssh.device_for(dev) + res = dev.runsh(_DUT_STATE_SH, timeout=30) + except subprocess.TimeoutExpired: + return "cannot read state: timed out" + except Exception as e: + return f"cannot read state: {type(e).__name__}" + if res.returncode == ssh.TRANSPORT_ERROR: + return "cannot read state: unreachable" + return res.stdout + + duts = list(_attached.items()) + out = util.parallel(*[lambda d=d: state(d) for _, d in duts]) + print("State of the DUTs at failure, bridge ports as port:bridge:state:") + for (name, _), text in zip(duts, out): + for line in text.strip().splitlines(): + print(f"{name}: {line}") + + +tap.on_failure(_dump_duts) + + class Env(object): def __init__(self, ltop=None, args=None, argv=sys.argv[1::], environ=os.environ, nodes_compatible=topology.compatible, @@ -158,7 +208,13 @@ def reset_one(node): def attach(self, node, port="mgmt", protocol=None, test_reset=True, username=None, password=None): """Attach to node on port using protocol.""" + dev = self._attach(node, port, protocol, test_reset, username, password) + if username in (None, "admin"): + phys = self.ltop.xlate(node) if self.ltop else node + _attached[f"{node} ({phys})"] = dev + return dev + def _attach(self, node, port, protocol, test_reset, username, password): name = node if self.ltop: mapping = self.ltop.mapping[node] @@ -203,7 +259,8 @@ def attach(self, node, port="mgmt", protocol=None, test_reset=True, username=Non return dev if protocol == "ssh": - return ssh.Device(name, ssh.Location(mgmtip, username, password)) + dev = ssh.Device(name, ssh.Location(mgmtip, username, password)) + return dev if protocol == "restconf": dev = restconf.Device(name, diff --git a/test/infamy/ptp.py b/test/infamy/ptp.py index 020b0bed7b..ce8c560974 100644 --- a/test/infamy/ptp.py +++ b/test/infamy/ptp.py @@ -4,18 +4,38 @@ All functions are None-safe and intended for use with until(): until(lambda: ptp.is_time_receiver(target), attempts=60) + +Port state changes are logged as they are seen, and if the test fails the +PTP state of every DUT queried here is dumped to the test log. """ +import subprocess -def _get_instance(target, idx=0): +from infamy import ssh, tap + +_targets = {} +_states = {} + + +def _instances(target): + _targets[target.name] = target data = target.get_data("/ieee1588-ptp-tt:ptp") or {} - instances = (data.get("ptp", {}) - .get("instances", {}) - .get("instance", [])) - for inst in instances: - if inst.get("instance-index") == idx: - return inst - return None + return (data.get("ptp", {}) + .get("instances", {}) + .get("instance", [])) + + +def _get_instance(target, idx=0): + return next((inst for inst in _instances(target) + if inst.get("instance-index") == idx), None) + + +def _scaled_ns(value): + """Scaled nanoseconds (int64 x 2^16, as a string) to nanoseconds.""" + try: + return int(value) // 65536 + except (TypeError, ValueError): + return None def port_state(target, port_idx=1, inst_idx=0): @@ -25,7 +45,13 @@ def port_state(target, port_idx=1, inst_idx=0): return None for port in inst.get("ports", {}).get("port", []): if port.get("port-index") == port_idx: - return port.get("port-ds", {}).get("port-state") + state = port.get("port-ds", {}).get("port-state") + key = (target.name, inst_idx, port_idx) + if _states.get(key) != state: + print(f"{target.name}: port {port_idx} " + f"({port.get('underlying-interface', '?')}) {state}") + _states[key] = state + return state return None @@ -47,11 +73,8 @@ def offset_ns(target, inst_idx=0): inst = _get_instance(target, inst_idx) if not inst: return None - raw = inst.get("current-ds", {}).get("offset-from-time-transmitter") - try: - return int(raw) // 65536 - except (TypeError, ValueError): - return None + return _scaled_ns(inst.get("current-ds", {}) + .get("offset-from-time-transmitter")) def steps_removed(target, inst_idx=0): @@ -108,15 +131,9 @@ def port_state_dbg(target, port_idx=1, inst_idx=0): until(lambda: is_time_transmitter(gm) or not print(port_state_dbg(gm)), attempts=60) """ - data = target.get_data("/ieee1588-ptp-tt:ptp") or {} - if not data: - return f"{target.name}: no PTP operational data (ptp4l not running?)" - - instances = (data.get("ptp", {}) - .get("instances", {}) - .get("instance", [])) + instances = _instances(target) if not instances: - return f"{target.name}: PTP data present but no instances" + return f"{target.name}: no PTP instances (ptp4l not running?)" parts = [] for inst in instances: @@ -129,6 +146,115 @@ def port_state_dbg(target, port_idx=1, inst_idx=0): return f"{target.name}: " + (", ".join(parts) if parts else "no ports") +def _ns(value): + ns = _scaled_ns(value) + return "?" if ns is None else ns + + +# Two samples, 2 s apart, of every ptp4l port's message counters and of the +# switch and kernel counters of the PTP ports. Prints the non-zero ones +# with their change between the samples. +_COUNTERS_SH = """ +snap() { + for c in $(sudo -n sh -c 'ls /etc/linuxptp/ptp4l-*.conf' 2>/dev/null); do + i=${c##*/} + sudo -n pmc -u -b 0 -f "$c" "GET PORT_STATS_NP" 2>/dev/null | + awk -v i="${i%.conf}" '/portIdentity/ {p = $2} + $1 ~ /^[rt]x_/ {print i, p, $1, $2}' + done + for n in @IFACES@; do + ethtool -S "$n" 2>/dev/null | + awk -v n="$n" 'NR > 1 {sub(":", "", $1); print n, "switch", $1, $2}' + for s in rx_packets rx_dropped rx_errors tx_packets tx_dropped tx_errors; do + echo "$n kernel $s $(cat /sys/class/net/$n/statistics/$s 2>/dev/null)" + done + done +} +a=$(mktemp); b=$(mktemp) +snap > "$a"; sleep 2; snap > "$b" +awk 'NR == FNR {v[$1 " " $2 " " $3] = $4; next} + $4 > 0 {printf "%s %s %s=%s(+%d)\\n", $1, $2, $3, $4, $4 - v[$1 " " $2 " " $3]}' "$a" "$b" +rm -f "$a" "$b" +""" + + +def _counters(target, ifaces): + """Print ptp4l, switch and kernel counters of target's PTP ports, over SSH.""" + dut = ssh.device_for(target) + script = _COUNTERS_SH.replace("@IFACES@", " ".join(sorted(ifaces))) + try: + res = dut.runsh(script, timeout=30) + except subprocess.TimeoutExpired: + print(f"{target.name}: cannot read counters: timed out") + return + if res.returncode: + print(f"{target.name}: cannot read counters: {res.stdout.strip()}") + return + lines = {} + for line in res.stdout.splitlines(): + fields = line.split(" ", 2) + if len(fields) == 3: + lines.setdefault(" ".join(fields[:2]), []).append(fields[2]) + for key, cnts in lines.items(): + print(f"{target.name}: {key}: {' '.join(cnts)}") + + +def dump(target): + """Print the PTP state of target: clocks, ports, and message counters. + + The gPTP counters tell apart a port that receives nothing, one whose + peer delay requests go unanswered, and one that ignores what it gets. + The switch and kernel counters tell whether frames left or reached + the port at all. + """ + instances = _instances(target) + if not instances: + print(f"{target.name}: no PTP instances (ptp4l not running?)") + return + + ifaces = set() + for inst in instances: + dds = inst.get("default-ds", {}) + cds = inst.get("current-ds", {}) + pds = inst.get("parent-ds", {}) + print(f"{target.name}: instance {inst.get('instance-index')} " + f"{dds.get('instance-type', '?')} " + f"clock {dds.get('clock-identity', '?')} " + f"gm {pds.get('grandmaster-identity', '?')} " + f"steps-removed {cds.get('steps-removed', '?')} " + f"offset {_ns(cds.get('offset-from-time-transmitter'))} ns " + f"mean-delay {_ns(cds.get('mean-delay'))} ns") + for port in inst.get("ports", {}).get("port", []): + ds = port.get("port-ds", {}) + if port.get("underlying-interface"): + ifaces.add(port["underlying-interface"]) + print(f"{target.name}: port {port.get('port-index')} " + f"({port.get('underlying-interface', '?')}) " + f"{ds.get('port-state', '?')} " + f"link-delay {_ns(ds.get('mean-link-delay'))} ns " + f"{ds.get('delay-mechanism', '?')}") + stats = port.get("ieee802-dot1as-gptp:port-statistics-ds") + if stats: + cnt = " ".join(f"{k.removesuffix('-count')}={v}" + for k, v in stats.items()) + print(f"{target.name}: {cnt}") + + print(f"{target.name}: counters, change over 2 s in parentheses:") + _counters(target, ifaces) + + +def dump_all(): + """Print the PTP state of every DUT the helpers have queried.""" + if not _targets: + return + print("PTP state of the DUTs at failure:") + for target in list(_targets.values()): + try: + dump(target) + except Exception as e: + print(f"{target.name}: cannot read PTP state: {e}") + + def default_threshold(env, logical_node, hops=1): """Return a convergence threshold suited to the node's timestamping capability. @@ -148,3 +274,6 @@ def default_threshold(env, logical_node, hops=1): for _, _, data in g.edges(phys, data=True) ) return 1_000 * hops if has_hwts else 100_000 + + +tap.on_failure(dump_all) diff --git a/test/infamy/ssh.py b/test/infamy/ssh.py index 0055e12615..3db52be135 100644 --- a/test/infamy/ssh.py +++ b/test/infamy/ssh.py @@ -69,6 +69,13 @@ def fetch_file(remote_user, remote_address, remote_file, local_file, key_file, c except OSError as e: print(f"Error removing fetched file {local_file}: {e}") +def device_for(target): + """SSH Device on the management address of a NETCONF/RESTCONF target""" + loc = target.location + return Device(target.name, Location(loc.host, loc.username, loc.password), + wait=False) + + class Device(object): def __init__(self, name: str, location: Location, wait: bool=True): self.name = name diff --git a/test/infamy/tap.py b/test/infamy/tap.py index 2ff55074db..65e8386882 100644 --- a/test/infamy/tap.py +++ b/test/infamy/tap.py @@ -36,6 +36,22 @@ import infamy.netns +_failure_hooks = [] + + +def on_failure(fn): + """Call fn() if the test fails, before cleanup, e.g. to log DUT state.""" + _failure_hooks.append(fn) + + +def _run_failure_hooks(): + for fn in _failure_hooks: + try: + fn() + except Exception as e: + print(f"Failure hook {fn.__qualname__} failed: {e}") + + class Test: def __init__(self, output=sys.stdout): self.out = output @@ -65,6 +81,8 @@ def __exit__(self, t, e, tb): self.out.write(f"# Exiting ({now})\n") self.out.flush() + if t not in (TestPass, TestSkip): + _run_failure_hooks() self.cleanup() if not e: diff --git a/utils/kernel-refresh.sh b/utils/kernel-refresh.sh index c7b2a269bf..d3a7d9fa1c 100755 --- a/utils/kernel-refresh.sh +++ b/utils/kernel-refresh.sh @@ -7,12 +7,14 @@ usage() Synchronize patches/linux/\$TAG with changes from a kernel GIT tree. Usage: - $0 -k kernel-dir -o old-version [-t kernel-tag] [-p patch-dir] [-d defconfig-dir] + $0 -k kernel-dir [-o old-version] [-t kernel-tag] [-p patch-dir] [-d defconfig-dir] Options: -h This help text -k kernel-dir Path to kernel tree - -o old-version Version to be replaced by new kernel version + -o old-version Version to be replaced by new kernel version. Only + when upgrading: removes patches/linux/\$old-version, + bumps the defconfigs and refreshes linux.hash -t kernel-tag Base tag from which to generate patches. Default: "v" + \$BR2_LINUX_KERNEL_VERSION -p patch-dir Path to kernel patches directory @@ -91,6 +93,12 @@ DEFCONFIG_DIR=$(readlink -f $DEFCONFIG_DIR) git ls-files --error-unmatch $PATCH_DIR 1>/dev/null 2>&1 && git -C $PATCH_DIR rm -f *.patch git -C $KERNEL_DIR format-patch --no-signoff --no-encode-email-headers --no-cover-letter --no-signature -o $PATCH_DIR $KERNEL_TAG..HEAD git -C $PATCH_DIR add *.patch + +# Same version: patches refreshed, nothing more to do +if [ -z "$OLD_VER" ] || [ "$OLD_VER" = "$NEW_VER" ]; then + exit 0 +fi + if [ -d ${PATCHES_BASE}/${OLD_VER} ]; then git rm -rf ${PATCHES_BASE}/${OLD_VER} fi