diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 32e838988f..bf2d069f8f 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -2,7 +2,7 @@ name: "\U0001F6E0️ Feature request" description: Suggest a new feature, idea or improvement of the OS labels: - - "feature" + - "enhancement" - "triage" body: 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 3f34691bb5..7f4377436f 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 3f34691bb5..7f4377436f 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 3a0635e4c5..97250ad2ee 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_R2_PRO=y BR2_PACKAGE_BANANAPI_BPI_R3=y @@ -119,6 +120,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 7458ddb53e..95946eb718 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -45,6 +45,10 @@ All notable changes to the project are documented in this file. YANG modules `infix-ospf` and `infix-rip`, installed only on devices with dynamic routing. The startup configuration is converted on upgrade; NETCONF and RESTCONF clients must use the new module names +- 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 - 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 - The LAN bridge in the factory config of the Banana Pi R3, R3 mini, R4, @@ -58,9 +62,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 - Wi-Fi 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` @@ -185,6 +189,9 @@ All notable changes to the project are documented in this file. layout is new, so existing x86_64 installations must be reinstalled from the new disk image, `infix-x86_64.disk` or `.qcow2`, they cannot be upgraded with a bundle +- 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 @@ -252,6 +259,90 @@ All notable changes to the project are documented in this file. interfaces. Without it, LAN clients could not reach the Internet - WebUI: RIP and OSPF could not be set up in the advanced configuration editor. Adding an area or setting a router ID was refused +- 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: OSPF neighbors never came up over a bridge. Hellos and other + link-local multicast, e.g. to 224.0.0.5 or ff02::5, passed between the + bridge ports but never reached the bridge itself +- 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 +- LAN969x: PTP could stop on every port until reboot. Each transmit + timestamp that arrived late used up one of 512 slots for good +- LAN969x: traffic between 10 Gbps ports was limited to about 940 Mbps + on the TSN and RED variants of the switch +- 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 +- 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 133de7c4d6..bca1791fa3 100644 --- a/doc/vpd.md +++ b/doc/vpd.md @@ -80,7 +80,8 @@ theirs from the base MAC as described in [Wi-Fi](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 18dc883065..093d830329 100644 --- a/package/Config.in +++ b/package/Config.in @@ -44,6 +44,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.59/0001-net-phy-marvell10g-Support-firmware-loading-on-88X33.patch b/patches/linux/6.18.59/0001-net-phy-marvell10g-Support-firmware-loading-on-88X33.patch index f8275f3b7e..4aa503cf3f 100644 --- a/patches/linux/6.18.59/0001-net-phy-marvell10g-Support-firmware-loading-on-88X33.patch +++ b/patches/linux/6.18.59/0001-net-phy-marvell10g-Support-firmware-loading-on-88X33.patch @@ -1,7 +1,7 @@ From be9e488785558be53772aebbed94ec31a7e4e016 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 19 Sep 2023 18:38:10 +0200 -Subject: [PATCH 001/108] net: phy: marvell10g: Support firmware loading on +Subject: [PATCH 001/133] 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.59/0002-net-phy-marvell10g-Fix-power-up-when-strapped-to-sta.patch b/patches/linux/6.18.59/0002-net-phy-marvell10g-Fix-power-up-when-strapped-to-sta.patch index 9d72329c22..2d4d1a4980 100644 --- a/patches/linux/6.18.59/0002-net-phy-marvell10g-Fix-power-up-when-strapped-to-sta.patch +++ b/patches/linux/6.18.59/0002-net-phy-marvell10g-Fix-power-up-when-strapped-to-sta.patch @@ -1,7 +1,7 @@ From ab97ae60afb006052e5fa064fc505027bd5e05e9 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 21 Nov 2023 20:15:24 +0100 -Subject: [PATCH 002/108] net: phy: marvell10g: Fix power-up when strapped to +Subject: [PATCH 002/133] 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.59/0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch b/patches/linux/6.18.59/0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch index fc4c42c61c..01006f155f 100644 --- a/patches/linux/6.18.59/0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch +++ b/patches/linux/6.18.59/0003-net-phy-marvell10g-Add-LED-support-for-88X3310.patch @@ -1,7 +1,7 @@ From 743bb6420fa7f0061d6980f4c944a1e377ab384c Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 15 Nov 2023 20:58:42 +0100 -Subject: [PATCH 003/108] net: phy: marvell10g: Add LED support for 88X3310 +Subject: [PATCH 003/133] 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.59/0004-net-phy-marvell10g-Support-LEDs-tied-to-a-single-med.patch b/patches/linux/6.18.59/0004-net-phy-marvell10g-Support-LEDs-tied-to-a-single-med.patch index 6718ca4cc5..e238351012 100644 --- a/patches/linux/6.18.59/0004-net-phy-marvell10g-Support-LEDs-tied-to-a-single-med.patch +++ b/patches/linux/6.18.59/0004-net-phy-marvell10g-Support-LEDs-tied-to-a-single-med.patch @@ -1,7 +1,7 @@ From 7ac5820cb66bb4660e48bd2482c2ecfec73472a7 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 12 Dec 2023 09:51:05 +0100 -Subject: [PATCH 004/108] net: phy: marvell10g: Support LEDs tied to a single +Subject: [PATCH 004/133] 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.59/0005-net-phy-Do-not-resume-PHY-when-attaching.patch b/patches/linux/6.18.59/0005-net-phy-Do-not-resume-PHY-when-attaching.patch index 7b9eb98d76..19b7254346 100644 --- a/patches/linux/6.18.59/0005-net-phy-Do-not-resume-PHY-when-attaching.patch +++ b/patches/linux/6.18.59/0005-net-phy-Do-not-resume-PHY-when-attaching.patch @@ -1,7 +1,7 @@ From 7e8ada27f783e13ceb7d241af6c66cda952005d8 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 27 Mar 2024 10:10:19 +0100 -Subject: [PATCH 005/108] net: phy: Do not resume PHY when attaching +Subject: [PATCH 005/133] 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.59/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch b/patches/linux/6.18.59/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch index cafb88171c..1a104aae03 100644 --- a/patches/linux/6.18.59/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch +++ b/patches/linux/6.18.59/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch @@ -1,7 +1,7 @@ From 172e50a493fbcee1d040f321dd8a5508de413755 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 4 Mar 2024 16:47:28 +0100 -Subject: [PATCH 006/108] net: bridge: avoid classifying unknown multicast as +Subject: [PATCH 006/133] 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.59/0007-net-bridge-Ignore-router-ports-when-forwarding-L2-mu.patch b/patches/linux/6.18.59/0007-net-bridge-Ignore-router-ports-when-forwarding-L2-mu.patch index 95b98899f4..1db1d49c29 100644 --- a/patches/linux/6.18.59/0007-net-bridge-Ignore-router-ports-when-forwarding-L2-mu.patch +++ b/patches/linux/6.18.59/0007-net-bridge-Ignore-router-ports-when-forwarding-L2-mu.patch @@ -1,7 +1,7 @@ From dceaee463f2db31705b4a65b33ce029dab57ec05 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 5 Mar 2024 06:44:41 +0100 -Subject: [PATCH 007/108] net: bridge: Ignore router ports when forwarding L2 +Subject: [PATCH 007/133] 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.59/0008-net-bridge-drop-delay-for-applying-strict-multicast-.patch b/patches/linux/6.18.59/0008-net-bridge-drop-delay-for-applying-strict-multicast-.patch index 6cee206bc5..339a6bd9ee 100644 --- a/patches/linux/6.18.59/0008-net-bridge-drop-delay-for-applying-strict-multicast-.patch +++ b/patches/linux/6.18.59/0008-net-bridge-drop-delay-for-applying-strict-multicast-.patch @@ -1,7 +1,7 @@ From b03081e706b0116e1cbca21fe51b956589a3c347 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Thu, 4 Apr 2024 16:36:30 +0200 -Subject: [PATCH 008/108] net: bridge: drop delay for applying strict multicast +Subject: [PATCH 008/133] 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.59/0009-net-bridge-Differentiate-MDB-additions-from-modifica.patch b/patches/linux/6.18.59/0009-net-bridge-Differentiate-MDB-additions-from-modifica.patch index ad11a3c4e4..94e3de39e8 100644 --- a/patches/linux/6.18.59/0009-net-bridge-Differentiate-MDB-additions-from-modifica.patch +++ b/patches/linux/6.18.59/0009-net-bridge-Differentiate-MDB-additions-from-modifica.patch @@ -1,7 +1,7 @@ From 424fd59deb93c52955dee66ebf58c3e6da005506 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 16 May 2024 14:51:54 +0200 -Subject: [PATCH 009/108] net: bridge: Differentiate MDB additions from +Subject: [PATCH 009/133] 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.59/0010-nvmem-layouts-onie-tlv-Let-device-probe-even-when-TL.patch b/patches/linux/6.18.59/0010-nvmem-layouts-onie-tlv-Let-device-probe-even-when-TL.patch index 9b12e3375f..264f4a14ce 100644 --- a/patches/linux/6.18.59/0010-nvmem-layouts-onie-tlv-Let-device-probe-even-when-TL.patch +++ b/patches/linux/6.18.59/0010-nvmem-layouts-onie-tlv-Let-device-probe-even-when-TL.patch @@ -1,7 +1,7 @@ From c4ff25a083b8773e089315ef60b849c1861094b7 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Fri, 24 Nov 2023 23:29:55 +0100 -Subject: [PATCH 010/108] nvmem: layouts: onie-tlv: Let device probe even when +Subject: [PATCH 010/133] 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.59/0011-net-usb-r8152-add-r8153b-support-for-link-activity-L.patch b/patches/linux/6.18.59/0011-net-usb-r8152-add-r8153b-support-for-link-activity-L.patch index 2879ec4ae1..1aa14903ae 100644 --- a/patches/linux/6.18.59/0011-net-usb-r8152-add-r8153b-support-for-link-activity-L.patch +++ b/patches/linux/6.18.59/0011-net-usb-r8152-add-r8153b-support-for-link-activity-L.patch @@ -1,7 +1,7 @@ From c516cff652aea313ef63b7b1fdef074488833b46 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 11 Aug 2024 11:27:35 +0200 -Subject: [PATCH 011/108] net: usb: r8152: add r8153b support for link/activity +Subject: [PATCH 011/133] 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.59/0012-arm64-dts-mediatek-mt7986a-rename-BPi-R3-ports-to-ma.patch b/patches/linux/6.18.59/0012-arm64-dts-mediatek-mt7986a-rename-BPi-R3-ports-to-ma.patch index bdf5d4fadf..4581a0667f 100644 --- a/patches/linux/6.18.59/0012-arm64-dts-mediatek-mt7986a-rename-BPi-R3-ports-to-ma.patch +++ b/patches/linux/6.18.59/0012-arm64-dts-mediatek-mt7986a-rename-BPi-R3-ports-to-ma.patch @@ -1,7 +1,7 @@ From 1f17d0fed9deb5ee18da30170c834b5c57d0ef58 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 10 Aug 2025 18:52:54 +0200 -Subject: [PATCH 012/108] arm64: dts: mediatek: mt7986a: rename BPi R3 ports to +Subject: [PATCH 012/133] 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.59/0013-drm-panel-simple-Add-a-timing-for-the-Raspberry-Pi-7.patch b/patches/linux/6.18.59/0013-drm-panel-simple-Add-a-timing-for-the-Raspberry-Pi-7.patch index 8742e36f69..5e0961494d 100644 --- a/patches/linux/6.18.59/0013-drm-panel-simple-Add-a-timing-for-the-Raspberry-Pi-7.patch +++ b/patches/linux/6.18.59/0013-drm-panel-simple-Add-a-timing-for-the-Raspberry-Pi-7.patch @@ -1,7 +1,7 @@ From b3b4640353555bb44b1bb98726dea33b17164ef4 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 20 Aug 2025 21:38:24 +0200 -Subject: [PATCH 013/108] drm/panel-simple: Add a timing for the Raspberry Pi +Subject: [PATCH 013/133] 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.59/0014-input-touchscreen-edt-ft5x06-Add-polled-mode.patch b/patches/linux/6.18.59/0014-input-touchscreen-edt-ft5x06-Add-polled-mode.patch index 9a08a4f894..bcc0527d57 100644 --- a/patches/linux/6.18.59/0014-input-touchscreen-edt-ft5x06-Add-polled-mode.patch +++ b/patches/linux/6.18.59/0014-input-touchscreen-edt-ft5x06-Add-polled-mode.patch @@ -1,7 +1,7 @@ From 579480ad13f26e52470d97013b28b1d6d0bd25ad Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Thu, 21 Aug 2025 11:20:23 +0200 -Subject: [PATCH 014/108] input:touchscreen:edt-ft5x06: Add polled mode +Subject: [PATCH 014/133] 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.59/0015-FIX-net-dsa-mv88e6xxx-Fix-timeout-on-waiting-for-PPU.patch b/patches/linux/6.18.59/0015-FIX-net-dsa-mv88e6xxx-Fix-timeout-on-waiting-for-PPU.patch index 43d7f33833..a20f53017f 100644 --- a/patches/linux/6.18.59/0015-FIX-net-dsa-mv88e6xxx-Fix-timeout-on-waiting-for-PPU.patch +++ b/patches/linux/6.18.59/0015-FIX-net-dsa-mv88e6xxx-Fix-timeout-on-waiting-for-PPU.patch @@ -1,7 +1,7 @@ From 3c70299a0c6ea96825d6f9235064653793e0aa81 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 12 Mar 2024 10:27:24 +0100 -Subject: [PATCH 015/108] [FIX] net: dsa: mv88e6xxx: Fix timeout on waiting for +Subject: [PATCH 015/133] [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.59/0016-net-dsa-mv88e6xxx-Improve-indirect-register-access-p.patch b/patches/linux/6.18.59/0016-net-dsa-mv88e6xxx-Improve-indirect-register-access-p.patch index 4eb1a2e734..2d64dbd8c6 100644 --- a/patches/linux/6.18.59/0016-net-dsa-mv88e6xxx-Improve-indirect-register-access-p.patch +++ b/patches/linux/6.18.59/0016-net-dsa-mv88e6xxx-Improve-indirect-register-access-p.patch @@ -1,7 +1,7 @@ From c404b9691e3d5843ca2d71a1a74143f54eaff06f Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 27 Mar 2024 15:52:43 +0100 -Subject: [PATCH 016/108] net: dsa: mv88e6xxx: Improve indirect register access +Subject: [PATCH 016/133] 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.59/0017-net-dsa-mv88e6xxx-Honor-ports-being-managed-via-in-b.patch b/patches/linux/6.18.59/0017-net-dsa-mv88e6xxx-Honor-ports-being-managed-via-in-b.patch index 4c9c72681f..180edae9f2 100644 --- a/patches/linux/6.18.59/0017-net-dsa-mv88e6xxx-Honor-ports-being-managed-via-in-b.patch +++ b/patches/linux/6.18.59/0017-net-dsa-mv88e6xxx-Honor-ports-being-managed-via-in-b.patch @@ -1,7 +1,7 @@ From 0e4e2f9a0a43aefe61bf2e10196cc9216997dee0 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Mon, 22 Apr 2024 23:18:01 +0200 -Subject: [PATCH 017/108] net: dsa: mv88e6xxx: Honor ports being managed via +Subject: [PATCH 017/133] 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.59/0018-net-dsa-mv88e6xxx-Limit-rsvd2cpu-policy-to-user-port.patch b/patches/linux/6.18.59/0018-net-dsa-mv88e6xxx-Limit-rsvd2cpu-policy-to-user-port.patch index e243c7157a..ced319a06a 100644 --- a/patches/linux/6.18.59/0018-net-dsa-mv88e6xxx-Limit-rsvd2cpu-policy-to-user-port.patch +++ b/patches/linux/6.18.59/0018-net-dsa-mv88e6xxx-Limit-rsvd2cpu-policy-to-user-port.patch @@ -1,7 +1,7 @@ From 388dc8205c88f43b5add32ec6872e8ab928e6624 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 24 Apr 2024 22:41:04 +0200 -Subject: [PATCH 018/108] net: dsa: mv88e6xxx: Limit rsvd2cpu policy to user +Subject: [PATCH 018/133] 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.59/0019-net-dsa-tag_dsa-Use-tag-priority-as-initial-skb-prio.patch b/patches/linux/6.18.59/0019-net-dsa-tag_dsa-Use-tag-priority-as-initial-skb-prio.patch index 4b0edbff3d..87fe469a04 100644 --- a/patches/linux/6.18.59/0019-net-dsa-tag_dsa-Use-tag-priority-as-initial-skb-prio.patch +++ b/patches/linux/6.18.59/0019-net-dsa-tag_dsa-Use-tag-priority-as-initial-skb-prio.patch @@ -1,7 +1,7 @@ From 44e4c6768bc720d3b570d8ce0edfa82958781eb0 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 28 May 2024 10:38:42 +0200 -Subject: [PATCH 019/108] net: dsa: tag_dsa: Use tag priority as initial +Subject: [PATCH 019/133] 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.59/0020-net-dsa-Support-MDB-memberships-whose-L2-addresses-o.patch b/patches/linux/6.18.59/0020-net-dsa-Support-MDB-memberships-whose-L2-addresses-o.patch index 7518b0d2c6..f1919718a9 100644 --- a/patches/linux/6.18.59/0020-net-dsa-Support-MDB-memberships-whose-L2-addresses-o.patch +++ b/patches/linux/6.18.59/0020-net-dsa-Support-MDB-memberships-whose-L2-addresses-o.patch @@ -1,7 +1,7 @@ From 0275b3a1e8d4a65b7fd144f80fc1773f21712a2d Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 16 Jan 2024 16:00:55 +0100 -Subject: [PATCH 020/108] net: dsa: Support MDB memberships whose L2 addresses +Subject: [PATCH 020/133] 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.59/0021-net-dsa-Support-EtherType-based-priority-overrides.patch b/patches/linux/6.18.59/0021-net-dsa-Support-EtherType-based-priority-overrides.patch index 9091aa83f6..0d31ea6646 100644 --- a/patches/linux/6.18.59/0021-net-dsa-Support-EtherType-based-priority-overrides.patch +++ b/patches/linux/6.18.59/0021-net-dsa-Support-EtherType-based-priority-overrides.patch @@ -1,7 +1,7 @@ From 97dfed9ed5d4f2cf29d8541192443b184e85377a Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 21 Mar 2024 19:12:15 +0100 -Subject: [PATCH 021/108] net: dsa: Support EtherType based priority overrides +Subject: [PATCH 021/133] net: dsa: Support EtherType based priority overrides --- include/net/dsa.h | 4 ++++ diff --git a/patches/linux/6.18.59/0022-net-dsa-mv88e6xxx-Support-EtherType-based-priority-o.patch b/patches/linux/6.18.59/0022-net-dsa-mv88e6xxx-Support-EtherType-based-priority-o.patch index f529ce2b68..eb7e710f7d 100644 --- a/patches/linux/6.18.59/0022-net-dsa-mv88e6xxx-Support-EtherType-based-priority-o.patch +++ b/patches/linux/6.18.59/0022-net-dsa-mv88e6xxx-Support-EtherType-based-priority-o.patch @@ -1,7 +1,7 @@ From de8ee7a44f9b4fb42564e794d0657c974e1f797f Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Fri, 22 Mar 2024 16:15:43 +0100 -Subject: [PATCH 022/108] net: dsa: mv88e6xxx: Support EtherType based priority +Subject: [PATCH 022/133] net: dsa: mv88e6xxx: Support EtherType based priority overrides --- diff --git a/patches/linux/6.18.59/0023-net-dsa-mv88e6xxx-Add-mqprio-qdisc-support.patch b/patches/linux/6.18.59/0023-net-dsa-mv88e6xxx-Add-mqprio-qdisc-support.patch index ff3c40196c..400d7ecca5 100644 --- a/patches/linux/6.18.59/0023-net-dsa-mv88e6xxx-Add-mqprio-qdisc-support.patch +++ b/patches/linux/6.18.59/0023-net-dsa-mv88e6xxx-Add-mqprio-qdisc-support.patch @@ -1,7 +1,7 @@ From f84774cf099db4e6cc6749398c55c971067bc0d4 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 28 May 2024 11:04:22 +0200 -Subject: [PATCH 023/108] net: dsa: mv88e6xxx: Add mqprio qdisc support +Subject: [PATCH 023/133] 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.59/0024-net-dsa-mv88e6xxx-Use-VLAN-prio-over-IP-when-both-ar.patch b/patches/linux/6.18.59/0024-net-dsa-mv88e6xxx-Use-VLAN-prio-over-IP-when-both-ar.patch index 4e720465b5..84163eba27 100644 --- a/patches/linux/6.18.59/0024-net-dsa-mv88e6xxx-Use-VLAN-prio-over-IP-when-both-ar.patch +++ b/patches/linux/6.18.59/0024-net-dsa-mv88e6xxx-Use-VLAN-prio-over-IP-when-both-ar.patch @@ -1,7 +1,7 @@ From 032ea099e1fb440b6bb0469fd1be71617a695f92 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 29 May 2024 13:20:41 +0200 -Subject: [PATCH 024/108] net: dsa: mv88e6xxx: Use VLAN prio over IP when both +Subject: [PATCH 024/133] 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.59/0025-FIX-net-dsa-mv88e6xxx-Trap-locally-terminated-VLANs.patch b/patches/linux/6.18.59/0025-FIX-net-dsa-mv88e6xxx-Trap-locally-terminated-VLANs.patch index 5670d7af4e..64c69e2df7 100644 --- a/patches/linux/6.18.59/0025-FIX-net-dsa-mv88e6xxx-Trap-locally-terminated-VLANs.patch +++ b/patches/linux/6.18.59/0025-FIX-net-dsa-mv88e6xxx-Trap-locally-terminated-VLANs.patch @@ -1,7 +1,7 @@ From fc5944a84effc28c94796730f1ffb0d9274192bc Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 26 Nov 2024 19:45:59 +0100 -Subject: [PATCH 025/108] [FIX] net: dsa: mv88e6xxx: Trap locally terminated +Subject: [PATCH 025/133] [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.59/0026-net-dsa-mv88e6xxx-collapse-disabled-state-into-block.patch b/patches/linux/6.18.59/0026-net-dsa-mv88e6xxx-collapse-disabled-state-into-block.patch index a4b826436a..a8c7e31c51 100644 --- a/patches/linux/6.18.59/0026-net-dsa-mv88e6xxx-collapse-disabled-state-into-block.patch +++ b/patches/linux/6.18.59/0026-net-dsa-mv88e6xxx-collapse-disabled-state-into-block.patch @@ -1,7 +1,7 @@ From 9c7d43d1750b57ebb19a2f50d9ea1c300e0b0cb4 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Thu, 16 Jan 2025 12:35:12 +0100 -Subject: [PATCH 026/108] net: dsa: mv88e6xxx: collapse disabled state into +Subject: [PATCH 026/133] 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.59/0027-net-dsa-mv88e6xxx-Only-activate-LAG-offloading-when-.patch b/patches/linux/6.18.59/0027-net-dsa-mv88e6xxx-Only-activate-LAG-offloading-when-.patch index af999edf79..3203230331 100644 --- a/patches/linux/6.18.59/0027-net-dsa-mv88e6xxx-Only-activate-LAG-offloading-when-.patch +++ b/patches/linux/6.18.59/0027-net-dsa-mv88e6xxx-Only-activate-LAG-offloading-when-.patch @@ -1,7 +1,7 @@ From 33890f49f7ec51198dd07ed8fe49592aaf6e7f99 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 12 Feb 2025 22:03:14 +0100 -Subject: [PATCH 027/108] net: dsa: mv88e6xxx: Only activate LAG offloading +Subject: [PATCH 027/133] 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.59/0028-net-dsa-mv88e6xxx-Add-LED-support-for-6393X.patch b/patches/linux/6.18.59/0028-net-dsa-mv88e6xxx-Add-LED-support-for-6393X.patch index 7e7ab9e0fb..570901569c 100644 --- a/patches/linux/6.18.59/0028-net-dsa-mv88e6xxx-Add-LED-support-for-6393X.patch +++ b/patches/linux/6.18.59/0028-net-dsa-mv88e6xxx-Add-LED-support-for-6393X.patch @@ -1,7 +1,7 @@ From 071d46647e5adffed2180cc4632b6c30f2b69a54 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 14 Jan 2026 18:22:41 +0100 -Subject: [PATCH 028/108] net: dsa: mv88e6xxx: Add LED support for 6393X +Subject: [PATCH 028/133] net: dsa: mv88e6xxx: Add LED support for 6393X Original commit: commit 462277b926140ee2d231317e92afb6cabf640268 diff --git a/patches/linux/6.18.59/0029-wifi-brcmfmac-check-connection-state-before-querying.patch b/patches/linux/6.18.59/0029-wifi-brcmfmac-check-connection-state-before-querying.patch index 7ba0fd4971..16b4a36dbb 100644 --- a/patches/linux/6.18.59/0029-wifi-brcmfmac-check-connection-state-before-querying.patch +++ b/patches/linux/6.18.59/0029-wifi-brcmfmac-check-connection-state-before-querying.patch @@ -1,7 +1,7 @@ From 83244a35d2ef03f6b3243720931928d5ac7510c9 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Mon, 19 Jan 2026 13:06:53 +0100 -Subject: [PATCH 029/108] wifi: brcmfmac: check connection state before +Subject: [PATCH 029/133] 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.59/0030-wifi-brcmfmac-suppress-log-spam-for-regulatory-restr.patch b/patches/linux/6.18.59/0030-wifi-brcmfmac-suppress-log-spam-for-regulatory-restr.patch index 94e990952a..ed117af7cd 100644 --- a/patches/linux/6.18.59/0030-wifi-brcmfmac-suppress-log-spam-for-regulatory-restr.patch +++ b/patches/linux/6.18.59/0030-wifi-brcmfmac-suppress-log-spam-for-regulatory-restr.patch @@ -1,7 +1,7 @@ From 301477c66e349862fe21c78515d8575373fb80db Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 20 Jan 2026 20:12:10 +0100 -Subject: [PATCH 030/108] wifi: brcmfmac: suppress log spam for +Subject: [PATCH 030/133] 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.59/0031-wifi-brcmfmac-reduce-log-noise-during-AP-to-station-.patch b/patches/linux/6.18.59/0031-wifi-brcmfmac-reduce-log-noise-during-AP-to-station-.patch index 3f902a6527..7c8598dfac 100644 --- a/patches/linux/6.18.59/0031-wifi-brcmfmac-reduce-log-noise-during-AP-to-station-.patch +++ b/patches/linux/6.18.59/0031-wifi-brcmfmac-reduce-log-noise-during-AP-to-station-.patch @@ -1,7 +1,7 @@ From 4ec0423abf5fa4619c8fb952d9c5f0812eaac866 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 20 Jan 2026 20:18:45 +0100 -Subject: [PATCH 031/108] wifi: brcmfmac: reduce log noise during AP to station +Subject: [PATCH 031/133] 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.59/0032-net-phy-air_en8811h-add-OF-device-table-for-auto-loa.patch b/patches/linux/6.18.59/0032-net-phy-air_en8811h-add-OF-device-table-for-auto-loa.patch index f499301898..a10ee1fa39 100644 --- a/patches/linux/6.18.59/0032-net-phy-air_en8811h-add-OF-device-table-for-auto-loa.patch +++ b/patches/linux/6.18.59/0032-net-phy-air_en8811h-add-OF-device-table-for-auto-loa.patch @@ -1,7 +1,7 @@ From bfbb2230ce1ae8ca23bff07f07846df91a81f09c Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 17 Feb 2026 21:59:59 +0100 -Subject: [PATCH 032/108] net: phy: air_en8811h: add OF device table for +Subject: [PATCH 032/133] 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.59/0033-drm-vc4-dsi-enable-video-and-then-retry-failed-trans.patch b/patches/linux/6.18.59/0033-drm-vc4-dsi-enable-video-and-then-retry-failed-trans.patch index d2a4f6431b..0a6badcb0b 100644 --- a/patches/linux/6.18.59/0033-drm-vc4-dsi-enable-video-and-then-retry-failed-trans.patch +++ b/patches/linux/6.18.59/0033-drm-vc4-dsi-enable-video-and-then-retry-failed-trans.patch @@ -1,7 +1,7 @@ From 98feb11995e230a3a4aec3a00719289c3833cf91 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 20 Sep 2024 12:05:18 +0100 -Subject: [PATCH 033/108] drm: vc4: dsi: enable video and then retry failed +Subject: [PATCH 033/133] 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.59/0034-drm-vc4-dsi-Clocks-should-be-running-before-reset.patch b/patches/linux/6.18.59/0034-drm-vc4-dsi-Clocks-should-be-running-before-reset.patch index b6a5796946..78238a8159 100644 --- a/patches/linux/6.18.59/0034-drm-vc4-dsi-Clocks-should-be-running-before-reset.patch +++ b/patches/linux/6.18.59/0034-drm-vc4-dsi-Clocks-should-be-running-before-reset.patch @@ -1,7 +1,7 @@ From d2ef33196f976419f1dcd82edf707ea8686d0f00 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 8 Jun 2022 17:23:47 +0100 -Subject: [PATCH 034/108] drm: vc4: dsi: Clocks should be running before reset +Subject: [PATCH 034/133] 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.59/0035-drm-vc4-Ensure-DSI-is-enabled-for-FIFO-resets.patch b/patches/linux/6.18.59/0035-drm-vc4-Ensure-DSI-is-enabled-for-FIFO-resets.patch index 1c83d73c0c..8376ab636f 100644 --- a/patches/linux/6.18.59/0035-drm-vc4-Ensure-DSI-is-enabled-for-FIFO-resets.patch +++ b/patches/linux/6.18.59/0035-drm-vc4-Ensure-DSI-is-enabled-for-FIFO-resets.patch @@ -1,7 +1,7 @@ From b0f0106675b6b3a34e286f372d6b1cf73e995381 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 5 Apr 2024 17:51:55 +0100 -Subject: [PATCH 035/108] drm/vc4: Ensure DSI is enabled for FIFO resets +Subject: [PATCH 035/133] 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.59/0036-drm-vc4-Reset-DSI-AFE-on-disable.patch b/patches/linux/6.18.59/0036-drm-vc4-Reset-DSI-AFE-on-disable.patch index 4a6b9aa180..ab017b386b 100644 --- a/patches/linux/6.18.59/0036-drm-vc4-Reset-DSI-AFE-on-disable.patch +++ b/patches/linux/6.18.59/0036-drm-vc4-Reset-DSI-AFE-on-disable.patch @@ -1,7 +1,7 @@ From 1db287fcd82667736d111eac9ae596e8ea799dde Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 26 May 2022 18:56:19 +0100 -Subject: [PATCH 036/108] drm: vc4: Reset DSI AFE on disable +Subject: [PATCH 036/133] 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.59/0037-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch b/patches/linux/6.18.59/0037-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch index 971137d59c..bb360de0ad 100644 --- a/patches/linux/6.18.59/0037-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch +++ b/patches/linux/6.18.59/0037-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch @@ -1,7 +1,7 @@ From 5886e450164e5e91a88ca5becbe89ce8cab5ead1 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 20 Nov 2024 13:58:08 +0000 -Subject: [PATCH 037/108] drm: vc4: dsi: Handle the different command FIFO +Subject: [PATCH 037/133] 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.59/0038-drm-bridge-tc358762-Program-the-DPI-mode-into-the-ch.patch b/patches/linux/6.18.59/0038-drm-bridge-tc358762-Program-the-DPI-mode-into-the-ch.patch index 8a71d3f08f..d6ceb76873 100644 --- a/patches/linux/6.18.59/0038-drm-bridge-tc358762-Program-the-DPI-mode-into-the-ch.patch +++ b/patches/linux/6.18.59/0038-drm-bridge-tc358762-Program-the-DPI-mode-into-the-ch.patch @@ -1,7 +1,7 @@ From 72c5bf7f04e5ad4b27720aec2e81f48e23e026ca Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 9 Jan 2024 17:37:00 +0000 -Subject: [PATCH 038/108] drm/bridge: tc358762: Program the DPI mode into the +Subject: [PATCH 038/133] 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.59/0039-drm-bridge-tc358762-revert-move-ops-to-enable.patch b/patches/linux/6.18.59/0039-drm-bridge-tc358762-revert-move-ops-to-enable.patch index 9cad1438a8..cf404bab3e 100644 --- a/patches/linux/6.18.59/0039-drm-bridge-tc358762-revert-move-ops-to-enable.patch +++ b/patches/linux/6.18.59/0039-drm-bridge-tc358762-revert-move-ops-to-enable.patch @@ -1,7 +1,7 @@ From 9c16c2617eb0ff3048494c86818e9f3318725fbf Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 9 Jan 2024 18:44:49 +0000 -Subject: [PATCH 039/108] drm/bridge: tc358762: revert move ops to enable +Subject: [PATCH 039/133] 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.59/0040-drm-bridge-tc358762-Set-pre_enabled-on-pre_enable-to.patch b/patches/linux/6.18.59/0040-drm-bridge-tc358762-Set-pre_enabled-on-pre_enable-to.patch index 1e93b70bc1..7b0d2faf6c 100644 --- a/patches/linux/6.18.59/0040-drm-bridge-tc358762-Set-pre_enabled-on-pre_enable-to.patch +++ b/patches/linux/6.18.59/0040-drm-bridge-tc358762-Set-pre_enabled-on-pre_enable-to.patch @@ -1,7 +1,7 @@ From ce118985a1304a964cdecfaa47a261b753177f12 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Sat, 4 Apr 2026 18:04:19 +0200 -Subject: [PATCH 040/108] drm/bridge: tc358762: Set pre_enabled on pre_enable +Subject: [PATCH 040/133] 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.59/0041-net-pcs-add-standalone-PCS-registration-infrastructu.patch b/patches/linux/6.18.59/0041-net-pcs-add-standalone-PCS-registration-infrastructu.patch index 4d9805fdca..0ee9a643cb 100644 --- a/patches/linux/6.18.59/0041-net-pcs-add-standalone-PCS-registration-infrastructu.patch +++ b/patches/linux/6.18.59/0041-net-pcs-add-standalone-PCS-registration-infrastructu.patch @@ -1,7 +1,7 @@ From e3c435d2bee04679d666778bd9f0ea7924f07a43 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 5 Apr 2026 11:33:00 +0200 -Subject: [PATCH 041/108] net/pcs: add standalone PCS registration +Subject: [PATCH 041/133] 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.59/0042-net-pcs-add-MediaTek-MT7988-USXGMII-PCS-driver.patch b/patches/linux/6.18.59/0042-net-pcs-add-MediaTek-MT7988-USXGMII-PCS-driver.patch index edf3d4b1aa..8f7fc53a0b 100644 --- a/patches/linux/6.18.59/0042-net-pcs-add-MediaTek-MT7988-USXGMII-PCS-driver.patch +++ b/patches/linux/6.18.59/0042-net-pcs-add-MediaTek-MT7988-USXGMII-PCS-driver.patch @@ -1,7 +1,7 @@ From 9556d118e86e317d8d582929f3322fa00f25b368 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 6 Apr 2026 14:14:23 +0200 -Subject: [PATCH 042/108] net/pcs: add MediaTek MT7988 USXGMII PCS driver +Subject: [PATCH 042/133] 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.59/0043-net-ethernet-mediatek-add-USXGMII-support-for-MT7988.patch b/patches/linux/6.18.59/0043-net-ethernet-mediatek-add-USXGMII-support-for-MT7988.patch index a80a31108b..76670a3638 100644 --- a/patches/linux/6.18.59/0043-net-ethernet-mediatek-add-USXGMII-support-for-MT7988.patch +++ b/patches/linux/6.18.59/0043-net-ethernet-mediatek-add-USXGMII-support-for-MT7988.patch @@ -1,7 +1,7 @@ From 768e7fd3e9c8de7564446791ff3b90fdf2e9f73f Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 6 Apr 2026 14:15:43 +0200 -Subject: [PATCH 043/108] net: ethernet: mediatek: add USXGMII support for +Subject: [PATCH 043/133] 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.59/0044-arm64-dts-mediatek-mt7988a-add-USXGMII-PCS-nodes.patch b/patches/linux/6.18.59/0044-arm64-dts-mediatek-mt7988a-add-USXGMII-PCS-nodes.patch index 5cc87babdb..892e62c2b5 100644 --- a/patches/linux/6.18.59/0044-arm64-dts-mediatek-mt7988a-add-USXGMII-PCS-nodes.patch +++ b/patches/linux/6.18.59/0044-arm64-dts-mediatek-mt7988a-add-USXGMII-PCS-nodes.patch @@ -1,7 +1,7 @@ From 3b91b464868461e9e655da57173a957df8bd389d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 6 Apr 2026 14:15:56 +0200 -Subject: [PATCH 044/108] arm64: dts: mediatek: mt7988a: add USXGMII PCS nodes +Subject: [PATCH 044/133] 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.59/0045-arm64-dts-mediatek-bananapi-bpi-r4-enable-SFP-ports-.patch b/patches/linux/6.18.59/0045-arm64-dts-mediatek-bananapi-bpi-r4-enable-SFP-ports-.patch index 25abf85076..24b8bb7a14 100644 --- a/patches/linux/6.18.59/0045-arm64-dts-mediatek-bananapi-bpi-r4-enable-SFP-ports-.patch +++ b/patches/linux/6.18.59/0045-arm64-dts-mediatek-bananapi-bpi-r4-enable-SFP-ports-.patch @@ -1,7 +1,7 @@ From 2ab5daf523dd5884d255ec095934ab63cf06aa38 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 6 Apr 2026 14:16:11 +0200 -Subject: [PATCH 045/108] arm64: dts: mediatek: bananapi-bpi-r4: enable SFP+ +Subject: [PATCH 045/133] 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.59/0046-net-phy-sfp-add-OEM-SFP-10G-T-I-quirk.patch b/patches/linux/6.18.59/0046-net-phy-sfp-add-OEM-SFP-10G-T-I-quirk.patch index 2f4e42c7f2..1dec851273 100644 --- a/patches/linux/6.18.59/0046-net-phy-sfp-add-OEM-SFP-10G-T-I-quirk.patch +++ b/patches/linux/6.18.59/0046-net-phy-sfp-add-OEM-SFP-10G-T-I-quirk.patch @@ -1,7 +1,7 @@ From 1020e4e04d23c8b9a337a40d58b55f6230304214 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 7 Apr 2026 07:34:52 +0200 -Subject: [PATCH 046/108] net: phy: sfp: add OEM SFP-10G-T-I quirk +Subject: [PATCH 046/133] 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.59/0047-net-dsa-mv88e6xxx-Trap-PTP-frames-on-timestamping-po.patch b/patches/linux/6.18.59/0047-net-dsa-mv88e6xxx-Trap-PTP-frames-on-timestamping-po.patch index b802c9badd..114eee5de7 100644 --- a/patches/linux/6.18.59/0047-net-dsa-mv88e6xxx-Trap-PTP-frames-on-timestamping-po.patch +++ b/patches/linux/6.18.59/0047-net-dsa-mv88e6xxx-Trap-PTP-frames-on-timestamping-po.patch @@ -1,7 +1,7 @@ From 39a648cd8d03bd2c79506e5ffe071fadeffb75b8 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Fri, 17 Apr 2026 09:13:04 +0000 -Subject: [PATCH 047/108] net: dsa: mv88e6xxx: Trap PTP frames on timestamping +Subject: [PATCH 047/133] 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.59/0048-wifi-mt76-mt7615-add-MODULE_DEVICE_TABLE-for-mt7622-.patch b/patches/linux/6.18.59/0048-wifi-mt76-mt7615-add-MODULE_DEVICE_TABLE-for-mt7622-.patch index 165ec97f79..0ba31b64ee 100644 --- a/patches/linux/6.18.59/0048-wifi-mt76-mt7615-add-MODULE_DEVICE_TABLE-for-mt7622-.patch +++ b/patches/linux/6.18.59/0048-wifi-mt76-mt7615-add-MODULE_DEVICE_TABLE-for-mt7622-.patch @@ -1,7 +1,7 @@ From 20a13d3152f6716b05aa724677d0780a14c173da Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 28 Apr 2026 15:30:01 +0200 -Subject: [PATCH 048/108] wifi: mt76: mt7615: add MODULE_DEVICE_TABLE for +Subject: [PATCH 048/133] 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.59/0049-PCI-mediatek-gen3-Fix-PERST-control-timing-during-sy.patch b/patches/linux/6.18.59/0049-PCI-mediatek-gen3-Fix-PERST-control-timing-during-sy.patch index 250c7d7ec6..ff5aae40fc 100644 --- a/patches/linux/6.18.59/0049-PCI-mediatek-gen3-Fix-PERST-control-timing-during-sy.patch +++ b/patches/linux/6.18.59/0049-PCI-mediatek-gen3-Fix-PERST-control-timing-during-sy.patch @@ -1,7 +1,7 @@ From 642882dd9a78dd820ae29ea41ffd889f2d07bdc8 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 22 Apr 2026 10:24:43 +0200 -Subject: [PATCH 049/108] PCI: mediatek-gen3: Fix PERST# control timing during +Subject: [PATCH 049/133] 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.59/0050-net-dsa-mv88e6xxx-Derive-LED-names-from-device-name.patch b/patches/linux/6.18.59/0050-net-dsa-mv88e6xxx-Derive-LED-names-from-device-name.patch index e0d44b7845..08f9675f04 100644 --- a/patches/linux/6.18.59/0050-net-dsa-mv88e6xxx-Derive-LED-names-from-device-name.patch +++ b/patches/linux/6.18.59/0050-net-dsa-mv88e6xxx-Derive-LED-names-from-device-name.patch @@ -1,7 +1,7 @@ From f58effd61f5e001957a123e587b28a46a8830040 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 12 Aug 2026 10:08:53 +0200 -Subject: [PATCH 050/108] net: dsa: mv88e6xxx: Derive LED names from device +Subject: [PATCH 050/133] 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.59/0051-phy-sparx5-serdes-make-it-selectable-for-ARCH_LAN969.patch b/patches/linux/6.18.59/0051-phy-sparx5-serdes-make-it-selectable-for-ARCH_LAN969.patch index d7cb70ad88..a805801d6e 100644 --- a/patches/linux/6.18.59/0051-phy-sparx5-serdes-make-it-selectable-for-ARCH_LAN969.patch +++ b/patches/linux/6.18.59/0051-phy-sparx5-serdes-make-it-selectable-for-ARCH_LAN969.patch @@ -1,7 +1,7 @@ From 3b8c1832fb91d76e6efa8a41ce67525ddc33486d Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 31 Oct 2025 13:18:12 +0100 -Subject: [PATCH 051/108] phy: sparx5-serdes: make it selectable for +Subject: [PATCH 051/133] 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.59/0052-net-sparx5-fix-wrong-chip-ids-for-TSN-SKUs.patch b/patches/linux/6.18.59/0052-net-sparx5-fix-wrong-chip-ids-for-TSN-SKUs.patch index ab3a0cb35e..90bdf5ea80 100644 --- a/patches/linux/6.18.59/0052-net-sparx5-fix-wrong-chip-ids-for-TSN-SKUs.patch +++ b/patches/linux/6.18.59/0052-net-sparx5-fix-wrong-chip-ids-for-TSN-SKUs.patch @@ -1,7 +1,7 @@ From 7d3eb6d26ab67a95156c9102f125c09e51d6897a Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Wed, 6 May 2026 09:25:38 +0200 -Subject: [PATCH 052/108] net: sparx5: fix wrong chip ids for TSN SKUs +Subject: [PATCH 052/133] 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.59/0053-net-sparx5-configure-serdes-for-1000BASE-X-in-sparx5.patch b/patches/linux/6.18.59/0053-net-sparx5-configure-serdes-for-1000BASE-X-in-sparx5.patch index e7a826123d..5e200f9ac8 100644 --- a/patches/linux/6.18.59/0053-net-sparx5-configure-serdes-for-1000BASE-X-in-sparx5.patch +++ b/patches/linux/6.18.59/0053-net-sparx5-configure-serdes-for-1000BASE-X-in-sparx5.patch @@ -1,7 +1,7 @@ From 981a6430a736f7f8870ca55b046a228a1154f58c Mon Sep 17 00:00:00 2001 From: Daniel Machon Date: Wed, 6 May 2026 09:25:39 +0200 -Subject: [PATCH 053/108] net: sparx5: configure serdes for 1000BASE-X in +Subject: [PATCH 053/133] 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.59/0054-dt-bindings-mmc-atmel-sama5d2-sdhci-add-microchip-la.patch b/patches/linux/6.18.59/0054-dt-bindings-mmc-atmel-sama5d2-sdhci-add-microchip-la.patch index 593b73b980..0ba2376184 100644 --- a/patches/linux/6.18.59/0054-dt-bindings-mmc-atmel-sama5d2-sdhci-add-microchip-la.patch +++ b/patches/linux/6.18.59/0054-dt-bindings-mmc-atmel-sama5d2-sdhci-add-microchip-la.patch @@ -1,7 +1,7 @@ From 11e6966c1694232015e0441818add5ea96c8547c Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:35:29 +0200 -Subject: [PATCH 054/108] dt-bindings: mmc: atmel,sama5d2-sdhci: add +Subject: [PATCH 054/133] 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.59/0055-mmc-sdhci-of-at91-add-LAN969x-support.patch b/patches/linux/6.18.59/0055-mmc-sdhci-of-at91-add-LAN969x-support.patch index 7515c0291c..e1f2243ac1 100644 --- a/patches/linux/6.18.59/0055-mmc-sdhci-of-at91-add-LAN969x-support.patch +++ b/patches/linux/6.18.59/0055-mmc-sdhci-of-at91-add-LAN969x-support.patch @@ -1,7 +1,7 @@ From f8392367b259f3b4ff378f563f4bdfbb802ca633 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:35:44 +0200 -Subject: [PATCH 055/108] mmc: sdhci-of-at91: add LAN969x support +Subject: [PATCH 055/133] 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.59/0056-mmc-sdhci-of-at91-stop-SDCLK-on-reset-and-add-eMMC-h.patch b/patches/linux/6.18.59/0056-mmc-sdhci-of-at91-stop-SDCLK-on-reset-and-add-eMMC-h.patch index cc8b760fb9..335b96129d 100644 --- a/patches/linux/6.18.59/0056-mmc-sdhci-of-at91-stop-SDCLK-on-reset-and-add-eMMC-h.patch +++ b/patches/linux/6.18.59/0056-mmc-sdhci-of-at91-stop-SDCLK-on-reset-and-add-eMMC-h.patch @@ -1,7 +1,7 @@ From 3166f2ce50eb2a51c85fd13342a167304a36e8bb Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:37:48 +0200 -Subject: [PATCH 056/108] mmc: sdhci-of-at91: stop SDCLK on reset and add eMMC +Subject: [PATCH 056/133] 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.59/0057-net-sparx5-lan969x-populate-netdev-of_node.patch b/patches/linux/6.18.59/0057-net-sparx5-lan969x-populate-netdev-of_node.patch index 13c1a4abd7..9732201072 100644 --- a/patches/linux/6.18.59/0057-net-sparx5-lan969x-populate-netdev-of_node.patch +++ b/patches/linux/6.18.59/0057-net-sparx5-lan969x-populate-netdev-of_node.patch @@ -1,7 +1,7 @@ From a67a554df2c3762cf3921a1128464fcc98be7f3d Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 10 Nov 2025 13:42:53 +0100 -Subject: [PATCH 057/108] net: sparx5/lan969x: populate netdev of_node +Subject: [PATCH 057/133] 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.59/0058-arm64-dts-microchip-add-LAN969x-clock-header-file.patch b/patches/linux/6.18.59/0058-arm64-dts-microchip-add-LAN969x-clock-header-file.patch index 886c396482..51241cc634 100644 --- a/patches/linux/6.18.59/0058-arm64-dts-microchip-add-LAN969x-clock-header-file.patch +++ b/patches/linux/6.18.59/0058-arm64-dts-microchip-add-LAN969x-clock-header-file.patch @@ -1,7 +1,7 @@ From abc84adc4a31eee67ffd3c5c2f746de3bf1e3e2a Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 2 Mar 2026 12:20:11 +0100 -Subject: [PATCH 058/108] arm64: dts: microchip: add LAN969x clock header file +Subject: [PATCH 058/133] 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.59/0059-arm64-dts-microchip-add-LAN969x-support.patch b/patches/linux/6.18.59/0059-arm64-dts-microchip-add-LAN969x-support.patch index 15ac6d6227..cd0f0c9d27 100644 --- a/patches/linux/6.18.59/0059-arm64-dts-microchip-add-LAN969x-support.patch +++ b/patches/linux/6.18.59/0059-arm64-dts-microchip-add-LAN969x-support.patch @@ -1,7 +1,7 @@ From dbac00db214c4fec755959d9bea211e00cf62564 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 2 Mar 2026 12:20:12 +0100 -Subject: [PATCH 059/108] arm64: dts: microchip: add LAN969x support +Subject: [PATCH 059/133] 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.59/0060-arm64-dts-microchip-add-EV23X71A-board.patch b/patches/linux/6.18.59/0060-arm64-dts-microchip-add-EV23X71A-board.patch index 9afdfa31c6..487ea93d78 100644 --- a/patches/linux/6.18.59/0060-arm64-dts-microchip-add-EV23X71A-board.patch +++ b/patches/linux/6.18.59/0060-arm64-dts-microchip-add-EV23X71A-board.patch @@ -1,7 +1,7 @@ From 0c0cd939039f7d49bc659682a0916fce5e28ae07 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 2 Mar 2026 12:20:14 +0100 -Subject: [PATCH 060/108] arm64: dts: microchip: add EV23X71A board +Subject: [PATCH 060/133] arm64: dts: microchip: add EV23X71A board Microchip EV23X71A is an LAN9696 based evaluation board. diff --git a/patches/linux/6.18.59/0061-arm64-dts-microchip-lan969x-add-OTP-node.patch b/patches/linux/6.18.59/0061-arm64-dts-microchip-lan969x-add-OTP-node.patch index e9ed5678cd..a4421a7e89 100644 --- a/patches/linux/6.18.59/0061-arm64-dts-microchip-lan969x-add-OTP-node.patch +++ b/patches/linux/6.18.59/0061-arm64-dts-microchip-lan969x-add-OTP-node.patch @@ -1,7 +1,7 @@ From de086daa02308a13048089b83fcfcf692f236684 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 15 May 2026 13:59:09 +0200 -Subject: [PATCH 061/108] arm64: dts: microchip: lan969x: add OTP node +Subject: [PATCH 061/133] arm64: dts: microchip: lan969x: add OTP node Add the required OTP on LAN969x. diff --git a/patches/linux/6.18.59/0062-arm64-dts-microchip-lan969x-add-SDMMC-nodes.patch b/patches/linux/6.18.59/0062-arm64-dts-microchip-lan969x-add-SDMMC-nodes.patch index 45e5adc8f3..32132034b4 100644 --- a/patches/linux/6.18.59/0062-arm64-dts-microchip-lan969x-add-SDMMC-nodes.patch +++ b/patches/linux/6.18.59/0062-arm64-dts-microchip-lan969x-add-SDMMC-nodes.patch @@ -1,7 +1,7 @@ From 92a6fd212318ae3c570d29762a556d7d0675d559 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:38:37 +0200 -Subject: [PATCH 062/108] arm64: dts: microchip: lan969x: add SDMMC nodes +Subject: [PATCH 062/133] 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.59/0063-arm64-dts-microchip-ev23x71a-enable-eMMC.patch b/patches/linux/6.18.59/0063-arm64-dts-microchip-ev23x71a-enable-eMMC.patch index 46597e235e..a4a68f794d 100644 --- a/patches/linux/6.18.59/0063-arm64-dts-microchip-ev23x71a-enable-eMMC.patch +++ b/patches/linux/6.18.59/0063-arm64-dts-microchip-ev23x71a-enable-eMMC.patch @@ -1,7 +1,7 @@ From 5cc7ebc8df15820ec06ee090e54f750d3d2da147 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 19 Aug 2026 11:38:37 +0200 -Subject: [PATCH 063/108] arm64: dts: microchip: ev23x71a: enable eMMC +Subject: [PATCH 063/133] 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.59/0064-wifi-brcmfmac-survey-the-requested-interface-not-the.patch b/patches/linux/6.18.59/0064-wifi-brcmfmac-survey-the-requested-interface-not-the.patch index 652ae536fa..28ab2e7d7d 100644 --- a/patches/linux/6.18.59/0064-wifi-brcmfmac-survey-the-requested-interface-not-the.patch +++ b/patches/linux/6.18.59/0064-wifi-brcmfmac-survey-the-requested-interface-not-the.patch @@ -1,7 +1,7 @@ From 2d0618d3b40b4c7d0061bb10fae00f76322b84af Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 30 Jun 2026 15:42:48 +0200 -Subject: [PATCH 064/108] wifi: brcmfmac: survey the requested interface, not +Subject: [PATCH 064/133] 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.59/0065-wifi-brcmfmac-honor-caller-s-rtnl-lock-when-stopping.patch b/patches/linux/6.18.59/0065-wifi-brcmfmac-honor-caller-s-rtnl-lock-when-stopping.patch index b3406815cc..610410cf5b 100644 --- a/patches/linux/6.18.59/0065-wifi-brcmfmac-honor-caller-s-rtnl-lock-when-stopping.patch +++ b/patches/linux/6.18.59/0065-wifi-brcmfmac-honor-caller-s-rtnl-lock-when-stopping.patch @@ -1,7 +1,7 @@ From 73f75742586cd6d53a3fe893ea9908544aaa7a1d Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 30 Jun 2026 15:43:14 +0200 -Subject: [PATCH 065/108] wifi: brcmfmac: honor caller's rtnl lock when +Subject: [PATCH 065/133] 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.59/0066-wifi-brcmfmac-let-cfg_to_ndev-return-NULL-and-harden.patch b/patches/linux/6.18.59/0066-wifi-brcmfmac-let-cfg_to_ndev-return-NULL-and-harden.patch index 4bf1a0d45d..095b8f0184 100644 --- a/patches/linux/6.18.59/0066-wifi-brcmfmac-let-cfg_to_ndev-return-NULL-and-harden.patch +++ b/patches/linux/6.18.59/0066-wifi-brcmfmac-let-cfg_to_ndev-return-NULL-and-harden.patch @@ -1,7 +1,7 @@ From 2d03a462bc81b2d42c21040b4fe1963e736de253 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 30 Jun 2026 15:46:25 +0200 -Subject: [PATCH 066/108] wifi: brcmfmac: let cfg_to_ndev() return NULL and +Subject: [PATCH 066/133] 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.59/0067-wifi-brcmfmac-support-deletion-and-recreation-of-the.patch b/patches/linux/6.18.59/0067-wifi-brcmfmac-support-deletion-and-recreation-of-the.patch index c39d255352..1fba70cf9e 100644 --- a/patches/linux/6.18.59/0067-wifi-brcmfmac-support-deletion-and-recreation-of-the.patch +++ b/patches/linux/6.18.59/0067-wifi-brcmfmac-support-deletion-and-recreation-of-the.patch @@ -1,7 +1,7 @@ From e3087dbad8dbde1c0937b000b6af43aa07d31805 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 30 Jun 2026 16:05:17 +0200 -Subject: [PATCH 067/108] wifi: brcmfmac: support deletion and recreation of +Subject: [PATCH 067/133] 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.59/0068-wifi-brcmfmac-report-port-authorized-after-offloaded.patch b/patches/linux/6.18.59/0068-wifi-brcmfmac-report-port-authorized-after-offloaded.patch index 57dd540d16..a8f8aa621f 100644 --- a/patches/linux/6.18.59/0068-wifi-brcmfmac-report-port-authorized-after-offloaded.patch +++ b/patches/linux/6.18.59/0068-wifi-brcmfmac-report-port-authorized-after-offloaded.patch @@ -1,7 +1,7 @@ From 3a229524607e5de259bba2dca0f00707c8fb5b2f Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Thu, 10 Sep 2026 08:29:47 +0200 -Subject: [PATCH 068/108] wifi: brcmfmac: report port authorized after +Subject: [PATCH 068/133] 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.59/0069-dt-bindings-arm-AT91-document-Novarq-Tactical-1000.patch b/patches/linux/6.18.59/0069-dt-bindings-arm-AT91-document-Novarq-Tactical-1000.patch index 86f1777a46..6d20d052ab 100644 --- a/patches/linux/6.18.59/0069-dt-bindings-arm-AT91-document-Novarq-Tactical-1000.patch +++ b/patches/linux/6.18.59/0069-dt-bindings-arm-AT91-document-Novarq-Tactical-1000.patch @@ -1,7 +1,7 @@ From 65134a6569b04f281c70d4be63ca7800c2a93ecc Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 9 Jan 2026 13:27:00 +0100 -Subject: [PATCH 069/108] dt-bindings: arm: AT91: document Novarq Tactical 1000 +Subject: [PATCH 069/133] 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.59/0070-arm64-dts-microchip-add-Novarq-Tactical-1000.patch b/patches/linux/6.18.59/0070-arm64-dts-microchip-add-Novarq-Tactical-1000.patch index 97b518e560..e4233c34fb 100644 --- a/patches/linux/6.18.59/0070-arm64-dts-microchip-add-Novarq-Tactical-1000.patch +++ b/patches/linux/6.18.59/0070-arm64-dts-microchip-add-Novarq-Tactical-1000.patch @@ -1,7 +1,7 @@ From a175baab9da7dcb7e826c8682dec3a03e3378fc8 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 30 Sep 2025 13:37:49 +0200 -Subject: [PATCH 070/108] arm64: dts: microchip: add Novarq Tactical 1000 +Subject: [PATCH 070/133] 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.59/0071-arm64-dts-microchip-tactical-1000-add-port-names.patch b/patches/linux/6.18.59/0071-arm64-dts-microchip-tactical-1000-add-port-names.patch index 3ec56b26c5..641c3ef937 100644 --- a/patches/linux/6.18.59/0071-arm64-dts-microchip-tactical-1000-add-port-names.patch +++ b/patches/linux/6.18.59/0071-arm64-dts-microchip-tactical-1000-add-port-names.patch @@ -1,7 +1,7 @@ From 316b22b3455d501e142d3b0aaadf2960ef5293e0 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 30 Jun 2026 11:23:47 +0200 -Subject: [PATCH 071/108] arm64: dts: microchip: tactical-1000: add port names +Subject: [PATCH 071/133] 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.59/0072-arm64-dts-microchip-tactical-1000-adapt-to-6.18.patch b/patches/linux/6.18.59/0072-arm64-dts-microchip-tactical-1000-adapt-to-6.18.patch index 31316be478..a8bcd8aa0b 100644 --- a/patches/linux/6.18.59/0072-arm64-dts-microchip-tactical-1000-adapt-to-6.18.patch +++ b/patches/linux/6.18.59/0072-arm64-dts-microchip-tactical-1000-adapt-to-6.18.patch @@ -1,7 +1,7 @@ From 7305c3d65b801f42f8528eec7bd0b6b6e1a8d794 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 20 Sep 2026 12:22:43 +0200 -Subject: [PATCH 072/108] arm64: dts: microchip: tactical-1000: adapt to 6.18 +Subject: [PATCH 072/133] 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.59/0073-dt-bindings-arm-AT91-document-EV23X71A-board.patch b/patches/linux/6.18.59/0073-dt-bindings-arm-AT91-document-EV23X71A-board.patch index acf314348b..a593513b6c 100644 --- a/patches/linux/6.18.59/0073-dt-bindings-arm-AT91-document-EV23X71A-board.patch +++ b/patches/linux/6.18.59/0073-dt-bindings-arm-AT91-document-EV23X71A-board.patch @@ -1,7 +1,7 @@ From 90dbfb2125a1b5f585ede981e0f6d0b632f5b1c5 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 2 Mar 2026 12:20:13 +0100 -Subject: [PATCH 073/108] dt-bindings: arm: AT91: document EV23X71A board +Subject: [PATCH 073/133] dt-bindings: arm: AT91: document EV23X71A board Microchip EV23X71A board is an LAN9696 based evaluation board. diff --git a/patches/linux/6.18.59/0074-net-dsa-Skip-DCB-default-priority-init-on-unsupporte.patch b/patches/linux/6.18.59/0074-net-dsa-Skip-DCB-default-priority-init-on-unsupporte.patch index f6bb6853c5..99fe9308b1 100644 --- a/patches/linux/6.18.59/0074-net-dsa-Skip-DCB-default-priority-init-on-unsupporte.patch +++ b/patches/linux/6.18.59/0074-net-dsa-Skip-DCB-default-priority-init-on-unsupporte.patch @@ -1,7 +1,7 @@ From e29b0d24fe1462b86170a863e8eb68dc006df4f9 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 9 Sep 2026 17:04:21 +0200 -Subject: [PATCH 074/108] net: dsa: Skip DCB default priority init on +Subject: [PATCH 074/133] 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.59/0075-net-dsa-Generalise-the-global-DCB-APP-mirroring-help.patch b/patches/linux/6.18.59/0075-net-dsa-Generalise-the-global-DCB-APP-mirroring-help.patch index a26d15e700..ed9cfa6ccf 100644 --- a/patches/linux/6.18.59/0075-net-dsa-Generalise-the-global-DCB-APP-mirroring-help.patch +++ b/patches/linux/6.18.59/0075-net-dsa-Generalise-the-global-DCB-APP-mirroring-help.patch @@ -1,7 +1,7 @@ From 664ed53f4cf53592f4e2b119ad3c08672e37c5b4 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 22 Sep 2026 13:51:07 +0200 -Subject: [PATCH 075/108] net: dsa: Generalise the global DCB APP mirroring +Subject: [PATCH 075/133] 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.59/0076-net-dsa-Support-the-PCP-APP-selector.patch b/patches/linux/6.18.59/0076-net-dsa-Support-the-PCP-APP-selector.patch index f3620ef72b..6b593feed7 100644 --- a/patches/linux/6.18.59/0076-net-dsa-Support-the-PCP-APP-selector.patch +++ b/patches/linux/6.18.59/0076-net-dsa-Support-the-PCP-APP-selector.patch @@ -1,7 +1,7 @@ From f4356c8af749801cd0d5d10a4428a937cf3e975e Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 9 Sep 2026 17:04:22 +0200 -Subject: [PATCH 076/108] net: dsa: Support the PCP APP selector +Subject: [PATCH 076/133] 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.59/0077-net-dsa-Support-DCB-priority-rewrite.patch b/patches/linux/6.18.59/0077-net-dsa-Support-DCB-priority-rewrite.patch index 3c300a93dc..f7cfe96827 100644 --- a/patches/linux/6.18.59/0077-net-dsa-Support-DCB-priority-rewrite.patch +++ b/patches/linux/6.18.59/0077-net-dsa-Support-DCB-priority-rewrite.patch @@ -1,7 +1,7 @@ From dc55db71e9182528af2374f75826856d62dd1ccb Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 9 Sep 2026 17:10:32 +0200 -Subject: [PATCH 077/108] net: dsa: Support DCB priority rewrite +Subject: [PATCH 077/133] 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.59/0078-net-dsa-Support-the-IEEE-ETS-managed-object.patch b/patches/linux/6.18.59/0078-net-dsa-Support-the-IEEE-ETS-managed-object.patch index c84c54b7ec..63129b6400 100644 --- a/patches/linux/6.18.59/0078-net-dsa-Support-the-IEEE-ETS-managed-object.patch +++ b/patches/linux/6.18.59/0078-net-dsa-Support-the-IEEE-ETS-managed-object.patch @@ -1,7 +1,7 @@ From aa0880d6b756477b527efa792265102da05bc0a8 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 22 Sep 2026 13:53:23 +0200 -Subject: [PATCH 078/108] net: dsa: Support the IEEE ETS managed object +Subject: [PATCH 078/133] 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.59/0079-net-sparx5-fix-sleep-in-atomic-context-in-MAC-table-.patch b/patches/linux/6.18.59/0079-net-sparx5-fix-sleep-in-atomic-context-in-MAC-table-.patch index 243f53b17f..b89abc69ab 100644 --- a/patches/linux/6.18.59/0079-net-sparx5-fix-sleep-in-atomic-context-in-MAC-table-.patch +++ b/patches/linux/6.18.59/0079-net-sparx5-fix-sleep-in-atomic-context-in-MAC-table-.patch @@ -1,7 +1,7 @@ From 7ccc23bb79ed44327cfa5210f7d9036ef6f6bb0d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 7 Oct 2026 11:42:12 +0200 -Subject: [PATCH 079/108] net: sparx5: fix sleep in atomic context in MAC table +Subject: [PATCH 079/133] 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.59/0080-misc-sparx5-symreg-symreg-debugfs-driver-DBB-1045.patch b/patches/linux/6.18.59/0080-misc-sparx5-symreg-symreg-debugfs-driver-DBB-1045.patch index 7fbff00ee0..f4ffe41a7d 100644 --- a/patches/linux/6.18.59/0080-misc-sparx5-symreg-symreg-debugfs-driver-DBB-1045.patch +++ b/patches/linux/6.18.59/0080-misc-sparx5-symreg-symreg-debugfs-driver-DBB-1045.patch @@ -1,7 +1,7 @@ From 144be9133926a9d4c647acbed8b52f312c8cb70f Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Wed, 21 Jan 2026 10:09:45 +0100 -Subject: [PATCH 080/108] misc: sparx5-symreg; symreg debugfs driver (DBB-1045) +Subject: [PATCH 080/133] 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.59/0081-net-ethernet-mtk_wed-map-WO-memory-regions-without-r.patch b/patches/linux/6.18.59/0081-net-ethernet-mtk_wed-map-WO-memory-regions-without-r.patch index 77b7fe3928..df6caf246a 100644 --- a/patches/linux/6.18.59/0081-net-ethernet-mtk_wed-map-WO-memory-regions-without-r.patch +++ b/patches/linux/6.18.59/0081-net-ethernet-mtk_wed-map-WO-memory-regions-without-r.patch @@ -1,7 +1,7 @@ From 4f498b7e3e6d6b55bd2b4f4fc83f308086daf99b Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Mon, 5 Oct 2026 14:37:56 +0200 -Subject: [PATCH 081/108] net: ethernet: mtk_wed: map WO memory regions without +Subject: [PATCH 081/133] 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.59/0082-wifi-mt76-wed-fix-kernel-panic-on-non-DBDC-MT7986.patch b/patches/linux/6.18.59/0082-wifi-mt76-wed-fix-kernel-panic-on-non-DBDC-MT7986.patch index f8a2ed10a2..582739f161 100644 --- a/patches/linux/6.18.59/0082-wifi-mt76-wed-fix-kernel-panic-on-non-DBDC-MT7986.patch +++ b/patches/linux/6.18.59/0082-wifi-mt76-wed-fix-kernel-panic-on-non-DBDC-MT7986.patch @@ -1,7 +1,7 @@ From c89e743a6c9cb5e8f4faf20affd026500697ccf1 Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Wed, 15 Jul 2026 23:21:12 +0800 -Subject: [PATCH 082/108] wifi: mt76: wed: fix kernel panic on non-DBDC MT7986 +Subject: [PATCH 082/133] 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.59/0083-wifi-mt76-mt7915-set-up-WPDMA-RX-ring-0-for-WED-on-a.patch b/patches/linux/6.18.59/0083-wifi-mt76-mt7915-set-up-WPDMA-RX-ring-0-for-WED-on-a.patch index e5a66aff81..02c70aa78e 100644 --- a/patches/linux/6.18.59/0083-wifi-mt76-mt7915-set-up-WPDMA-RX-ring-0-for-WED-on-a.patch +++ b/patches/linux/6.18.59/0083-wifi-mt76-mt7915-set-up-WPDMA-RX-ring-0-for-WED-on-a.patch @@ -1,7 +1,7 @@ From 6fced2710fb0e2593deb8a992713c2c64a1ccceb Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 7 Oct 2026 22:20:00 +0200 -Subject: [PATCH 083/108] wifi: mt76: mt7915: set up WPDMA RX ring 0 for WED on +Subject: [PATCH 083/133] 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.59/0084-wifi-mt76-mt7915-add-WDS-support-when-WED-is-enabled.patch b/patches/linux/6.18.59/0084-wifi-mt76-mt7915-add-WDS-support-when-WED-is-enabled.patch index d8bf24bdb7..7d1b3f3ccf 100644 --- a/patches/linux/6.18.59/0084-wifi-mt76-mt7915-add-WDS-support-when-WED-is-enabled.patch +++ b/patches/linux/6.18.59/0084-wifi-mt76-mt7915-add-WDS-support-when-WED-is-enabled.patch @@ -1,7 +1,7 @@ From 08f642ca9fba3c606de516c9df48fe96d59b66ac Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 20 Oct 2025 15:59:12 +0200 -Subject: [PATCH 084/108] wifi: mt76: mt7915: add WDS support when WED is +Subject: [PATCH 084/133] 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.59/0085-net-ethernet-mtk_wed-increase-WED-v2-WDMA-RESV_BUFF-.patch b/patches/linux/6.18.59/0085-net-ethernet-mtk_wed-increase-WED-v2-WDMA-RESV_BUFF-.patch index 5e2a263706..d926e0baa3 100644 --- a/patches/linux/6.18.59/0085-net-ethernet-mtk_wed-increase-WED-v2-WDMA-RESV_BUFF-.patch +++ b/patches/linux/6.18.59/0085-net-ethernet-mtk_wed-increase-WED-v2-WDMA-RESV_BUFF-.patch @@ -1,7 +1,7 @@ From aeece1ac8387c1e3e9e93664c1e58bfddbdc8b16 Mon Sep 17 00:00:00 2001 From: Shiji Yang Date: Wed, 19 Aug 2026 21:07:07 +0800 -Subject: [PATCH 085/108] net: ethernet: mtk_wed: increase WED v2 WDMA +Subject: [PATCH 085/133] 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.59/0086-wifi-mt76-mt7915-publish-wcid-before-MCU-add-command.patch b/patches/linux/6.18.59/0086-wifi-mt76-mt7915-publish-wcid-before-MCU-add-command.patch index 55ded89f52..30bef5815b 100644 --- a/patches/linux/6.18.59/0086-wifi-mt76-mt7915-publish-wcid-before-MCU-add-command.patch +++ b/patches/linux/6.18.59/0086-wifi-mt76-mt7915-publish-wcid-before-MCU-add-command.patch @@ -1,7 +1,7 @@ From 8fa947ba6f5dec39e2bc5ae5c0006c7abbe6c7f7 Mon Sep 17 00:00:00 2001 From: Ryan Leung Date: Fri, 21 Aug 2026 09:00:01 +0000 -Subject: [PATCH 086/108] wifi: mt76: mt7915: publish wcid before MCU add +Subject: [PATCH 086/133] 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.59/0087-riscv-dts-spacemit-add-UART-pinctrl-combinations.patch b/patches/linux/6.18.59/0087-riscv-dts-spacemit-add-UART-pinctrl-combinations.patch index a4ed3fc332..74590fc0c9 100644 --- a/patches/linux/6.18.59/0087-riscv-dts-spacemit-add-UART-pinctrl-combinations.patch +++ b/patches/linux/6.18.59/0087-riscv-dts-spacemit-add-UART-pinctrl-combinations.patch @@ -1,7 +1,7 @@ From 2940f41f3d0f207382aa263617ca655982294405 Mon Sep 17 00:00:00 2001 From: Hendrik Hamerlinck Date: Wed, 17 Sep 2025 08:59:07 +0200 -Subject: [PATCH 087/108] riscv: dts: spacemit: add UART pinctrl combinations +Subject: [PATCH 087/133] 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.59/0088-riscv-dts-spacemit-enable-the-i2c8-adapter.patch b/patches/linux/6.18.59/0088-riscv-dts-spacemit-enable-the-i2c8-adapter.patch index 2af5874459..7bd108aa0d 100644 --- a/patches/linux/6.18.59/0088-riscv-dts-spacemit-enable-the-i2c8-adapter.patch +++ b/patches/linux/6.18.59/0088-riscv-dts-spacemit-enable-the-i2c8-adapter.patch @@ -1,7 +1,7 @@ From f1da50df705207472f023869c70f1ed12b504bb8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 25 Aug 2025 12:20:54 -0500 -Subject: [PATCH 088/108] riscv: dts: spacemit: enable the i2c8 adapter +Subject: [PATCH 088/133] 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.59/0089-riscv-dts-spacemit-define-fixed-regulators.patch b/patches/linux/6.18.59/0089-riscv-dts-spacemit-define-fixed-regulators.patch index 01814e28ab..2b04fcb77f 100644 --- a/patches/linux/6.18.59/0089-riscv-dts-spacemit-define-fixed-regulators.patch +++ b/patches/linux/6.18.59/0089-riscv-dts-spacemit-define-fixed-regulators.patch @@ -1,7 +1,7 @@ From 920a65862119e106131e88ca4168aa60f01df941 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 25 Aug 2025 12:20:55 -0500 -Subject: [PATCH 089/108] riscv: dts: spacemit: define fixed regulators +Subject: [PATCH 089/133] 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.59/0090-riscv-dts-spacemit-define-regulator-constraints.patch b/patches/linux/6.18.59/0090-riscv-dts-spacemit-define-regulator-constraints.patch index fe2ef19a42..2c14f906b8 100644 --- a/patches/linux/6.18.59/0090-riscv-dts-spacemit-define-regulator-constraints.patch +++ b/patches/linux/6.18.59/0090-riscv-dts-spacemit-define-regulator-constraints.patch @@ -1,7 +1,7 @@ From 63c79cd2394014b45f4ff25b306353d64f81685a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 25 Aug 2025 12:20:56 -0500 -Subject: [PATCH 090/108] riscv: dts: spacemit: define regulator constraints +Subject: [PATCH 090/133] 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.59/0091-riscv-dts-spacemit-pinctrl-update-register-and-IO-po.patch b/patches/linux/6.18.59/0091-riscv-dts-spacemit-pinctrl-update-register-and-IO-po.patch index a8a862391b..40e2817200 100644 --- a/patches/linux/6.18.59/0091-riscv-dts-spacemit-pinctrl-update-register-and-IO-po.patch +++ b/patches/linux/6.18.59/0091-riscv-dts-spacemit-pinctrl-update-register-and-IO-po.patch @@ -1,7 +1,7 @@ From fb101f0eeca11ee7de35f3b457ad530eb9bffb5f Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Thu, 8 Jan 2026 14:42:40 +0800 -Subject: [PATCH 091/108] riscv: dts: spacemit: pinctrl: update register and IO +Subject: [PATCH 091/133] 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.59/0092-riscv-dts-spacemit-Update-PMIC-supply-properties-for.patch b/patches/linux/6.18.59/0092-riscv-dts-spacemit-Update-PMIC-supply-properties-for.patch index 12579dec22..a4be2fa11c 100644 --- a/patches/linux/6.18.59/0092-riscv-dts-spacemit-Update-PMIC-supply-properties-for.patch +++ b/patches/linux/6.18.59/0092-riscv-dts-spacemit-Update-PMIC-supply-properties-for.patch @@ -1,7 +1,7 @@ From e4ba95346ad7d9bfe031ee82cf2d5f08e658a6b6 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 6 Feb 2026 10:32:04 +0800 -Subject: [PATCH 092/108] riscv: dts: spacemit: Update PMIC supply properties +Subject: [PATCH 092/133] 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.59/0093-riscv-dts-spacemit-k1-add-SD-card-controller-and-pin.patch b/patches/linux/6.18.59/0093-riscv-dts-spacemit-k1-add-SD-card-controller-and-pin.patch index fe05d9ae1e..59c1d325d6 100644 --- a/patches/linux/6.18.59/0093-riscv-dts-spacemit-k1-add-SD-card-controller-and-pin.patch +++ b/patches/linux/6.18.59/0093-riscv-dts-spacemit-k1-add-SD-card-controller-and-pin.patch @@ -1,7 +1,7 @@ From d18938bf9de1e6f8453915a7e858bb1b9fd3e19e Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 15 May 2026 12:48:59 +0200 -Subject: [PATCH 093/108] riscv: dts: spacemit: k1: add SD card controller and +Subject: [PATCH 093/133] 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.59/0094-riscv-dts-spacemit-k1-bananapi-f3-add-SD-card-suppor.patch b/patches/linux/6.18.59/0094-riscv-dts-spacemit-k1-bananapi-f3-add-SD-card-suppor.patch index 7a7fcb1165..8a84001df6 100644 --- a/patches/linux/6.18.59/0094-riscv-dts-spacemit-k1-bananapi-f3-add-SD-card-suppor.patch +++ b/patches/linux/6.18.59/0094-riscv-dts-spacemit-k1-bananapi-f3-add-SD-card-suppor.patch @@ -1,7 +1,7 @@ From 37175ea160fde74790ef8f3ea33df56ebfa26389 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 15 May 2026 12:49:01 +0200 -Subject: [PATCH 094/108] riscv: dts: spacemit: k1-bananapi-f3: add SD card +Subject: [PATCH 094/133] 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.59/0095-mmc-sdhci-of-k1-add-reset-support.patch b/patches/linux/6.18.59/0095-mmc-sdhci-of-k1-add-reset-support.patch index d2f6dd9287..5eb04d5c0b 100644 --- a/patches/linux/6.18.59/0095-mmc-sdhci-of-k1-add-reset-support.patch +++ b/patches/linux/6.18.59/0095-mmc-sdhci-of-k1-add-reset-support.patch @@ -1,7 +1,7 @@ From a7008106adaf9ada0287db39f83e41137660ba56 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 23 Dec 2025 10:24:50 +0800 -Subject: [PATCH 095/108] mmc: sdhci-of-k1: add reset support +Subject: [PATCH 095/133] 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.59/0096-mmc-sdhci-of-k1-enable-essential-clock-infrastructur.patch b/patches/linux/6.18.59/0096-mmc-sdhci-of-k1-enable-essential-clock-infrastructur.patch index 4badc1e657..b9360cf3ab 100644 --- a/patches/linux/6.18.59/0096-mmc-sdhci-of-k1-enable-essential-clock-infrastructur.patch +++ b/patches/linux/6.18.59/0096-mmc-sdhci-of-k1-enable-essential-clock-infrastructur.patch @@ -1,7 +1,7 @@ From 6c899441c883e70593850f4a89a974c9a44ef295 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:57 +0200 -Subject: [PATCH 096/108] mmc: sdhci-of-k1: enable essential clock +Subject: [PATCH 096/133] 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.59/0097-mmc-sdhci-of-k1-add-regulator-and-pinctrl-voltage-sw.patch b/patches/linux/6.18.59/0097-mmc-sdhci-of-k1-add-regulator-and-pinctrl-voltage-sw.patch index a1dbdb00fb..77c31ad9bf 100644 --- a/patches/linux/6.18.59/0097-mmc-sdhci-of-k1-add-regulator-and-pinctrl-voltage-sw.patch +++ b/patches/linux/6.18.59/0097-mmc-sdhci-of-k1-add-regulator-and-pinctrl-voltage-sw.patch @@ -1,7 +1,7 @@ From 4aa5cb546c15990bb7b012ebbf1e363046d0fa19 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:58 +0200 -Subject: [PATCH 097/108] mmc: sdhci-of-k1: add regulator and pinctrl voltage +Subject: [PATCH 097/133] 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.59/0098-mmc-sdhci-of-k1-add-comprehensive-SDR-tuning-support.patch b/patches/linux/6.18.59/0098-mmc-sdhci-of-k1-add-comprehensive-SDR-tuning-support.patch index 08c0604141..cb62fa785c 100644 --- a/patches/linux/6.18.59/0098-mmc-sdhci-of-k1-add-comprehensive-SDR-tuning-support.patch +++ b/patches/linux/6.18.59/0098-mmc-sdhci-of-k1-add-comprehensive-SDR-tuning-support.patch @@ -1,7 +1,7 @@ From 8928721ecea209cddfc8c4fe844bb8c058f3adac Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 11 May 2026 10:53:59 +0200 -Subject: [PATCH 098/108] mmc: sdhci-of-k1: add comprehensive SDR tuning +Subject: [PATCH 098/133] 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.59/0099-driver-reset-spacemit-p1-add-driver-for-poweroff-reb.patch b/patches/linux/6.18.59/0099-driver-reset-spacemit-p1-add-driver-for-poweroff-reb.patch index e54657a750..25f5909104 100644 --- a/patches/linux/6.18.59/0099-driver-reset-spacemit-p1-add-driver-for-poweroff-reb.patch +++ b/patches/linux/6.18.59/0099-driver-reset-spacemit-p1-add-driver-for-poweroff-reb.patch @@ -1,7 +1,7 @@ From 0f42606834228730c0593bbc5d0d6685d07482ba Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Mon, 3 Nov 2025 00:01:59 +0100 -Subject: [PATCH 099/108] driver: reset: spacemit-p1: add driver for +Subject: [PATCH 099/133] 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.59/0100-mfd-simple-mfd-i2c-Add-a-reboot-cell-for-the-Spacemi.patch b/patches/linux/6.18.59/0100-mfd-simple-mfd-i2c-Add-a-reboot-cell-for-the-Spacemi.patch index 1029977789..40f4224032 100644 --- a/patches/linux/6.18.59/0100-mfd-simple-mfd-i2c-Add-a-reboot-cell-for-the-Spacemi.patch +++ b/patches/linux/6.18.59/0100-mfd-simple-mfd-i2c-Add-a-reboot-cell-for-the-Spacemi.patch @@ -1,7 +1,7 @@ From 351249f976e78bedbd348651d157bbc07a24a8cc Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Mon, 3 Nov 2025 00:02:00 +0100 -Subject: [PATCH 100/108] mfd: simple-mfd-i2c: Add a reboot cell for the +Subject: [PATCH 100/133] 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.59/0101-nvmem-core-do-not-defer-on-a-cell-missing-from-a-bou.patch b/patches/linux/6.18.59/0101-nvmem-core-do-not-defer-on-a-cell-missing-from-a-bou.patch index 954f351349..f9aaa5bb2f 100644 --- a/patches/linux/6.18.59/0101-nvmem-core-do-not-defer-on-a-cell-missing-from-a-bou.patch +++ b/patches/linux/6.18.59/0101-nvmem-core-do-not-defer-on-a-cell-missing-from-a-bou.patch @@ -1,7 +1,7 @@ From 1cedd4f515d32a445773f483f793f33117177855 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 13 Oct 2026 10:52:54 +0200 -Subject: [PATCH 101/108] nvmem: core: do not defer on a cell missing from a +Subject: [PATCH 101/133] 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.59/0102-net-dsa-mt7530-lock-the-MDIO-bus-around-regmap-irq-a.patch b/patches/linux/6.18.59/0102-net-dsa-mt7530-lock-the-MDIO-bus-around-regmap-irq-a.patch index 8bdd083bfc..995a66aab4 100644 --- a/patches/linux/6.18.59/0102-net-dsa-mt7530-lock-the-MDIO-bus-around-regmap-irq-a.patch +++ b/patches/linux/6.18.59/0102-net-dsa-mt7530-lock-the-MDIO-bus-around-regmap-irq-a.patch @@ -1,7 +1,7 @@ From 7cac10fadad59a514a749109e06361831c56f943 Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Tue, 13 Oct 2026 15:48:00 +0200 -Subject: [PATCH 102/108] net: dsa: mt7530: lock the MDIO bus around regmap-irq +Subject: [PATCH 102/133] 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.59/0103-clk-mediatek-mt7988-keep-the-PCIe-port-clocks-enable.patch b/patches/linux/6.18.59/0103-clk-mediatek-mt7988-keep-the-PCIe-port-clocks-enable.patch index ea8b0ec225..98f974a5d2 100644 --- a/patches/linux/6.18.59/0103-clk-mediatek-mt7988-keep-the-PCIe-port-clocks-enable.patch +++ b/patches/linux/6.18.59/0103-clk-mediatek-mt7988-keep-the-PCIe-port-clocks-enable.patch @@ -1,7 +1,7 @@ From dec106afe81cfac5b35b34f7063bb32027bef93d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 18 Oct 2026 11:31:40 +0200 -Subject: [PATCH 103/108] clk: mediatek: mt7988: keep the PCIe port clocks +Subject: [PATCH 103/133] 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.59/0104-wifi-mt76-mt7615-add-a-force_dbdc-module-parameter.patch b/patches/linux/6.18.59/0104-wifi-mt76-mt7615-add-a-force_dbdc-module-parameter.patch index 51f651159c..ac122af0ae 100644 --- a/patches/linux/6.18.59/0104-wifi-mt76-mt7615-add-a-force_dbdc-module-parameter.patch +++ b/patches/linux/6.18.59/0104-wifi-mt76-mt7615-add-a-force_dbdc-module-parameter.patch @@ -1,7 +1,7 @@ From bdea9c3e1271379a2643f474727856061590b68d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 18 Oct 2026 13:47:49 +0200 -Subject: [PATCH 104/108] wifi: mt76: mt7615: add a force_dbdc module parameter +Subject: [PATCH 104/133] 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.59/0105-net-phy-mediatek-ge-break-the-MT7531-master-slave-ti.patch b/patches/linux/6.18.59/0105-net-phy-mediatek-ge-break-the-MT7531-master-slave-ti.patch index d923b2c012..7033e271a7 100644 --- a/patches/linux/6.18.59/0105-net-phy-mediatek-ge-break-the-MT7531-master-slave-ti.patch +++ b/patches/linux/6.18.59/0105-net-phy-mediatek-ge-break-the-MT7531-master-slave-ti.patch @@ -1,7 +1,7 @@ From d4dd4677963fcf33c72661ccdb260d490a9868ef Mon Sep 17 00:00:00 2001 From: Mattias Walström Date: Wed, 14 Oct 2026 19:56:12 +0200 -Subject: [PATCH 105/108] net: phy: mediatek-ge: break the MT7531 master/slave +Subject: [PATCH 105/133] net: phy: mediatek-ge: break the MT7531 master/slave tie after a resume Two MT7531 PHYs connected back to back fail to link up again when both diff --git a/patches/linux/6.18.59/0106-net-dsa-mt7530-untag-VLAN-aware-bridge-PVID.patch b/patches/linux/6.18.59/0106-net-dsa-mt7530-untag-VLAN-aware-bridge-PVID.patch index d4e995c60a..bb70642c99 100644 --- a/patches/linux/6.18.59/0106-net-dsa-mt7530-untag-VLAN-aware-bridge-PVID.patch +++ b/patches/linux/6.18.59/0106-net-dsa-mt7530-untag-VLAN-aware-bridge-PVID.patch @@ -1,7 +1,7 @@ From 501fe8d41841df572a2cd8c00b0ec944f7d3dca7 Mon Sep 17 00:00:00 2001 From: Edward Parker Date: Thu, 14 May 2026 15:05:12 +0100 -Subject: [PATCH 106/108] net: dsa: mt7530: untag VLAN-aware bridge PVID +Subject: [PATCH 106/133] net: dsa: mt7530: untag VLAN-aware bridge PVID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/patches/linux/6.18.59/0107-net-dsa-mt7530-error-out-on-failed-reads-in-ATC-VTCR.patch b/patches/linux/6.18.59/0107-net-dsa-mt7530-error-out-on-failed-reads-in-ATC-VTCR.patch index cee17087bf..d70723e697 100644 --- a/patches/linux/6.18.59/0107-net-dsa-mt7530-error-out-on-failed-reads-in-ATC-VTCR.patch +++ b/patches/linux/6.18.59/0107-net-dsa-mt7530-error-out-on-failed-reads-in-ATC-VTCR.patch @@ -1,7 +1,7 @@ From 6d7b1f8462b02c98bdc6665e2b01732e0a799ff4 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 28 Jul 2026 05:52:21 +0100 -Subject: [PATCH 107/108] net: dsa: mt7530: error out on failed reads in +Subject: [PATCH 107/133] net: dsa: mt7530: error out on failed reads in ATC/VTCR command polling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/patches/linux/6.18.59/0108-usb-probe-EHCI-OHCI-platform-controllers-synchronous.patch b/patches/linux/6.18.59/0108-usb-probe-EHCI-OHCI-platform-controllers-synchronous.patch index d7faf3da9a..1eb4c9f830 100644 --- a/patches/linux/6.18.59/0108-usb-probe-EHCI-OHCI-platform-controllers-synchronous.patch +++ b/patches/linux/6.18.59/0108-usb-probe-EHCI-OHCI-platform-controllers-synchronous.patch @@ -1,7 +1,7 @@ From 3a4eb22eab16fbbe69fd5975c5bac14968f37af1 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 24 Oct 2026 20:25:58 +0200 -Subject: [PATCH 108/108] usb: probe EHCI, OHCI platform controllers +Subject: [PATCH 108/133] usb: probe EHCI, OHCI platform controllers synchronously On SoCs with more than one EHCI/OHCI pair, e.g. RK3568, asynchronous diff --git a/patches/linux/6.18.59/0109-net-sparx5-fix-multicast-delivery-to-the-CPU-on-stan.patch b/patches/linux/6.18.59/0109-net-sparx5-fix-multicast-delivery-to-the-CPU-on-stan.patch new file mode 100644 index 0000000000..8256b38d54 --- /dev/null +++ b/patches/linux/6.18.59/0109-net-sparx5-fix-multicast-delivery-to-the-CPU-on-stan.patch @@ -0,0 +1,156 @@ +From 39d139714f0dc0ac8891b78a1cfc687de8ac4560 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 7 Oct 2026 17:30:17 +0200 +Subject: [PATCH 109/133] 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.59/0110-net-sparx5-fix-broadcast-to-the-CPU-on-bridge-ports-.patch b/patches/linux/6.18.59/0110-net-sparx5-fix-broadcast-to-the-CPU-on-bridge-ports-.patch new file mode 100644 index 0000000000..e1fb7605c3 --- /dev/null +++ b/patches/linux/6.18.59/0110-net-sparx5-fix-broadcast-to-the-CPU-on-bridge-ports-.patch @@ -0,0 +1,99 @@ +From 16c5c0ef246c191840948793fa90d6541b3ee549 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 8 Oct 2026 13:48:05 +0200 +Subject: [PATCH 110/133] 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.59/0111-net-sparx5-fix-MAC-table-entry-shared-by-bridge-VID-.patch b/patches/linux/6.18.59/0111-net-sparx5-fix-MAC-table-entry-shared-by-bridge-VID-.patch new file mode 100644 index 0000000000..ab5de2e6a5 --- /dev/null +++ b/patches/linux/6.18.59/0111-net-sparx5-fix-MAC-table-entry-shared-by-bridge-VID-.patch @@ -0,0 +1,156 @@ +From e4aaf67dea32c833d9ea575f4fb44743972c0503 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 8 Oct 2026 13:55:02 +0200 +Subject: [PATCH 111/133] 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.59/0112-net-sparx5-fix-loss-of-a-bridged-port-s-MAC-entry-on.patch b/patches/linux/6.18.59/0112-net-sparx5-fix-loss-of-a-bridged-port-s-MAC-entry-on.patch new file mode 100644 index 0000000000..6a623f3c39 --- /dev/null +++ b/patches/linux/6.18.59/0112-net-sparx5-fix-loss-of-a-bridged-port-s-MAC-entry-on.patch @@ -0,0 +1,73 @@ +From 48b329b7bf41d1b5a5b0cb18f2eb6eb2ab8f2c88 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 8 Oct 2026 13:55:20 +0200 +Subject: [PATCH 112/133] 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.59/0113-net-sparx5-share-the-CPU-MAC-entry-of-standalone-por.patch b/patches/linux/6.18.59/0113-net-sparx5-share-the-CPU-MAC-entry-of-standalone-por.patch new file mode 100644 index 0000000000..27cf76121e --- /dev/null +++ b/patches/linux/6.18.59/0113-net-sparx5-share-the-CPU-MAC-entry-of-standalone-por.patch @@ -0,0 +1,278 @@ +From df46425ad4d0c21d59e5897dfd0dc2564fc8ee49 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Fri, 9 Oct 2026 10:12:29 +0200 +Subject: [PATCH 113/133] 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.59/0114-net-phy-micrel-Add-support-for-non-PTP-SKUs-for-lan8.patch b/patches/linux/6.18.59/0114-net-phy-micrel-Add-support-for-non-PTP-SKUs-for-lan8.patch new file mode 100644 index 0000000000..d6f7b1c167 --- /dev/null +++ b/patches/linux/6.18.59/0114-net-phy-micrel-Add-support-for-non-PTP-SKUs-for-lan8.patch @@ -0,0 +1,121 @@ +From ff1faf2c8428666e455149dd18e1888c40197a32 Mon Sep 17 00:00:00 2001 +From: Horatiu Vultur +Date: Tue, 21 Oct 2025 09:07:26 +0200 +Subject: [PATCH 114/133] 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.59/0115-net-phy-micrel-move-lan8842-errata-tables-before-lan.patch b/patches/linux/6.18.59/0115-net-phy-micrel-move-lan8842-errata-tables-before-lan.patch new file mode 100644 index 0000000000..b7e03af7c4 --- /dev/null +++ b/patches/linux/6.18.59/0115-net-phy-micrel-move-lan8842-errata-tables-before-lan.patch @@ -0,0 +1,287 @@ +From 2176d48fe145d139bd25397c233c96d1aa68d049 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 15:34:01 +0200 +Subject: [PATCH 115/133] 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.59/0116-net-phy-micrel-lan8814-errata.patch b/patches/linux/6.18.59/0116-net-phy-micrel-lan8814-errata.patch new file mode 100644 index 0000000000..108195d4c8 --- /dev/null +++ b/patches/linux/6.18.59/0116-net-phy-micrel-lan8814-errata.patch @@ -0,0 +1,80 @@ +From 6ef5ea146eab4a361c8abe45f0a61a5ece5c01cf Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 15:34:03 +0200 +Subject: [PATCH 116/133] 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.59/0117-net-phy-micrel-lan8814-resync-QSGMII-after-every-PHY.patch b/patches/linux/6.18.59/0117-net-phy-micrel-lan8814-resync-QSGMII-after-every-PHY.patch new file mode 100644 index 0000000000..44b867bbdd --- /dev/null +++ b/patches/linux/6.18.59/0117-net-phy-micrel-lan8814-resync-QSGMII-after-every-PHY.patch @@ -0,0 +1,117 @@ +From e8246ddbc316480892389ae2c16fc904fe93eda4 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 15:42:34 +0200 +Subject: [PATCH 117/133] net: phy: micrel: lan8814: resync QSGMII after every + PHY reset + +On a LAN969x switch with LAN8804 quad PHYs, a copper port can come 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 +link flap does not recover it, a QSGMII soft reset of the sending end's +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 driver resets the QSGMII only at package init, from config_init, +before phylink has configured the MAC side, and phylib soft-resets the +PHY again on every attach. 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 more after its first +1000BASE-T link, as a workaround for CRC errors. + +Do the same: 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, which phylink calls with the MAC side set up. Restart aneg +once more after the first 1000BASE-T link of each port with both +receivers OK. With this, the test above passes 20 cycles out of 20. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/phy/micrel.c | 58 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 57 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c +index 6aa27b651e81..09d5c5daa2ff 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -455,6 +455,8 @@ struct kszphy_priv { + bool rmii_ref_clk_sel_val; + bool clk_enable; + bool is_ptp_available; ++ bool aneg_restarted; ++ bool qsgmii_resync; + u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; + struct kszphy_phy_stats phy_stats; + }; +@@ -4598,9 +4600,62 @@ 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); + } + ++/* 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 kszphy_priv *priv = phydev->priv; ++ bool resync = priv->qsgmii_resync; ++ int err; ++ ++ 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, ++ LAN8814_QSGMII_SOFT_RESET_BIT); ++ if (err < 0) ++ return err; ++ } ++ ++ 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) ++{ ++ 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 +6639,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.59/0118-net-phy-micrel-lan8814-disable-EEE.patch b/patches/linux/6.18.59/0118-net-phy-micrel-lan8814-disable-EEE.patch new file mode 100644 index 0000000000..9368fd56aa --- /dev/null +++ b/patches/linux/6.18.59/0118-net-phy-micrel-lan8814-disable-EEE.patch @@ -0,0 +1,33 @@ +From 13b421dbb4922b5f2ffc07cf2c8a7c47ab4b73fe Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:27:48 +0200 +Subject: [PATCH 118/133] 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 09d5c5daa2ff..aa99191d2f6b 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -4751,6 +4751,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.59/0119-net-phy-micrel-lan8814-restart-stuck-autonegotiation.patch b/patches/linux/6.18.59/0119-net-phy-micrel-lan8814-restart-stuck-autonegotiation.patch new file mode 100644 index 0000000000..83b2a8429c --- /dev/null +++ b/patches/linux/6.18.59/0119-net-phy-micrel-lan8814-restart-stuck-autonegotiation.patch @@ -0,0 +1,147 @@ +From 6b9e3f76e1e95efb326db13e0e7e7b38d246d037 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:27:50 +0200 +Subject: [PATCH 119/133] 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 aa99191d2f6b..94f8a9885483 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -457,6 +457,9 @@ struct kszphy_priv { + bool is_ptp_available; + bool aneg_restarted; + bool qsgmii_resync; ++ 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 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. +@@ -4626,6 +4692,8 @@ static int lan8814_config_aneg(struct phy_device *phydev) + return err; + } + ++ lan8814_link_change_notify(phydev); ++ + err = genphy_config_aneg(phydev); + if (err || !resync || phydev->autoneg != AUTONEG_ENABLE) + return err; +@@ -4756,9 +4824,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) +@@ -6643,9 +6721,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.59/0120-net-phy-micrel-lan8814-10BASE-T-transmitter-errata.patch b/patches/linux/6.18.59/0120-net-phy-micrel-lan8814-10BASE-T-transmitter-errata.patch new file mode 100644 index 0000000000..ab71a1b8c4 --- /dev/null +++ b/patches/linux/6.18.59/0120-net-phy-micrel-lan8814-10BASE-T-transmitter-errata.patch @@ -0,0 +1,54 @@ +From aa87a31f7d133cd7b96a9ec0ff8a8bb1b1b4c784 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:39:53 +0200 +Subject: [PATCH 120/133] 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 94f8a9885483..a34d125b73d0 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -4428,6 +4428,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) +@@ -4550,6 +4552,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; +@@ -4564,6 +4572,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.59/0121-net-phy-micrel-lan8814-half-duplex-FIFO-workaround.patch b/patches/linux/6.18.59/0121-net-phy-micrel-lan8814-half-duplex-FIFO-workaround.patch new file mode 100644 index 0000000000..ee4251b906 --- /dev/null +++ b/patches/linux/6.18.59/0121-net-phy-micrel-lan8814-half-duplex-FIFO-workaround.patch @@ -0,0 +1,99 @@ +From 8c2d3c2c145a2fdb293586131cf9e06022e2b12c Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:40:31 +0200 +Subject: [PATCH 121/133] 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 a34d125b73d0..6ecbc4b9b824 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -460,6 +460,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 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. +@@ -4704,7 +4749,7 @@ static int lan8814_config_aneg(struct phy_device *phydev) + return err; + } + +- lan8814_link_change_notify(phydev); ++ lan8814_aneg_arm(phydev); + + err = genphy_config_aneg(phydev); + if (err || !resync || phydev->autoneg != AUTONEG_ENABLE) diff --git a/patches/linux/6.18.59/0122-net-phy-micrel-lan8814-reset-DSP-on-stuck-forced-100.patch b/patches/linux/6.18.59/0122-net-phy-micrel-lan8814-reset-DSP-on-stuck-forced-100.patch new file mode 100644 index 0000000000..9fde2f497b --- /dev/null +++ b/patches/linux/6.18.59/0122-net-phy-micrel-lan8814-reset-DSP-on-stuck-forced-100.patch @@ -0,0 +1,209 @@ +From 88af64683c3d7be3ac6a05d1b68ab97baa9876c4 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Tue, 13 Oct 2026 16:42:51 +0200 +Subject: [PATCH 122/133] 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 6ecbc4b9b824..b0df8aee081b 100644 +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -458,8 +458,8 @@ struct kszphy_priv { + bool aneg_restarted; + bool qsgmii_resync; + 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 and restarts aneg after +@@ -4749,7 +4788,7 @@ static int lan8814_config_aneg(struct phy_device *phydev) + return err; + } + +- lan8814_aneg_arm(phydev); ++ lan8814_link_watch(phydev); + + err = genphy_config_aneg(phydev); + if (err || !resync || phydev->autoneg != AUTONEG_ENABLE) +@@ -4882,7 +4921,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; + } +@@ -4891,7 +4930,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.59/0123-net-sparx5-take-a-port-s-MAC-address-from-the-device.patch b/patches/linux/6.18.59/0123-net-sparx5-take-a-port-s-MAC-address-from-the-device.patch new file mode 100644 index 0000000000..5f46f05f97 --- /dev/null +++ b/patches/linux/6.18.59/0123-net-sparx5-take-a-port-s-MAC-address-from-the-device.patch @@ -0,0 +1,37 @@ +From b765e54520120bbca6bd9804dfc375ba68d0b753 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 14 Oct 2026 11:01:01 +0200 +Subject: [PATCH 123/133] 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.59/0124-net-sparx5-forward-in-a-VLAN-unaware-bridge-without-.patch b/patches/linux/6.18.59/0124-net-sparx5-forward-in-a-VLAN-unaware-bridge-without-.patch new file mode 100644 index 0000000000..8f43475031 --- /dev/null +++ b/patches/linux/6.18.59/0124-net-sparx5-forward-in-a-VLAN-unaware-bridge-without-.patch @@ -0,0 +1,66 @@ +From c4b94fa68b7f7ea7d040b05ee255c9fe874accef Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 14 Oct 2026 14:46:53 +0200 +Subject: [PATCH 124/133] 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.59/0125-net-sparx5-terminate-VLAN-uppers-of-bridged-ports.patch b/patches/linux/6.18.59/0125-net-sparx5-terminate-VLAN-uppers-of-bridged-ports.patch new file mode 100644 index 0000000000..55926eb1a0 --- /dev/null +++ b/patches/linux/6.18.59/0125-net-sparx5-terminate-VLAN-uppers-of-bridged-ports.patch @@ -0,0 +1,193 @@ +From b34507a4471478a21c083e91024a4b47abbc3d37 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 14 Oct 2026 15:18:34 +0200 +Subject: [PATCH 125/133] 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.59/0126-net-sparx5-do-not-double-tag-frames-from-the-CPU-on-.patch b/patches/linux/6.18.59/0126-net-sparx5-do-not-double-tag-frames-from-the-CPU-on-.patch new file mode 100644 index 0000000000..8caad2f3c1 --- /dev/null +++ b/patches/linux/6.18.59/0126-net-sparx5-do-not-double-tag-frames-from-the-CPU-on-.patch @@ -0,0 +1,42 @@ +From 1946cf709f88350f062b367104167fe981681d83 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 14 Oct 2026 15:21:35 +0200 +Subject: [PATCH 126/133] 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.59/0127-net-sparx5-ignore-switchdev-attributes-of-uppers-and.patch b/patches/linux/6.18.59/0127-net-sparx5-ignore-switchdev-attributes-of-uppers-and.patch new file mode 100644 index 0000000000..6ea3029f02 --- /dev/null +++ b/patches/linux/6.18.59/0127-net-sparx5-ignore-switchdev-attributes-of-uppers-and.patch @@ -0,0 +1,43 @@ +From 928b819b4ce50e6ae92210f1179c2aaa9df16f5f Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 15 Oct 2026 11:15:52 +0200 +Subject: [PATCH 127/133] 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.59/0128-net-sparx5-send-reserved-link-local-multicast-to-the.patch b/patches/linux/6.18.59/0128-net-sparx5-send-reserved-link-local-multicast-to-the.patch new file mode 100644 index 0000000000..5c956892c7 --- /dev/null +++ b/patches/linux/6.18.59/0128-net-sparx5-send-reserved-link-local-multicast-to-the.patch @@ -0,0 +1,53 @@ +From 18a7c9960e2ab8c03c8f8298b360b20b0fe11ec1 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Thu, 15 Oct 2026 11:19:46 +0200 +Subject: [PATCH 128/133] 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.59/0129-net-sparx5-copy-link-local-IP-multicast-to-the-CPU.patch b/patches/linux/6.18.59/0129-net-sparx5-copy-link-local-IP-multicast-to-the-CPU.patch new file mode 100644 index 0000000000..730545907f --- /dev/null +++ b/patches/linux/6.18.59/0129-net-sparx5-copy-link-local-IP-multicast-to-the-CPU.patch @@ -0,0 +1,43 @@ +From ed9e476555217dd3aab8b3243f70b62df6992167 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 21 Oct 2026 11:00:08 +0200 +Subject: [PATCH 129/133] net: sparx5: copy link-local IP multicast to the CPU + +OSPF neighbours never come up over a bridge of LAN969x ports. Hellos +to 224.0.0.5 and ff02::5 are flooded to the other bridge ports, but no +copy reaches the bridge itself. + +Link-local IP multicast is flooded with the PGID_IPV4_MC_CTRL and +PGID_IPV6_MC_CTRL masks. Neither has CPU copy enabled, and the bridge +adds no MDB entry with the CPU for 224.0.0.x, nor for any group when +snooping is disabled. + +Enable CPU copy on both flood PGIDs. Frames from bridged ports carry +offload_fwd_mark, so the bridge does not forward them again. + +Signed-off-by: Joachim Wiberg +--- + drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +index af63b982b6f5..6bfacc3af86f 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +@@ -720,11 +720,15 @@ static int sparx5_start(struct sparx5 *sparx5) + pgid <= sparx5_get_pgid(sparx5, PGID_BCAST); pgid++) + sparx5_pgid_clear(sparx5, pgid); + +- /* CPU copy CPU pgids */ ++ /* CPU copy CPU pgids and link-local IP multicast */ + spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), sparx5, + ANA_AC_PGID_MISC_CFG(sparx5_get_pgid(sparx5, PGID_CPU))); + spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), sparx5, + ANA_AC_PGID_MISC_CFG(sparx5_get_pgid(sparx5, PGID_BCAST))); ++ spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), sparx5, ++ ANA_AC_PGID_MISC_CFG(sparx5_get_pgid(sparx5, PGID_IPV4_MC_CTRL))); ++ spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), sparx5, ++ ANA_AC_PGID_MISC_CFG(sparx5_get_pgid(sparx5, PGID_IPV6_MC_CTRL))); + + /* Recalc injected frame FCS */ + for (idx = sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_0); diff --git a/patches/linux/6.18.59/0130-net-sparx5-let-the-bridge-forward-IGMP.patch b/patches/linux/6.18.59/0130-net-sparx5-let-the-bridge-forward-IGMP.patch new file mode 100644 index 0000000000..d7b5cacfa2 --- /dev/null +++ b/patches/linux/6.18.59/0130-net-sparx5-let-the-bridge-forward-IGMP.patch @@ -0,0 +1,169 @@ +From f587a37b363dccb88a058fe69584c8854ad1927f Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Sun, 18 Oct 2026 14:14:05 +0200 +Subject: [PATCH 130/133] 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 9d2a96eccfb2..18fa9075d8f6 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +@@ -458,6 +458,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 d9ef4ef137b8..3b732a0d6c59 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +@@ -1539,6 +1539,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/patches/linux/6.18.59/0131-net-sparx5-add-LAG-offload.patch b/patches/linux/6.18.59/0131-net-sparx5-add-LAG-offload.patch new file mode 100644 index 0000000000..efe153e0fe --- /dev/null +++ b/patches/linux/6.18.59/0131-net-sparx5-add-LAG-offload.patch @@ -0,0 +1,1562 @@ +From bba8e067f9f4641ce76c10be577548ff6cf2f628 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Fri, 16 Oct 2026 10:14:44 +0200 +Subject: [PATCH 131/133] 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 18fa9075d8f6..c02d339db858 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 { +@@ -477,6 +490,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, +@@ -503,6 +531,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]); +@@ -724,6 +753,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 3b732a0d6c59..937e85941725 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.59/0132-net-sparx5-fix-leak-of-in-flight-PTP-timestamp-reque.patch b/patches/linux/6.18.59/0132-net-sparx5-fix-leak-of-in-flight-PTP-timestamp-reque.patch new file mode 100644 index 0000000000..234560ea53 --- /dev/null +++ b/patches/linux/6.18.59/0132-net-sparx5-fix-leak-of-in-flight-PTP-timestamp-reque.patch @@ -0,0 +1,56 @@ +From 5708d34b363d834559c6a41c9a0cece49ca09d73 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Sat, 17 Oct 2026 09:14:50 +0200 +Subject: [PATCH 132/133] 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.59/0133-net-sparx5-configure-TAS-port-link-speed.patch b/patches/linux/6.18.59/0133-net-sparx5-configure-TAS-port-link-speed.patch new file mode 100644 index 0000000000..5e4da7f0b6 --- /dev/null +++ b/patches/linux/6.18.59/0133-net-sparx5-configure-TAS-port-link-speed.patch @@ -0,0 +1,242 @@ +From 8cac8bc7e8bd403f40ef68962a7cccbee3422a80 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 7 Jul 2026 19:04:48 +0200 +Subject: [PATCH 133/133] net: sparx5: configure TAS port link speed + +On the TSN and RED variants of LAN969x and SparX-5i TAS (Time-Aware Shaper) +is present in the silicon. + +Currently, the driver does not use configure it at all, which means that +the TAS_PROFILE_CONFIG.LINK_SPEED[1] value is left at the default of 3 +which means that its configured for 1 Gbps. + +So, running iperf between two 10G switch ports will result in only 940-ish +Mbps while we should be getting around 9.3 Gbps. + +Correctly populating the TAS_PROFILE_CONFIG.LINK_SPEED[1] with the current +port speed fixes this issue and we achieve around 9.4 Gbps between two 10G +switch ports. + +So, port the TAS port link speed setting from the vendor BSP 6.18 kernel[2] + +[1] https://microchip-ung.github.io/lan969x-industrial_reginfo/reginfo_LAN969x-Industrial.html?select=hsch,tas_profile_cfg,tas_profile_config,link_speed +[2] https://github.com/microchip-ung/linux/tree/bsp-6.18-2026 + +Signed-off-by: Robert Marko +Link: https://patch.msgid.link/20260707170531.1129866-1-robert.marko@sartura.hr +Signed-off-by: Paolo Abeni +--- + .../microchip/sparx5/lan969x/lan969x_regs.c | 3 ++ + .../microchip/sparx5/sparx5_main_regs.h | 12 +++++ + .../ethernet/microchip/sparx5/sparx5_port.c | 4 ++ + .../ethernet/microchip/sparx5/sparx5_qos.c | 49 +++++++++++++++++++ + .../ethernet/microchip/sparx5/sparx5_qos.h | 1 + + .../ethernet/microchip/sparx5/sparx5_regs.c | 3 ++ + .../ethernet/microchip/sparx5/sparx5_regs.h | 3 ++ + 7 files changed, 75 insertions(+) + +diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c +index 8722aec120f7..231d165a8b23 100644 +--- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c ++++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c +@@ -96,6 +96,7 @@ const unsigned int lan969x_gaddr[GADDR_LAST] = { + [GA_HSCH_SYSTEM] = 37384, + [GA_HSCH_MMGT] = 36260, + [GA_HSCH_TAS_CONFIG] = 37696, ++ [GA_HSCH_TAS_PROFILE_CFG] = 37712, + [GA_PTP_PTP_CFG] = 512, + [GA_PTP_PTP_TOD_DOMAINS] = 528, + [GA_PTP_PHASE_DETECTOR_CTRL] = 628, +@@ -130,6 +131,7 @@ const unsigned int lan969x_gcnt[GCNT_LAST] = { + [GC_GCB_SIO_CTRL] = 1, + [GC_HSCH_HSCH_CFG] = 1120, + [GC_HSCH_HSCH_DWRR] = 32, ++ [GC_HSCH_TAS_PROFILE_CFG] = 30, + [GC_PTP_PTP_PINS] = 8, + [GC_PTP_PHASE_DETECTOR_CTRL] = 8, + [GC_REW_PORT] = 35, +@@ -145,6 +147,7 @@ const unsigned int lan969x_gsize[GSIZE_LAST] = { + [GW_FDMA_FDMA] = 448, + [GW_GCB_CHIP_REGS] = 180, + [GW_HSCH_TAS_CONFIG] = 16, ++ [GW_HSCH_TAS_PROFILE_CFG] = 68, + [GW_PTP_PHASE_DETECTOR_CTRL] = 12, + [GW_QSYS_PAUSE_CFG] = 988, + }; +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +index 937e85941725..1a3954e5df9a 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +@@ -5504,6 +5504,18 @@ extern const struct sparx5_regs *regs; + #define HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_GET(x)\ + FIELD_GET(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY, x) + ++/* HSCH:TAS_PROFILE_CFG:TAS_PROFILE_CONFIG */ ++#define HSCH_TAS_PROFILE_CONFIG(g) \ ++ __REG(TARGET_HSCH, 0, 1, regs->gaddr[GA_HSCH_TAS_PROFILE_CFG], g, \ ++ regs->gcnt[GC_HSCH_TAS_PROFILE_CFG], \ ++ regs->gsize[GW_HSCH_TAS_PROFILE_CFG], 32, 0, 1, 4) ++ ++#define HSCH_TAS_PROFILE_CONFIG_LINK_SPEED GENMASK(10, 8) ++#define HSCH_TAS_PROFILE_CONFIG_LINK_SPEED_SET(x)\ ++ FIELD_PREP(HSCH_TAS_PROFILE_CONFIG_LINK_SPEED, x) ++#define HSCH_TAS_PROFILE_CONFIG_LINK_SPEED_GET(x)\ ++ FIELD_GET(HSCH_TAS_PROFILE_CONFIG_LINK_SPEED, x) ++ + /* LAN969X ONLY */ + /* HSIOWRAP:XMII_CFG:XMII_CFG */ + #define HSIO_WRAP_XMII_CFG(g) \ +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +index bfdbfed1c929..9553763fe144 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +@@ -11,6 +11,7 @@ + #include "sparx5_main_regs.h" + #include "sparx5_main.h" + #include "sparx5_port.h" ++#include "sparx5_qos.h" + + #define SPX5_ETYPE_TAG_C 0x8100 + #define SPX5_ETYPE_TAG_S 0x88a8 +@@ -1055,6 +1056,9 @@ int sparx5_port_config(struct sparx5 *sparx5, + sparx5, + QFWD_SWITCH_PORT_MODE(port->portno)); + ++ /* Notify TAS about the speed. */ ++ sparx5_tas_speed(port, conf->speed); ++ + /* Save the new values */ + port->conf = *conf; + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c +index e580670f3992..972da8a71f5a 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c +@@ -9,6 +9,17 @@ + #include "sparx5_main.h" + #include "sparx5_qos.h" + ++enum sparx5_tas_link_speed { ++ TAS_SPEED_NO_GB, ++ TAS_SPEED_10, ++ TAS_SPEED_100, ++ TAS_SPEED_1000, ++ TAS_SPEED_2500, ++ TAS_SPEED_5000, ++ TAS_SPEED_10000, ++ TAS_SPEED_25000, ++}; ++ + /* Calculate new base_time based on cycle_time. + * + * The hardware requires a base_time that is always in the future. +@@ -581,3 +592,41 @@ int sparx5_tc_ets_del(struct sparx5_port *port) + + return sparx5_dwrr_conf_set(port, &dwrr); + } ++ ++void sparx5_tas_speed(struct sparx5_port *port, int speed) ++{ ++ struct sparx5 *sparx5 = port->sparx5; ++ u8 spd; ++ ++ switch (speed) { ++ case SPEED_10: ++ spd = TAS_SPEED_10; ++ break; ++ case SPEED_100: ++ spd = TAS_SPEED_100; ++ break; ++ case SPEED_1000: ++ spd = TAS_SPEED_1000; ++ break; ++ case SPEED_2500: ++ spd = TAS_SPEED_2500; ++ break; ++ case SPEED_5000: ++ spd = TAS_SPEED_5000; ++ break; ++ case SPEED_10000: ++ spd = TAS_SPEED_10000; ++ break; ++ case SPEED_25000: ++ spd = TAS_SPEED_25000; ++ break; ++ default: ++ netdev_err(port->ndev, "TAS: Unsupported speed: %d\n", speed); ++ return; ++ } ++ ++ spx5_rmw(HSCH_TAS_PROFILE_CONFIG_LINK_SPEED_SET(spd), ++ HSCH_TAS_PROFILE_CONFIG_LINK_SPEED, ++ sparx5, ++ HSCH_TAS_PROFILE_CONFIG(port->portno)); ++} +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h +index 04f76f1e23f6..a92a699c551f 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h +@@ -60,6 +60,7 @@ struct sparx5_dwrr { + }; + + int sparx5_qos_init(struct sparx5 *sparx5); ++void sparx5_tas_speed(struct sparx5_port *port, int speed); + + /* Multi-Queue Priority */ + int sparx5_tc_mqprio_add(struct net_device *ndev, u8 num_tc); +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c +index d6c0dc3194fe..66a3e56ce9a2 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c +@@ -96,6 +96,7 @@ const unsigned int sparx5_gaddr[GADDR_LAST] = { + [GA_HSCH_SYSTEM] = 184000, + [GA_HSCH_MMGT] = 162368, + [GA_HSCH_TAS_CONFIG] = 162384, ++ [GA_HSCH_TAS_PROFILE_CFG] = 188416, + [GA_PTP_PTP_CFG] = 320, + [GA_PTP_PTP_TOD_DOMAINS] = 336, + [GA_PTP_PHASE_DETECTOR_CTRL] = 420, +@@ -130,6 +131,7 @@ const unsigned int sparx5_gcnt[GCNT_LAST] = { + [GC_GCB_SIO_CTRL] = 3, + [GC_HSCH_HSCH_CFG] = 5040, + [GC_HSCH_HSCH_DWRR] = 72, ++ [GC_HSCH_TAS_PROFILE_CFG] = 100, + [GC_PTP_PTP_PINS] = 5, + [GC_PTP_PHASE_DETECTOR_CTRL] = 5, + [GC_REW_PORT] = 70, +@@ -145,6 +147,7 @@ const unsigned int sparx5_gsize[GSIZE_LAST] = { + [GW_FDMA_FDMA] = 428, + [GW_GCB_CHIP_REGS] = 424, + [GW_HSCH_TAS_CONFIG] = 12, ++ [GW_HSCH_TAS_PROFILE_CFG] = 64, + [GW_PTP_PHASE_DETECTOR_CTRL] = 8, + [GW_QSYS_PAUSE_CFG] = 1128, + }; +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h +index 3e433b34b7c6..a33dc11958ce 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h +@@ -105,6 +105,7 @@ enum sparx5_gaddr_enum { + GA_HSCH_SYSTEM, + GA_HSCH_MMGT, + GA_HSCH_TAS_CONFIG, ++ GA_HSCH_TAS_PROFILE_CFG, + GA_PTP_PTP_CFG, + GA_PTP_PTP_TOD_DOMAINS, + GA_PTP_PHASE_DETECTOR_CTRL, +@@ -140,6 +141,7 @@ enum sparx5_gcnt_enum { + GC_GCB_SIO_CTRL, + GC_HSCH_HSCH_CFG, + GC_HSCH_HSCH_DWRR, ++ GC_HSCH_TAS_PROFILE_CFG, + GC_PTP_PTP_PINS, + GC_PTP_PHASE_DETECTOR_CTRL, + GC_REW_PORT, +@@ -156,6 +158,7 @@ enum sparx5_gsize_enum { + GW_FDMA_FDMA, + GW_GCB_CHIP_REGS, + GW_HSCH_TAS_CONFIG, ++ GW_HSCH_TAS_PROFILE_CFG, + GW_PTP_PHASE_DETECTOR_CTRL, + GW_QSYS_PAUSE_CFG, + GSIZE_LAST, 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 13c876f4a6..ddb5088127 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/yangerd/internal/ptpmonitor/mapping.go b/src/yangerd/internal/ptpmonitor/mapping.go index 4b91171f84..3c416f2bc4 100644 --- a/src/yangerd/internal/ptpmonitor/mapping.go +++ b/src/yangerd/internal/ptpmonitor/mapping.go @@ -87,15 +87,13 @@ func timeSourceName(ts uint8) string { return "ieee1588-ptp-tt:internal-oscillator" } -func delayMechanismName(dm uint8) string { - switch dm { - case 2: - return "p2p" - case 0: /* linuxptp DM_AUTO */ - return "no-mechanism" - default: - return "e2e" - } +// delayMechanismName maps IEEE 1588 delayMechanism values to YANG enum names. +var delayMechanismName = map[uint8]string{ + 1: "e2e", + 2: "p2p", + 3: "common-p2p", + 4: "special", + 0xfe: "no-mechanism", } // fmtClockIdentity renders a clock identity in the YANG clock-identity @@ -197,7 +195,7 @@ func portDSMap(d *portDataSetTLV) map[string]interface{} { if !ok { state = "disabled" } - return map[string]interface{}{ + ds := map[string]interface{}{ "port-identity": fmtPortIdentity(d.PortIdentity), "port-state": state, "log-min-delay-req-interval": int(d.LogMinDelayReqInterval), @@ -205,10 +203,13 @@ func portDSMap(d *portDataSetTLV) map[string]interface{} { "log-announce-interval": int(d.LogAnnounceInterval), "announce-receipt-timeout": int(d.AnnounceReceiptTimeout), "log-sync-interval": int(d.LogSyncInterval), - "delay-mechanism": delayMechanismName(d.DelayMechanism), "log-min-pdelay-req-interval": int(d.LogMinPdelayReqInterval), "version-number": int(d.VersionNumber), } + if dm, ok := delayMechanismName[d.DelayMechanism]; ok { + ds["delay-mechanism"] = dm + } + return ds } // IEEE 1588 message type indices in the PORT_STATS_NP counter arrays. diff --git a/src/yangerd/internal/ptpmonitor/ptpmonitor_test.go b/src/yangerd/internal/ptpmonitor/ptpmonitor_test.go index 8f2564d0d0..97e5515482 100644 --- a/src/yangerd/internal/ptpmonitor/ptpmonitor_test.go +++ b/src/yangerd/internal/ptpmonitor/ptpmonitor_test.go @@ -199,7 +199,8 @@ func TestPortDSMapStates(t *testing.T) { }{ {6, 1, "time-transmitter", "e2e"}, {9, 2, "time-receiver", "p2p"}, - {4, 0, "listening", "no-mechanism"}, + {9, 3, "time-receiver", "common-p2p"}, + {4, 0xfe, "listening", "no-mechanism"}, {99, 1, "disabled", "e2e"}, /* unknown state */ } @@ -212,6 +213,10 @@ func TestPortDSMapStates(t *testing.T) { t.Fatalf("mech %d: got %v, want %v", tc.mech, ds["delay-mechanism"], tc.mechS) } } + + if ds := portDSMap(&portDataSetTLV{DelayMechanism: 0}); ds["delay-mechanism"] != nil { + t.Fatalf("mech 0: got %v, want no leaf", ds["delay-mechanism"]) + } } func TestTimePropertiesUtcOffsetWhenCondition(t *testing.T) { diff --git a/test/case/routing/ospf_bfd/test.py b/test/case/routing/ospf_bfd/test.py index 34cf3df08e..9be6f7e01a 100755 --- a/test/case/routing/ospf_bfd/test.py +++ b/test/case/routing/ospf_bfd/test.py @@ -30,13 +30,15 @@ def __init__(self): self.add_argument("--version", type=str.lower, choices=["ospfv2", "ospfv3"]) +# Generous OSPF dead interval, so that a fail-over completing well within +# it can only have been triggered by BFD. Also the ospf6d wait timer, so +# OSPFv3 DR election takes this long. +DEAD = 60 + PARAM = { "ospfv2": { "af": "ipv4", "len": {"data": 24, "link": 30}, - # Generous OSPF dead interval, so that a fail-over completing well - # within it can only have been triggered by BFD. - "dead": 60, "R1": {"rid": "192.168.1.1", "data": "192.168.10.1", "fast": "192.168.100.1", "slow": "192.168.200.1"}, "R2": {"rid": "192.168.1.2", "data": "192.168.20.1", @@ -49,9 +51,6 @@ def __init__(self): "ospfv3": { "af": "ipv6", "len": {"data": 64, "link": 64}, - # ospf6d only starts DR election once its wait timer, one dead - # interval, has expired, so keep the dead interval down. - "dead": 10, "R1": {"rid": "192.168.1.1", "data": "2001:db8:10::1", "fast": "2001:db8:100::1", "slow": "2001:db8:200::1"}, "R2": {"rid": "192.168.1.2", "data": "2001:db8:20::1", @@ -87,7 +86,7 @@ def ospf_interface(ifname, cost): }, "name": ifname, "hello-interval": 1, - "dead-interval": p["dead"], + "dead-interval": DEAD, "cost": cost, } @@ -236,9 +235,9 @@ def failed_over(): start = time.monotonic() until(failed_over, attempts=30) took = time.monotonic() - start - assert took < param["dead"] / 2, \ - f"Fail-over took {took:.1f}s, too slow for BFD; " \ - f"OSPF dead interval ({param['dead']}s) suspected" + print(f"Fail-over took {took:.1f}s") + assert took < DEAD / 2, \ + f"Fail-over too slow for BFD, OSPF dead interval ({DEAD}s) suspected" with test.step("Verify connectivity from PC:src to PC:dst via slow link"): h1net.must_reach(param["h2"]) diff --git a/test/infamy/firewall.py b/test/infamy/firewall.py index bce25252de..041db66900 100644 --- a/test/infamy/firewall.py +++ b/test/infamy/firewall.py @@ -58,7 +58,7 @@ def _match(expected, actual): return str(expected) == str(actual) @staticmethod - def wait_for_operational(target, expected_zones, timeout=30): + def wait_for_operational(target, expected_zones, timeout=90): """Wait for firewall config to be activated/available in operational""" def check_operational(): try: diff --git a/test/infamy/restconf.py b/test/infamy/restconf.py index 6e95153675..de41da0044 100644 --- a/test/infamy/restconf.py +++ b/test/infamy/restconf.py @@ -17,10 +17,14 @@ # We know we have a self-signed certificate, silence warning about it warnings.simplefilter('ignore', InsecureRequestWarning) -# (connect, read) in seconds. nginx gives up on rousette after 60 s -# and replies 504, so only a lost request or reply reaches the read -# timeout. Without one, requests waits forever. -TIMEOUT = (10, 90) +# (connect, read) in seconds. A firewall reload drops new connections +# until it is done, which takes over half a minute on a single-core +# system. nginx gives up on rousette after 60 s and replies 504, so +# only a lost request or reply reaches the read timeout. Without one, +# requests waits forever. Reachability probes are retried by their +# caller, so they give up sooner. +TIMEOUT = (60, 90) +PROBE_TIMEOUT = (10, 90) @dataclass class Location: @@ -53,7 +57,8 @@ def xpath_to_uri(xpath, extra=None): # Workaround for bug in requests 2.32.x: https://github.com/psf/requests/issues/6735 -def requests_workaround(method, url, json, headers, auth, verify=False, retry=0): +def requests_workaround(method, url, json, headers, auth, verify=False, retry=0, + timeout=TIMEOUT): # Create a session session = requests.Session() @@ -64,7 +69,7 @@ def requests_workaround(method, url, json, headers, auth, verify=False, retry=0) # A zone id like %sw2p2 is not a valid escape, which makes requests # re-quote every '%' in the URL, mangling percent-encoded list keys prepared_request.url = url - response = session.send(prepared_request, verify=verify, timeout=TIMEOUT) + response = session.send(prepared_request, verify=verify, timeout=timeout) try: # Raise exceptions for HTTP errors response.raise_for_status() @@ -75,7 +80,7 @@ def requests_workaround(method, url, json, headers, auth, verify=False, retry=0) print(f"{method} {url}: HTTP error 502, retrying({retry})") time.sleep(1) response = requests_workaround(method, url, json, headers, auth, - verify, retry) + verify, retry, timeout) else: raise e @@ -111,8 +116,8 @@ def restconf_reachable(neigh, password): url = f"https://[{neigh}]/restconf/data/ietf-system:system/hostname" auth = HTTPBasicAuth("admin", password) - response = requests_workaround_get(url, headers=headers, auth=auth, - verify=False) + response = requests_workaround('GET', url, None, headers, auth, + timeout=PROBE_TIMEOUT) if response.status_code == 200: return True except: 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