Merge Official Source

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen
2026-05-26 12:23:08 +08:00
273 changed files with 10352 additions and 1240 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_VERSION:=2025.10
PKG_HASH:=b4f032848e56cc8f213ad59f9132c084dbbb632bc29176d024e58220e0efdf4a
PKG_VERSION:=2026.04
PKG_HASH:=ac7c04b8b7004923b00a4e5d6699c5df4d21233bac9fda690d8cfbc209fff2fd
UBOOT_USE_INTREE_DTC:=1
+62 -49
View File
@@ -421,6 +421,19 @@ define U-Boot/mt7981_cudy_wr3000p-v1
DEPENDS:=+trusted-firmware-a-mt7981-cudy-ddr4
endef
define U-Boot/mt7981_gatonetworks_gdsp
NAME:=GatoNetworks GDSP
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=gatonetworks_gdsp
UBOOT_CONFIG:=mt7981_gatonetworks_gdsp
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=nor
BL2_SOC:=mt7981
BL2_DDRTYPE:=ddr3
DEPENDS:=+trusted-firmware-a-mt7981-nor-ddr3
FIP_COMPRESS:=1
endef
define U-Boot/mt7981_glinet_gl-mt2500
NAME:=GL.iNet GL-MT2500
BUILD_SUBTARGET:=filogic
@@ -554,6 +567,18 @@ define U-Boot/mt7981_livinet_zr-3020
DEPENDS:=+trusted-firmware-a-mt7981-spim-nand-ddr3
endef
define U-Boot/mt7981_netis_eap930-v1
NAME:=netis EAP930 V1
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=netis_eap930-v1
UBOOT_CONFIG:=mt7981_netis_eap930-v1
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=spim-nand
BL2_SOC:=mt7981
BL2_DDRTYPE:=ddr3
DEPENDS:=+trusted-firmware-a-mt7981-spim-nand-ddr3
endef
define U-Boot/mt7981_netis_nx30v2
NAME:=netis NX30 V2
BUILD_SUBTARGET:=filogic
@@ -651,19 +676,6 @@ define U-Boot/mt7981_rfb-emmc
DEPENDS:=+trusted-firmware-a-mt7981-emmc-ddr3
endef
define U-Boot/mt7981_gatonetworks_gdsp
NAME:=GatoNetworks GDSP
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=gatonetworks_gdsp
UBOOT_CONFIG:=mt7981_gatonetworks_gdsp
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=nor
BL2_SOC:=mt7981
BL2_DDRTYPE:=ddr3
DEPENDS:=+trusted-firmware-a-mt7981-nor-ddr3
FIP_COMPRESS:=1
endef
define U-Boot/mt7981_rfb-nor
NAME:=MT7981 Reference Board
BUILD_SUBTARGET:=filogic
@@ -1074,6 +1086,42 @@ define U-Boot/mt7987_bananapi_bpi-r4-lite-nor
FIP_COMPRESS:=1
endef
define U-Boot/mt7987_rfb-emmc
NAME:=MT7987 Reference Board
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=mediatek_mt7987a-rfb
UBOOT_CONFIG:=mt7987_emmc_rfb
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=emmc
BL2_SOC:=mt7987
BL2_DDRTYPE:=comb
DEPENDS:=+trusted-firmware-a-mt7987-emmc-comb
endef
define U-Boot/mt7987_rfb-spim-nand
NAME:=MT7987 Reference Board
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=mediatek_mt7987a-rfb
UBOOT_CONFIG:=mt7987_rfb
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=spim-nand0-ubi
BL2_SOC:=mt7987
BL2_DDRTYPE:=comb
DEPENDS:=+trusted-firmware-a-mt7987-spim-nand0-ubi-comb
endef
define U-Boot/mt7987_rfb-sd
NAME:=MT7987 Reference Board
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=mediatek_mt7987a-rfb
UBOOT_CONFIG:=mt7987_sd_rfb
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=sdmmc
BL2_SOC:=mt7987
BL2_DDRTYPE:=comb
DEPENDS:=+trusted-firmware-a-mt7987-sdmmc-comb
endef
define U-Boot/mt7987_routerich_be7200
NAME:=Routerich BE7200
BUILD_SUBTARGET:=filogic
@@ -1217,42 +1265,6 @@ define U-Boot/mt7988_tplink_tl-7dr7250-v1
DEPENDS:=+trusted-firmware-a-mt7988-spim-nand-ddr4
endef
define U-Boot/mt7987_rfb-emmc
NAME:=MT7987 Reference Board
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=mediatek_mt7987a-rfb
UBOOT_CONFIG:=mt7987_emmc_rfb
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=sdmmc
BL2_SOC:=mt7987
BL2_DDRTYPE:=comb
DEPENDS:=+trusted-firmware-a-mt7987-emmc-comb
endef
define U-Boot/mt7987_rfb-spim-nand
NAME:=MT7987 Reference Board
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=mediatek_mt7987a-rfb
UBOOT_CONFIG:=mt7987_rfb
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=spim-nand0-ubi
BL2_SOC:=mt7987
BL2_DDRTYPE:=comb
DEPENDS:=+trusted-firmware-a-mt7987-spim-nand0-ubi-comb
endef
define U-Boot/mt7987_rfb-sd
NAME:=MT7987 Reference Board
BUILD_SUBTARGET:=filogic
BUILD_DEVICES:=mediatek_mt7987a-rfb
UBOOT_CONFIG:=mt7987_sd_rfb
UBOOT_IMAGE:=u-boot.fip
BL2_BOOTDEV:=sdmmc
BL2_SOC:=mt7987
BL2_DDRTYPE:=comb
DEPENDS:=+trusted-firmware-a-mt7987-sdmmc-comb
endef
define U-Boot/mt7988_rfb-spim-nand
NAME:=MT7988 Reference Board
BUILD_SUBTARGET:=filogic
@@ -1363,6 +1375,7 @@ UBOOT_TARGETS := \
mt7981_konka_komi-a31-emmc \
mt7981_konka_komi-a31-nor \
mt7981_livinet_zr-3020 \
mt7981_netis_eap930-v1 \
mt7981_netis_nx30v2 \
mt7981_netis_nx31 \
mt7981_netis_nx32u \
@@ -0,0 +1,91 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ryan Leung <untilscour@protonmail.com>
Date: Wed, 20 May 2026 15:16:13 +1000
Subject: [PATCH] mtd: nand: spi: otp: add NULL guards to OTP size functions
U-Boot v2026.01 introduced drivers/mtd/nand/spi/otp.c which adds `spinand_user_otp_size()` and
`spinand_fact_otp_size()`. These functions are called unconditionally from `spinand_init()` in
drivers/mtd/nand/spi/core.c to determine whether to set up OTP methods:
if (spinand_user_otp_size(spinand) || spinand_fact_otp_size(spinand)) {
ret = spinand_set_mtd_otp_ops(spinand); /* drivers/mtd/nand/spi/core.c:2369 */
...
}
Both OTP size functions pass `&spinand->user_otp->layout` or `&spinand->fact_otp->layout` to
`spinand_otp_size()` without first checking whether the pointer is NULL. In the standard probing
path, these pointers are assigned by `spinand_match_and_init()` from the per-chip `spinand_info`
table. `100-21-mtd-spi-nand-add-CASN-page-support.patch` adds CASN detection to `spinand_detect()`
so that when a CASN page is found, `spinand_init_via_casn()` is called instead of
`spinand_id_detect()`. As `spinand_match_and_init()` is only called via `spinand_id_detect()`, it
is never invoked for CASN-probed devices.
As a result, `spinand->user_otp` and `spinand->fact_otp` remain NULL from the zero-initialised DM
priv allocation. Both `spinand_user_otp_size()` and `spinand_fact_otp_size()` spuriously return
non-zero by reading `layout->npages` from a small mapped address computed from their respective
NULL pointer, causing `spinand_init()` to call `spinand_set_mtd_otp_ops()` which reads
`spinand->user_otp->ops` and thereby silently assigns a garbage value to `user_ops` that is then
dereferenced at `if (user_ops->info)`.
int spinand_set_mtd_otp_ops(struct spinand_device *spinand)
{
struct mtd_info *mtd = spinand_to_mtd(spinand);
const struct spinand_fact_otp_ops *fact_ops = spinand->fact_otp->ops;
const struct spinand_user_otp_ops *user_ops = spinand->user_otp->ops;
...
if (user_ops) {
if (user_ops->info) /* drivers/mtd/nand/spi/otp.c:343 */
...
...
}
...
}
On COMFAST CF-WR632AX (MT7981, Winbond W25N01GV) this results in a "Synchronous Abort" during boot.
The crash was confirmed via `addr2line` on the U-Boot ELF:
"Synchronous Abort" handler, esr 0x96000004, far 0xeafffffeeafffffe
elr: 0000000041e2ff44 (drivers/mtd/nand/spi/otp.c:343)
lr : 0000000041e2f2f8 (drivers/mtd/nand/spi/core.c:2369)
The same was observed on Acer Predator Connect W6x (MT7986, Winbond W25N02KV):
"Synchronous Abort" handler, esr 0x96000004, far 0xeafffffeeafffffe
elr: 0000000041e2c868 (drivers/mtd/nand/spi/otp.c:343)
lr : 0000000041e2bc1c (drivers/mtd/nand/spi/core.c:2369)
Fix this by adding NULL guards to both OTP size functions so that they return 0 when the pointer is
NULL. With both functions returning 0, the condition in `spinand_init()` evaluates to false and
`spinand_set_mtd_otp_ops()` is never called, skipping setup of OTP methods entirely for CASN-probed
SPI NAND chips.
This change has no effect on SPI NAND chips that do have OTP data but not CASN. For those chips,
`spinand_match_and_init()` sets `spinand->user_otp` and `spinand->fact_otp` to non-NULL values from
the per-chip table, the NULL guard does not activate, and the existing behaviour is unchanged.
Fixes: b94de14bafd06660536691ed633f364edf5fbe4d ("uboot-mediatek: update to v2026.01")
Signed-off-by: Ryan Leung <untilscour@protonmail.com>
---
drivers/mtd/nand/spi/otp.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/drivers/mtd/nand/spi/otp.c
+++ b/drivers/mtd/nand/spi/otp.c
@@ -39,6 +39,8 @@ static size_t spinand_otp_size(struct sp
*/
size_t spinand_fact_otp_size(struct spinand_device *spinand)
{
+ if (!spinand->fact_otp)
+ return 0;
return spinand_otp_size(spinand, &spinand->fact_otp->layout);
}
@@ -50,6 +52,8 @@ size_t spinand_fact_otp_size(struct spin
*/
size_t spinand_user_otp_size(struct spinand_device *spinand)
{
+ if (!spinand->user_otp)
+ return 0;
return spinand_otp_size(spinand, &spinand->user_otp->layout);
}
@@ -167,7 +167,7 @@
+ label = "blue:wan";
+ gpios = <&pio 10 GPIO_ACTIVE_LOW>;
+ };
+
+
+ led_wan_red {
+ label = "red:wan";
+ gpios = <&pio 4 GPIO_ACTIVE_LOW>;
@@ -242,10 +242,10 @@
+ };
+
+ led-green {
+ label = "blue:status";
+ label = "green:status";
+ gpios = <&pio 30 GPIO_ACTIVE_HIGH>;
+ };
+
+
+ led-blue {
+ label = "blue:status";
+ gpios = <&pio 31 GPIO_ACTIVE_HIGH>;
@@ -0,0 +1,328 @@
--- /dev/null
+++ b/configs/mt7981_netis_eap930-v1_defconfig
@@ -0,0 +1,112 @@
+CONFIG_ARM=y
+CONFIG_ARM_SMCCC=y
+CONFIG_SYS_HAS_NONCACHED_MEMORY=y
+CONFIG_POSITION_INDEPENDENT=y
+CONFIG_ARCH_MEDIATEK=y
+CONFIG_TEXT_BASE=0x41e00000
+CONFIG_SYS_MALLOC_F_LEN=0x4000
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_DEFAULT_DEVICE_TREE="mt7981-netis_eap930-v1"
+CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_TARGET_MT7981=y
+CONFIG_SYS_LOAD_ADDR=0x46000000
+CONFIG_PRE_CON_BUF_ADDR=0x4007ef00
+CONFIG_DEBUG_UART_BASE=0x11002000
+CONFIG_DEBUG_UART_CLOCK=40000000
+CONFIG_DEBUG_UART=y
+CONFIG_FIT=y
+CONFIG_BOOTDELAY=30
+CONFIG_AUTOBOOT_KEYED=y
+CONFIG_AUTOBOOT_MENU_SHOW=y
+CONFIG_USE_PREBOOT=y
+CONFIG_DEFAULT_FDT_FILE="mediatek/mt7981-netis_eap930-v1.dtb"
+CONFIG_LOGLEVEL=7
+CONFIG_PRE_CONSOLE_BUFFER=y
+CONFIG_LOG=y
+# CONFIG_BOARD_INIT is not set
+CONFIG_BOARD_LATE_INIT=y
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="MT7981> "
+CONFIG_CMD_CPU=y
+CONFIG_CMD_LICENSE=y
+CONFIG_CMD_BOOTMENU=y
+CONFIG_CMD_ASKENV=y
+CONFIG_CMD_ERASEENV=y
+CONFIG_CMD_ENV_FLAGS=y
+CONFIG_CMD_RNG=y
+CONFIG_CMD_STRINGS=y
+CONFIG_CMD_DM=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_GPT=y
+CONFIG_CMD_MTD=y
+CONFIG_CMD_PART=y
+CONFIG_CMD_TFTPSRV=y
+CONFIG_CMD_RARP=y
+CONFIG_CMD_CDP=y
+CONFIG_CMD_SNTP=y
+CONFIG_CMD_LINK_LOCAL=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_DNS=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_PXE=y
+CONFIG_CMD_CACHE=y
+CONFIG_CMD_PSTORE=y
+CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000
+CONFIG_CMD_UUID=y
+CONFIG_CMD_HASH=y
+CONFIG_CMD_SMC=y
+CONFIG_CMD_UBI=y
+CONFIG_CMD_UBI_RENAME=y
+CONFIG_OF_EMBED=y
+CONFIG_ENV_OVERWRITE=y
+CONFIG_ENV_IS_IN_UBI=y
+CONFIG_ENV_REDUNDANT=y
+CONFIG_ENV_UBI_PART="ubi"
+CONFIG_ENV_UBI_VOLUME="ubootenv"
+CONFIG_ENV_UBI_VOLUME_REDUND="ubootenv2"
+CONFIG_ENV_RELOC_GD_ENV_ADDR=y
+CONFIG_ENV_USE_DEFAULT_ENV_TEXT_FILE=y
+CONFIG_ENV_DEFAULT_ENV_TEXT_FILE="defenvs/netis_eap930-v1_env"
+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
+CONFIG_VERSION_VARIABLE=y
+CONFIG_NETCONSOLE=y
+CONFIG_USE_IPADDR=y
+CONFIG_IPADDR="192.168.1.1"
+CONFIG_USE_SERVERIP=y
+CONFIG_SERVERIP="192.168.1.254"
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_BUTTON=y
+CONFIG_BUTTON_GPIO=y
+CONFIG_CLK=y
+CONFIG_GPIO_HOG=y
+CONFIG_LED=y
+CONFIG_LED_BLINK=y
+CONFIG_LED_GPIO=y
+# CONFIG_MMC is not set
+CONFIG_MTD=y
+CONFIG_DM_MTD=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_MTD_UBI_FASTMAP=y
+CONFIG_PHY_FIXED=y
+CONFIG_MEDIATEK_ETH=y
+CONFIG_PHY=y
+CONFIG_PHY_MTK_TPHY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCONF=y
+CONFIG_PINCTRL_MT7981=y
+CONFIG_POWER_DOMAIN=y
+CONFIG_MTK_POWER_DOMAIN=y
+CONFIG_DM_REGULATOR=y
+CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_DM_REGULATOR_GPIO=y
+CONFIG_RAM=y
+CONFIG_DM_RNG=y
+CONFIG_RNG_MTK_V2=y
+CONFIG_DM_SERIAL=y
+CONFIG_SERIAL_RX_BUFFER=y
+CONFIG_MTK_SERIAL=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_MTK_SPIM=y
+CONFIG_ZSTD=y
+CONFIG_HEXDUMP=y
--- /dev/null
+++ b/arch/arm/dts/mt7981-netis_eap930-v1.dts
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026
+ * Author: Mikhail Zhilkin <csharper2005@gmail.com>
+ */
+
+/dts-v1/;
+#include "mt7981.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/linux-event-codes.h>
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ model = "netis EAP930 V1";
+ compatible = "netis,eap930-v1", "mediatek,mt7981";
+
+ chosen {
+ stdout-path = &uart0;
+ tick-timer = &timer0;
+ };
+
+ memory@40000000 {
+ device_type = "memory";
+ reg = <0x40000000 0x10000000>;
+ };
+
+ keys {
+ compatible = "gpio-keys";
+
+ button-0 {
+ label = "reset";
+ linux,code = <KEY_RESTART>;
+ gpios = <&pio 1 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led-0 {
+ label = "red:system";
+ gpios = <&pio 6 GPIO_ACTIVE_LOW>;
+ };
+
+ led-1 {
+ label = "blue:system";
+ gpios = <&pio 7 GPIO_ACTIVE_LOW>;
+ };
+ };
+};
+
+&uart0 {
+ mediatek,force-highspeed;
+ status = "okay";
+};
+
+&eth {
+ status = "okay";
+ mediatek,gmac-id = <1>;
+ phy-mode = "gmii";
+ phy-handle = <&int_gbe_phy>;
+
+ mdio {
+ int_gbe_phy: ethernet-phy@0 {
+ reg = <0>;
+ compatible = "ethernet-phy-ieee802.3-c22";
+ phy-mode = "gmii";
+ phy-is-integrated;
+ };
+ };
+};
+
+&pio {
+ spi_flash_pins: spi0-pins-func-1 {
+ mux {
+ function = "flash";
+ groups = "spi0", "spi0_wp_hold";
+ };
+
+ conf-pu {
+ pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP";
+ drive-strength = <MTK_DRIVE_8mA>;
+ bias-pull-up = <MTK_PUPD_SET_R1R0_11>;
+ };
+
+ conf-pd {
+ pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO";
+ drive-strength = <MTK_DRIVE_8mA>;
+ bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
+ };
+ };
+};
+
+&spi0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi_flash_pins>;
+ status = "okay";
+ must_tx;
+ enhance_timing;
+ dma_ext;
+ ipm_design;
+ support_quad;
+ tick_dly = <2>;
+ sample_sel = <0>;
+
+ spi_nand@0 {
+ compatible = "spi-nand";
+ reg = <0>;
+ spi-max-frequency = <52000000>;
+
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ label = "BL2";
+ reg = <0x0 0x100000>;
+ };
+
+ partition@100000 {
+ label = "u-boot-env";
+ reg = <0x100000 0x80000>;
+ };
+
+ partition@180000 {
+ label = "Factory";
+ reg = <0x180000 0x200000>;
+ };
+
+ partition@380000 {
+ label = "FIP";
+ reg = <0x380000 0x200000>;
+ };
+
+ partition@580000 {
+ label = "ubi";
+ reg = <0x580000 0x7a80000>;
+ };
+ };
+ };
+};
+
+&watchdog {
+ status = "disabled";
+};
--- /dev/null
+++ b/defenvs/netis_eap930-v1_env
@@ -0,0 +1,58 @@
+ipaddr=192.168.1.1
+serverip=192.168.1.254
+loadaddr=0x46000000
+console=earlycon=uart8250,mmio32,0x11002000 console=ttyS0
+bootcmd=run check_buttons ; if pstore check ; then run boot_recovery ; else run boot_ubi ; fi
+bootconf=config-1
+bootdelay=0
+bootfile=immortalwrt-mediatek-filogic-netis_eap930-v1-initramfs-recovery.itb
+bootfile_bl2=immortalwrt-mediatek-filogic-netis_eap930-v1-preloader.bin
+bootfile_fip=immortalwrt-mediatek-filogic-netis_eap930-v1-bl31-uboot.fip
+bootfile_upg=immortalwrt-mediatek-filogic-netis_eap930-v1-squashfs-sysupgrade.itb
+bootled_status=red:system
+bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60
+bootmenu_default=0
+bootmenu_delay=0
+bootmenu_title= ( ( ( OpenWrt ) ) )
+bootmenu_0=Initialize environment.=run _firstboot
+bootmenu_0d=Run default boot command.=run boot_default
+bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
+bootmenu_2=Boot production system from NAND.=run boot_production ; run bootmenu_confirm_return
+bootmenu_3=Boot recovery system from NAND.=run boot_recovery ; run bootmenu_confirm_return
+bootmenu_4=Load production system via TFTP then write to NAND.=setenv noboot 1 ; setenv replacevol 1 ; run boot_tftp_production ; setenv noboot ; setenv replacevol ; run bootmenu_confirm_return
+bootmenu_5=Load recovery system via TFTP then write to NAND.=setenv noboot 1 ; setenv replacevol 1 ; run boot_tftp_recovery ; setenv noboot ; setenv replacevol ; run bootmenu_confirm_return
+bootmenu_6=Load BL31+U-Boot FIP via TFTP then write to NAND.=run boot_tftp_write_fip ; run bootmenu_confirm_return
+bootmenu_7=Load BL2 preloader via TFTP then write to NAND.=run boot_tftp_write_bl2 ; run bootmenu_confirm_return
+bootmenu_8=Reboot.=reset
+bootmenu_9=Reset all settings to factory defaults.=run reset_factory ; reset
+boot_first=if button reset ; then led $bootled_status on ; run boot_default ; fi ; bootmenu
+boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; setenv replacevol 1 ; run boot_tftp_forever
+boot_production=led $bootled_status on ; run ubi_read_production && bootm $loadaddr#$bootconf ; led $bootled_status off
+boot_recovery=led $bootled_status on ; run ubi_read_recovery && bootm $loadaddr#$bootconf ; led $bootled_status off
+boot_ubi=run boot_production ; run boot_recovery ; run boot_tftp_forever
+boot_tftp_forever=led $bootled_status on ; while true ; do run boot_tftp ; sleep 1 ; done
+boot_tftp_production=tftpboot $loadaddr $bootfile_upg && env exists replacevol && iminfo $loadaddr && run ubi_write_production ; if env exists noboot ; then else bootm $loadaddr#$bootconf ; fi
+boot_tftp_recovery=tftpboot $loadaddr $bootfile && env exists replacevol && iminfo $loadaddr && run ubi_write_recovery ; if env exists noboot ; then else bootm $loadaddr#$bootconf ; fi
+boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr#$bootconf
+boot_tftp_write_fip=tftpboot $loadaddr $bootfile_fip && run mtd_write_fip && run reset_factory
+boot_tftp_write_bl2=tftpboot $loadaddr $bootfile_bl2 && run mtd_write_bl2
+preboot=led $bootled_status on
+check_buttons=if button reset ; then run boot_tftp ; fi
+ethaddr_factory=mtd read Factory 0x40080000 0x1e0000 0x20000 && env readmem -b ethaddr 0x4009ef20 0x6 ; setenv ethaddr_factory
+part_default=production
+part_recovery=recovery
+reset_factory=ubi part ubi ; mw $loadaddr 0x0 0x800 ; ubi write $loadaddr ubootenv 0x800 ; ubi write $loadaddr ubootenv2 0x800
+mtd_write_fip=mtd erase FIP && mtd write FIP $loadaddr
+mtd_write_bl2=mtd erase BL2 && mtd write BL2 $loadaddr
+ubi_create_env=ubi check ubootenv || ubi create ubootenv 0x100000 dynamic || run ubi_format ; ubi check ubootenv2 || ubi create ubootenv2 0x100000 dynamic || run ubi_format
+ubi_format=ubi detach ; mtd erase ubi && ubi part ubi ; reset
+ubi_prepare_rootfs=if ubi check rootfs_data ; then else if env exists rootfs_data_max ; then ubi create rootfs_data $rootfs_data_max dynamic || ubi create rootfs_data - dynamic ; else ubi create rootfs_data - dynamic ; fi ; fi
+ubi_read_production=ubi read $loadaddr fit && iminfo $loadaddr && run ubi_prepare_rootfs
+ubi_read_recovery=ubi check recovery && ubi read $loadaddr recovery
+ubi_remove_rootfs=ubi check rootfs_data && ubi remove rootfs_data
+ubi_write_production=ubi check fit && ubi remove fit ; run ubi_remove_rootfs ; ubi create fit $filesize dynamic && ubi write $loadaddr fit $filesize
+ubi_write_recovery=ubi check recovery && ubi remove recovery ; run ubi_remove_rootfs ; ubi create recovery $filesize dynamic && ubi write $loadaddr recovery $filesize
+_init_env=setenv _init_env ; run ubi_create_env ; saveenv ; saveenv
+_firstboot=setenv _firstboot ; run ethaddr_factory ; run _switch_to_menu ; run _init_env ; run boot_first
+_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
+_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title $ver"
@@ -167,7 +167,7 @@
+/ {
+ model = "Zyxel wx5600-T0 ubootmod";
+ compatible = "zyxel,wx-5600", "mediatek,mt7986";
+
+
+ options {
+ u-boot {
+ compatible = "u-boot,config";
@@ -1,6 +1,6 @@
--- /dev/null
+++ b/arch/arm/dts/mt7981-globitel-bt-r320.dts
@@ -0,0 +1,160 @@
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/dts-v1/;
@@ -161,7 +161,7 @@
+};
--- /dev/null
+++ b/defenvs/mt7981_globitel_bt-r320_env
@@ -0,0 +1,52 @@
@@ -0,0 +1,53 @@
+ipaddr=192.168.1.1
+serverip=192.168.1.254
+loadaddr=0x46000000
@@ -217,7 +217,7 @@
+_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title $ver"
--- /dev/null
+++ b/configs/mt7981_globitel_bt-r320_defconfig
@@ -0,0 +1,126 @@
@@ -0,0 +1,128 @@
+CONFIG_ARM=y
+CONFIG_ARM_SMCCC=y
+CONFIG_SYS_HAS_NONCACHED_MEMORY=y
@@ -165,7 +165,7 @@
+ label = "blue:power";
+ gpios = <&pio 13 GPIO_ACTIVE_LOW>;
+ };
+
+
+ led_warning: led_fault {
+ label = "red:fault";
+ gpios = <&pio 34 GPIO_ACTIVE_LOW>;
@@ -41,6 +41,7 @@ livinet,zr-3020-ubootmod|\
mercusys,mr90x-v1-ubi|\
netcore,n60|\
netcore,n60-pro|\
netis,eap930-v1|\
netis,nx30v2|\
netis,nx31|\
netis,nx32u|\
@@ -34,7 +34,8 @@ linksys,e8450-ubi)
;;
esac
;;
buffalo,wsr-2533dhp2)
buffalo,wsr-2533dhp2|\
buffalo,wsr-2533dhp3)
ubootenv_add_uci_config "/dev/mtd3" "0x0" "0x1000" "0x20000"
;;
dlink,eagle-pro-ai-m32-a1|\
@@ -21,6 +21,9 @@ checkpoint,v-80|\
checkpoint,v-81)
ubootenv_add_uci_config "/dev/mmcblk1boot0" "0x1f0000" "0x10000"
;;
cznic,turris-mox)
ubootenv_add_uci_config "/dev/mtd2" "0x0" "0x10000" "0x10000"
;;
cznic,turris-omnia)
idx="$(find_mtd_index u-boot-env)"
if [ -n "$idx" ]; then
@@ -500,13 +500,6 @@ static int gpio_keys_button_probe(struct platform_device *pdev,
goto out;
}
if (button->irq) {
dev_err(dev, "skipping button %s (only gpio buttons supported)\n",
button->desc);
bdata->b = &pdata->buttons[i];
continue;
}
if (gpio_is_valid(button->gpio)) {
/* legacy platform data... but is it the lookup table? */
bdata->gpiod = devm_gpiod_get_index(dev, desc, i,
@@ -541,6 +534,13 @@ static int gpio_keys_button_probe(struct platform_device *pdev,
if (IS_ERR_OR_NULL(bdata->gpiod)) {
error = IS_ERR(bdata->gpiod) ? PTR_ERR(bdata->gpiod) :
-EINVAL;
if (button->irq && error == -ENOENT) {
dev_err(dev, "skipping button %s (only gpio buttons supported)\n",
button->desc);
continue;
}
goto out;
}
@@ -598,6 +598,9 @@ static int gpio_keys_probe(struct platform_device *pdev)
struct gpio_keys_button_data *bdata = &bdev->data[i];
unsigned long irqflags = IRQF_ONESHOT;
if (!bdata->gpiod)
continue;
INIT_DELAYED_WORK(&bdata->work, gpio_keys_irq_work_func);
if (!button->irq) {
@@ -662,8 +665,10 @@ static void gpio_keys_irq_close(struct gpio_keys_button_dev *bdev)
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button_data *bdata = &bdev->data[i];
disable_irq(bdata->irq);
cancel_delayed_work_sync(&bdata->work);
if (bdata->irq) {
disable_irq(bdata->irq);
cancel_delayed_work_sync(&bdata->work);
}
}
}
+3 -3
View File
@@ -11,9 +11,9 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/iwinfo.git
PKG_SOURCE_DATE:=2026-01-14
PKG_SOURCE_VERSION:=f5dd57a84cc31a403a1383dd14944fa2e2b5824a
PKG_MIRROR_HASH:=a9ed29ea9f222d222fc554238e9c79675e1784293685198f759785da9940571f
PKG_SOURCE_DATE:=2026-05-26
PKG_SOURCE_VERSION:=66bdd1a071895d91babc9b9228bb84626bbce226
PKG_MIRROR_HASH:=e58dab2e33c52b0c217134e81b3ba5376b7a3b57dc1885edf62826573d9f6292
PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
PKG_LICENSE:=GPL-2.0
@@ -235,6 +235,7 @@
&gdm4 {
status = "okay";
managed = "in-band-status";
phy-handle = <&as21xx_1>;
phy-mode = "usxgmii";
};
@@ -1,42 +0,0 @@
From 1d304174106c93ce05f6088813ad7203b3eb381a Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 12 Oct 2024 11:01:11 +0200
Subject: [PATCH] net: airoha: Implement BQL support
Introduce BQL support in the airoha_eth driver reporting to the kernel
info about tx hw DMA queues in order to avoid bufferbloat and keep the
latency small.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20241012-en7581-bql-v2-1-4deb4efdb60b@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/mediatek/airoha_eth.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
--- a/drivers/net/ethernet/mediatek/airoha_eth.c
+++ b/drivers/net/ethernet/mediatek/airoha_eth.c
@@ -1729,9 +1729,11 @@ static int airoha_qdma_tx_napi_poll(stru
WRITE_ONCE(desc->msg1, 0);
if (skb) {
+ u16 queue = skb_get_queue_mapping(skb);
struct netdev_queue *txq;
- txq = netdev_get_tx_queue(skb->dev, qid);
+ txq = netdev_get_tx_queue(skb->dev, queue);
+ netdev_tx_completed_queue(txq, 1, skb->len);
if (netif_tx_queue_stopped(txq) &&
q->ndesc - q->queued >= q->free_thr)
netif_tx_wake_queue(txq);
@@ -2510,7 +2512,9 @@ static netdev_tx_t airoha_dev_xmit(struc
q->queued += i;
skb_tx_timestamp(skb);
- if (!netdev_xmit_more())
+ netdev_tx_sent_queue(txq, skb->len);
+
+ if (netif_xmit_stopped(txq) || !netdev_xmit_more())
airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid),
TX_RING_CPU_IDX_MASK,
FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head));
@@ -17,7 +17,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/mediatek/airoha_eth.c
+++ b/drivers/net/ethernet/mediatek/airoha_eth.c
@@ -1685,8 +1685,12 @@ static int airoha_qdma_tx_napi_poll(stru
@@ -1690,8 +1690,12 @@ static int airoha_qdma_tx_napi_poll(stru
irq_queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status);
while (irq_queued > 0 && done < budget) {
@@ -31,7 +31,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (val == 0xff)
break;
@@ -1696,9 +1700,7 @@ static int airoha_qdma_tx_napi_poll(stru
@@ -1701,9 +1705,7 @@ static int airoha_qdma_tx_napi_poll(stru
irq_queued--;
done++;
@@ -41,7 +41,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (qid >= ARRAY_SIZE(qdma->q_tx))
continue;
@@ -1706,46 +1708,53 @@ static int airoha_qdma_tx_napi_poll(stru
@@ -1711,46 +1713,53 @@ static int airoha_qdma_tx_napi_poll(stru
if (!q->ndesc)
continue;
@@ -14,7 +14,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/mediatek/airoha_eth.c
+++ b/drivers/net/ethernet/mediatek/airoha_eth.c
@@ -1805,6 +1805,10 @@ static int airoha_qdma_init_tx_queue(str
@@ -1810,6 +1810,10 @@ static int airoha_qdma_init_tx_queue(str
WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val));
}
@@ -29,7 +29,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
#define AIROHA_NUM_TX_RING 32
#define AIROHA_NUM_RX_RING 32
#define AIROHA_FE_MC_MAX_VLAN_TABLE 64
@@ -2437,21 +2439,44 @@ static void airoha_dev_get_stats64(struc
@@ -2442,21 +2444,44 @@ static void airoha_dev_get_stats64(struc
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
@@ -76,7 +76,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
if (skb->ip_summed == CHECKSUM_PARTIAL)
msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) |
FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) |
@@ -2625,6 +2650,7 @@ static const struct net_device_ops airoh
@@ -2630,6 +2655,7 @@ static const struct net_device_ops airoh
.ndo_init = airoha_dev_init,
.ndo_open = airoha_dev_open,
.ndo_stop = airoha_dev_stop,
@@ -100,7 +100,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
};
struct airoha_eth {
@@ -1977,6 +2018,27 @@ static void airoha_qdma_init_qos(struct
@@ -1982,6 +2023,27 @@ static void airoha_qdma_init_qos(struct
FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40));
}
@@ -128,7 +128,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static int airoha_qdma_hw_init(struct airoha_qdma *qdma)
{
int i;
@@ -2027,6 +2089,7 @@ static int airoha_qdma_hw_init(struct ai
@@ -2032,6 +2094,7 @@ static int airoha_qdma_hw_init(struct ai
airoha_qdma_set(qdma, REG_TXQ_CNGST_CFG,
TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN);
@@ -136,7 +136,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
return 0;
}
@@ -2646,6 +2709,135 @@ airoha_ethtool_get_rmon_stats(struct net
@@ -2651,6 +2714,135 @@ airoha_ethtool_get_rmon_stats(struct net
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
@@ -272,7 +272,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static const struct net_device_ops airoha_netdev_ops = {
.ndo_init = airoha_dev_init,
.ndo_open = airoha_dev_open,
@@ -2654,6 +2846,7 @@ static const struct net_device_ops airoh
@@ -2659,6 +2851,7 @@ static const struct net_device_ops airoh
.ndo_start_xmit = airoha_dev_xmit,
.ndo_get_stats64 = airoha_dev_get_stats64,
.ndo_set_mac_address = airoha_dev_set_macaddr,
@@ -280,7 +280,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
};
static const struct ethtool_ops airoha_ethtool_ops = {
@@ -2703,7 +2896,8 @@ static int airoha_alloc_gdm_port(struct
@@ -2708,7 +2901,8 @@ static int airoha_alloc_gdm_port(struct
dev->watchdog_timeo = 5 * HZ;
dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
@@ -94,7 +94,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
/* qos stats counters */
u64 cpu_tx_packets;
u64 fwd_tx_packets;
@@ -2825,6 +2866,243 @@ static int airoha_tc_setup_qdisc_ets(str
@@ -2830,6 +2871,243 @@ static int airoha_tc_setup_qdisc_ets(str
}
}
@@ -338,7 +338,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
@@ -2833,6 +3111,8 @@ static int airoha_dev_tc_setup(struct ne
@@ -2838,6 +3116,8 @@ static int airoha_dev_tc_setup(struct ne
switch (type) {
case TC_SETUP_QDISC_ETS:
return airoha_tc_setup_qdisc_ets(port, type_data);
@@ -347,7 +347,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
default:
return -EOPNOTSUPP;
}
@@ -2883,7 +3163,8 @@ static int airoha_alloc_gdm_port(struct
@@ -2888,7 +3168,8 @@ static int airoha_alloc_gdm_port(struct
}
dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port),
@@ -357,7 +357,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
if (!dev) {
dev_err(eth->dev, "alloc_etherdev failed\n");
return -ENOMEM;
@@ -2903,6 +3184,11 @@ static int airoha_alloc_gdm_port(struct
@@ -2908,6 +3189,11 @@ static int airoha_alloc_gdm_port(struct
dev->irq = qdma->irq;
SET_NETDEV_DEV(dev, eth->dev);
@@ -20,7 +20,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/mediatek/airoha_eth.c
+++ b/drivers/net/ethernet/mediatek/airoha_eth.c
@@ -2801,7 +2801,7 @@ static int airoha_qdma_set_tx_ets_sched(
@@ -2806,7 +2806,7 @@ static int airoha_qdma_set_tx_ets_sched(
struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params;
enum tx_sched_mode mode = TC_SCH_SP;
u16 w[AIROHA_NUM_QOS_QUEUES] = {};
@@ -29,7 +29,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (p->bands > AIROHA_NUM_QOS_QUEUES)
return -EINVAL;
@@ -2815,7 +2815,20 @@ static int airoha_qdma_set_tx_ets_sched(
@@ -2820,7 +2820,20 @@ static int airoha_qdma_set_tx_ets_sched(
if (nstrict == AIROHA_NUM_QOS_QUEUES - 1)
return -EINVAL;
@@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/mediatek/airoha_eth.c
+++ b/drivers/net/ethernet/mediatek/airoha_eth.c
@@ -2564,11 +2564,10 @@ static u16 airoha_dev_select_queue(struc
@@ -2569,11 +2569,10 @@ static u16 airoha_dev_select_queue(struc
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -37,7 +37,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
struct netdev_queue *txq;
struct airoha_queue *q;
void *data = skb->data;
@@ -2591,8 +2590,9 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2596,8 +2595,9 @@ static netdev_tx_t airoha_dev_xmit(struc
if (skb_cow_head(skb, 0))
goto error;
@@ -49,7 +49,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
tcp_hdr(skb)->check = (__force __sum16)csum;
msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1);
@@ -2621,7 +2621,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2626,7 +2626,7 @@ static netdev_tx_t airoha_dev_xmit(struc
for (i = 0; i < nr_frags; i++) {
struct airoha_qdma_desc *desc = &q->desc[index];
struct airoha_queue_entry *e = &q->entry[index];
@@ -16,7 +16,7 @@ Signed-off-by: Andrew Lunn <andrew@lunn.ch>
--- a/drivers/net/ethernet/mediatek/airoha_eth.c
+++ b/drivers/net/ethernet/mediatek/airoha_eth.c
@@ -2484,7 +2484,7 @@ static int airoha_dev_stop(struct net_de
@@ -2489,7 +2489,7 @@ static int airoha_dev_stop(struct net_de
{
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
@@ -25,7 +25,7 @@ Signed-off-by: Andrew Lunn <andrew@lunn.ch>
netif_tx_disable(dev);
err = airoha_set_gdm_ports(qdma->eth, false);
@@ -2495,6 +2495,14 @@ static int airoha_dev_stop(struct net_de
@@ -2500,6 +2500,14 @@ static int airoha_dev_stop(struct net_de
GLOBAL_CFG_TX_DMA_EN_MASK |
GLOBAL_CFG_RX_DMA_EN_MASK);
@@ -101,7 +101,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-obj-$(CONFIG_NET_AIROHA) += airoha_eth.o
--- /dev/null
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -0,0 +1,3373 @@
@@ -0,0 +1,3378 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 AIROHA Inc
@@ -1763,14 +1763,18 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+ dma_addr_t dma_addr;
+
+ q->buf_size = PAGE_SIZE / 2;
+ q->ndesc = ndesc;
+ q->qdma = qdma;
+
+ q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
+ q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
+ GFP_KERNEL);
+ if (!q->entry)
+ return -ENOMEM;
+
+ q->desc = dmam_alloc_coherent(eth->dev, ndesc * sizeof(*q->desc),
+ &dma_addr, GFP_KERNEL);
+ if (!q->desc)
+ return -ENOMEM;
+
+ q->page_pool = page_pool_create(&pp_params);
+ if (IS_ERR(q->page_pool)) {
+ int err = PTR_ERR(q->page_pool);
@@ -1779,11 +1783,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+ return err;
+ }
+
+ q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc),
+ &dma_addr, GFP_KERNEL);
+ if (!q->desc)
+ return -ENOMEM;
+
+ q->ndesc = ndesc;
+ netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
+
+ airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr);
@@ -1830,6 +1830,11 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+ }
+
+ q->head = q->tail;
+ /* Set RX_DMA_IDX to RX_CPU_IDX to notify the hw the QDMA RX ring is
+ * empty.
+ */
+ airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK,
+ FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head));
+ airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
+ FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail));
+}
@@ -3477,7 +3482,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+MODULE_DESCRIPTION("Ethernet driver for Airoha SoC");
--- a/drivers/net/ethernet/mediatek/airoha_eth.c
+++ /dev/null
@@ -1,3373 +0,0 @@
@@ -1,3378 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2024 AIROHA Inc
@@ -5139,14 +5144,18 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
- dma_addr_t dma_addr;
-
- q->buf_size = PAGE_SIZE / 2;
- q->ndesc = ndesc;
- q->qdma = qdma;
-
- q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
- q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
- GFP_KERNEL);
- if (!q->entry)
- return -ENOMEM;
-
- q->desc = dmam_alloc_coherent(eth->dev, ndesc * sizeof(*q->desc),
- &dma_addr, GFP_KERNEL);
- if (!q->desc)
- return -ENOMEM;
-
- q->page_pool = page_pool_create(&pp_params);
- if (IS_ERR(q->page_pool)) {
- int err = PTR_ERR(q->page_pool);
@@ -5155,11 +5164,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
- return err;
- }
-
- q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc),
- &dma_addr, GFP_KERNEL);
- if (!q->desc)
- return -ENOMEM;
-
- q->ndesc = ndesc;
- netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
-
- airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr);
@@ -5206,6 +5211,11 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
- }
-
- q->head = q->tail;
- /* Set RX_DMA_IDX to RX_CPU_IDX to notify the hw the QDMA RX ring is
- * empty.
- */
- airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK,
- FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head));
- airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
- FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail));
-}
@@ -74,7 +74,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
napi_gro_receive(&q->napi, skb);
done++;
@@ -1652,25 +1671,76 @@ static u16 airoha_dev_select_queue(struc
@@ -1657,25 +1676,76 @@ static u16 airoha_dev_select_queue(struc
return queue < dev->num_tx_queues ? queue : 0;
}
@@ -155,7 +155,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
if (skb->ip_summed == CHECKSUM_PARTIAL)
msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) |
FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) |
@@ -1701,6 +1771,8 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -1706,6 +1776,8 @@ static netdev_tx_t airoha_dev_xmit(struc
spin_lock_bh(&q->lock);
txq = netdev_get_tx_queue(dev, qid);
@@ -164,7 +164,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
if (q->queued + nr_frags > q->ndesc) {
/* not enough space in the queue */
netif_tx_stop_queue(txq);
@@ -1708,7 +1780,10 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -1713,7 +1785,10 @@ static netdev_tx_t airoha_dev_xmit(struc
return NETDEV_TX_BUSY;
}
@@ -175,7 +175,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
for (i = 0; i < nr_frags; i++) {
struct airoha_qdma_desc *desc = &q->desc[index];
struct airoha_queue_entry *e = &q->entry[index];
@@ -2239,6 +2314,37 @@ static const struct ethtool_ops airoha_e
@@ -2244,6 +2319,37 @@ static const struct ethtool_ops airoha_e
.get_rmon_stats = airoha_ethtool_get_rmon_stats,
};
@@ -213,7 +213,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np)
{
const __be32 *id_ptr = of_get_property(np, "reg", NULL);
@@ -2311,6 +2417,10 @@ static int airoha_alloc_gdm_port(struct
@@ -2316,6 +2422,10 @@ static int airoha_alloc_gdm_port(struct
port->id = id;
eth->ports[index] = port;
@@ -224,7 +224,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
return register_netdev(dev);
}
@@ -2403,8 +2513,10 @@ error_hw_cleanup:
@@ -2408,8 +2518,10 @@ error_hw_cleanup:
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -236,7 +236,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
}
free_netdev(eth->napi_dev);
platform_set_drvdata(pdev, NULL);
@@ -2429,6 +2541,7 @@ static void airoha_remove(struct platfor
@@ -2434,6 +2546,7 @@ static void airoha_remove(struct platfor
continue;
unregister_netdev(port->dev);
@@ -25,7 +25,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1578,6 +1578,7 @@ static int airoha_dev_open(struct net_de
@@ -1583,6 +1583,7 @@ static int airoha_dev_open(struct net_de
airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
GLOBAL_CFG_RX_DMA_EN_MASK);
@@ -33,7 +33,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
return 0;
}
@@ -1593,16 +1594,20 @@ static int airoha_dev_stop(struct net_de
@@ -1598,16 +1599,20 @@ static int airoha_dev_stop(struct net_de
if (err)
return err;
@@ -62,7 +62,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
}
return 0;
@@ -2345,13 +2350,14 @@ static void airoha_metadata_dst_free(str
@@ -2350,13 +2355,14 @@ static void airoha_metadata_dst_free(str
}
}
@@ -79,7 +79,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
u32 id;
if (!id_ptr) {
@@ -2360,14 +2366,14 @@ static int airoha_alloc_gdm_port(struct
@@ -2365,14 +2371,14 @@ static int airoha_alloc_gdm_port(struct
}
id = be32_to_cpup(id_ptr);
@@ -96,7 +96,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
dev_err(eth->dev, "duplicate gdm port id: %d\n", id);
return -EINVAL;
}
@@ -2415,7 +2421,7 @@ static int airoha_alloc_gdm_port(struct
@@ -2420,7 +2426,7 @@ static int airoha_alloc_gdm_port(struct
port->qdma = qdma;
port->dev = dev;
port->id = id;
@@ -105,7 +105,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
err = airoha_metadata_dst_alloc(port);
if (err)
@@ -2487,6 +2493,7 @@ static int airoha_probe(struct platform_
@@ -2492,6 +2498,7 @@ static int airoha_probe(struct platform_
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
airoha_qdma_start_napi(&eth->qdma[i]);
@@ -113,7 +113,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
for_each_child_of_node(pdev->dev.of_node, np) {
if (!of_device_is_compatible(np, "airoha,eth-mac"))
continue;
@@ -2494,7 +2501,7 @@ static int airoha_probe(struct platform_
@@ -2499,7 +2506,7 @@ static int airoha_probe(struct platform_
if (!of_device_is_available(np))
continue;
@@ -63,7 +63,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p),
GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
@@ -1630,8 +1621,11 @@ static int airoha_dev_set_macaddr(struct
@@ -1635,8 +1626,11 @@ static int airoha_dev_set_macaddr(struct
static int airoha_dev_init(struct net_device *dev)
{
struct airoha_gdm_port *port = netdev_priv(dev);
@@ -83,7 +83,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static void airoha_fe_maccr_init(struct airoha_eth *eth)
{
int p;
@@ -1555,7 +1528,7 @@ static int airoha_dev_open(struct net_de
@@ -1560,7 +1533,7 @@ static int airoha_dev_open(struct net_de
int err;
netif_tx_start_all_queues(dev);
@@ -92,7 +92,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
if (err)
return err;
@@ -1581,7 +1554,7 @@ static int airoha_dev_stop(struct net_de
@@ -1586,7 +1559,7 @@ static int airoha_dev_stop(struct net_de
int i, err;
netif_tx_disable(dev);
@@ -76,7 +76,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
napi_gro_receive(&q->napi, skb);
done++;
@@ -1317,6 +1326,10 @@ static int airoha_hw_init(struct platfor
@@ -1322,6 +1331,10 @@ static int airoha_hw_init(struct platfor
return err;
}
@@ -87,7 +87,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
set_bit(DEV_STATE_INITIALIZED, &eth->state);
return 0;
@@ -2181,6 +2194,47 @@ static int airoha_tc_htb_alloc_leaf_queu
@@ -2186,6 +2199,47 @@ static int airoha_tc_htb_alloc_leaf_queu
return 0;
}
@@ -135,7 +135,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue)
{
struct net_device *dev = port->dev;
@@ -2264,6 +2318,9 @@ static int airoha_dev_tc_setup(struct ne
@@ -2269,6 +2323,9 @@ static int airoha_dev_tc_setup(struct ne
return airoha_tc_setup_qdisc_ets(port, type_data);
case TC_SETUP_QDISC_HTB:
return airoha_tc_setup_qdisc_htb(port, type_data);
@@ -145,7 +145,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
default:
return -EOPNOTSUPP;
}
@@ -2519,6 +2576,7 @@ static void airoha_remove(struct platfor
@@ -2524,6 +2581,7 @@ static void airoha_remove(struct platfor
}
free_netdev(eth->napi_dev);
@@ -18,7 +18,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1604,14 +1604,81 @@ static int airoha_dev_set_macaddr(struct
@@ -1609,14 +1609,81 @@ static int airoha_dev_set_macaddr(struct
return 0;
}
@@ -34,7 +34,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK,
FIELD_PREP(CDM1_VLAN_MASK, 0x8100));
@@ -1536,9 +1531,9 @@ static void airoha_update_hw_stats(struc
@@ -1541,9 +1536,9 @@ static void airoha_update_hw_stats(struc
static int airoha_dev_open(struct net_device *dev)
{
@@ -45,7 +45,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
netif_tx_start_all_queues(dev);
err = airoha_set_vip_for_gdm_port(port, true);
@@ -1552,6 +1547,11 @@ static int airoha_dev_open(struct net_de
@@ -1557,6 +1552,11 @@ static int airoha_dev_open(struct net_de
airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
@@ -132,7 +132,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
airoha_qdma_fill_rx_queue(q);
@@ -1177,7 +1194,6 @@ static int airoha_qdma_hw_init(struct ai
@@ -1182,7 +1199,6 @@ static int airoha_qdma_hw_init(struct ai
}
airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG,
@@ -16,7 +16,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1721,6 +1721,20 @@ static void airoha_dev_get_stats64(struc
@@ -1726,6 +1726,20 @@ static void airoha_dev_get_stats64(struc
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
@@ -37,7 +37,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
struct net_device *sb_dev)
{
@@ -2413,6 +2427,7 @@ static const struct net_device_ops airoh
@@ -2418,6 +2432,7 @@ static const struct net_device_ops airoh
.ndo_init = airoha_dev_init,
.ndo_open = airoha_dev_open,
.ndo_stop = airoha_dev_stop,
@@ -17,7 +17,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2518,6 +2518,7 @@ static int airoha_alloc_gdm_port(struct
@@ -2523,6 +2523,7 @@ static int airoha_alloc_gdm_port(struct
NETIF_F_SG | NETIF_F_TSO |
NETIF_F_HW_TC;
dev->features |= dev->hw_features;
@@ -25,7 +25,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1757,18 +1757,13 @@ static u32 airoha_get_dsa_tag(struct sk_
@@ -1762,18 +1762,13 @@ static u32 airoha_get_dsa_tag(struct sk_
{
#if IS_ENABLED(CONFIG_NET_DSA)
struct ethhdr *ehdr;
@@ -66,7 +66,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2371,7 +2371,7 @@ static int airoha_tc_get_htb_get_leaf_qu
@@ -2376,7 +2376,7 @@ static int airoha_tc_get_htb_get_leaf_qu
return -EINVAL;
}
@@ -22,7 +22,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2044,7 +2044,7 @@ static int airoha_qdma_set_tx_ets_sched(
@@ -2049,7 +2049,7 @@ static int airoha_qdma_set_tx_ets_sched(
struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params;
enum tx_sched_mode mode = TC_SCH_SP;
u16 w[AIROHA_NUM_QOS_QUEUES] = {};
@@ -31,7 +31,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (p->bands > AIROHA_NUM_QOS_QUEUES)
return -EINVAL;
@@ -2062,17 +2062,17 @@ static int airoha_qdma_set_tx_ets_sched(
@@ -2067,17 +2067,17 @@ static int airoha_qdma_set_tx_ets_sched(
* lowest priorities with respect to SP ones.
* e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn
*/
@@ -35,7 +35,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2467,6 +2467,19 @@ static void airoha_metadata_dst_free(str
@@ -2472,6 +2472,19 @@ static void airoha_metadata_dst_free(str
}
}
@@ -55,7 +55,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
/* enable 1:N vlan action, init vlan table */
airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK);
@@ -1647,7 +1666,6 @@ static void airhoha_set_gdm2_loopback(st
@@ -1652,7 +1671,6 @@ static void airhoha_set_gdm2_loopback(st
if (port->id == 3) {
/* FIXME: handle XSI_PCE1_PORT */
@@ -63,7 +63,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
airoha_fe_rmw(eth, REG_FE_WAN_PORT,
WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT));
@@ -2122,6 +2140,125 @@ static int airoha_tc_setup_qdisc_ets(str
@@ -2127,6 +2145,125 @@ static int airoha_tc_setup_qdisc_ets(str
}
}
@@ -189,7 +189,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel,
u32 addr, enum trtcm_param_type param,
enum trtcm_mode_type mode,
@@ -2286,10 +2423,142 @@ static int airoha_tc_htb_alloc_leaf_queu
@@ -2291,10 +2428,142 @@ static int airoha_tc_htb_alloc_leaf_queu
return 0;
}
@@ -93,7 +93,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
RX_DONE_INT_MASK);
return done;
@@ -960,7 +964,7 @@ unlock:
@@ -965,7 +969,7 @@ unlock:
}
if (done < budget && napi_complete(napi))
@@ -102,7 +102,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
TX_DONE_INT_MASK(id));
return done;
@@ -1191,13 +1195,16 @@ static int airoha_qdma_hw_init(struct ai
@@ -1196,13 +1200,16 @@ static int airoha_qdma_hw_init(struct ai
int i;
/* clear pending irqs */
@@ -123,7 +123,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
/* setup irq binding */
for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
@@ -1242,13 +1249,14 @@ static int airoha_qdma_hw_init(struct ai
@@ -1247,13 +1254,14 @@ static int airoha_qdma_hw_init(struct ai
static irqreturn_t airoha_irq_handler(int irq, void *dev_instance)
{
@@ -142,7 +142,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]);
}
@@ -1256,7 +1264,7 @@ static irqreturn_t airoha_irq_handler(in
@@ -1261,7 +1269,7 @@ static irqreturn_t airoha_irq_handler(in
return IRQ_NONE;
if (intr[1] & RX_DONE_INT_MASK) {
@@ -151,7 +151,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
RX_DONE_INT_MASK);
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
@@ -1273,7 +1281,7 @@ static irqreturn_t airoha_irq_handler(in
@@ -1278,7 +1286,7 @@ static irqreturn_t airoha_irq_handler(in
if (!(intr[0] & TX_DONE_INT_MASK(i)))
continue;
@@ -160,7 +160,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
TX_DONE_INT_MASK(i));
napi_schedule(&qdma->q_tx_irq[i].napi);
}
@@ -1282,6 +1290,39 @@ static irqreturn_t airoha_irq_handler(in
@@ -1287,6 +1295,39 @@ static irqreturn_t airoha_irq_handler(in
return IRQ_HANDLED;
}
@@ -200,7 +200,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
static int airoha_qdma_init(struct platform_device *pdev,
struct airoha_eth *eth,
struct airoha_qdma *qdma)
@@ -1289,9 +1330,7 @@ static int airoha_qdma_init(struct platf
@@ -1294,9 +1335,7 @@ static int airoha_qdma_init(struct platf
int err, id = qdma - &eth->qdma[0];
const char *res;
@@ -210,7 +210,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id);
if (!res)
return -ENOMEM;
@@ -1301,12 +1340,7 @@ static int airoha_qdma_init(struct platf
@@ -1306,12 +1345,7 @@ static int airoha_qdma_init(struct platf
return dev_err_probe(eth->dev, PTR_ERR(qdma->regs),
"failed to iomap qdma%d regs\n", id);
@@ -224,7 +224,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (err)
return err;
@@ -2797,7 +2831,7 @@ static int airoha_alloc_gdm_port(struct
@@ -2802,7 +2836,7 @@ static int airoha_alloc_gdm_port(struct
dev->features |= dev->hw_features;
dev->vlan_features = dev->hw_features;
dev->dev.of_node = np;
@@ -58,7 +58,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
return done;
}
@@ -1194,17 +1204,24 @@ static int airoha_qdma_hw_init(struct ai
@@ -1199,17 +1209,24 @@ static int airoha_qdma_hw_init(struct ai
{
int i;
@@ -91,7 +91,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
/* setup irq binding */
for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
@@ -1251,6 +1268,7 @@ static irqreturn_t airoha_irq_handler(in
@@ -1256,6 +1273,7 @@ static irqreturn_t airoha_irq_handler(in
{
struct airoha_irq_bank *irq_bank = dev_instance;
struct airoha_qdma *qdma = irq_bank->qdma;
@@ -99,7 +99,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
u32 intr[ARRAY_SIZE(irq_bank->irqmask)];
int i;
@@ -1263,17 +1281,24 @@ static irqreturn_t airoha_irq_handler(in
@@ -1268,17 +1286,24 @@ static irqreturn_t airoha_irq_handler(in
if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state))
return IRQ_NONE;
@@ -20,7 +20,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1093,17 +1093,13 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1098,17 +1098,13 @@ static int airoha_qdma_init_hfwd_queues(
int size;
size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc);
@@ -27,7 +27,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
#include <linux/platform_device.h>
#include <linux/tcp.h>
#include <linux/u64_stats_sync.h>
@@ -1088,9 +1089,11 @@ static void airoha_qdma_cleanup_tx_queue
@@ -1093,9 +1094,11 @@ static void airoha_qdma_cleanup_tx_queue
static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma)
{
struct airoha_eth *eth = qdma->eth;
@@ -40,7 +40,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc);
if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL))
@@ -1098,10 +1101,34 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1103,10 +1106,34 @@ static int airoha_qdma_init_hfwd_queues(
airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr);
@@ -38,7 +38,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
{
struct airoha_eth *eth = port->qdma->eth;
@@ -1140,7 +1131,7 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1145,7 +1136,7 @@ static int airoha_qdma_init_hfwd_queues(
LMGR_INIT_START | LMGR_SRAM_MODE_MASK |
HW_FWD_DESC_NUM_MASK,
FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) |
@@ -23,7 +23,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2896,7 +2896,15 @@ static int airoha_alloc_gdm_port(struct
@@ -2901,7 +2901,15 @@ static int airoha_alloc_gdm_port(struct
if (err)
return err;
@@ -19,7 +19,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1081,19 +1081,13 @@ static void airoha_qdma_cleanup_tx_queue
@@ -1086,19 +1086,13 @@ static void airoha_qdma_cleanup_tx_queue
static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma)
{
@@ -40,7 +40,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id);
if (!name)
return -ENOMEM;
@@ -1115,8 +1109,12 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1120,8 +1114,12 @@ static int airoha_qdma_init_hfwd_queues(
rmem = of_reserved_mem_lookup(np);
of_node_put(np);
dma_addr = rmem->base;
@@ -54,7 +54,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (!dmam_alloc_coherent(eth->dev, size, &dma_addr,
GFP_KERNEL))
return -ENOMEM;
@@ -1124,6 +1122,11 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1129,6 +1127,11 @@ static int airoha_qdma_init_hfwd_queues(
airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr);
@@ -66,7 +66,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG,
HW_FWD_DSCP_PAYLOAD_SIZE_MASK,
FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0));
@@ -1132,7 +1135,7 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1137,7 +1140,7 @@ static int airoha_qdma_init_hfwd_queues(
airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG,
LMGR_INIT_START | LMGR_SRAM_MODE_MASK |
HW_FWD_DESC_NUM_MASK,
@@ -21,7 +21,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1084,14 +1084,15 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1089,14 +1089,15 @@ static int airoha_qdma_init_hfwd_queues(
int size, index, num_desc = HW_DSCP_NUM;
struct airoha_eth *eth = qdma->eth;
int id = qdma - &eth->qdma[0];
@@ -38,7 +38,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
index = of_property_match_string(eth->dev->of_node,
"memory-region-names", name);
if (index >= 0) {
@@ -1112,9 +1113,9 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1117,9 +1118,9 @@ static int airoha_qdma_init_hfwd_queues(
/* Compute the number of hw descriptors according to the
* reserved memory size and the payload buffer size
*/
@@ -50,7 +50,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (!dmam_alloc_coherent(eth->dev, size, &dma_addr,
GFP_KERNEL))
return -ENOMEM;
@@ -1127,9 +1128,10 @@ static int airoha_qdma_init_hfwd_queues(
@@ -1132,9 +1133,10 @@ static int airoha_qdma_init_hfwd_queues(
return -ENOMEM;
airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr);
@@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2617,13 +2617,15 @@ static int airoha_dev_setup_tc_block_cb(
@@ -2622,13 +2622,15 @@ static int airoha_dev_setup_tc_block_cb(
void *type_data, void *cb_priv)
{
struct net_device *dev = cb_priv;
@@ -18,7 +18,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2199,11 +2199,14 @@ static int airoha_qdma_get_tx_ets_stats(
@@ -2204,11 +2204,14 @@ static int airoha_qdma_get_tx_ets_stats(
static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port,
struct tc_ets_qopt_offload *opt)
{
@@ -18,7 +18,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1731,7 +1731,9 @@ static void airhoha_set_gdm2_loopback(st
@@ -1736,7 +1736,9 @@ static void airhoha_set_gdm2_loopback(st
airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff);
airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2),
LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK,
@@ -19,7 +19,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2043,8 +2043,12 @@ static void airoha_ethtool_get_mac_stats
@@ -2048,8 +2048,12 @@ static void airoha_ethtool_get_mac_stats
airoha_update_hw_stats(port);
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
@@ -15,7 +15,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2791,6 +2791,7 @@ static const struct ethtool_ops airoha_e
@@ -2796,6 +2796,7 @@ static const struct ethtool_ops airoha_e
.get_drvinfo = airoha_ethtool_get_drvinfo,
.get_eth_mac_stats = airoha_ethtool_get_mac_stats,
.get_rmon_stats = airoha_ethtool_get_rmon_stats,
@@ -22,7 +22,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1894,6 +1894,20 @@ static u32 airoha_get_dsa_tag(struct sk_
@@ -1899,6 +1899,20 @@ static u32 airoha_get_dsa_tag(struct sk_
#endif
}
@@ -43,7 +43,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -1947,7 +1961,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -1952,7 +1966,7 @@ static netdev_tx_t airoha_dev_xmit(struc
txq = netdev_get_tx_queue(dev, qid);
nr_frags = 1 + skb_shinfo(skb)->nr_frags;
@@ -21,7 +21,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1408,8 +1408,7 @@ static int airoha_hw_init(struct platfor
@@ -1413,8 +1413,7 @@ static int airoha_hw_init(struct platfor
int err, i;
/* disable xsi */
@@ -31,7 +31,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
if (err)
return err;
@@ -2943,6 +2942,7 @@ free_metadata_dst:
@@ -2948,6 +2947,7 @@ free_metadata_dst:
static int airoha_probe(struct platform_device *pdev)
{
@@ -39,7 +39,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
struct device_node *np;
struct airoha_eth *eth;
int i, err;
@@ -2951,6 +2951,10 @@ static int airoha_probe(struct platform_
@@ -2956,6 +2956,10 @@ static int airoha_probe(struct platform_
if (!eth)
return -ENOMEM;
@@ -50,7 +50,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
eth->dev = &pdev->dev;
err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32));
@@ -2975,13 +2979,18 @@ static int airoha_probe(struct platform_
@@ -2980,13 +2984,18 @@ static int airoha_probe(struct platform_
return err;
}
@@ -75,7 +75,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
eth->xsi_rsts);
if (err) {
dev_err(eth->dev, "failed to get bulk xsi reset lines\n");
@@ -3067,8 +3076,23 @@ static void airoha_remove(struct platfor
@@ -3072,8 +3081,23 @@ static void airoha_remove(struct platfor
platform_set_drvdata(pdev, NULL);
}
@@ -59,7 +59,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
}
/* GMD4 */
for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++)
@@ -1783,8 +1790,11 @@ static int airoha_dev_init(struct net_de
@@ -1788,8 +1795,11 @@ static int airoha_dev_init(struct net_de
airhoha_set_gdm2_loopback(port);
fallthrough;
case 2:
@@ -47,7 +47,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
/* enable 1:N vlan action, init vlan table */
airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK);
@@ -1777,8 +1758,10 @@ static void airhoha_set_gdm2_loopback(st
@@ -1782,8 +1763,10 @@ static void airhoha_set_gdm2_loopback(st
static int airoha_dev_init(struct net_device *dev)
{
struct airoha_gdm_port *port = netdev_priv(dev);
@@ -60,7 +60,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
airoha_set_macaddr(port, dev->dev_addr);
@@ -1791,16 +1774,27 @@ static int airoha_dev_init(struct net_de
@@ -1796,16 +1779,27 @@ static int airoha_dev_init(struct net_de
fallthrough;
case 2:
if (airoha_ppe_is_enabled(eth, 1)) {
@@ -22,7 +22,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1703,13 +1703,17 @@ static int airoha_dev_set_macaddr(struct
@@ -1708,13 +1708,17 @@ static int airoha_dev_set_macaddr(struct
return 0;
}
@@ -43,7 +43,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port);
airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC);
@@ -1730,29 +1734,25 @@ static void airhoha_set_gdm2_loopback(st
@@ -1735,29 +1739,25 @@ static void airhoha_set_gdm2_loopback(st
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2));
airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2));
@@ -89,7 +89,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
}
static int airoha_dev_init(struct net_device *dev)
@@ -1769,8 +1769,13 @@ static int airoha_dev_init(struct net_de
@@ -1774,8 +1774,13 @@ static int airoha_dev_init(struct net_de
case 3:
case 4:
/* If GDM2 is active we can't enable loopback */
@@ -105,7 +105,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
fallthrough;
case 2:
if (airoha_ppe_is_enabled(eth, 1)) {
@@ -3088,11 +3093,38 @@ static const char * const en7581_xsi_rst
@@ -3093,11 +3098,38 @@ static const char * const en7581_xsi_rst
"xfp-mac",
};
@@ -17,7 +17,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1705,10 +1705,8 @@ static int airoha_dev_set_macaddr(struct
@@ -1710,10 +1710,8 @@ static int airoha_dev_set_macaddr(struct
static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)
{
@@ -29,7 +29,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
int src_port;
/* Forward the traffic to the proper GDM port */
@@ -1720,6 +1718,8 @@ static int airhoha_set_gdm2_loopback(str
@@ -1725,6 +1723,8 @@ static int airhoha_set_gdm2_loopback(str
/* Enable GDM2 loopback */
airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff);
airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff);
@@ -38,7 +38,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2),
LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK,
FIELD_PREP(LPBK_CHAN_MASK, chan) |
@@ -1734,6 +1734,8 @@ static int airhoha_set_gdm2_loopback(str
@@ -1739,6 +1739,8 @@ static int airhoha_set_gdm2_loopback(str
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2));
airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2));
@@ -47,7 +47,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
src_port = eth->soc->ops.get_src_port_id(port, nbq);
if (src_port < 0)
return src_port;
@@ -1747,7 +1749,7 @@ static int airhoha_set_gdm2_loopback(str
@@ -1752,7 +1754,7 @@ static int airhoha_set_gdm2_loopback(str
SP_CPORT_MASK(val),
FE_PSE_PORT_CDM2 << __ffs(SP_CPORT_MASK(val)));
@@ -56,7 +56,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6,
FC_ID_OF_SRC_PORT24_MASK,
FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2));
@@ -1916,6 +1918,22 @@ static bool airoha_dev_tx_queue_busy(str
@@ -1921,6 +1923,22 @@ static bool airoha_dev_tx_queue_busy(str
return index >= tail;
}
@@ -79,7 +79,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -1956,7 +1974,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -1961,7 +1979,7 @@ static netdev_tx_t airoha_dev_xmit(struc
}
}
@@ -88,7 +88,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
@@ -3117,6 +3135,35 @@ static int airoha_en7581_get_src_port_id
@@ -3122,6 +3140,35 @@ static int airoha_en7581_get_src_port_id
return -EINVAL;
}
@@ -124,7 +124,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static const struct airoha_eth_soc_data en7581_soc_data = {
.version = 0x7581,
.xsi_rsts_names = en7581_xsi_rsts_names,
@@ -3127,8 +3174,19 @@ static const struct airoha_eth_soc_data
@@ -3132,8 +3179,19 @@ static const struct airoha_eth_soc_data
},
};
@@ -172,7 +172,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK,
FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) |
FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3));
@@ -1713,7 +1713,7 @@ static int airhoha_set_gdm2_loopback(str
@@ -1718,7 +1718,7 @@ static int airhoha_set_gdm2_loopback(str
pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
: FE_PSE_PORT_GDM4;
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port);
@@ -19,7 +19,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -3060,6 +3060,7 @@ static int airoha_probe(struct platform_
@@ -3065,6 +3065,7 @@ static int airoha_probe(struct platform_
error_napi_stop:
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
airoha_qdma_stop_napi(&eth->qdma[i]);
@@ -19,7 +19,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -3006,11 +3006,11 @@ static int airoha_probe(struct platform_
@@ -3011,11 +3011,11 @@ static int airoha_probe(struct platform_
return err;
}
@@ -23,7 +23,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -908,19 +908,13 @@ static int airoha_qdma_tx_napi_poll(stru
@@ -913,19 +913,13 @@ static int airoha_qdma_tx_napi_poll(stru
dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
DMA_TO_DEVICE);
@@ -46,7 +46,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (skb) {
u16 queue = skb_get_queue_mapping(skb);
struct netdev_queue *txq;
@@ -965,6 +959,7 @@ static int airoha_qdma_init_tx_queue(str
@@ -970,6 +964,7 @@ static int airoha_qdma_init_tx_queue(str
q->ndesc = size;
q->qdma = qdma;
q->free_thr = 1 + MAX_SKB_FRAGS;
@@ -54,7 +54,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
GFP_KERNEL);
@@ -977,9 +972,9 @@ static int airoha_qdma_init_tx_queue(str
@@ -982,9 +977,9 @@ static int airoha_qdma_init_tx_queue(str
return -ENOMEM;
for (i = 0; i < q->ndesc; i++) {
@@ -66,7 +66,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val));
}
@@ -989,9 +984,9 @@ static int airoha_qdma_init_tx_queue(str
@@ -994,9 +989,9 @@ static int airoha_qdma_init_tx_queue(str
airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr);
airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK,
@@ -78,7 +78,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
return 0;
}
@@ -1047,17 +1042,21 @@ static int airoha_qdma_init_tx(struct ai
@@ -1052,17 +1047,21 @@ static int airoha_qdma_init_tx(struct ai
static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q)
{
struct airoha_eth *eth = q->qdma->eth;
@@ -104,7 +104,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
q->queued--;
}
spin_unlock_bh(&q->lock);
@@ -1899,20 +1898,6 @@ static u32 airoha_get_dsa_tag(struct sk_
@@ -1904,20 +1903,6 @@ static u32 airoha_get_dsa_tag(struct sk_
#endif
}
@@ -125,7 +125,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
static int airoha_get_fe_port(struct airoha_gdm_port *port)
{
struct airoha_qdma *qdma = port->qdma;
@@ -1935,8 +1920,10 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -1940,8 +1925,10 @@ static netdev_tx_t airoha_dev_xmit(struc
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
u32 nr_frags, tag, msg0, msg1, len;
@@ -136,7 +136,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
void *data;
int i, qid;
u16 index;
@@ -1982,7 +1969,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -1987,7 +1974,7 @@ static netdev_tx_t airoha_dev_xmit(struc
txq = netdev_get_tx_queue(dev, qid);
nr_frags = 1 + skb_shinfo(skb)->nr_frags;
@@ -145,7 +145,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
/* not enough space in the queue */
netif_tx_stop_queue(txq);
spin_unlock_bh(&q->lock);
@@ -1991,11 +1978,13 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -1996,11 +1983,13 @@ static netdev_tx_t airoha_dev_xmit(struc
len = skb_headlen(skb);
data = skb->data;
@@ -161,7 +161,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
dma_addr_t addr;
u32 val;
@@ -2005,7 +1994,14 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2010,7 +1999,14 @@ static netdev_tx_t airoha_dev_xmit(struc
if (unlikely(dma_mapping_error(dev->dev.parent, addr)))
goto error_unmap;
@@ -177,7 +177,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
val = FIELD_PREP(QDMA_DESC_LEN_MASK, len);
if (i < nr_frags - 1)
@@ -2018,15 +2014,9 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2023,15 +2019,9 @@ static netdev_tx_t airoha_dev_xmit(struc
WRITE_ONCE(desc->msg1, cpu_to_le32(msg1));
WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff));
@@ -193,7 +193,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
q->queued += i;
skb_tx_timestamp(skb);
@@ -2035,7 +2025,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2040,7 +2030,7 @@ static netdev_tx_t airoha_dev_xmit(struc
if (netif_xmit_stopped(txq) || !netdev_xmit_more())
airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid),
TX_RING_CPU_IDX_MASK,
@@ -202,7 +202,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (q->ndesc - q->queued < q->free_thr)
netif_tx_stop_queue(txq);
@@ -2045,10 +2035,13 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2050,10 +2040,13 @@ static netdev_tx_t airoha_dev_xmit(struc
return NETDEV_TX_OK;
error_unmap:
@@ -23,7 +23,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2940,19 +2940,26 @@ static int airoha_alloc_gdm_port(struct
@@ -2945,19 +2945,26 @@ static int airoha_alloc_gdm_port(struct
port->id = id;
eth->ports[p] = port;
@@ -60,7 +60,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
}
static int airoha_probe(struct platform_device *pdev)
@@ -3043,6 +3050,10 @@ static int airoha_probe(struct platform_
@@ -3048,6 +3055,10 @@ static int airoha_probe(struct platform_
}
}
@@ -71,7 +71,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
return 0;
error_napi_stop:
@@ -3056,10 +3067,12 @@ error_hw_cleanup:
@@ -3061,10 +3072,12 @@ error_hw_cleanup:
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -40,7 +40,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
airoha_fe_crsn_qsel_init(eth);
@@ -1706,27 +1706,29 @@ static int airhoha_set_gdm2_loopback(str
@@ -1711,27 +1711,29 @@ static int airhoha_set_gdm2_loopback(str
/* Forward the traffic to the proper GDM port */
pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
: FE_PSE_PORT_GDM4;
@@ -78,7 +78,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
/* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */
nbq = port->id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
@@ -1762,8 +1764,8 @@ static int airoha_dev_init(struct net_de
@@ -1767,8 +1769,8 @@ static int airoha_dev_init(struct net_de
airoha_set_macaddr(port, dev->dev_addr);
switch (port->id) {
@@ -89,7 +89,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
/* If GDM2 is active we can't enable loopback */
if (!eth->ports[1]) {
int err;
@@ -1773,7 +1775,7 @@ static int airoha_dev_init(struct net_de
@@ -1778,7 +1780,7 @@ static int airoha_dev_init(struct net_de
return err;
}
fallthrough;
@@ -98,7 +98,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (airoha_ppe_is_enabled(eth, 1)) {
/* For PPE2 always use secondary cpu port. */
fe_cpu_port = FE_PSE_PORT_CDM2;
@@ -3116,14 +3118,14 @@ static const char * const en7581_xsi_rst
@@ -3121,14 +3123,14 @@ static const char * const en7581_xsi_rst
static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq)
{
switch (port->id) {
@@ -115,7 +115,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
/* 7581 SoC supports eth and usb serdes on GDM4 port */
if (!nbq)
return HSGMII_LAN_7581_ETH_SRCPORT;
@@ -3147,12 +3149,12 @@ static const char * const an7583_xsi_rst
@@ -3152,12 +3154,12 @@ static const char * const an7583_xsi_rst
static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq)
{
switch (port->id) {
@@ -16,7 +16,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2821,6 +2821,7 @@ static const struct ethtool_ops airoha_e
@@ -2826,6 +2826,7 @@ static const struct ethtool_ops airoha_e
.get_drvinfo = airoha_ethtool_get_drvinfo,
.get_eth_mac_stats = airoha_ethtool_get_mac_stats,
.get_rmon_stats = airoha_ethtool_get_rmon_stats,
@@ -15,7 +15,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1743,7 +1743,7 @@ static int airhoha_set_gdm2_loopback(str
@@ -1748,7 +1748,7 @@ static int airhoha_set_gdm2_loopback(str
airoha_fe_rmw(eth,
REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
SP_CPORT_MASK(val),
@@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
if (port->id != AIROHA_GDM3_IDX && airoha_is_7581(eth))
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6,
@@ -1797,7 +1797,7 @@ static int airoha_dev_init(struct net_de
@@ -1802,7 +1802,7 @@ static int airoha_dev_init(struct net_de
ppe_id = pse_port == FE_PSE_PORT_PPE2 ? 1 : 0;
airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id),
DFT_CPORT_MASK(port->id),
@@ -33,7 +33,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
return 0;
}
@@ -2154,7 +2154,7 @@ static int airoha_qdma_set_chan_tx_sched
@@ -2159,7 +2159,7 @@ static int airoha_qdma_set_chan_tx_sched
airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3),
CHAN_QOS_MODE_MASK(channel),
@@ -19,7 +19,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1745,10 +1745,12 @@ static int airhoha_set_gdm2_loopback(str
@@ -1750,10 +1750,12 @@ static int airhoha_set_gdm2_loopback(str
SP_CPORT_MASK(val),
__field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
@@ -18,7 +18,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1627,6 +1627,7 @@ static int airoha_dev_open(struct net_de
@@ -1632,6 +1632,7 @@ static int airoha_dev_open(struct net_de
int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN;
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
@@ -26,7 +26,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
netif_tx_start_all_queues(dev);
err = airoha_set_vip_for_gdm_port(port, true);
@@ -1650,6 +1651,14 @@ static int airoha_dev_open(struct net_de
@@ -1655,6 +1656,14 @@ static int airoha_dev_open(struct net_de
GLOBAL_CFG_RX_DMA_EN_MASK);
atomic_inc(&qdma->users);
@@ -41,7 +41,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
return 0;
}
@@ -1667,6 +1676,9 @@ static int airoha_dev_stop(struct net_de
@@ -1672,6 +1681,9 @@ static int airoha_dev_stop(struct net_de
for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++)
netdev_tx_reset_subqueue(dev, i);
@@ -51,7 +51,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
if (atomic_dec_and_test(&qdma->users)) {
airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
@@ -1760,7 +1772,7 @@ static int airoha_dev_init(struct net_de
@@ -1765,7 +1777,7 @@ static int airoha_dev_init(struct net_de
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
@@ -60,7 +60,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
u8 ppe_id;
airoha_set_macaddr(port, dev->dev_addr);
@@ -1781,7 +1793,7 @@ static int airoha_dev_init(struct net_de
@@ -1786,7 +1798,7 @@ static int airoha_dev_init(struct net_de
if (airoha_ppe_is_enabled(eth, 1)) {
/* For PPE2 always use secondary cpu port. */
fe_cpu_port = FE_PSE_PORT_CDM2;
@@ -69,7 +69,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
break;
}
fallthrough;
@@ -1790,13 +1802,11 @@ static int airoha_dev_init(struct net_de
@@ -1795,13 +1807,11 @@ static int airoha_dev_init(struct net_de
/* For PPE1 select cpu port according to the running QDMA. */
fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1;
@@ -39,7 +39,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1770,11 +1770,13 @@ static int airhoha_set_gdm2_loopback(str
@@ -1775,11 +1775,13 @@ static int airhoha_set_gdm2_loopback(str
static int airoha_dev_init(struct net_device *dev)
{
struct airoha_gdm_port *port = netdev_priv(dev);
@@ -55,7 +55,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
airoha_set_macaddr(port, dev->dev_addr);
switch (port->id) {
@@ -1798,7 +1800,7 @@ static int airoha_dev_init(struct net_de
@@ -1803,7 +1805,7 @@ static int airoha_dev_init(struct net_de
}
fallthrough;
default: {
@@ -64,7 +64,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
/* For PPE1 select cpu port according to the running QDMA. */
fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1;
@@ -2882,11 +2884,10 @@ bool airoha_is_valid_gdm_port(struct air
@@ -2887,11 +2889,10 @@ bool airoha_is_valid_gdm_port(struct air
}
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
@@ -77,7 +77,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
struct net_device *dev;
int err, p;
u32 id;
@@ -2917,7 +2918,6 @@ static int airoha_alloc_gdm_port(struct
@@ -2922,7 +2923,6 @@ static int airoha_alloc_gdm_port(struct
return -ENOMEM;
}
@@ -85,7 +85,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
dev->netdev_ops = &airoha_netdev_ops;
dev->ethtool_ops = &airoha_ethtool_ops;
dev->max_mtu = AIROHA_MAX_MTU;
@@ -2929,7 +2929,6 @@ static int airoha_alloc_gdm_port(struct
@@ -2934,7 +2934,6 @@ static int airoha_alloc_gdm_port(struct
dev->features |= dev->hw_features;
dev->vlan_features = dev->hw_features;
dev->dev.of_node = np;
@@ -93,7 +93,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
SET_NETDEV_DEV(dev, eth->dev);
/* reserve hw queues for HTB offloading */
@@ -2950,7 +2949,7 @@ static int airoha_alloc_gdm_port(struct
@@ -2955,7 +2954,7 @@ static int airoha_alloc_gdm_port(struct
port = netdev_priv(dev);
u64_stats_init(&port->stats.syncp);
spin_lock_init(&port->stats.lock);
@@ -102,7 +102,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
port->dev = dev;
port->id = id;
eth->ports[p] = port;
@@ -3050,7 +3049,6 @@ static int airoha_probe(struct platform_
@@ -3055,7 +3054,6 @@ static int airoha_probe(struct platform_
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
airoha_qdma_start_napi(&eth->qdma[i]);
@@ -110,7 +110,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
for_each_child_of_node(pdev->dev.of_node, np) {
if (!of_device_is_compatible(np, "airoha,eth-mac"))
continue;
@@ -3058,7 +3056,7 @@ static int airoha_probe(struct platform_
@@ -3063,7 +3061,7 @@ static int airoha_probe(struct platform_
if (!of_device_is_available(np))
continue;
@@ -32,7 +32,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1771,8 +1771,7 @@ static int airoha_dev_init(struct net_de
@@ -1776,8 +1776,7 @@ static int airoha_dev_init(struct net_de
{
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_eth *eth = port->eth;
@@ -42,7 +42,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
port->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
@@ -1790,28 +1789,13 @@ static int airoha_dev_init(struct net_de
@@ -1795,28 +1794,13 @@ static int airoha_dev_init(struct net_de
if (err)
return err;
}
@@ -75,7 +75,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
return 0;
}
@@ -1914,7 +1898,7 @@ static u32 airoha_get_dsa_tag(struct sk_
@@ -1919,7 +1903,7 @@ static u32 airoha_get_dsa_tag(struct sk_
#endif
}
@@ -23,7 +23,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1384,6 +1384,33 @@ static int airoha_qdma_init(struct platf
@@ -1389,6 +1389,33 @@ static int airoha_qdma_init(struct platf
return airoha_qdma_hw_init(qdma);
}
@@ -57,7 +57,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
static int airoha_hw_init(struct platform_device *pdev,
struct airoha_eth *eth)
{
@@ -1411,41 +1438,30 @@ static int airoha_hw_init(struct platfor
@@ -1416,41 +1443,30 @@ static int airoha_hw_init(struct platfor
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) {
err = airoha_qdma_init(pdev, eth, &eth->qdma[i]);
if (err)
@@ -110,7 +110,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
}
static void airoha_qdma_start_napi(struct airoha_qdma *qdma)
@@ -3028,7 +3044,7 @@ static int airoha_probe(struct platform_
@@ -3033,7 +3049,7 @@ static int airoha_probe(struct platform_
err = airoha_hw_init(pdev, eth);
if (err)
@@ -119,7 +119,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
airoha_qdma_start_napi(&eth->qdma[i]);
@@ -3056,10 +3072,6 @@ static int airoha_probe(struct platform_
@@ -3061,10 +3077,6 @@ static int airoha_probe(struct platform_
error_napi_stop:
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
airoha_qdma_stop_napi(&eth->qdma[i]);
@@ -130,7 +130,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -3071,6 +3083,8 @@ error_hw_cleanup:
@@ -3076,6 +3088,8 @@ error_hw_cleanup:
unregister_netdev(port->dev);
airoha_metadata_dst_free(port);
}
@@ -139,7 +139,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
free_netdev(eth->napi_dev);
platform_set_drvdata(pdev, NULL);
@@ -3082,10 +3096,8 @@ static void airoha_remove(struct platfor
@@ -3087,10 +3101,8 @@ static void airoha_remove(struct platfor
struct airoha_eth *eth = platform_get_drvdata(pdev);
int i;
@@ -151,7 +151,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -3096,9 +3108,9 @@ static void airoha_remove(struct platfor
@@ -3101,9 +3113,9 @@ static void airoha_remove(struct platfor
unregister_netdev(port->dev);
airoha_metadata_dst_free(port);
}
@@ -26,7 +26,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2973,6 +2973,8 @@ static int airoha_register_gdm_devices(s
@@ -2978,6 +2978,8 @@ static int airoha_register_gdm_devices(s
return err;
}
@@ -1,34 +0,0 @@
From 656121b155030086b01cfce9bd31b0c925ee6860 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 8 Apr 2026 20:26:56 +0200
Subject: [PATCH] net: airoha: Add missing RX_CPU_IDX() configuration in
airoha_qdma_cleanup_rx_queue()
When the descriptor index written in REG_RX_CPU_IDX() is equal to the one
stored in REG_RX_DMA_IDX(), the hw will stop since the QDMA RX ring is
empty.
Add missing REG_RX_CPU_IDX() configuration in airoha_qdma_cleanup_rx_queue
routine during QDMA RX ring cleanup.
Fixes: 514aac359987 ("net: airoha: Add missing cleanup bits in airoha_qdma_cleanup_rx_queue()")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260408-airoha-cpu-idx-airoha_qdma_cleanup_rx_queue-v1-1-8efa64844308@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 5 +++++
1 file changed, 5 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -824,6 +824,11 @@ static void airoha_qdma_cleanup_rx_queue
}
q->head = q->tail;
+ /* Set RX_DMA_IDX to RX_CPU_IDX to notify the hw the QDMA RX ring is
+ * empty.
+ */
+ airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK,
+ FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head));
airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail));
}
@@ -1,61 +0,0 @@
From 379050947a1828826ad7ea50c95245a56929b35a Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 20 Apr 2026 10:07:47 +0200
Subject: [PATCH] net: airoha: Move ndesc initialization at end of
airoha_qdma_init_rx_queue()
If queue entry or DMA descriptor list allocation fails in
airoha_qdma_init_rx_queue routine, airoha_qdma_cleanup() will trigger a
NULL pointer dereference running netif_napi_del() for RX queue NAPIs
since netif_napi_add() has never been executed to this particular RX NAPI.
The issue is due to the early ndesc initialization in
airoha_qdma_init_rx_queue() since airoha_qdma_cleanup() relies on ndesc
value to check if the queue is properly initialized. Fix the issue moving
ndesc initialization at end of airoha_qdma_init_tx routine.
Move page_pool allocation after descriptor list allocation in order to
avoid memory leaks if desc allocation fails.
Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260420-airoha_qdma_init_rx_queue-fix-v2-1-d99347e5c18d@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -745,14 +745,18 @@ static int airoha_qdma_init_rx_queue(str
dma_addr_t dma_addr;
q->buf_size = PAGE_SIZE / 2;
- q->ndesc = ndesc;
q->qdma = qdma;
- q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
+ q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
GFP_KERNEL);
if (!q->entry)
return -ENOMEM;
+ q->desc = dmam_alloc_coherent(eth->dev, ndesc * sizeof(*q->desc),
+ &dma_addr, GFP_KERNEL);
+ if (!q->desc)
+ return -ENOMEM;
+
q->page_pool = page_pool_create(&pp_params);
if (IS_ERR(q->page_pool)) {
int err = PTR_ERR(q->page_pool);
@@ -761,11 +765,7 @@ static int airoha_qdma_init_rx_queue(str
return err;
}
- q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc),
- &dma_addr, GFP_KERNEL);
- if (!q->desc)
- return -ENOMEM;
-
+ q->ndesc = ndesc;
netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr);
@@ -0,0 +1,495 @@
From f62cea6483cc55360863d66300790a5fb9de5f7c Mon Sep 17 00:00:00 2001
Message-ID: <f62cea6483cc55360863d66300790a5fb9de5f7c.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Tue, 24 Feb 2026 18:43:05 +0100
Subject: [PATCH 02/13] net: airoha: Move airoha_qdma pointer in airoha_gdm_dev
struct
Move airoha_qdma pointer from airoha_gdm_port struct to airoha_gdm_dev
one since the QDMA block used depends on the particular net_device
WAN/LAN configuration and in the current codebase net_device pointer is
associated to airoha_gdm_dev struct.
This is a preliminary patch to support multiple net_devices connected
to the same GDM{3,4} port via an external hw arbiter.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 105 +++++++++++------------
drivers/net/ethernet/airoha/airoha_eth.h | 9 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 17 ++--
3 files changed, 64 insertions(+), 67 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -80,9 +80,10 @@ static bool airhoa_is_phy_external(struc
}
#endif
-static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
+static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, reg;
reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
@@ -94,7 +95,7 @@ static void airoha_set_macaddr(struct ai
airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
- airoha_ppe_init_upd_mem(port);
+ airoha_ppe_init_upd_mem(dev);
}
static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
@@ -110,10 +111,10 @@ static void airoha_set_gdm_port_fwd_cfg(
FIELD_PREP(GDM_UCFQ_MASK, val));
}
-static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port,
- bool enable)
+static int airoha_set_vip_for_gdm_port(struct airoha_gdm_dev *dev, bool enable)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 vip_port;
vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
@@ -994,10 +995,13 @@ static void airoha_qdma_wake_netdev_txqs
if (!port)
continue;
- if (port->qdma != qdma)
+ dev = port->dev;
+ if (!dev)
+ continue;
+
+ if (dev->qdma != qdma)
continue;
- dev = port->dev;
for (j = 0; j < dev->dev->num_tx_queues; j++) {
if (airoha_qdma_get_txq(qdma, j) != qid)
continue;
@@ -1702,9 +1706,10 @@ static void airoha_qdma_stop_napi(struct
}
}
-static void airoha_update_hw_stats(struct airoha_gdm_port *port)
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
spin_lock(&port->stats.lock);
@@ -1851,7 +1856,7 @@ static int airoha_dev_open(struct net_de
int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
#if defined(CONFIG_PCS_AIROHA)
@@ -1868,7 +1873,7 @@ static int airoha_dev_open(struct net_de
#endif
netif_tx_start_all_queues(netdev);
- err = airoha_set_vip_for_gdm_port(port, true);
+ err = airoha_set_vip_for_gdm_port(dev, true);
if (err)
return err;
@@ -1905,11 +1910,11 @@ static int airoha_dev_stop(struct net_de
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
int i;
netif_tx_disable(netdev);
- airoha_set_vip_for_gdm_port(port, false);
+ airoha_set_vip_for_gdm_port(dev, false);
for (i = 0; i < netdev->num_tx_queues; i++)
netdev_tx_reset_subqueue(netdev, i);
@@ -1942,21 +1947,21 @@ static int airoha_dev_stop(struct net_de
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int err;
err = eth_mac_addr(netdev, p);
if (err)
return err;
- airoha_set_macaddr(port, netdev->dev_addr);
+ airoha_set_macaddr(dev, netdev->dev_addr);
return 0;
}
-static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
+static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, pse_port, chan;
int i, src_port;
@@ -2003,7 +2008,7 @@ static int airoha_set_gdm2_loopback(stru
__field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX);
+ airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
@@ -2023,9 +2028,9 @@ static int airoha_dev_init(struct net_de
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- port->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
- dev->dev->irq = port->qdma->irq_banks[0].irq;
- airoha_set_macaddr(port, netdev->dev_addr);
+ dev->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
+ dev->dev->irq = dev->qdma->irq_banks[0].irq;
+ airoha_set_macaddr(dev, netdev->dev_addr);
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -2034,7 +2039,7 @@ static int airoha_dev_init(struct net_de
if (!eth->ports[1]) {
int err;
- err = airoha_set_gdm2_loopback(port);
+ err = airoha_set_gdm2_loopback(dev);
if (err)
return err;
}
@@ -2044,8 +2049,7 @@ static int airoha_dev_init(struct net_de
}
for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(port, i,
- airoha_get_fe_port(port));
+ airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
return 0;
}
@@ -2057,7 +2061,7 @@ static void airoha_dev_get_stats64(struc
struct airoha_gdm_port *port = dev->port;
unsigned int start;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
storage->rx_packets = port->stats.rx_ok_pkts;
@@ -2077,8 +2081,8 @@ static int airoha_dev_change_mtu(struct
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
+ struct airoha_eth *eth = dev->eth;
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
GDM_LONG_LEN_MASK,
@@ -2152,10 +2156,10 @@ static u32 airoha_get_dsa_tag(struct sk_
#endif
}
-int airoha_get_fe_port(struct airoha_gdm_port *port)
+int airoha_get_fe_port(struct airoha_gdm_dev *dev)
{
- struct airoha_qdma *qdma = port->qdma;
- struct airoha_eth *eth = qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
switch (eth->soc->version) {
case 0x7583:
@@ -2174,8 +2178,7 @@ static int airoha_dev_set_features(struc
netdev_features_t diff = netdev->features ^ features;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
int i;
@@ -2213,10 +2216,10 @@ static int airoha_dev_set_features(struc
if (!p)
continue;
- if (p->qdma != qdma)
+ d = p->dev;
+ if (d->qdma != qdma)
continue;
- d = p->dev;
if (d->dev == netdev)
continue;
@@ -2233,8 +2236,7 @@ static netdev_tx_t airoha_dev_xmit(struc
struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
struct netdev_queue *txq;
@@ -2272,7 +2274,7 @@ static netdev_tx_t airoha_dev_xmit(struc
}
}
- fport = airoha_get_fe_port(port);
+ fport = airoha_get_fe_port(dev);
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
@@ -2375,8 +2377,7 @@ static void airoha_ethtool_get_drvinfo(s
struct ethtool_drvinfo *info)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_eth *eth = dev->eth;
strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
@@ -2389,7 +2390,7 @@ static void airoha_ethtool_get_mac_stats
struct airoha_gdm_port *port = dev->port;
unsigned int start;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
@@ -2429,7 +2430,7 @@ airoha_ethtool_get_rmon_stats(struct net
ARRAY_SIZE(hw_stats->rx_len) + 1);
*ranges = airoha_ethtool_rmon_ranges;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
int i;
@@ -2449,18 +2450,17 @@ static int airoha_qdma_set_chan_tx_sched
const u16 *weights, u8 n_weights)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int i;
for (i = 0; i < AIROHA_NUM_TX_RING; i++)
- airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel),
+ airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
for (i = 0; i < n_weights; i++) {
u32 status;
int err;
- airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG,
+ airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG,
TWRR_RW_CMD_MASK |
FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
@@ -2468,13 +2468,12 @@ static int airoha_qdma_set_chan_tx_sched
err = read_poll_timeout(airoha_qdma_rr, status,
status & TWRR_RW_CMD_DONE,
USEC_PER_MSEC, 10 * USEC_PER_MSEC,
- true, port->qdma,
- REG_TXWRR_WEIGHT_CFG);
+ true, dev->qdma, REG_TXWRR_WEIGHT_CFG);
if (err)
return err;
}
- airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3),
+ airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3),
CHAN_QOS_MODE_MASK(channel),
__field_prep(CHAN_QOS_MODE_MASK(channel), mode));
@@ -2540,9 +2539,9 @@ static int airoha_qdma_get_tx_ets_stats(
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
+ u64 cpu_tx_packets = airoha_qdma_rr(dev->qdma,
REG_CNTR_VAL(channel << 1));
- u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
+ u64 fwd_tx_packets = airoha_qdma_rr(dev->qdma,
REG_CNTR_VAL((channel << 1) + 1));
u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
(fwd_tx_packets - port->fwd_tx_packets);
@@ -2806,17 +2805,16 @@ static int airoha_qdma_set_tx_rate_limit
u32 bucket_size)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int i, err;
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
- err = airoha_qdma_set_trtcm_config(port->qdma, channel,
+ err = airoha_qdma_set_trtcm_config(dev->qdma, channel,
REG_EGRESS_TRTCM_CFG, i,
!!rate, TRTCM_METER_MODE);
if (err)
return err;
- err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel,
+ err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel,
REG_EGRESS_TRTCM_CFG,
i, rate, bucket_size);
if (err)
@@ -2866,11 +2864,11 @@ static int airoha_tc_htb_alloc_leaf_queu
return 0;
}
-static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port,
+static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
u32 rate, u32 bucket_size,
enum trtcm_unit_type unit_type)
{
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
int i;
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
@@ -2949,7 +2947,6 @@ static int airoha_dev_tc_matchall(struct
{
enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
u32 rate = 0, bucket_size = 0;
switch (f->command) {
@@ -2974,7 +2971,7 @@ static int airoha_dev_tc_matchall(struct
fallthrough;
}
case TC_CLSMATCHALL_DESTROY:
- return airoha_qdma_set_rx_meter(port, rate, bucket_size,
+ return airoha_qdma_set_rx_meter(dev, rate, bucket_size,
unit_type);
default:
return -EOPNOTSUPP;
@@ -2986,8 +2983,7 @@ static int airoha_dev_setup_tc_block_cb(
{
struct net_device *netdev = cb_priv;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_eth *eth = dev->eth;
if (!tc_can_offload(netdev))
return -EOPNOTSUPP;
@@ -3222,7 +3218,7 @@ static void airoha_mac_link_up(struct ph
struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev,
phylink_config);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
u32 frag_size_tx, frag_size_rx;
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -548,6 +548,7 @@ struct airoha_qdma {
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
+ struct airoha_qdma *qdma;
struct net_device *dev;
struct airoha_eth *eth;
@@ -558,7 +559,6 @@ struct airoha_gdm_dev {
};
struct airoha_gdm_port {
- struct airoha_qdma *qdma;
struct airoha_gdm_dev *dev;
int id;
int nbq;
@@ -694,19 +694,18 @@ static inline bool airoha_qdma_is_lro_qu
return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
}
-int airoha_get_fe_port(struct airoha_gdm_port *port);
+int airoha_get_fe_port(struct airoha_gdm_dev *dev);
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id,
- u8 fport);
+void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
u16 hash, bool rx_wlan);
int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
int airoha_ppe_init(struct airoha_eth *eth);
void airoha_ppe_deinit(struct airoha_eth *eth);
-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port);
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev);
u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe);
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
u32 hash);
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -85,9 +85,9 @@ static u32 airoha_ppe_get_timestamp(stru
AIROHA_FOE_IB1_BIND_TIMESTAMP);
}
-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport)
+void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
{
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
u8 qdma_id = qdma - &eth->qdma[0];
u32 fe_cpu_port;
@@ -181,8 +181,8 @@ static void airoha_ppe_hw_init(struct ai
if (!port)
continue;
- airoha_ppe_set_cpu_port(port, i,
- airoha_get_fe_port(port));
+ airoha_ppe_set_cpu_port(port->dev, i,
+ airoha_get_fe_port(port->dev));
}
}
}
@@ -1483,11 +1483,12 @@ void airoha_ppe_check_skb(struct airoha_
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
}
-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
- struct net_device *dev = port->dev->dev;
- const u8 *addr = dev->dev_addr;
+ struct airoha_gdm_port *port = dev->port;
+ struct net_device *netdev = dev->dev;
+ struct airoha_eth *eth = dev->eth;
+ const u8 *addr = netdev->dev_addr;
u32 val;
val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
@@ -0,0 +1,73 @@
From 32bfd008c19f9ad55514181d8cd02e14bf384475 Mon Sep 17 00:00:00 2001
Message-ID: <32bfd008c19f9ad55514181d8cd02e14bf384475.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 13 Dec 2025 10:06:45 +0100
Subject: [PATCH 03/13] net: airoha: Rely on airoha_gdm_dev pointer in
airoha_is_lan_gdm_port()
Rename airoha_is_lan_gdm_port in airoha_is_lan_gdm_dev. Moreover, rely
on airoha_gdm_dev pointer in airoha_is_lan_gdm_dev() instead of
airoha_gdm_port one.
This is a preliminary patch to support multiple net_devices connected to
the same GDM{3,4} port via an external hw arbiter.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 6 ++----
drivers/net/ethernet/airoha/airoha_eth.h | 4 +++-
drivers/net/ethernet/airoha/airoha_ppe.c | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -82,12 +82,10 @@ static bool airhoa_is_phy_external(struc
static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
- struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
u32 val, reg;
- reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
- : REG_FE_WAN_MAC_H;
+ reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H;
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
airoha_fe_wr(eth, reg, val);
@@ -2028,7 +2026,7 @@ static int airoha_dev_init(struct net_de
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- dev->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
+ dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
dev->dev->irq = dev->qdma->irq_banks[0].irq;
airoha_set_macaddr(dev, netdev->dev_addr);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -663,8 +663,10 @@ static inline u16 airoha_qdma_get_txq(st
return qid % ARRAY_SIZE(qdma->q_tx);
}
-static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port)
+static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
+ struct airoha_gdm_port *port = dev->port;
+
/* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
* GDM{2,3,4} can be used as wan port connected to an external
* phy module.
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -365,7 +365,7 @@ static int airoha_ppe_foe_entry_prepare(
/* For downlink traffic consume SRAM memory for hw
* forwarding descriptors queue.
*/
- if (airoha_is_lan_gdm_port(port))
+ if (airoha_is_lan_gdm_dev(dev))
val |= AIROHA_FOE_IB2_FAST_PATH;
if (dsa_port >= 0)
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ,
@@ -0,0 +1,184 @@
From 634d75285db77f3385aa85a1bf2b185396225100 Mon Sep 17 00:00:00 2001
Message-ID: <634d75285db77f3385aa85a1bf2b185396225100.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 10 Apr 2026 14:35:32 +0200
Subject: [PATCH 04/13] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
Since now multiple net_devices connected to different QDMA blocks can
share the same GDM port, qos_sq_bmap field can be overwritten with the
configuration obtained from a net_device connected to a different QDMA
block. In order to fix the issue move qos_sq_bmap field from
airoha_gdm_port struct to airoha_gdm_dev one.
Add qos_channel_map bitmap in airoha_qdma struct to track if a shared
QDMA channel is already in use by another net_device.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 58 +++++++++++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 6 ++-
2 files changed, 40 insertions(+), 24 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2822,30 +2822,40 @@ static int airoha_qdma_set_tx_rate_limit
return 0;
}
-static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
- struct tc_htb_qopt_offload *opt)
+static int airoha_tc_htb_modify_queue(struct net_device *dev,
+ struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
- int err, num_tx_queues = netdev->real_num_tx_queues;
- struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
return -EINVAL;
}
- err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
- opt->quantum);
- if (err) {
+ return airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
+}
+
+static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
+ struct tc_htb_qopt_offload *opt)
+{
+ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
+ int err, num_tx_queues = netdev->real_num_tx_queues;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma = dev->qdma;
+
+ /* Here we need to check the requested QDMA channel is not already
+ * in use by another net_device running on the same QDMA block.
+ */
+ if (test_and_set_bit(channel, qdma->qos_channel_map)) {
NL_SET_ERR_MSG_MOD(opt->extack,
- "failed configuring htb offload");
- return err;
+ "qdma qos channel already in use");
+ return -EBUSY;
}
- if (opt->command == TC_HTB_NODE_MODIFY)
- return 0;
+ err = airoha_tc_htb_modify_queue(netdev, opt);
+ if (err)
+ goto error;
err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
if (err) {
@@ -2853,13 +2863,17 @@ static int airoha_tc_htb_alloc_leaf_queu
opt->quantum);
NL_SET_ERR_MSG_MOD(opt->extack,
"failed setting real_num_tx_queues");
- return err;
+ goto error;
}
- set_bit(channel, port->qos_sq_bmap);
+ set_bit(channel, dev->qos_sq_bmap);
opt->qid = AIROHA_NUM_TX_RING + channel;
return 0;
+error:
+ clear_bit(channel, qdma->qos_channel_map);
+
+ return err;
}
static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
@@ -3040,11 +3054,13 @@ static int airoha_dev_setup_tc_block(str
static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ struct airoha_qdma *qdma = dev->qdma;
netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
- clear_bit(queue, port->qos_sq_bmap);
+
+ clear_bit(queue, qdma->qos_channel_map);
+ clear_bit(queue, dev->qos_sq_bmap);
}
static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
@@ -3052,9 +3068,8 @@ static int airoha_tc_htb_delete_leaf_que
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- if (!test_bit(channel, port->qos_sq_bmap)) {
+ if (!test_bit(channel, dev->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
@@ -3067,10 +3082,9 @@ static int airoha_tc_htb_delete_leaf_que
static int airoha_tc_htb_destroy(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int q;
- for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
+ for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
airoha_tc_remove_htb_queue(netdev, q);
return 0;
@@ -3081,9 +3095,8 @@ static int airoha_tc_get_htb_get_leaf_qu
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- if (!test_bit(channel, port->qos_sq_bmap)) {
+ if (!test_bit(channel, dev->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
@@ -3102,6 +3115,7 @@ static int airoha_tc_setup_qdisc_htb(str
case TC_HTB_DESTROY:
return airoha_tc_htb_destroy(dev);
case TC_HTB_NODE_MODIFY:
+ return airoha_tc_htb_modify_queue(dev, opt);
case TC_HTB_LEAF_ALLOC_QUEUE:
return airoha_tc_htb_alloc_leaf_queue(dev, opt);
case TC_HTB_LEAF_DEL:
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -544,6 +544,8 @@ struct airoha_qdma {
struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
+
+ DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
};
struct airoha_gdm_dev {
@@ -556,6 +558,8 @@ struct airoha_gdm_dev {
struct phylink *phylink;
struct phylink_config phylink_config;
#endif
+
+ DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
};
struct airoha_gdm_port {
@@ -565,8 +569,6 @@ struct airoha_gdm_port {
struct airoha_hw_stats stats;
- DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
-
/* qos stats counters */
u64 cpu_tx_packets;
u64 fwd_tx_packets;
@@ -0,0 +1,73 @@
From 00272dbf6a52241a21145631f22dc5f03891078b Mon Sep 17 00:00:00 2001
Message-ID: <00272dbf6a52241a21145631f22dc5f03891078b.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 10 Apr 2026 14:47:08 +0200
Subject: [PATCH 05/13] net: airoha: Move {cpu,fwd}_tx_packets in
airoha_gdm_dev struct
Since now multiple net_devices connected to different QDMA blocks can
share the same GDM port, cpu_tx_packets and fwd_tx_packets fields can
be overwritten with the value from a different QDMA block. In order to
fix the issue move cpu_tx_packets and fwd_tx_packets fields from
airoha_gdm_port struct to airoha_gdm_dev one.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 16 +++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 7 +++----
2 files changed, 10 insertions(+), 13 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2535,19 +2535,17 @@ static int airoha_qdma_get_tx_ets_stats(
struct tc_ets_qopt_offload *opt)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ struct airoha_qdma *qdma = dev->qdma;
- u64 cpu_tx_packets = airoha_qdma_rr(dev->qdma,
- REG_CNTR_VAL(channel << 1));
- u64 fwd_tx_packets = airoha_qdma_rr(dev->qdma,
+ u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
+ u64 fwd_tx_packets = airoha_qdma_rr(qdma,
REG_CNTR_VAL((channel << 1) + 1));
- u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
- (fwd_tx_packets - port->fwd_tx_packets);
+ u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
+ (fwd_tx_packets - dev->fwd_tx_packets);
_bstats_update(opt->stats.bstats, 0, tx_packets);
-
- port->cpu_tx_packets = cpu_tx_packets;
- port->fwd_tx_packets = fwd_tx_packets;
+ dev->cpu_tx_packets = cpu_tx_packets;
+ dev->fwd_tx_packets = fwd_tx_packets;
return 0;
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -560,6 +560,9 @@ struct airoha_gdm_dev {
#endif
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
+ /* qos stats counters */
+ u64 cpu_tx_packets;
+ u64 fwd_tx_packets;
};
struct airoha_gdm_port {
@@ -569,10 +572,6 @@ struct airoha_gdm_port {
struct airoha_hw_stats stats;
- /* qos stats counters */
- u64 cpu_tx_packets;
- u64 fwd_tx_packets;
-
struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
};
@@ -0,0 +1,714 @@
From 8eb0a71bfbe92b6fbc668c5d9ebdcbf6523a89ad Mon Sep 17 00:00:00 2001
Message-ID: <8eb0a71bfbe92b6fbc668c5d9ebdcbf6523a89ad.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 1 Nov 2025 11:27:48 +0100
Subject: [PATCH 06/13] net: airoha: Support multiple net_devices for a single
FE GDM port
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
EN7581 or AN7583 SoCs support connecting multiple external SerDes (e.g.
Ethernet or USB SerDes) to GDM3 or GDM4 ports via a hw arbiter that
manages the traffic in a TDM manner. As a result multiple net_devices can
connect to the same GDM{3,4} port and there is a theoretical "1:n"
relation between GDM ports and net_devices.
┌─────────────────────────────────┐
│ │ ┌──────┐
│ P1 GDM1 ├────►MT7530│
│ │ └──────┘
│ │ ETH0 (DSA conduit)
│ │
│ PSE/FE │
│ │
│ │
│ │ ┌─────┐
│ P0 CDM1 ├────►QDMA0│
│ P4 P9 GDM4 │ └─────┘
└──┬─────────────────────────┬────┘
│ │
┌──▼──┐ ┌────▼────┐
│ PPE │ │ ARB │
└─────┘ └─┬─────┬─┘
│ │
┌──▼──┐┌─▼───┐
│ ETH ││ USB │
└─────┘└─────┘
ETH1 ETH2
Introduce support for multiple net_devices connected to the same Frame
Engine (FE) GDM port (GDM3 or GDM4) via an external hw arbiter.
Please note GDM1 or GDM2 does not support the connection with the external
arbiter.
Add get_dev_from_sport callback since EN7581 and AN7583 have different
logics for the net_device type connected to GDM3 or GDM4.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 274 +++++++++++++++++------
drivers/net/ethernet/airoha/airoha_eth.h | 10 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 13 +-
3 files changed, 228 insertions(+), 69 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -115,7 +115,7 @@ static int airoha_set_vip_for_gdm_port(s
struct airoha_eth *eth = dev->eth;
u32 vip_port;
- vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
+ vip_port = eth->soc->ops.get_vip_port(port, dev->nbq);
if (enable) {
airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port);
airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port);
@@ -618,30 +618,26 @@ static int airoha_qdma_fill_rx_queue(str
return nframes;
}
-static int airoha_qdma_get_gdm_port(struct airoha_eth *eth,
- struct airoha_qdma_desc *desc)
+static struct airoha_gdm_dev *
+airoha_qdma_get_gdm_dev(struct airoha_eth *eth, struct airoha_qdma_desc *desc)
{
- u32 port, sport, msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
+ struct airoha_gdm_port *port;
+ u16 p, d;
- sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1);
- switch (sport) {
- case 0x18:
- port = 3; /* GDM4 */
- break;
- case 0x16:
- port = 2; /* GDM3 */
- break;
- case 0x10 ... 0x14:
- port = 0; /* GDM1 */
- break;
- case 0x2 ... 0x4:
- port = sport - 1;
- break;
- default:
- return -EINVAL;
- }
+ if (eth->soc->ops.get_dev_from_sport(desc, &p, &d))
+ return ERR_PTR(-ENODEV);
+
+ if (p >= ARRAY_SIZE(eth->ports))
+ return ERR_PTR(-ENODEV);
+
+ port = eth->ports[p];
+ if (!port)
+ return ERR_PTR(-ENODEV);
+
+ if (d >= ARRAY_SIZE(port->devs))
+ return ERR_PTR(-ENODEV);
- return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
+ return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV);
}
static int airoha_qdma_lro_rx_process(struct airoha_queue *q,
@@ -725,9 +721,8 @@ static int airoha_qdma_rx_process(struct
struct airoha_queue_entry *e = &q->entry[q->tail];
struct airoha_qdma_desc *desc = &q->desc[q->tail];
u32 hash, reason, msg1, desc_ctrl;
- struct airoha_gdm_port *port;
- struct net_device *netdev;
- int data_len, len, p;
+ struct airoha_gdm_dev *dev;
+ int data_len, len;
struct page *page;
desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
@@ -748,15 +743,10 @@ static int airoha_qdma_rx_process(struct
if (!len || data_len < len)
goto free_frag;
- p = airoha_qdma_get_gdm_port(eth, desc);
- if (p < 0 || !eth->ports[p])
- goto free_frag;
-
- port = eth->ports[p];
- if (!port->dev)
+ dev = airoha_qdma_get_gdm_dev(eth, desc);
+ if (IS_ERR(dev))
goto free_frag;
- netdev = port->dev->dev;
if (!q->skb) { /* first buffer */
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
q->buf_size);
@@ -766,15 +756,15 @@ static int airoha_qdma_rx_process(struct
skb_reserve(q->skb, AIROHA_RX_HEADROOM);
__skb_put(q->skb, len);
skb_mark_for_recycle(q->skb);
- q->skb->dev = netdev;
+ q->skb->dev = dev->dev;
q->skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(q->skb, qid);
- if (lro_q && (netdev->features & NETIF_F_LRO) &&
+ if (lro_q && (dev->dev->features & NETIF_F_LRO) &&
airoha_qdma_lro_rx_process(q, desc) < 0)
goto free_frag;
- q->skb->protocol = eth_type_trans(q->skb, netdev);
+ q->skb->protocol = eth_type_trans(q->skb, dev->dev);
} else { /* scattered frame */
struct skb_shared_info *shinfo = skb_shinfo(q->skb);
int nr_frags = shinfo->nr_frags;
@@ -790,7 +780,9 @@ static int airoha_qdma_rx_process(struct
if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl))
continue;
- if (netdev_uses_dsa(netdev)) {
+ if (netdev_uses_dsa(dev->dev)) {
+ struct airoha_gdm_port *port = dev->port;
+
/* PPE module requires untagged packets to work
* properly and it provides DSA port index via the
* DMA descriptor. Report DSA tag to the DSA stack
@@ -987,24 +979,27 @@ static void airoha_qdma_wake_netdev_txqs
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- struct airoha_gdm_dev *dev;
- int j;
+ int d;
if (!port)
continue;
- dev = port->dev;
- if (!dev)
- continue;
+ for (d = 0; d < ARRAY_SIZE(port->devs); d++) {
+ struct airoha_gdm_dev *dev = port->devs[d];
+ int j;
- if (dev->qdma != qdma)
- continue;
+ if (!dev)
+ continue;
- for (j = 0; j < dev->dev->num_tx_queues; j++) {
- if (airoha_qdma_get_txq(qdma, j) != qid)
+ if (dev->qdma != qdma)
continue;
- netif_wake_subqueue(dev->dev, j);
+ for (j = 0; j < dev->dev->num_tx_queues; j++) {
+ if (airoha_qdma_get_txq(qdma, j) != qid)
+ continue;
+
+ netif_wake_subqueue(dev->dev, j);
+ }
}
}
q->txq_stopped = false;
@@ -1893,11 +1888,9 @@ static int airoha_dev_open(struct net_de
GLOBAL_CFG_RX_DMA_EN_MASK);
atomic_inc(&qdma->users);
- if (port->id == AIROHA_GDM2_IDX &&
- airoha_ppe_is_enabled(qdma->eth, 1)) {
- /* For PPE2 always use secondary cpu port. */
+ if (!airoha_is_lan_gdm_dev(dev) &&
+ airoha_ppe_is_enabled(qdma->eth, 1))
pse_port = FE_PSE_PORT_PPE2;
- }
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
pse_port);
@@ -1992,7 +1985,7 @@ static int airoha_set_gdm2_loopback(stru
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
- src_port = eth->soc->ops.get_sport(port, port->nbq);
+ src_port = eth->soc->ops.get_sport(port, dev->nbq);
if (src_port < 0)
return src_port;
@@ -2009,7 +2002,7 @@ static int airoha_set_gdm2_loopback(stru
airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
- u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
__field_prep(mask, AIROHA_GDM2_IDX));
@@ -2023,7 +2016,7 @@ static int airoha_dev_init(struct net_de
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
- int i;
+ int ppe_id;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
@@ -2046,8 +2039,8 @@ static int airoha_dev_init(struct net_de
break;
}
- for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
+ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
+ airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
return 0;
}
@@ -2209,20 +2202,26 @@ static int airoha_dev_set_features(struc
} else {
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *p = eth->ports[i];
- struct airoha_gdm_dev *d;
+ int j;
if (!p)
continue;
- d = p->dev;
- if (d->qdma != qdma)
- continue;
+ for (j = 0; j < ARRAY_SIZE(p->devs); j++) {
+ struct airoha_gdm_dev *d = p->devs[j];
- if (d->dev == netdev)
- continue;
+ if (!d)
+ continue;
- if (d->dev->features & NETIF_F_LRO)
- return 0;
+ if (d->qdma != qdma)
+ continue;
+
+ if (d->dev == netdev)
+ continue;
+
+ if (d->dev->features & NETIF_F_LRO)
+ return 0;
+ }
}
airoha_fe_lro_disable(eth, qdma_id);
}
@@ -2273,7 +2272,8 @@ static netdev_tx_t airoha_dev_xmit(struc
}
fport = airoha_get_fe_port(dev);
- msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
+ msg1 = FIELD_PREP(QDMA_ETH_TXMSG_NBOQ_MASK, dev->nbq) |
+ FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
q = &qdma->q_tx[qid];
@@ -3209,12 +3209,15 @@ bool airoha_is_valid_gdm_dev(struct airo
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ int j;
if (!port)
continue;
- if (port->dev == dev)
- return true;
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ if (port->devs[j] == dev)
+ return true;
+ }
}
return false;
@@ -3338,10 +3341,11 @@ static int airoha_setup_phylink(struct n
static int airoha_alloc_gdm_device(struct airoha_eth *eth,
struct airoha_gdm_port *port,
- struct device_node *np)
+ int nbq, struct device_node *np)
{
- struct airoha_gdm_dev *dev;
struct net_device *netdev;
+ struct airoha_gdm_dev *dev;
+ u8 index;
int err;
netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
@@ -3359,7 +3363,6 @@ static int airoha_alloc_gdm_device(struc
netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO;
netdev->features |= AIROHA_HW_FEATURES;
netdev->vlan_features = AIROHA_HW_FEATURES;
- netdev->dev.of_node = np;
SET_NETDEV_DEV(netdev, eth->dev);
/* reserve hw queues for HTB offloading */
@@ -3377,11 +3380,25 @@ static int airoha_alloc_gdm_device(struc
netdev->dev_addr);
}
+ /* Allowed nbq for EN7581 on GDM3 port are 4 and 5 for PCIE0
+ * and PCIE1 respectively.
+ */
+ index = nbq;
+ if (index && airoha_is_7581(eth) && port->id == AIROHA_GDM3_IDX)
+ index -= 4;
+
+ if (index >= ARRAY_SIZE(port->devs) || port->devs[index]) {
+ dev_err(eth->dev, "invalid nbq id: %d\n", nbq);
+ return -EINVAL;
+ }
+
+ netdev->dev.of_node = of_node_get(np);
dev = netdev_priv(netdev);
dev->dev = netdev;
dev->port = port;
- port->dev = dev;
dev->eth = eth;
+ dev->nbq = nbq;
+ port->devs[index] = dev;
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
@@ -3399,7 +3416,8 @@ static int airoha_alloc_gdm_port(struct
{
const __be32 *id_ptr = of_get_property(np, "reg", NULL);
struct airoha_gdm_port *port;
- int err, p;
+ struct device_node *node;
+ int err, nbq, p, d = 0;
u32 id;
if (!id_ptr) {
@@ -3427,15 +3445,51 @@ static int airoha_alloc_gdm_port(struct
u64_stats_init(&port->stats.syncp);
spin_lock_init(&port->stats.lock);
port->id = id;
- /* XXX: Read nbq from DTS */
- port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
err = airoha_metadata_dst_alloc(port);
if (err)
return err;
- return airoha_alloc_gdm_device(eth, port, np);
+ /* Default nbq value to ensure backward compatibility */
+ nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
+
+ for_each_child_of_node(np, node) {
+ /* Multiple external serdes connected to the FE GDM port via an
+ * external arbiter.
+ */
+ const __be32 *nbq_ptr;
+
+ if (!of_device_is_compatible(node, "airoha,eth-port"))
+ continue;
+
+ d++;
+ if (!of_device_is_available(node))
+ continue;
+
+ nbq_ptr = of_get_property(node, "reg", NULL);
+ if (!nbq_ptr) {
+ dev_err(eth->dev, "missing nbq id\n");
+ of_node_put(node);
+ return -EINVAL;
+ }
+
+ /* Verify the provided nbq parameter is valid */
+ nbq = be32_to_cpup(nbq_ptr);
+ err = eth->soc->ops.get_sport(port, nbq);
+ if (err < 0) {
+ of_node_put(node);
+ return err;
+ }
+
+ err = airoha_alloc_gdm_device(eth, port, nbq, node);
+ if (err) {
+ of_node_put(node);
+ return err;
+ }
+ }
+
+ return !d ? airoha_alloc_gdm_device(eth, port, nbq, np) : 0;
}
static int airoha_register_gdm_devices(struct airoha_eth *eth)
@@ -3444,14 +3498,22 @@ static int airoha_register_gdm_devices(s
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- int err;
+ int j;
if (!port)
continue;
- err = register_netdev(port->dev->dev);
- if (err)
- return err;
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ int err;
+
+ if (!dev)
+ continue;
+
+ err = register_netdev(dev->dev);
+ if (err)
+ return err;
+ }
}
set_bit(DEV_STATE_REGISTERED, &eth->state);
@@ -3558,18 +3620,27 @@ error_napi_stop:
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- struct airoha_gdm_dev *dev;
+ int j;
if (!port)
continue;
- dev = port->dev;
- if (dev && dev->dev->reg_state == NETREG_REGISTERED) {
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ struct net_device *netdev;
+
+ if (!dev)
+ continue;
+
+ netdev = dev->dev;
+ if (netdev->reg_state == NETREG_REGISTERED) {
#if defined(CONFIG_PCS_AIROHA)
- if (airhoa_is_phy_external(port))
- phylink_destroy(dev->phylink);
+ if (airhoa_is_phy_external(port))
+ phylink_destroy(dev->phylink);
#endif
- unregister_netdev(dev->dev);
+ unregister_netdev(netdev);
+ }
+ of_node_put(netdev->dev.of_node);
}
airoha_metadata_dst_free(port);
}
@@ -3591,19 +3662,26 @@ static void airoha_remove(struct platfor
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- struct airoha_gdm_dev *dev;
+ int j;
if (!port)
continue;
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ struct net_device *netdev;
+
+ if (!dev)
+ continue;
+
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port))
phylink_destroy(dev->phylink);
#endif
-
- dev = port->dev;
- if (dev)
- unregister_netdev(dev->dev);
+ netdev = dev->dev;
+ unregister_netdev(netdev);
+ of_node_put(netdev->dev.of_node);
+ }
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3665,6 +3743,39 @@ static u32 airoha_en7581_get_vip_port(st
return 0;
}
+static int airoha_en7581_get_dev_from_sport(struct airoha_qdma_desc *desc,
+ u16 *port, u16 *dev)
+{
+ u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK,
+ le32_to_cpu(READ_ONCE(desc->msg1)));
+
+ *dev = 0;
+ switch (sport) {
+ case 0x10 ... 0x14:
+ *port = 0; /* GDM1 */
+ break;
+ case 0x2 ... 0x4:
+ *port = sport - 1;
+ break;
+ case HSGMII_LAN_7581_PCIE1_SRCPORT:
+ *dev = 1;
+ fallthrough;
+ case HSGMII_LAN_7581_PCIE0_SRCPORT:
+ *port = 2; /* GDM3 */
+ break;
+ case HSGMII_LAN_7581_USB_SRCPORT:
+ *dev = 1;
+ fallthrough;
+ case HSGMII_LAN_7581_ETH_SRCPORT:
+ *port = 3; /* GDM4 */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const char * const an7583_xsi_rsts_names[] = {
"hsi0-mac",
"hsi1-mac",
@@ -3713,6 +3824,36 @@ static u32 airoha_an7583_get_vip_port(st
return 0;
}
+static int airoha_an7583_get_dev_from_sport(struct airoha_qdma_desc *desc,
+ u16 *port, u16 *dev)
+{
+ u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK,
+ le32_to_cpu(READ_ONCE(desc->msg1)));
+
+ *dev = 0;
+ switch (sport) {
+ case 0x10 ... 0x14:
+ *port = 0; /* GDM1 */
+ break;
+ case 0x2 ... 0x4:
+ *port = sport - 1;
+ break;
+ case HSGMII_LAN_7583_ETH_SRCPORT:
+ *port = 2; /* GDM3 */
+ break;
+ case HSGMII_LAN_7583_USB_SRCPORT:
+ *dev = 1;
+ fallthrough;
+ case HSGMII_LAN_7583_PCIE_SRCPORT:
+ *port = 3; /* GDM4 */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct airoha_eth_soc_data en7581_soc_data = {
.version = 0x7581,
.xsi_rsts_names = en7581_xsi_rsts_names,
@@ -3721,6 +3862,7 @@ static const struct airoha_eth_soc_data
.ops = {
.get_sport = airoha_en7581_get_sport,
.get_vip_port = airoha_en7581_get_vip_port,
+ .get_dev_from_sport = airoha_en7581_get_dev_from_sport,
},
};
@@ -3732,6 +3874,7 @@ static const struct airoha_eth_soc_data
.ops = {
.get_sport = airoha_an7583_get_sport,
.get_vip_port = airoha_an7583_get_vip_port,
+ .get_dev_from_sport = airoha_an7583_get_dev_from_sport,
},
};
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -17,6 +17,7 @@
#include <net/dsa.h>
#define AIROHA_MAX_NUM_GDM_PORTS 4
+#define AIROHA_MAX_NUM_GDM_DEVS 2
#define AIROHA_MAX_NUM_QDMA 2
#define AIROHA_MAX_NUM_IRQ_BANKS 4
#define AIROHA_MAX_DSA_PORTS 7
@@ -551,8 +552,8 @@ struct airoha_qdma {
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
struct airoha_qdma *qdma;
- struct net_device *dev;
struct airoha_eth *eth;
+ struct net_device *dev;
#if defined(CONFIG_PCS_AIROHA)
struct phylink *phylink;
@@ -563,12 +564,13 @@ struct airoha_gdm_dev {
/* qos stats counters */
u64 cpu_tx_packets;
u64 fwd_tx_packets;
+
+ int nbq;
};
struct airoha_gdm_port {
- struct airoha_gdm_dev *dev;
+ struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS];
int id;
- int nbq;
struct airoha_hw_stats stats;
@@ -604,6 +606,8 @@ struct airoha_eth_soc_data {
struct {
int (*get_sport)(struct airoha_gdm_port *port, int nbq);
u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq);
+ int (*get_dev_from_sport)(struct airoha_qdma_desc *desc,
+ u16 *port, u16 *dev);
} ops;
};
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -168,9 +168,7 @@ static void airoha_ppe_hw_init(struct ai
airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
PPE_FLOW_CFG_IP6_6RD_MASK);
- for (p = 0; p < ARRAY_SIZE(eth->ports); p++) {
- struct airoha_gdm_port *port = eth->ports[p];
-
+ for (p = 0; p < ARRAY_SIZE(eth->ports); p++)
airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
FP0_EGRESS_MTU_MASK |
FP1_EGRESS_MTU_MASK,
@@ -178,11 +176,24 @@ static void airoha_ppe_hw_init(struct ai
AIROHA_MAX_MTU) |
FIELD_PREP(FP1_EGRESS_MTU_MASK,
AIROHA_MAX_MTU));
- if (!port)
+ }
+
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *port = eth->ports[i];
+ int j;
+
+ if (!port)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ u8 fport;
+
+ if (!dev)
continue;
- airoha_ppe_set_cpu_port(port->dev, i,
- airoha_get_fe_port(port->dev));
+ fport = airoha_get_fe_port(dev);
+ airoha_ppe_set_cpu_port(dev, i, fport);
}
}
}
@@ -0,0 +1,132 @@
From de856a5b802cf030c8e242e98df3bc88446a4ea1 Mon Sep 17 00:00:00 2001
Message-ID: <de856a5b802cf030c8e242e98df3bc88446a4ea1.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 20 Mar 2026 11:09:40 +0100
Subject: [PATCH 07/13] net: airoha: Do not stop GDM port if it is shared
Theoretically, in the current codebase, two independent net_devices can
be connected to the same GDM port so we need to check the GDM port is not
used by any other running net_device before setting the forward
configuration to FE_PSE_PORT_DROP.
Moreover, always set in GDM_LONG_LEN_MASK field of REG_GDM_LEN_CFG
register the maximum MTU of all running net_devices connected to the same
GDM port.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 59 +++++++++++++++++++-----
drivers/net/ethernet/airoha/airoha_eth.h | 1 +
2 files changed, 48 insertions(+), 12 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1849,8 +1849,8 @@ static int airoha_dev_open(struct net_de
int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
+ u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma = dev->qdma;
- u32 pse_port = FE_PSE_PORT_PPE1;
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
@@ -1878,10 +1878,20 @@ static int airoha_dev_open(struct net_de
airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
- FIELD_PREP(GDM_LONG_LEN_MASK, len));
+ cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ GDM_LONG_LEN_MASK);
+ if (!port->users || len > cur_len) {
+ /* Opening a sibling net_device with a larger MTU updates the
+ * MTU of already running devices. This is required to allow
+ * multiple net_devices with different MTUs to share the same
+ * GDM port.
+ */
+ airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
+ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
+ FIELD_PREP(GDM_LONG_LEN_MASK, len));
+ }
+ port->users++;
airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
@@ -1897,6 +1907,30 @@ static int airoha_dev_open(struct net_de
return 0;
}
+static void airoha_set_port_mtu(struct airoha_eth *eth,
+ struct airoha_gdm_port *port)
+{
+ u32 len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
+ struct airoha_gdm_dev *dev = port->devs[i];
+ struct net_device *netdev;
+
+ if (!dev)
+ continue;
+
+ netdev = dev->dev;
+ if (netif_running(netdev))
+ len = max_t(u32, len, netdev->mtu);
+ }
+ len += ETH_HLEN + ETH_FCS_LEN;
+
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
+ GDM_LONG_LEN_MASK,
+ FIELD_PREP(GDM_LONG_LEN_MASK, len));
+}
+
static int airoha_dev_stop(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -1909,8 +1943,12 @@ static int airoha_dev_stop(struct net_de
for (i = 0; i < netdev->num_tx_queues; i++)
netdev_tx_reset_subqueue(netdev, i);
- airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
- FE_PSE_PORT_DROP);
+ if (--port->users)
+ airoha_set_port_mtu(dev->eth, port);
+ else
+ airoha_set_gdm_port_fwd_cfg(qdma->eth,
+ REG_GDM_FWD_CFG(port->id),
+ FE_PSE_PORT_DROP);
if (atomic_dec_and_test(&qdma->users)) {
airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
@@ -2072,13 +2110,10 @@ static int airoha_dev_change_mtu(struct
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
- struct airoha_eth *eth = dev->eth;
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
- GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_LONG_LEN_MASK, len));
WRITE_ONCE(netdev->mtu, mtu);
+ if (port->users)
+ airoha_set_port_mtu(dev->eth, port);
return 0;
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -571,6 +571,7 @@ struct airoha_gdm_dev {
struct airoha_gdm_port {
struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS];
int id;
+ int users;
struct airoha_hw_stats stats;
@@ -0,0 +1,174 @@
From c4f3077948eda05a6b9d1a11304d82c3e0300151 Mon Sep 17 00:00:00 2001
Message-ID: <c4f3077948eda05a6b9d1a11304d82c3e0300151.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 3 Apr 2026 12:07:27 +0200
Subject: [PATCH 08/13] net: airoha: Introduce WAN device flag
Introduce WAN flag to specify if a given device is used to transmit/receive
WAN or LAN traffic. Current codebase supports specifying LAN/WAN device
configuration in ndo_init() callback during device bootstrap.
In order to consider setups where LAN configuration is used even for
GDM3/GDM4 devices, check airoha_is_lan_gdm_dev() to select pse_port in
airoha_ppe_foe_entry_prepare().
Please note after this patch, it will be possible to specify multiple LAN
devices but just a single WAN one. Please note this change is not visible
to the user since airoha_eth driver currently supports just the internal
phy available via the MT7530 DSA switch and there are no WAN interfaces
officially supported since PCS/external phy is not merged mainline yet
(it will be posted with following patches).
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 72 +++++++++++++++++++-----
drivers/net/ethernet/airoha/airoha_eth.h | 13 ++---
drivers/net/ethernet/airoha/airoha_ppe.c | 2 +-
3 files changed, 65 insertions(+), 22 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2049,36 +2049,80 @@ static int airoha_set_gdm2_loopback(stru
return 0;
}
-static int airoha_dev_init(struct net_device *netdev)
+static struct airoha_gdm_dev *
+airoha_get_wan_gdm_dev(struct airoha_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *port = eth->ports[i];
+ int j;
+
+ if (!port)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+
+ if (dev && !airoha_is_lan_gdm_dev(dev))
+ return dev;
+ }
+ }
+
+ return NULL;
+}
+
+static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
{
- struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
int ppe_id;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
dev->dev->irq = dev->qdma->irq_banks[0].irq;
- airoha_set_macaddr(dev, netdev->dev_addr);
+
+ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
+ airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
+}
+
+static int airoha_dev_init(struct net_device *netdev)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
switch (port->id) {
case AIROHA_GDM3_IDX:
- case AIROHA_GDM4_IDX:
- /* If GDM2 is active we can't enable loopback */
- if (!eth->ports[1]) {
- int err;
-
- err = airoha_set_gdm2_loopback(dev);
- if (err)
- return err;
- }
+ case AIROHA_GDM4_IDX: {
+ struct airoha_eth *eth = dev->eth;
+
+ /* GDM2 supports a single net_device */
+ if (eth->ports[1] && eth->ports[1]->devs[0])
+ break;
+
+ if (airoha_get_wan_gdm_dev(eth))
+ break;
+
+ fallthrough;
+ }
+ case AIROHA_GDM2_IDX:
+ /* GDM2 is always used as wan */
+ dev->flags |= AIROHA_PRIV_F_WAN;
break;
default:
break;
}
- ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
- airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
+ airoha_dev_set_qdma(dev);
+ airoha_set_macaddr(dev, netdev->dev_addr);
+
+ if (!airoha_is_lan_gdm_dev(dev) &&
+ (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
+ int err;
+
+ err = airoha_set_gdm2_loopback(dev);
+ if (err)
+ return err;
+ }
return 0;
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -549,6 +549,10 @@ struct airoha_qdma {
DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
};
+enum airoha_priv_flags {
+ AIROHA_PRIV_F_WAN = BIT(0),
+};
+
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
struct airoha_qdma *qdma;
@@ -565,6 +569,7 @@ struct airoha_gdm_dev {
u64 cpu_tx_packets;
u64 fwd_tx_packets;
+ u32 flags;
int nbq;
};
@@ -671,13 +676,7 @@ static inline u16 airoha_qdma_get_txq(st
static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
- struct airoha_gdm_port *port = dev->port;
-
- /* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
- * GDM{2,3,4} can be used as wan port connected to an external
- * phy module.
- */
- return port->id == 1;
+ return !(dev->flags & AIROHA_PRIV_F_WAN);
}
static inline bool airoha_is_7581(struct airoha_eth *eth)
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -351,7 +351,7 @@ static int airoha_ppe_foe_entry_prepare(
return -EINVAL;
port = dev->port;
- if (dsa_port >= 0 || eth->ports[1])
+ if (dsa_port >= 0 || airoha_is_lan_gdm_dev(dev))
pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
: port->id;
else
@@ -0,0 +1,161 @@
From 144f0e6e55896625e3411aad02399a5ebb48d8f9 Mon Sep 17 00:00:00 2001
Message-ID: <144f0e6e55896625e3411aad02399a5ebb48d8f9.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 13 Apr 2026 17:38:51 +0200
Subject: [PATCH 09/13] net: airoha: Support multiple LAN/WAN interfaces for hw
MAC address configuration
The EN7581 and AN7583 SoCs provide registers to configure hardware LAN/WAN
MAC addresses, used to determine whether received traffic is destined for
this host or should be forwarded to another device.
The SoC hardware design assumes all interfaces configured as LAN (or WAN)
share a common upper MAC address, which is programmed into the
REG_FE_{LAN,WAN}_MAC_H register. The lower bytes of 'local' addresses can
be expressed as a range via the REG_FE_MAC_LMIN and REG_FE_MAC_LMAX
registers.
Previously, only a single interface was considered when programming these
registers. Extend the logic to derive the correct minimum and maximum
values for REG_FE_MAC_LMIN/REG_FE_MAC_LMAX when two or more interfaces are
configured as LAN or WAN.
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 77 ++++++++++++++++++++----
drivers/net/ethernet/airoha/airoha_eth.h | 2 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 4 +-
3 files changed, 68 insertions(+), 15 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -80,20 +80,74 @@ static bool airhoa_is_phy_external(struc
}
#endif
-static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
+static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
+ u8 ref_addr[ETH_ALEN] __aligned(2);
struct airoha_eth *eth = dev->eth;
- u32 val, reg;
+ u32 reg, val, lmin, lmax;
+ int i;
+
+ eth_zero_addr(ref_addr);
+ lmin = (addr[3] << 16) | (addr[4] << 8) | addr[5];
+ lmax = lmin;
+
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *port = eth->ports[i];
+ int j;
+
+ if (!port)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *iter_dev;
+ struct net_device *netdev;
+
+ iter_dev = port->devs[j];
+ if (!iter_dev || iter_dev == dev)
+ continue;
+
+ if (airoha_is_lan_gdm_dev(iter_dev) !=
+ airoha_is_lan_gdm_dev(dev))
+ continue;
+
+ netdev = iter_dev->dev;
+ if (netdev->reg_state != NETREG_REGISTERED)
+ continue;
+
+ ether_addr_copy(ref_addr, netdev->dev_addr);
+ val = (netdev->dev_addr[3] << 16) |
+ (netdev->dev_addr[4] << 8) | netdev->dev_addr[5];
+ if (val < lmin)
+ lmin = val;
+ if (val > lmax)
+ lmax = val;
+ }
+ }
+
+ if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
+ /* According to the HW design, hw mac address MSBs must be
+ * the same for each net_device with the same LAN/WAN
+ * configuration.
+ */
+ dev_warn(eth->dev,
+ "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n",
+ dev->dev->name, ref_addr[0], ref_addr[1],
+ ref_addr[2]);
+ dev_warn(eth->dev, "FE hw forwarding won't work properly\n");
+
+ return -EINVAL;
+ }
reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H;
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
airoha_fe_wr(eth, reg, val);
- val = (addr[3] << 16) | (addr[4] << 8) | addr[5];
- airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
- airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
+ airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), lmin);
+ airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), lmax);
+
+ airoha_ppe_init_upd_mem(dev, addr);
- airoha_ppe_init_upd_mem(dev);
+ return 0;
}
static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
@@ -1976,13 +2030,18 @@ static int airoha_dev_stop(struct net_de
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct sockaddr *addr = p;
int err;
- err = eth_mac_addr(netdev, p);
+ err = eth_prepare_mac_addr_change(netdev, p);
+ if (err)
+ return err;
+
+ err = airoha_set_macaddr(dev, addr->sa_data);
if (err)
return err;
- airoha_set_macaddr(dev, netdev->dev_addr);
+ eth_commit_mac_addr_change(netdev, p);
return 0;
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -712,7 +712,7 @@ void airoha_ppe_check_skb(struct airoha_
int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
int airoha_ppe_init(struct airoha_eth *eth);
void airoha_ppe_deinit(struct airoha_eth *eth);
-void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev);
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr);
u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe);
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
u32 hash);
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1494,12 +1494,10 @@ void airoha_ppe_check_skb(struct airoha_
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
}
-void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr)
{
struct airoha_gdm_port *port = dev->port;
- struct net_device *netdev = dev->dev;
struct airoha_eth *eth = dev->eth;
- const u8 *addr = netdev->dev_addr;
u32 val;
val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
@@ -0,0 +1,37 @@
From 917f959d54efd09719bd5047aa4b9543615e131e Mon Sep 17 00:00:00 2001
Message-ID: <917f959d54efd09719bd5047aa4b9543615e131e.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 19 Dec 2025 23:12:32 +0100
Subject: [PATCH 10/13] net: airoha: Rename airoha_set_gdm2_loopback in
airoha_enable_gdm2_loopback
This is a preliminary patch in order to allow the user to select if the
configured device will be used as hw lan or wan.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2046,7 +2046,7 @@ static int airoha_dev_set_macaddr(struct
return 0;
}
-static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
+static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
@@ -2178,7 +2178,7 @@ static int airoha_dev_init(struct net_de
(port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
int err;
- err = airoha_set_gdm2_loopback(dev);
+ err = airoha_enable_gdm2_loopback(dev);
if (err)
return err;
}
@@ -0,0 +1,246 @@
From e928621a0bbd010b75624c77105ce31f2b8d2faf Mon Sep 17 00:00:00 2001
Message-ID: <e928621a0bbd010b75624c77105ce31f2b8d2faf.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 13 Dec 2025 09:45:09 +0100
Subject: [PATCH 12/13] net: airoha: Add ethtool priv_flags callbacks
Introduce ethtool priv_flags callbacks in order to allow the user to
select if the configured device will be used as hw lan or wan and enable
or disable gdm2 loopback (used for hw QoS).
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 173 ++++++++++++++++++++++
drivers/net/ethernet/airoha/airoha_regs.h | 1 +
2 files changed, 174 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1906,6 +1906,11 @@ static int airoha_dev_open(struct net_de
u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma = dev->qdma;
+ if (port->id == AIROHA_GDM2_IDX && airoha_is_lan_gdm_dev(dev)) {
+ /* GDM2 can be used just as wan */
+ return -EBUSY;
+ }
+
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0);
@@ -2108,6 +2113,45 @@ static int airoha_enable_gdm2_loopback(s
return 0;
}
+static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)
+{
+ struct airoha_eth *eth = dev->eth;
+ int i, src_port;
+ u32 pse_port;
+
+ src_port = eth->soc->ops.get_sport(dev->port, dev->nbq);
+ if (src_port < 0)
+ return src_port;
+
+ airoha_fe_clear(eth,
+ REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
+ SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK));
+
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ FE_PSE_PORT_DROP);
+ airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
+ LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);
+ pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
+ : FE_PSE_PORT_PPE1;
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ pse_port);
+
+ airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,
+ FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));
+
+ for (i = 0; i < eth->soc->num_ppe; i++)
+ airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),
+ DFT_CPORT_MASK(AIROHA_GDM2_IDX));
+
+ /* Enable VIP and IFC for GDM2 */
+ airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
+ airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
+
+ airoha_fe_wr(eth, REG_SRC_PORT_FC_MAP6, FC_MAP6_DEF_VALUE);
+
+ return 0;
+}
+
static struct airoha_gdm_dev *
airoha_get_wan_gdm_dev(struct airoha_eth *eth)
{
@@ -2509,6 +2553,80 @@ error:
return NETDEV_TX_OK;
}
+struct airoha_ethool_priv_flags {
+ char name[ETH_GSTRING_LEN];
+ int (*handler)(struct net_device *netdev, u32 flags);
+};
+
+static int airoha_dev_set_wan_flag(struct net_device *netdev, u32 flags)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
+
+ if (!((dev->flags ^ flags) & AIROHA_PRIV_F_WAN))
+ return 0;
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ if (flags & AIROHA_PRIV_F_WAN) {
+ struct airoha_gdm_dev *wan_dev;
+
+ /* Verify the wan device is not already configured */
+ wan_dev = airoha_get_wan_gdm_dev(eth);
+ if (wan_dev && wan_dev != dev)
+ return -EBUSY;
+
+ switch (port->id) {
+ case AIROHA_GDM2_IDX:
+ dev->flags |= AIROHA_PRIV_F_WAN;
+ airoha_dev_set_qdma(dev);
+ break;
+ case AIROHA_GDM3_IDX:
+ case AIROHA_GDM4_IDX: {
+ int err;
+
+ dev->flags |= AIROHA_PRIV_F_WAN;
+ airoha_dev_set_qdma(dev);
+
+ err = airoha_enable_gdm2_loopback(dev);
+ if (err) {
+ dev->flags &= ~AIROHA_PRIV_F_WAN;
+ return err;
+ }
+ break;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+ } else {
+ switch (port->id) {
+ case AIROHA_GDM3_IDX:
+ case AIROHA_GDM4_IDX: {
+ int err;
+
+ err = airoha_disable_gdm2_loopback(dev);
+ if (err)
+ return err;
+ break;
+ }
+ default:
+ break;
+ }
+
+ dev->flags &= ~AIROHA_PRIV_F_WAN;
+ airoha_dev_set_qdma(dev);
+ }
+
+ return airoha_set_macaddr(dev, netdev->dev_addr);
+}
+
+static const struct airoha_ethool_priv_flags airoha_eth_priv_flags[] = {
+ { "wan", airoha_dev_set_wan_flag },
+};
+#define AIROHA_PRIV_FLAGS_STR_LEN ARRAY_SIZE(airoha_eth_priv_flags)
+
static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
@@ -2517,6 +2635,7 @@ static void airoha_ethtool_get_drvinfo(s
strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
+ info->n_priv_flags = AIROHA_PRIV_FLAGS_STR_LEN;
}
static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
@@ -2581,6 +2700,56 @@ airoha_ethtool_get_rmon_stats(struct net
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
+static int airoha_ethtool_set_priv_flags(struct net_device *netdev, u32 flags)
+{
+ int i;
+
+ for (i = 0; i < AIROHA_PRIV_FLAGS_STR_LEN; i++) {
+ int err;
+
+ if (!airoha_eth_priv_flags[i].handler)
+ continue;
+
+ err = airoha_eth_priv_flags[i].handler(netdev, flags);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static u32 airoha_ethtool_get_priv_flags(struct net_device *netdev)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+
+ return dev->flags;
+}
+
+static int airoha_ethtool_get_sset_count(struct net_device *netdev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_PRIV_FLAGS:
+ return AIROHA_PRIV_FLAGS_STR_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void airoha_ethtool_get_strings(struct net_device *netdev,
+ u32 stringset, u8 *data)
+{
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_PRIV_FLAGS:
+ for (i = 0; i < AIROHA_PRIV_FLAGS_STR_LEN; i++)
+ ethtool_puts(&data, airoha_eth_priv_flags[i].name);
+ break;
+ default:
+ break;
+ }
+}
+
static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
int channel, enum tx_sched_mode mode,
const u16 *weights, u8 n_weights)
@@ -3302,6 +3471,10 @@ static const struct ethtool_ops airoha_e
.get_rmon_stats = airoha_ethtool_get_rmon_stats,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.get_link = ethtool_op_get_link,
+ .set_priv_flags = airoha_ethtool_set_priv_flags,
+ .get_priv_flags = airoha_ethtool_get_priv_flags,
+ .get_sset_count = airoha_ethtool_get_sset_count,
+ .get_strings = airoha_ethtool_get_strings,
};
static void airoha_mac_config(struct phylink_config *config, unsigned int mode,
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -402,6 +402,7 @@
#define REG_SRC_PORT_FC_MAP6 0x2298
#define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
+#define FC_MAP6_DEF_VALUE 0x1b1a1918
#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4
@@ -0,0 +1,223 @@
From 99e204d255c9b47a188dd79762b9406d9c5fed9b Mon Sep 17 00:00:00 2001
Message-ID: <99e204d255c9b47a188dd79762b9406d9c5fed9b.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 20 May 2026 17:16:39 +0200
Subject: [PATCH 13/13] net: airoha: Rework MTU configuration
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 85 +++++++++++------------
drivers/net/ethernet/airoha/airoha_eth.h | 1 +
drivers/net/ethernet/airoha/airoha_ppe.c | 8 +--
drivers/net/ethernet/airoha/airoha_regs.h | 9 ++-
4 files changed, 49 insertions(+), 54 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -185,10 +185,15 @@ static void airoha_fe_maccr_init(struct
{
int p;
- for (p = 1; p <= ARRAY_SIZE(eth->ports); p++)
+ for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) {
airoha_fe_set(eth, REG_GDM_FWD_CFG(p),
GDM_TCP_CKSUM_MASK | GDM_UDP_CKSUM_MASK |
GDM_IP4_CKSUM_MASK | GDM_DROP_CRC_ERR_MASK);
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p),
+ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
+ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
+ FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU));
+ }
airoha_fe_rmw(eth, REG_CDM_VLAN_CTRL(1), CDM_VLAN_MASK,
FIELD_PREP(CDM_VLAN_MASK, 0x8100));
@@ -1898,13 +1903,37 @@ static void airoha_update_hw_stats(struc
spin_unlock(&port->stats.lock);
}
+void airoha_set_port_mtu(struct airoha_eth *eth, struct airoha_gdm_port *port)
+{
+ u32 mtu = 0, index;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
+ struct airoha_gdm_dev *dev = port->devs[i];
+ struct net_device *netdev;
+
+ if (!dev)
+ continue;
+
+ netdev = dev->dev;
+ if (netif_running(netdev))
+ mtu = max_t(u32, mtu, netdev->mtu);
+ }
+
+ index = port->id == AIROHA_GDM4_IDX ? 7 : port->id;
+ for (i = 0; i < eth->soc->num_ppe; i++)
+ airoha_fe_rmw(eth, REG_PPE_MTU(i, index),
+ FP_EGRESS_MTU_MASK(index),
+ __field_prep(FP_EGRESS_MTU_MASK(index), mtu));
+}
+
static int airoha_dev_open(struct net_device *netdev)
{
- int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma = dev->qdma;
+ u32 pse_port = FE_PSE_PORT_PPE1;
+ int err;
if (port->id == AIROHA_GDM2_IDX && airoha_is_lan_gdm_dev(dev)) {
/* GDM2 can be used just as wan */
@@ -1937,19 +1966,12 @@ static int airoha_dev_open(struct net_de
airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
- cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
- GDM_LONG_LEN_MASK);
- if (!port->users || len > cur_len) {
- /* Opening a sibling net_device with a larger MTU updates the
- * MTU of already running devices. This is required to allow
- * multiple net_devices with different MTUs to share the same
- * GDM port.
- */
- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
- FIELD_PREP(GDM_LONG_LEN_MASK, len));
- }
+ airoha_set_port_mtu(qdma->eth, port);
+ if (!airoha_is_lan_gdm_dev(dev))
+ airoha_fe_rmw(qdma->eth, REG_WAN_MTU0,
+ WAN_MTU0_MASK,
+ FIELD_PREP(WAN_MTU0_MASK, netdev->mtu));
+
port->users++;
airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
@@ -1966,30 +1988,6 @@ static int airoha_dev_open(struct net_de
return 0;
}
-static void airoha_set_port_mtu(struct airoha_eth *eth,
- struct airoha_gdm_port *port)
-{
- u32 len = 0;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
- struct airoha_gdm_dev *dev = port->devs[i];
- struct net_device *netdev;
-
- if (!dev)
- continue;
-
- netdev = dev->dev;
- if (netif_running(netdev))
- len = max_t(u32, len, netdev->mtu);
- }
- len += ETH_HLEN + ETH_FCS_LEN;
-
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
- GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_LONG_LEN_MASK, len));
-}
-
static int airoha_dev_stop(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -2073,10 +2071,6 @@ static int airoha_enable_gdm2_loopback(s
FIELD_PREP(LPBK_CHAN_MASK, chan) |
LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK |
LBK_CHAN_MODE_MASK | LPBK_EN_MASK);
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(AIROHA_GDM2_IDX),
- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
- FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU));
/* Forward the traffic to the proper GDM port */
pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
: FE_PSE_PORT_GDM4;
@@ -2600,6 +2594,9 @@ static int airoha_dev_set_wan_flag(struc
default:
return -EOPNOTSUPP;
}
+ airoha_fe_rmw(eth, REG_WAN_MTU0,
+ WAN_MTU0_MASK,
+ FIELD_PREP(WAN_MTU0_MASK, netdev->mtu));
} else {
switch (port->id) {
case AIROHA_GDM3_IDX:
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -705,6 +705,7 @@ int airoha_get_fe_port(struct airoha_gdm
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
+void airoha_set_port_mtu(struct airoha_eth *eth, struct airoha_gdm_port *port);
void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -116,8 +116,6 @@ static void airoha_ppe_hw_init(struct ai
PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries);
for (i = 0; i < eth->soc->num_ppe; i++) {
- int p;
-
airoha_fe_wr(eth, REG_PPE_TB_BASE(i),
ppe->foe_dma + sram_tb_size);
@@ -167,15 +165,6 @@ static void airoha_ppe_hw_init(struct ai
airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED);
airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
PPE_FLOW_CFG_IP6_6RD_MASK);
-
- for (p = 0; p < ARRAY_SIZE(eth->ports); p++)
- airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
- FP0_EGRESS_MTU_MASK |
- FP1_EGRESS_MTU_MASK,
- FIELD_PREP(FP0_EGRESS_MTU_MASK,
- AIROHA_MAX_MTU) |
- FIELD_PREP(FP1_EGRESS_MTU_MASK,
- AIROHA_MAX_MTU));
}
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
@@ -185,6 +174,7 @@ static void airoha_ppe_hw_init(struct ai
if (!port)
continue;
+ airoha_set_port_mtu(eth, port);
for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
struct airoha_gdm_dev *dev = port->devs[j];
u8 fport;
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -341,9 +341,8 @@
#define PPE_SRAM_TABLE_EN_MASK BIT(0)
#define REG_PPE_MTU_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x304)
-#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + ((_n) << 2))
-#define FP1_EGRESS_MTU_MASK GENMASK(29, 16)
-#define FP0_EGRESS_MTU_MASK GENMASK(13, 0)
+#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + (((_n) / 2) << 2))
+#define FP_EGRESS_MTU_MASK(_n) GENMASK(13 + (((_n) % 2) << 4), ((_n) % 2) << 4)
#define REG_PPE_RAM_CTRL(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x31c)
#define PPE_SRAM_CTRL_ACK_MASK BIT(31)
@@ -404,6 +403,10 @@
#define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
#define FC_MAP6_DEF_VALUE 0x1b1a1918
+#define REG_WAN_MTU0 0x2300
+#define WAN_MTU1_MASK GENMASK(29, 16)
+#define WAN_MTU0_MASK GENMASK(13, 0)
+
#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4
/* QDMA */
@@ -0,0 +1,415 @@
From 9652322e0b47eacfef497828f6476a2a3169fd13 Mon Sep 17 00:00:00 2001
Message-ID: <9652322e0b47eacfef497828f6476a2a3169fd13.1779351672.git.lorenzo@kernel.org>
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Sat, 17 Jan 2026 14:46:12 +0100
Subject: [PATCH] net: airoha: Better handle MIB for GDM with multiple port
attached
In the context of a GDM that can have multiple port attached (GDM3/4)
the HW counter (MIB) are global for the GDM port. This cause duplicated
stats reported to the kernel for the related interface.
The SoC supports a split MIB feature where each counter is tracked based
on the relevant HW channel (NBQ) to account for this scenario and
provide a way to select the related counter on accessing the MIB
registers.
Enable this feature for GDM3 and GDM4 and configure the relevant HW
channel before updating the HW stats to report correct HW counter to the
kernel for the related interface.
Also move the stats struct from port to dev since HW counter are
now specific to the network interface instead of the GDM port.
Co-developed-by: Brown Huang <Brown.huang@airoha.com>
Signed-off-by: Brown Huang <Brown.huang@airoha.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 191 +++++++++++++----------
drivers/net/ethernet/airoha/airoha_eth.h | 7 +-
2 files changed, 112 insertions(+), 86 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -611,6 +611,14 @@ static int airoha_fe_init(struct airoha_
airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX),
GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK);
+ /* Enable split for MIB counters for GDM3 and GDM4 */
+ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM3_IDX),
+ FE_GDM_TX_MIB_SPLIT_EN_MASK |
+ FE_GDM_RX_MIB_SPLIT_EN_MASK);
+ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM4_IDX),
+ FE_GDM_TX_MIB_SPLIT_EN_MASK |
+ FE_GDM_RX_MIB_SPLIT_EN_MASK);
+
airoha_fe_crsn_qsel_init(eth);
airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK);
@@ -1758,149 +1766,169 @@ static void airoha_qdma_stop_napi(struct
}
}
-static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
+static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
- spin_lock(&port->stats.lock);
- u64_stats_update_begin(&port->stats.syncp);
+ u64_stats_update_begin(&dev->stats.syncp);
+
+ /* Read relevant MIB for GDM with multiple port attached */
+ if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
+ airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id),
+ FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK,
+ FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) |
+ FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq));
/* TX */
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
- port->stats.tx_ok_pkts += ((u64)val << 32);
+ dev->stats.tx_ok_pkts += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
- port->stats.tx_ok_pkts += val;
+ dev->stats.tx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
- port->stats.tx_ok_bytes += ((u64)val << 32);
+ dev->stats.tx_ok_bytes += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
- port->stats.tx_ok_bytes += val;
+ dev->stats.tx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
- port->stats.tx_drops += val;
+ dev->stats.tx_drops += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
- port->stats.tx_broadcast += val;
+ dev->stats.tx_broadcast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
- port->stats.tx_multicast += val;
+ dev->stats.tx_multicast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
- port->stats.tx_len[i] += val;
+ dev->stats.tx_len[i] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
/* RX */
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
- port->stats.rx_ok_pkts += ((u64)val << 32);
+ dev->stats.rx_ok_pkts += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
- port->stats.rx_ok_pkts += val;
+ dev->stats.rx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
- port->stats.rx_ok_bytes += ((u64)val << 32);
+ dev->stats.rx_ok_bytes += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
- port->stats.rx_ok_bytes += val;
+ dev->stats.rx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
- port->stats.rx_drops += val;
+ dev->stats.rx_drops += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
- port->stats.rx_broadcast += val;
+ dev->stats.rx_broadcast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
- port->stats.rx_multicast += val;
+ dev->stats.rx_multicast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
- port->stats.rx_errors += val;
+ dev->stats.rx_errors += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
- port->stats.rx_crc_error += val;
+ dev->stats.rx_crc_error += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
- port->stats.rx_over_errors += val;
+ dev->stats.rx_over_errors += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
- port->stats.rx_fragment += val;
+ dev->stats.rx_fragment += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
- port->stats.rx_jabber += val;
+ dev->stats.rx_jabber += val;
i = 0;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
- port->stats.rx_len[i] += val;
+ dev->stats.rx_len[i] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
+
+ u64_stats_update_end(&dev->stats.syncp);
+}
- /* reset mib counters */
- airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
+{
+ struct airoha_gdm_port *port = dev->port;
+ int i;
+
+ spin_lock(&port->stats_lock);
+
+ for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
+ if (port->devs[i])
+ airoha_dev_get_hw_stats(port->devs[i]);
+ }
+
+ /* Reset MIB counters */
+ airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id),
FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
- u64_stats_update_end(&port->stats.syncp);
- spin_unlock(&port->stats.lock);
+ spin_unlock(&port->stats_lock);
}
void airoha_set_port_mtu(struct airoha_eth *eth, struct airoha_gdm_port *port)
@@ -2228,23 +2256,22 @@ static void airoha_dev_get_stats64(struc
struct rtnl_link_stats64 *storage)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(dev);
do {
- start = u64_stats_fetch_begin(&port->stats.syncp);
- storage->rx_packets = port->stats.rx_ok_pkts;
- storage->tx_packets = port->stats.tx_ok_pkts;
- storage->rx_bytes = port->stats.rx_ok_bytes;
- storage->tx_bytes = port->stats.tx_ok_bytes;
- storage->multicast = port->stats.rx_multicast;
- storage->rx_errors = port->stats.rx_errors;
- storage->rx_dropped = port->stats.rx_drops;
- storage->tx_dropped = port->stats.tx_drops;
- storage->rx_crc_errors = port->stats.rx_crc_error;
- storage->rx_over_errors = port->stats.rx_over_errors;
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
+ storage->rx_packets = dev->stats.rx_ok_pkts;
+ storage->tx_packets = dev->stats.tx_ok_pkts;
+ storage->rx_bytes = dev->stats.rx_ok_bytes;
+ storage->tx_bytes = dev->stats.tx_ok_bytes;
+ storage->multicast = dev->stats.rx_multicast;
+ storage->rx_errors = dev->stats.rx_errors;
+ storage->rx_dropped = dev->stats.rx_drops;
+ storage->tx_dropped = dev->stats.tx_drops;
+ storage->rx_crc_errors = dev->stats.rx_crc_error;
+ storage->rx_over_errors = dev->stats.rx_over_errors;
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
@@ -2639,20 +2666,19 @@ static void airoha_ethtool_get_mac_stats
struct ethtool_eth_mac_stats *stats)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(dev);
do {
- start = u64_stats_fetch_begin(&port->stats.syncp);
- stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
- stats->OctetsTransmittedOK = port->stats.tx_ok_bytes;
- stats->MulticastFramesXmittedOK = port->stats.tx_multicast;
- stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast;
- stats->FramesReceivedOK = port->stats.rx_ok_pkts;
- stats->OctetsReceivedOK = port->stats.rx_ok_bytes;
- stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast;
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
+ stats->FramesTransmittedOK = dev->stats.tx_ok_pkts;
+ stats->OctetsTransmittedOK = dev->stats.tx_ok_bytes;
+ stats->MulticastFramesXmittedOK = dev->stats.tx_multicast;
+ stats->BroadcastFramesXmittedOK = dev->stats.tx_broadcast;
+ stats->FramesReceivedOK = dev->stats.rx_ok_pkts;
+ stats->OctetsReceivedOK = dev->stats.rx_ok_bytes;
+ stats->BroadcastFramesReceivedOK = dev->stats.rx_broadcast;
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
@@ -2672,8 +2698,7 @@ airoha_ethtool_get_rmon_stats(struct net
const struct ethtool_rmon_hist_range **ranges)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_hw_stats *hw_stats = &port->stats;
+ struct airoha_hw_stats *hw_stats = &dev->stats;
unsigned int start;
BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
@@ -2686,7 +2711,7 @@ airoha_ethtool_get_rmon_stats(struct net
do {
int i;
- start = u64_stats_fetch_begin(&port->stats.syncp);
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
stats->fragments = hw_stats->rx_fragment;
stats->jabbers = hw_stats->rx_jabber;
for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1;
@@ -2694,7 +2719,7 @@ airoha_ethtool_get_rmon_stats(struct net
stats->hist[i] = hw_stats->rx_len[i];
stats->hist_tx[i] = hw_stats->tx_len[i];
}
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static int airoha_ethtool_set_priv_flags(struct net_device *netdev, u32 flags)
@@ -3702,6 +3727,7 @@ static int airoha_alloc_gdm_device(struc
netdev->dev.of_node = of_node_get(np);
dev = netdev_priv(netdev);
+ u64_stats_init(&dev->stats.syncp);
dev->dev = netdev;
dev->port = port;
dev->eth = eth;
@@ -3750,9 +3776,8 @@ static int airoha_alloc_gdm_port(struct
if (!port)
return -ENOMEM;
- u64_stats_init(&port->stats.syncp);
- spin_lock_init(&port->stats.lock);
port->id = id;
+ spin_lock_init(&port->stats_lock);
eth->ports[p] = port;
err = airoha_metadata_dst_alloc(port);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -226,8 +226,6 @@ struct airoha_tx_irq_queue {
};
struct airoha_hw_stats {
- /* protect concurrent hw_stats accesses */
- spinlock_t lock;
struct u64_stats_sync syncp;
/* get_stats64 */
@@ -571,6 +569,8 @@ struct airoha_gdm_dev {
u32 flags;
int nbq;
+
+ struct airoha_hw_stats stats;
};
struct airoha_gdm_port {
@@ -578,7 +578,8 @@ struct airoha_gdm_port {
int id;
int users;
- struct airoha_hw_stats stats;
+ /* protect concurrent hw_stats accesses */
+ spinlock_t stats_lock;
struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
};
@@ -0,0 +1,40 @@
From 1e596dac503da44a9fcd3e2654a4963db3e2ee6a Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Wed, 28 Jan 2026 02:38:24 +0100
Subject: [PATCH] net: airoha: fix wrong airoha_get_fe_port()
It seems the SDK where the airoha_get_fe_port() logic was taken was
actually wrong and the AN7583 SoC doesn't have such difference. Instead
it does follow the same port order of AN7581 SoC.
Drop the switch case and apply the same condition on both AN7581 and
AN7583 SoC.
Fixes: e4e5ce823bdd ("net: airoha: Add AN7583 SoC support")
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2353,17 +2353,9 @@ static u32 airoha_get_dsa_tag(struct sk_
int airoha_get_fe_port(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = dev->eth;
- switch (eth->soc->version) {
- case 0x7583:
- return port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
- : port->id;
- case 0x7581:
- default:
- return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4
- : port->id;
- }
+ return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4
+ : port->id;
}
static int airoha_dev_set_features(struct net_device *netdev,
+1
View File
@@ -10,6 +10,7 @@ FEATURES:=fpu dt gpio ramdisk squashfs usb
SUBTARGETS:=nand sata
KERNEL_PATCHVER:=6.12
KERNEL_TESTING_PATCHVER:=6.18
define Target/Description
Build images for AppliedMicro APM821xx based boards.
+269
View File
@@ -0,0 +1,269 @@
# CONFIG_440_CPU is not set
CONFIG_44x=y
CONFIG_464_CPU=y
CONFIG_4xx=y
# CONFIG_ADVANCED_OPTIONS is not set
CONFIG_APM821xx=y
# CONFIG_APOLLO3G is not set
# CONFIG_ARCHES is not set
CONFIG_ARCH_32BIT_OFF_T=y
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_KEEP_MEMBLOCK=y
CONFIG_ARCH_MAY_HAVE_PC_FDC=y
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
CONFIG_ARCH_MMAP_RND_BITS=11
CONFIG_ARCH_MMAP_RND_BITS_MAX=17
CONFIG_ARCH_MMAP_RND_BITS_MIN=11
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=17
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11
CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y
CONFIG_ARCH_PKEY_BITS=5
CONFIG_ARCH_SPLIT_ARG64=y
CONFIG_ARCH_STACKWALK=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_ARCH_WEAK_RELEASE_ACQUIRE=y
CONFIG_AUDIT_ARCH=y
# CONFIG_BAMBOO is not set
# CONFIG_BLK_DEV_INITRD is not set
CONFIG_BLUESTONE=y
CONFIG_BOOKE=y
CONFIG_BOOKE_WDT=y
# CONFIG_CANYONLANDS is not set
CONFIG_CLONE_BACKWARDS=y
CONFIG_CMDLINE="rootfstype=squashfs noinitrd"
CONFIG_CMDLINE_FROM_BOOTLOADER=y
CONFIG_COMMON_CLK=y
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
CONFIG_COMPAT_32BIT_TIME=y
CONFIG_CPU_BIG_ENDIAN=y
CONFIG_CPU_MITIGATIONS=y
CONFIG_CRC16=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_JITTERENTROPY=y
CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKS=64
CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKSIZE=32
CONFIG_CRYPTO_JITTERENTROPY_OSR=1
CONFIG_CRYPTO_LIB_GF128MUL=y
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1
CONFIG_CRYPTO_LIB_UTILS=y
CONFIG_CRYPTO_LZO=y
CONFIG_CRYPTO_RNG=y
CONFIG_CRYPTO_SHA3=y
CONFIG_DATA_SHIFT=12
CONFIG_DEBUG_INFO=y
CONFIG_DMADEVICES=y
CONFIG_DMA_DIRECT_REMAP=y
CONFIG_DMA_ENGINE=y
CONFIG_DMA_NEED_SYNC=y
CONFIG_DMA_OF=y
CONFIG_DTC=y
CONFIG_DW_DMAC=y
CONFIG_DW_DMAC_CORE=y
CONFIG_EARLY_PRINTK=y
# CONFIG_EBONY is not set
CONFIG_EDAC_ATOMIC_SCRUB=y
CONFIG_EDAC_SUPPORT=y
# CONFIG_EIGER is not set
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
CONFIG_EXTRA_TARGETS="uImage"
CONFIG_FIXED_PHY=y
CONFIG_FORCE_NR_CPUS=y
CONFIG_FORCE_PCI=y
# CONFIG_FSL_LBC is not set
CONFIG_FS_IOMAP=y
CONFIG_FUNCTION_ALIGNMENT=4
CONFIG_FUNCTION_ALIGNMENT_4B=y
CONFIG_FWNODE_MDIO=y
CONFIG_FW_LOADER_PAGED_BUF=y
CONFIG_FW_LOADER_SYSFS=y
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_EARLY_IOREMAP=y
CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IOREMAP=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_MSI_IRQ=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_PHY=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_STRNCPY_FROM_USER=y
CONFIG_GENERIC_STRNLEN_USER=y
CONFIG_GENERIC_TIME_VSYSCALL=y
# CONFIG_GEN_RTC is not set
# CONFIG_GLACIER is not set
CONFIG_GPIO_CDEV=y
CONFIG_GPIO_GENERIC=y
CONFIG_GPIO_GENERIC_PLATFORM=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_IBM_IIC=y
CONFIG_IBM_EMAC=y
CONFIG_IBM_EMAC_EMAC4=y
CONFIG_IBM_EMAC_POLL_WEIGHT=32
CONFIG_IBM_EMAC_RGMII=y
CONFIG_IBM_EMAC_RXB=128
CONFIG_IBM_EMAC_RX_COPY_THRESHOLD=256
CONFIG_IBM_EMAC_TAH=y
CONFIG_IBM_EMAC_TXB=128
# CONFIG_ICON is not set
CONFIG_ILLEGAL_POINTER_VALUE=0
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_WORK=y
CONFIG_ISA_DMA_API=y
# CONFIG_JFFS2_FS is not set
# CONFIG_KATMAI is not set
CONFIG_KERNEL_START=0xc0000000
CONFIG_LEDS_TRIGGER_MTD=y
CONFIG_LEDS_TRIGGER_PATTERN=y
CONFIG_LIBFDT=y
CONFIG_LOCK_DEBUGGING_SUPPORT=y
CONFIG_LOWMEM_SIZE=0x30000000
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
# CONFIG_MATH_EMULATION is not set
CONFIG_MDIO_BUS=y
CONFIG_MIGRATION=y
CONFIG_MMU_GATHER_MERGE_VMAS=y
CONFIG_MMU_GATHER_PAGE_SIZE=y
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
CONFIG_MODULES_SIZE=1
CONFIG_MODULES_USE_ELF_RELA=y
CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y
CONFIG_MTD_CFI_ADV_OPTIONS=y
# CONFIG_MTD_CFI_GEOMETRY is not set
# CONFIG_MTD_SPLIT_SQUASHFS_ROOT is not set
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_PER_CPU_KM=y
CONFIG_NEED_SG_DMA_LENGTH=y
CONFIG_NET_EGRESS=y
CONFIG_NET_INGRESS=y
CONFIG_NET_SELFTESTS=y
CONFIG_NET_XGRESS=y
CONFIG_NOT_COHERENT_CACHE=y
CONFIG_NO_HZ=y
CONFIG_NO_HZ_COMMON=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NR_CPUS=1
CONFIG_NR_IRQS=512
CONFIG_NVMEM=y
CONFIG_NVMEM_LAYOUTS=y
CONFIG_NVMEM_SYSFS=y
CONFIG_OBJTOOL=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
CONFIG_OLD_SIGACTION=y
CONFIG_OLD_SIGSUSPEND=y
CONFIG_PACKING=y
CONFIG_PAGE_BLOCK_MAX_ORDER=11
CONFIG_PAGE_OFFSET=0xc0000000
CONFIG_PAGE_POOL=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
CONFIG_PCI=y
CONFIG_PCIEAER=y
CONFIG_PCIEPORTBUS=y
CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
CONFIG_PCI_DOMAINS=y
CONFIG_PCI_MSI=y
CONFIG_PCI_MSI_ARCH_FALLBACKS=y
CONFIG_PGTABLE_LEVELS=2
CONFIG_PHYLIB=y
CONFIG_PHYLIB_LEDS=y
CONFIG_PHYSICAL_START=0x00000000
CONFIG_PHYS_64BIT=y
CONFIG_PHYS_ADDR_T_64BIT=y
# CONFIG_PMU_SYSFS is not set
CONFIG_PPC=y
CONFIG_PPC32=y
CONFIG_PPC44x_SIMPLE=y
CONFIG_PPC4xx_GPIO=y
CONFIG_PPC4xx_PCI_EXPRESS=y
# CONFIG_PPC64 is not set
# CONFIG_PPC_47x is not set
# CONFIG_PPC_85xx is not set
# CONFIG_PPC_8xx is not set
CONFIG_PPC_ADV_DEBUG_DACS=2
CONFIG_PPC_ADV_DEBUG_DAC_RANGE=y
CONFIG_PPC_ADV_DEBUG_DVCS=2
CONFIG_PPC_ADV_DEBUG_IACS=4
CONFIG_PPC_ADV_DEBUG_REGS=y
# CONFIG_PPC_BOOK3S_32 is not set
CONFIG_PPC_DCR=y
CONFIG_PPC_DCR_NATIVE=y
# CONFIG_PPC_EARLY_DEBUG is not set
CONFIG_PPC_FPU=y
CONFIG_PPC_FPU_REGS=y
CONFIG_PPC_INDIRECT_PCI=y
CONFIG_PPC_KUAP=y
# CONFIG_PPC_KUAP_DEBUG is not set
CONFIG_PPC_KUEP=y
CONFIG_PPC_MMU_NOHASH=y
# CONFIG_PPC_PCI_BUS_NUM_DOMAIN_DEPENDENT is not set
CONFIG_PPC_UDBG_16550=y
CONFIG_PPC_WERROR=y
CONFIG_PTE_64BIT=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
# CONFIG_RAINIER is not set
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RAS=y
CONFIG_RATIONAL=y
CONFIG_REGULATOR=y
CONFIG_RSEQ=y
# CONFIG_SAM440EP is not set
# CONFIG_SCOM_DEBUGFS is not set
# CONFIG_SEQUOIA is not set
CONFIG_SERIAL_8250_FSL=y
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SMT_NUM_THREADS_DYNAMIC=y
CONFIG_SOFTIRQ_ON_OWN_STACK=y
CONFIG_SPARSE_IRQ=y
# CONFIG_STATIC_CALL_SELFTEST is not set
CONFIG_SWPHY=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
# CONFIG_TAISHAN is not set
CONFIG_TARGET_CPU="464"
CONFIG_TARGET_CPU_BOOL=y
CONFIG_TASK_SIZE=0xc0000000
CONFIG_THREAD_INFO_IN_TASK=y
CONFIG_THREAD_SHIFT=13
CONFIG_TICK_CPU_ACCOUNTING=y
CONFIG_TINY_SRCU=y
# CONFIG_TOOLCHAIN_DEFAULT_CPU is not set
CONFIG_USB_SUPPORT=y
CONFIG_VDSO32=y
CONFIG_VDSO_GETRANDOM=y
# CONFIG_VIRTIO_MENU is not set
# CONFIG_VIRT_CPU_ACCOUNTING_NATIVE is not set
# CONFIG_WARP is not set
CONFIG_WATCHDOG_CORE=y
CONFIG_XZ_DEC_BCJ=y
CONFIG_XZ_DEC_POWERPC=y
# CONFIG_YOSEMITE is not set
CONFIG_ZLIB_DEFLATE=y
CONFIG_ZLIB_INFLATE=y
@@ -0,0 +1,30 @@
From 0f18b7fdec817f94ad9ebeb6076ad914e0334299 Mon Sep 17 00:00:00 2001
From: Rosen Penev <rosenp@gmail.com>
Date: Fri, 15 May 2026 15:54:59 -0700
Subject: [PATCH] powerpc/44x: Set GPIO chip firmware node
The PPC4xx GPIO driver stopped assigning an explicit firmware node
to the gpio_chip when it moved away from of_mm_gpiochip_add_data().
Restore that association from the platform device so OF GPIO lookup
can match phandles to the registered gpiochip.
Tested on: Cisco MX60W. No more probe deferral.
Assisted-by: Codex:GPT-5.5
Fixes: 1044dbaf2a77 ("powerpc/44x: Change GPIO driver to a proper platform driver")
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
arch/powerpc/platforms/44x/gpio.c | 2 ++
1 file changed, 2 insertions(+)
--- a/arch/powerpc/platforms/44x/gpio.c
+++ b/arch/powerpc/platforms/44x/gpio.c
@@ -169,6 +169,7 @@ static int ppc4xx_gpio_probe(struct plat
gc = &chip->gc;
+ gc->parent = dev;
gc->base = -1;
gc->ngpio = 32;
gc->direction_input = ppc4xx_gpio_dir_in;
@@ -0,0 +1,30 @@
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -118,6 +118,17 @@ config CANYONLANDS
help
This option enables support for the AMCC PPC460EX evaluation board.
+config APOLLO3G
+ bool "Apollo3G"
+ depends on 44x
+ default n
+ select PPC44x_SIMPLE
+ select APM821xx
+ select IBM_EMAC_RGMII
+ select 460EX
+ help
+ This option enables support for the AMCC Apollo 3G board.
+
config GLACIER
bool "Glacier"
depends on 44x
--- a/arch/powerpc/platforms/44x/ppc44x_simple.c
+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c
@@ -46,6 +46,7 @@ machine_device_initcall(ppc44x_simple, p
* board.c file for it rather than adding it to this list.
*/
static char *board[] __initdata = {
+ "amcc,apollo3g",
"amcc,arches",
"amcc,bamboo",
"apm,bluestone",
@@ -0,0 +1,51 @@
--- a/arch/powerpc/platforms/44x/pci.c
+++ b/arch/powerpc/platforms/44x/pci.c
@@ -1053,15 +1053,24 @@ static int __init apm821xx_pciex_init_po
u32 val;
/*
- * Do a software reset on PCIe ports.
- * This code is to fix the issue that pci drivers doesn't re-assign
- * bus number for PCIE devices after Uboot
- * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000
- * PT quad port, SAS LSI 1064E)
+ * Only reset the PHY when no link is currently established.
+ * This is for the Atheros PCIe board which has problems to establish
+ * the link (again) after this PHY reset. All other currently tested
+ * PCIe boards don't show this problem.
*/
-
- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0);
- mdelay(10);
+ val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
+ if (!(val & 0x00001000)) {
+ /*
+ * Do a software reset on PCIe ports.
+ * This code is to fix the issue that pci drivers doesn't re-assign
+ * bus number for PCIE devices after Uboot
+ * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000
+ * PT quad port, SAS LSI 1064E)
+ */
+
+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0);
+ mdelay(10);
+ }
if (port->endpoint)
val = PTYPE_LEGACY_ENDPOINT << 20;
@@ -1078,9 +1087,12 @@ static int __init apm821xx_pciex_init_po
mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130);
mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006);
- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000);
- mdelay(50);
- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000);
+ val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
+ if (!(val & 0x00001000)) {
+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000);
+ mdelay(50);
+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000);
+ }
mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) |
@@ -0,0 +1,14 @@
--- a/arch/powerpc/platforms/44x/pci.c
+++ b/arch/powerpc/platforms/44x/pci.c
@@ -1795,9 +1795,9 @@ static void __init ppc4xx_configure_pcie
* if it works
*/
out_le32(mbase + PECFG_PIM0LAL, 0x00000000);
- out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
+ out_le32(mbase + PECFG_PIM0LAH, 0x00000008);
out_le32(mbase + PECFG_PIM1LAL, 0x00000000);
- out_le32(mbase + PECFG_PIM1LAH, 0x00000000);
+ out_le32(mbase + PECFG_PIM1LAH, 0x0000000c);
out_le32(mbase + PECFG_PIM01SAH, 0xffff0000);
out_le32(mbase + PECFG_PIM01SAL, 0x00000000);
@@ -0,0 +1,49 @@
From f992336a598ce4508b75e25dff755715f38b9b8e Mon Sep 17 00:00:00 2001
From: Rosen Penev <rosenp@gmail.com>
Date: Sat, 16 May 2026 16:31:09 -0700
Subject: [PATCH] net: ibm: emac: Clear MAL descriptors without memset
Clear MAL descriptor rings with explicit field stores instead of
memset(). The descriptor rings are carved from MAL coherent DMA memory,
which may be mapped uncached on 32-bit powerpc. The optimized memset()
path can use dcbz there and trigger an alignment warning.
The skb tracking arrays remain ordinary CPU memory and still use memset().
Assisted-by: Codex:GPT-5.5
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -1161,6 +1161,17 @@ static void emac_clean_rx_ring(struct em
}
}
+static void emac_clear_mal_desc(struct mal_descriptor *desc, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ desc[i].ctrl = 0;
+ desc[i].data_len = 0;
+ desc[i].data_ptr = 0;
+ }
+}
+
static int
__emac_prepare_rx_skb(struct sk_buff *skb, struct emac_instance *dev, int slot)
{
@@ -3086,8 +3097,8 @@ static int emac_probe(struct platform_de
DBG(dev, "rx_desc %p" NL, dev->rx_desc);
/* Clean rings */
- memset(dev->tx_desc, 0, NUM_TX_BUFF * sizeof(struct mal_descriptor));
- memset(dev->rx_desc, 0, NUM_RX_BUFF * sizeof(struct mal_descriptor));
+ emac_clear_mal_desc(dev->tx_desc, NUM_TX_BUFF);
+ emac_clear_mal_desc(dev->rx_desc, NUM_RX_BUFF);
memset(dev->tx_skb, 0, NUM_TX_BUFF * sizeof(struct sk_buff *));
memset(dev->rx_skb, 0, NUM_RX_BUFF * sizeof(struct sk_buff *));
@@ -0,0 +1,83 @@
From 286eda6cb8c69e38ef83b2e73b14bb92b482ad9d Mon Sep 17 00:00:00 2001
From: Rosen Penev <rosenp@gmail.com>
Date: Thu, 21 May 2026 13:52:11 -0700
Subject: [PATCH] net: ibm_emac: Use napi_gro_receive() for Rx packets
emac_poll_rx() already runs in NAPI context and TAH-equipped EMACs set
CHECKSUM_UNNECESSARY on verified frames, which lets GRO coalesce TCP
segments without a software checksum on the merge path. Replace the
per-poll rx_list batched with netif_receive_skb_list() with direct
napi_gro_receive() calls so the stack can merge segments into super-skbs
and skip a full traversal per packet -- a meaningful win on the slow
4xx-class CPUs this driver targets.
Small routing speed improvement tested on a Cisco Meraki MX60W:
Tested with iperf3
Before:
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 494 MBytes 414 Mbits/sec 839 sender
[ 5] 0.00-10.04 sec 492 MBytes 411 Mbits/sec receiver
After:
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 510 MBytes 428 Mbits/sec 580 sender
[ 5] 0.00-10.04 sec 508 MBytes 424 Mbits/sec receiver
Traffic to and from the router seems to be slow no matter what:
Tested with iperf3 --bidir
Before:
[ ID][Role] Interval Transfer Bitrate Retr
[ 8][TX-C] 0.00-10.00 sec 297 MBytes 249 Mbits/sec 35 sender
[ 8][TX-C] 0.00-10.00 sec 293 MBytes 245 Mbits/sec receiver
[ 10][RX-C] 0.00-10.00 sec 184 MBytes 154 Mbits/sec 0 sender
[ 10][RX-C] 0.00-10.00 sec 184 MBytes 154 Mbits/sec receiver
After:
[ ID][Role] Interval Transfer Bitrate Retr
[ 8][TX-C] 0.00-10.00 sec 295 MBytes 248 Mbits/sec 31 sender
[ 8][TX-C] 0.00-10.00 sec 294 MBytes 246 Mbits/sec receiver
[ 10][RX-C] 0.00-10.00 sec 181 MBytes 152 Mbits/sec 0 sender
[ 10][RX-C] 0.00-10.00 sec 181 MBytes 152 Mbits/sec receiver
Assisted-by: Claude:Opus-4.7
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -1738,7 +1738,6 @@ static inline int emac_rx_sg_append(stru
/* NAPI poll context */
static int emac_poll_rx(void *param, int budget)
{
- LIST_HEAD(rx_list);
struct emac_instance *dev = param;
int slot = dev->rx_slot, received = 0;
@@ -1795,7 +1794,7 @@ static int emac_poll_rx(void *param, int
skb->protocol = eth_type_trans(skb, dev->ndev);
emac_rx_csum(dev, skb, ctrl);
- list_add_tail(&skb->list, &rx_list);
+ napi_gro_receive(&dev->mal->napi, skb);
next:
++dev->stats.rx_packets;
skip:
@@ -1839,8 +1838,6 @@ static int emac_poll_rx(void *param, int
goto next;
}
- netif_receive_skb_list(&rx_list);
-
if (received) {
DBG2(dev, "rx %d BDs" NL, received);
dev->rx_slot = slot;
@@ -0,0 +1,29 @@
From c9395ad54e2cabb87d408becc37566f3d8248933 Mon Sep 17 00:00:00 2001
From: Christian Lamparter <chunkeey@gmail.com>
Date: Sun, 1 Dec 2019 02:08:23 +0100
Subject: [PATCH] powerpc: bootwrapper: force gzip as mkimage's compression
method
Due to CONFIG_KERNEL_XZ symbol, the bootwrapper code tries to
instruct the mkimage to use the xz compression, which isn't
supported. This patch forces the gzip compression, which is
supported and doesn't matter because the generated uImage for
the apm821xx target gets ignored as the OpenWrt toolchain will
do separate U-Boot kernel images for each device individually.
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
---
arch/powerpc/boot/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -270,7 +270,7 @@ compressor-$(CONFIG_KERNEL_LZO) := lzo
# args (to if_changed): 1 = (this rule), 2 = platform, 3 = dts 4=dtb 5=initrd
quiet_cmd_wrap = WRAP $@
- cmd_wrap =$(CONFIG_SHELL) $(wrapper) -Z $(compressor-y) -c -o $@ -p $2 \
+ cmd_wrap =$(CONFIG_SHELL) $(wrapper) -Z gzip -c -o $@ -p $2 \
$(CROSSWRAP) $(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) \
vmlinux
@@ -0,0 +1,72 @@
From 78bddfc01439de243624b1e60a7f599ec11b55b7 Mon Sep 17 00:00:00 2001
From: Rosen Penev <rosenp@gmail.com>
Date: Sat, 16 May 2026 16:08:49 -0700
Subject: [PATCH] dma-direct: Clear pages before coherent remap
Clear pages through their page mapping before creating a coherent
remap for dma-direct allocations. Some architectures implement the
coherent remap as uncached memory, where the generic memset() path may
use cache-only zeroing instructions that are not valid for the returned
CPU mapping.
Keep the existing memset() for non-remapped allocations, but avoid
normal memset() on the remapped coherent allocation path.
Assisted-by: Codex:GPT-5.5
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
kernel/dma/direct.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -8,6 +8,7 @@
#include <linux/export.h>
#include <linux/mm.h>
#include <linux/dma-map-ops.h>
+#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/pfn.h>
#include <linux/vmalloc.h>
@@ -104,6 +105,15 @@ static void __dma_direct_free_pages(stru
dma_free_contiguous(dev, page, size);
}
+static void dma_direct_zero_pages(struct page *page, size_t size)
+{
+ unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ unsigned long i;
+
+ for (i = 0; i < count; i++)
+ clear_highpage(page + i);
+}
+
static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size)
{
struct page *page = swiotlb_alloc(dev, size);
@@ -268,6 +278,13 @@ void *dma_direct_alloc(struct device *de
if (remap) {
pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);
+ /*
+ * Zero via the page mapping before creating a potentially
+ * uncached remap. Some architectures cannot safely run normal
+ * memset on uncached memory.
+ */
+ dma_direct_zero_pages(page, size);
+
if (force_dma_unencrypted(dev))
prot = pgprot_decrypted(prot);
@@ -283,10 +300,9 @@ void *dma_direct_alloc(struct device *de
ret = page_address(page);
if (dma_set_decrypted(dev, ret, size))
goto out_leak_pages;
+ memset(ret, 0, size);
}
- memset(ret, 0, size);
-
if (set_uncached) {
arch_dma_prep_coherent(page, size);
ret = arch_dma_set_uncached(ret, size);
@@ -15,8 +15,6 @@ CONFIG_BROADCOM_PHY=y
CONFIG_BUFFER_HEAD=y
CONFIG_CRYPTO_CRC32=y
CONFIG_CRYPTO_CRC32C=y
CONFIG_CRYPTO_MD5_PPC=y
CONFIG_CRYPTO_SHA1_PPC=y
# CONFIG_DM_CRYPT is not set
# CONFIG_DM_INIT is not set
# CONFIG_DM_MIRROR is not set
@@ -49,12 +47,6 @@ CONFIG_PM_WAKELOCKS=y
CONFIG_PM_WAKELOCKS_GC=y
CONFIG_PM_WAKELOCKS_LIMIT=100
CONFIG_PPC4xx_CPM=y
CONFIG_PPC_EARLY_DEBUG=y
# CONFIG_PPC_EARLY_DEBUG_16550 is not set
CONFIG_PPC_EARLY_DEBUG_44x=y
CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH=0x4
CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW=0xef600300
# CONFIG_PPC_EARLY_DEBUG_MEMCONS is not set
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_SATA_DWC=y
CONFIG_SATA_HOST=y

Some files were not shown because too many files have changed in this diff Show More