Merge Official Source
Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
@@ -2,8 +2,6 @@
|
||||
|
||||
. /lib/functions/ipv4.sh
|
||||
|
||||
PROG="$(basename "$0")"
|
||||
|
||||
# wrapper to convert an integer to an address, unless we're using
|
||||
# decimal output format.
|
||||
# hook for library function
|
||||
@@ -21,7 +19,7 @@ _ip2str() {
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage: $PROG [ -d | -x ] address/prefix [ start limit ]" >&2
|
||||
echo "Usage: ${0##*/} [ -d | -x ] address/prefix [ start limit ]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ endef
|
||||
$(eval $(call BuildPackage,airoha-en8811h-firmware))
|
||||
|
||||
|
||||
Package/airoha-en7581-npu-firmware = $(call Package/firmware-default,Airoha EN7581 NPU firmware,,LICENSE.airoha)
|
||||
Package/airoha-en7581-npu-firmware = $(call Package/firmware-default,Airoha EN7581+MT7992 NPU firmware,,LICENSE.airoha)
|
||||
define Package/airoha-en7581-npu-firmware/install
|
||||
$(INSTALL_DIR) $(1)/lib/firmware/airoha
|
||||
$(CP) \
|
||||
|
||||
@@ -741,7 +741,7 @@ define KernelPackage/crypto-md5
|
||||
KCONFIG:= \
|
||||
CONFIG_CRYPTO_MD5 \
|
||||
CONFIG_CRYPTO_MD5_OCTEON \
|
||||
CONFIG_CRYPTO_MD5_PPC
|
||||
CONFIG_CRYPTO_MD5_PPC@lt6.18
|
||||
FILES:=$(LINUX_DIR)/crypto/md5.ko \
|
||||
$(LINUX_DIR)/lib/crypto/libmd5.ko@ge6.18
|
||||
AUTOLOAD:=$(call AutoLoad,09,md5 libmd5@ge6.18)
|
||||
@@ -754,8 +754,8 @@ define KernelPackage/crypto-md5/octeon
|
||||
endef
|
||||
|
||||
define KernelPackage/crypto-md5/powerpc
|
||||
FILES+=$(LINUX_DIR)/arch/powerpc/crypto/md5-ppc.ko
|
||||
AUTOLOAD+=$(call AutoLoad,09,md5-ppc)
|
||||
FILES+=$(LINUX_DIR)/arch/powerpc/crypto/md5-ppc.ko@lt6.18
|
||||
AUTOLOAD+=$(call AutoLoad,09,LINUX_6_12:md5-ppc)
|
||||
endef
|
||||
|
||||
ifdef KernelPackage/crypto-md5/$(ARCH)
|
||||
|
||||
@@ -700,7 +700,6 @@ endef
|
||||
|
||||
define KernelPackage/dwmac-motorcomm/description
|
||||
Supports the Motorcomm DWMAC-based PCI Ethernet controllers.
|
||||
Currently only YT6801 is supported.
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,dwmac-motorcomm))
|
||||
@@ -2451,6 +2450,7 @@ endef
|
||||
|
||||
$(eval $(call KernelPackage,mhi-wwan-mbim))
|
||||
|
||||
|
||||
define KernelPackage/mtk-t7xx
|
||||
SUBMENU:=$(NETWORK_DEVICES_MENU)
|
||||
TITLE:=MediaTek T7xx 5G modem
|
||||
@@ -2469,6 +2469,7 @@ endef
|
||||
|
||||
$(eval $(call KernelPackage,mtk-t7xx))
|
||||
|
||||
|
||||
define KernelPackage/atlantic
|
||||
SUBMENU:=$(NETWORK_DEVICES_MENU)
|
||||
TITLE:=Aquantia AQtion 10Gbps Ethernet NIC
|
||||
|
||||
@@ -862,7 +862,8 @@ define KernelPackage/ptp-qoriq
|
||||
TITLE:=Freescale QorIQ PTP support
|
||||
DEPENDS:=@(TARGET_mpc85xx||TARGET_qoriq) +kmod-ptp
|
||||
KCONFIG:=CONFIG_PTP_1588_CLOCK_QORIQ
|
||||
FILES:=$(LINUX_DIR)/drivers/ptp/ptp-qoriq.ko
|
||||
FILES:=$(LINUX_DIR)/drivers/ptp/ptp-qoriq.ko@lt6.18 \
|
||||
$(LINUX_DIR)/drivers/ptp/ptp_qoriq.ko@ge6.18
|
||||
AUTOLOAD:=$(call AutoProbe,ptp-qoriq)
|
||||
endef
|
||||
|
||||
|
||||
@@ -352,6 +352,22 @@ endef
|
||||
$(eval $(call KernelPackage,drm-buddy))
|
||||
|
||||
|
||||
define KernelPackage/drm-client-lib
|
||||
SUBMENU:=$(VIDEO_MENU)
|
||||
TITLE:=DRM client library setup helper
|
||||
DEPENDS:=@DISPLAY_SUPPORT +@LINUX_6_18 +kmod-drm +kmod-drm-kms-helper
|
||||
KCONFIG:=CONFIG_DRM_CLIENT_LIB
|
||||
FILES:= $(LINUX_DIR)/drivers/gpu/drm/clients/drm_client_lib.ko
|
||||
AUTOLOAD:=$(call AutoProbe,drm_client_lib)
|
||||
endef
|
||||
|
||||
define KernelPackage/drm-client-lib/description
|
||||
DRM client library setup helper
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,drm-client-lib))
|
||||
|
||||
|
||||
define KernelPackage/drm-display-helper
|
||||
SUBMENU:=$(VIDEO_MENU)
|
||||
TITLE:=DRM helpers for display adapters drivers
|
||||
@@ -424,7 +440,8 @@ define KernelPackage/drm-mipi-dbi
|
||||
SUBMENU:=$(VIDEO_MENU)
|
||||
HIDDEN:=1
|
||||
TITLE:=MIPI DBI helpers
|
||||
DEPENDS:=@DISPLAY_SUPPORT +kmod-backlight +kmod-drm-kms-helper
|
||||
DEPENDS:=@DISPLAY_SUPPORT +kmod-backlight +kmod-drm-kms-helper \
|
||||
+LINUX_6_18:kmod-drm-client-lib
|
||||
KCONFIG:=CONFIG_DRM_MIPI_DBI
|
||||
FILES:=$(LINUX_DIR)/drivers/gpu/drm/drm_mipi_dbi.ko
|
||||
AUTOLOAD:=$(call AutoProbe,drm_mipi_dbi)
|
||||
|
||||
@@ -36,6 +36,7 @@ microcontroller on the Turris Omnia router. It can also show state of MCU
|
||||
settings and configure MCU options (GPIOs, LEDs, power).
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += -std=gnu99
|
||||
TARGET_LDFLAGS += -lcrypto
|
||||
|
||||
define Build/Compile
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=util-linux
|
||||
PKG_VERSION:=2.41.3
|
||||
PKG_VERSION:=2.42
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=@KERNEL/linux/utils/$(PKG_NAME)/v2.41
|
||||
PKG_HASH:=3330d873f0fceb5560b89a7dc14e4f3288bbd880e96903ed9b50ec2b5799e58b
|
||||
PKG_SOURCE_URL:=@KERNEL/linux/utils/$(PKG_NAME)/v2.42
|
||||
PKG_HASH:=3452b260bbaa775d6e749ac3bb22111785003fc1f444970025c8da26dfa758e9
|
||||
PKG_CPE_ID:=cpe:/a:kernel:util-linux
|
||||
|
||||
PKG_INSTALL:=1
|
||||
@@ -202,7 +202,7 @@ endef
|
||||
define Package/column
|
||||
$(call Package/util-linux/Default)
|
||||
TITLE:=column formats input into multiple columns
|
||||
DEPENDS:= +libsmartcols
|
||||
DEPENDS:= +libsmartcols +libncursesw
|
||||
LICENSE:=LGPL-2.1-or-later
|
||||
LICENSE_FILES:=Documentation/licenses/COPYING.LGPL-2.1-or-later
|
||||
endef
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
From af6168533cb011489bd0b4b1a2fbbaaeec6859b2 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <thomas@t-8ch.de>
|
||||
Date: Fri, 10 Apr 2026 18:04:58 +0200
|
||||
Subject: [PATCH] meson: test for statx::stx_mnt_id in sys/stat.h
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Both the check for the general availability of 'struct statx' and the
|
||||
code actual code use sys/stat.h and not linux/stat.h.
|
||||
|
||||
The test for stx_mnt_id also needs to test in sys/stat.h as otherwise
|
||||
the detection result is wrong.
|
||||
|
||||
Signed-off-by: Thomas Weißschuh <thomas@t-8ch.de>
|
||||
---
|
||||
meson.build | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -111,7 +111,7 @@ conf.set('HAVE_MOUNTFD_API', have_mountf
|
||||
have_struct_statx = cc.has_type('struct statx', args : '-D_GNU_SOURCE', prefix : '#include <sys/stat.h>')
|
||||
conf.set('HAVE_STRUCT_STATX', have_struct_statx ? 1 : false)
|
||||
have = cc.has_member('struct statx', 'stx_mnt_id',
|
||||
- prefix : '#include <linux/stat.h>')
|
||||
+ prefix : '#include <sys/stat.h>')
|
||||
conf.set('HAVE_STRUCT_STATX_STX_MNT_ID', have ? 1 : false)
|
||||
|
||||
|
||||
@@ -352,7 +352,6 @@ ifneq ($(CONFIG_CCACHE),)
|
||||
export CCACHE_NOCOMPRESS:=true
|
||||
export CCACHE_BASEDIR:=$(TOPDIR)
|
||||
export CCACHE_DIR:=$(if $(call qstrip,$(CONFIG_CCACHE_DIR)),$(call qstrip,$(CONFIG_CCACHE_DIR)),$(TOPDIR)/.ccache)
|
||||
export CCACHE_COMPILERCHECK:=%compiler% -v -c
|
||||
endif
|
||||
|
||||
TARGET_CONFIGURE_OPTS = \
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from os import getenv, environ
|
||||
from pathlib import Path
|
||||
from subprocess import run, PIPE
|
||||
from subprocess import run, PIPE, DEVNULL
|
||||
from sys import argv
|
||||
import json
|
||||
import re
|
||||
@@ -92,6 +92,15 @@ if output:
|
||||
"vermagic": linux_vermagic,
|
||||
}
|
||||
|
||||
git_commit = run(
|
||||
["git", "rev-parse", "HEAD"],
|
||||
stdout=PIPE,
|
||||
stderr=DEVNULL,
|
||||
universal_newlines=True,
|
||||
)
|
||||
if git_commit.returncode == 0:
|
||||
output["git_commit"] = git_commit.stdout.strip()
|
||||
|
||||
for artifact in "imagebuilder", "sdk", "toolchain":
|
||||
filename = add_artifact(artifact)
|
||||
add_artifact("llvm-bpf", prefix="")
|
||||
|
||||
@@ -75,7 +75,7 @@ define Device/airoha_an7581-evb-emmc-kite
|
||||
DEVICE_VENDOR := Airoha
|
||||
DEVICE_MODEL := AN7581 Evaluation Board (eMMC + Kite)
|
||||
DEVICE_DTS := an7581-evb-emmc-kite
|
||||
DEVICE_PACKAGES := kmod-i2c-an7581 airoha-en7581-mt7992-npu-firmware \
|
||||
DEVICE_PACKAGES := kmod-i2c-an7581 airoha-en7581-npu-firmware \
|
||||
kmod-mt7992-firmware wpad-basic-mbedtls
|
||||
ARTIFACT/preloader.bin := an7581-preloader rfb
|
||||
ARTIFACT/bl31-uboot.fip := an7581-bl31-uboot rfb
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include "qca955x.dtsi"
|
||||
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
/ {
|
||||
compatible = "mikrotik,routerboard-960pgs", "qca,qca9557";
|
||||
model = "MikroTik RouterBOARD 960PGS (hEX PoE/PowerBox Pro)";
|
||||
|
||||
aliases {
|
||||
led-boot = &led_user;
|
||||
led-failsafe = &led_user;
|
||||
led-running = &led_user;
|
||||
led-upgrade = &led_user;
|
||||
};
|
||||
|
||||
gpio_export {
|
||||
compatible = "gpio-export";
|
||||
|
||||
buzzer {
|
||||
/* Beeper requires PWM for frequency selection */
|
||||
gpio-export,name = "buzzer";
|
||||
gpio-export,output = <0>;
|
||||
gpios = <&gpio 4 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c: i2c {
|
||||
compatible = "i2c-gpio";
|
||||
|
||||
i2c-gpio,delay-us = <5>;
|
||||
i2c-gpio,timeout-ms = <1>;
|
||||
sda-gpios = <&gpio 18 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
|
||||
scl-gpios = <&gpio 19 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
|
||||
};
|
||||
|
||||
keys {
|
||||
compatible = "gpio-keys";
|
||||
|
||||
reset {
|
||||
debounce-interval = <60>;
|
||||
gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
|
||||
label = "reset";
|
||||
linux,code = <KEY_RESTART>;
|
||||
};
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
led_user: user {
|
||||
label = "green:user";
|
||||
gpios = <&gpio 12 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
sfp {
|
||||
label = "green:sfp";
|
||||
gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
sfp1: sfp {
|
||||
compatible = "sff,sfp";
|
||||
|
||||
i2c-bus = <&i2c>;
|
||||
maximum-power-milliwatt = <1000>;
|
||||
los-gpios = <&gpio 21 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
|
||||
tx-disable-gpios = <&gpio 16 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
reg_usb_vbus {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-boot-on;
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-name = "usb_vbus";
|
||||
gpio = <&gpio 13 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
};
|
||||
};
|
||||
|
||||
&mdio0 {
|
||||
status = "okay";
|
||||
|
||||
switch@0 {
|
||||
compatible = "qca,qca8337";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
reg = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "cpu";
|
||||
ethernet = <ð0>;
|
||||
phy-mode = "rgmii-id";
|
||||
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan1";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&phy_port1>;
|
||||
|
||||
leds {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan2";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&phy_port2>;
|
||||
|
||||
leds {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan3";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&phy_port3>;
|
||||
|
||||
leds {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
port@4 {
|
||||
reg = <4>;
|
||||
label = "lan4";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&phy_port4>;
|
||||
|
||||
leds {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
port@5 {
|
||||
reg = <5>;
|
||||
label = "lan5";
|
||||
phy-mode = "internal";
|
||||
phy-handle = <&phy_port5>;
|
||||
|
||||
leds {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_LAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
phy_port1: phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
phy_port2: phy@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
phy_port3: phy@2 {
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
phy_port4: phy@3 {
|
||||
reg = <3>;
|
||||
};
|
||||
|
||||
phy_port5: phy@4 {
|
||||
reg = <4>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ð0 {
|
||||
status = "okay";
|
||||
|
||||
pll-data = <0x8f000000 0xa0000101 0xa0001313>;
|
||||
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
|
||||
&mdio1 {
|
||||
status = "okay";
|
||||
|
||||
phy_sfp: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
phy-mode = "sgmii";
|
||||
sfp = <&sfp1>;
|
||||
};
|
||||
};
|
||||
|
||||
ð1 {
|
||||
status = "okay";
|
||||
|
||||
phy-handle = <&phy_sfp>;
|
||||
pll-data = <0x03000000 0x00000101 0x00001616>;
|
||||
qca955x-sgmii-fixup;
|
||||
|
||||
gmac-config {
|
||||
device = <&gmac>;
|
||||
};
|
||||
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
|
||||
&spi {
|
||||
status = "okay";
|
||||
|
||||
flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <40000000>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
compatible = "mikrotik,routerboot-partitions";
|
||||
label = "RouterBoot";
|
||||
reg = <0x0 0x20000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "bootloader1";
|
||||
reg = <0x0 0x0>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
hard_config {
|
||||
read-only;
|
||||
};
|
||||
|
||||
bios {
|
||||
size = <0x1000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@10000 {
|
||||
label = "bootloader2";
|
||||
reg = <0x10000 0x0>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
soft_config {
|
||||
};
|
||||
};
|
||||
|
||||
partition@20000 {
|
||||
compatible = "mikrotik,minor";
|
||||
label = "firmware";
|
||||
reg = <0x020000 0xfe0000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usb_phy0 {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -128,6 +128,16 @@ define Device/mikrotik_routerboard-952ui-5ac2nd
|
||||
endef
|
||||
TARGET_DEVICES += mikrotik_routerboard-952ui-5ac2nd
|
||||
|
||||
define Device/mikrotik_routerboard-960pgs
|
||||
$(Device/mikrotik_nor)
|
||||
SOC := qca9557
|
||||
DEVICE_MODEL := RouterBOARD 960PGS (hEX PoE/PowerBox Pro)
|
||||
IMAGE_SIZE := 16256k
|
||||
DEVICE_PACKAGES += kmod-usb2 kmod-i2c-gpio kmod-sfp kmod-dsa-qca8k -swconfig \
|
||||
-kmod-switch-ar8xxx -iwinfo -kmod-ath9k -wpad-basic-mbedtls
|
||||
endef
|
||||
TARGET_DEVICES += mikrotik_routerboard-960pgs
|
||||
|
||||
define Device/mikrotik_routerboard-962uigs-5hact2hnt
|
||||
$(Device/mikrotik_nor)
|
||||
SOC := qca9558
|
||||
|
||||
@@ -47,6 +47,9 @@ ath79_setup_interfaces()
|
||||
mikrotik,routerboard-map-2nd)
|
||||
ucidef_set_interface_lan "eth0 eth1"
|
||||
;;
|
||||
mikrotik,routerboard-960pgs)
|
||||
ucidef_set_interfaces_lan_wan "lan2 lan3 lan4 lan5" "eth1 lan1"
|
||||
;;
|
||||
mikrotik,routerboard-951g-2hnd|\
|
||||
mikrotik,routerboard-962uigs-5hact2hnt)
|
||||
ucidef_add_switch "switch0" \
|
||||
@@ -90,6 +93,15 @@ ath79_setup_macs()
|
||||
lan_mac="$mac_base"
|
||||
wan_mac=$(macaddr_add $mac_base 1)
|
||||
;;
|
||||
mikrotik,routerboard-960pgs)
|
||||
label_mac=$mac_base
|
||||
ucidef_set_network_device_mac "eth1" "$mac_base"
|
||||
ucidef_set_network_device_mac "lan1" "$(macaddr_add $mac_base 1)"
|
||||
ucidef_set_network_device_mac "lan2" "$(macaddr_add $mac_base 2)"
|
||||
ucidef_set_network_device_mac "lan3" "$(macaddr_add $mac_base 3)"
|
||||
ucidef_set_network_device_mac "lan4" "$(macaddr_add $mac_base 4)"
|
||||
ucidef_set_network_device_mac "lan5" "$(macaddr_add $mac_base 5)"
|
||||
;;
|
||||
*)
|
||||
label_mac="$mac_base"
|
||||
wan_mac="$mac_base"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# CONFIG_AR8216_PHY is not set
|
||||
CONFIG_CRC16=y
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_HASH_INFO=y
|
||||
@@ -38,6 +39,7 @@ CONFIG_NET_DSA=y
|
||||
CONFIG_PHYLINK=y
|
||||
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
CONFIG_SPI_RB4XX=y
|
||||
# CONFIG_SWCONFIG is not set
|
||||
CONFIG_UBIFS_FS=y
|
||||
CONFIG_WATCHDOG_CORE=y
|
||||
CONFIG_XXHASH=y
|
||||
|
||||
@@ -3,7 +3,7 @@ FEATURES += minor nand
|
||||
KERNELNAME := vmlinux vmlinuz
|
||||
IMAGES_DIR := ../../..
|
||||
|
||||
DEFAULT_PACKAGES += wpad-openssl yafut
|
||||
DEFAULT_PACKAGES += wpad-openssl yafut kmod-switch-ar8xxx
|
||||
|
||||
define Target/Description
|
||||
Build firmware images for MikroTik devices based on Qualcomm Atheros
|
||||
|
||||
@@ -15,7 +15,7 @@ Signed-off-by: John Crispin <john@phrozen.org>
|
||||
|
||||
--- a/drivers/phy/Kconfig
|
||||
+++ b/drivers/phy/Kconfig
|
||||
@@ -25,6 +25,22 @@ config GENERIC_PHY_MIPI_DPHY
|
||||
@@ -47,6 +47,22 @@ config GENERIC_PHY_MIPI_DPHY
|
||||
Provides a number of helpers a core functions for MIPI D-PHY
|
||||
drivers to us.
|
||||
|
||||
@@ -40,9 +40,9 @@ Signed-off-by: John Crispin <john@phrozen.org>
|
||||
depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
|
||||
--- a/drivers/phy/Makefile
|
||||
+++ b/drivers/phy/Makefile
|
||||
@@ -4,6 +4,8 @@
|
||||
#
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
obj-$(CONFIG_PHY_COMMON_PROPS) += phy-common-props.o
|
||||
obj-$(CONFIG_PHY_COMMON_PROPS_TEST) += phy-common-props-test.o
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
+obj-$(CONFIG_PHY_AR7100_USB) += phy-ar7100-usb.o
|
||||
+obj-$(CONFIG_PHY_AR7200_USB) += phy-ar7200-usb.o
|
||||
|
||||
@@ -11,8 +11,7 @@ FEATURES:=ext4 squashfs usbgadget
|
||||
KERNELNAME:=Image dtbs
|
||||
SUBTARGETS:=generic
|
||||
|
||||
KERNEL_PATCHVER:=6.12
|
||||
KERNEL_TESTING_PATCHVER:=6.18
|
||||
KERNEL_PATCHVER:=6.18
|
||||
|
||||
include $(INCLUDE_DIR)/target.mk
|
||||
|
||||
|
||||
@@ -1,489 +0,0 @@
|
||||
CONFIG_64BIT=y
|
||||
# CONFIG_ACPI is not set
|
||||
# CONFIG_ARCH_CANAAN is not set
|
||||
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
|
||||
CONFIG_ARCH_DMA_DEFAULT_COHERENT=y
|
||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||
CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y
|
||||
# CONFIG_ARCH_MICROCHIP is not set
|
||||
CONFIG_ARCH_MMAP_RND_BITS=18
|
||||
CONFIG_ARCH_MMAP_RND_BITS_MAX=24
|
||||
CONFIG_ARCH_MMAP_RND_BITS_MIN=18
|
||||
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=17
|
||||
CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y
|
||||
CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y
|
||||
CONFIG_ARCH_PROC_KCORE_TEXT=y
|
||||
CONFIG_ARCH_RV64I=y
|
||||
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
|
||||
# CONFIG_ARCH_SIFIVE is not set
|
||||
# CONFIG_ARCH_SOPHGO is not set
|
||||
CONFIG_ARCH_SPARSEMEM_ENABLE=y
|
||||
CONFIG_ARCH_STACKWALK=y
|
||||
CONFIG_ARCH_SUNXI=y
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
# CONFIG_ARCH_THEAD is not set
|
||||
CONFIG_ARCH_WANTS_NO_INSTR=y
|
||||
CONFIG_ARCH_WANTS_THP_SWAP=y
|
||||
CONFIG_ASN1=y
|
||||
# CONFIG_AX45MP_L2_CACHE is not set
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_PM=y
|
||||
CONFIG_BUFFER_HEAD=y
|
||||
CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y
|
||||
CONFIG_CLKSRC_MMIO=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_CMODEL_MEDANY=y
|
||||
# CONFIG_CMODEL_MEDLOW is not set
|
||||
CONFIG_COMMON_CLK=y
|
||||
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
|
||||
# CONFIG_COMPAT_32BIT_TIME is not set
|
||||
CONFIG_COMPAT_BRK=y
|
||||
CONFIG_CONTEXT_TRACKING=y
|
||||
CONFIG_CONTEXT_TRACKING_IDLE=y
|
||||
CONFIG_COREDUMP=y
|
||||
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||
CONFIG_CPU_ISOLATION=y
|
||||
CONFIG_CPU_MITIGATIONS=y
|
||||
CONFIG_CRC16=y
|
||||
CONFIG_CRC7=y
|
||||
CONFIG_CRC_ITU_T=y
|
||||
CONFIG_CRYPTO_CBC=y
|
||||
CONFIG_CRYPTO_CRC32C=y
|
||||
CONFIG_CRYPTO_DES=y
|
||||
CONFIG_CRYPTO_DEV_ALLWINNER=y
|
||||
# CONFIG_CRYPTO_DEV_SUN4I_SS is not set
|
||||
CONFIG_CRYPTO_DEV_SUN8I_CE=y
|
||||
# CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG is not set
|
||||
CONFIG_CRYPTO_DEV_SUN8I_CE_HASH=y
|
||||
CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG=y
|
||||
CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG=y
|
||||
# CONFIG_CRYPTO_DEV_SUN8I_SS is not set
|
||||
CONFIG_CRYPTO_ECB=y
|
||||
CONFIG_CRYPTO_ENGINE=y
|
||||
CONFIG_CRYPTO_HW=y
|
||||
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
|
||||
CONFIG_CRYPTO_LIB_DES=y
|
||||
CONFIG_CRYPTO_LIB_GF128MUL=y
|
||||
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1
|
||||
CONFIG_CRYPTO_LIB_SHA1=y
|
||||
CONFIG_CRYPTO_LIB_SHA256=y
|
||||
CONFIG_CRYPTO_LIB_UTILS=y
|
||||
# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set
|
||||
# CONFIG_CRYPTO_MANAGER_EXTRA_TESTS is not set
|
||||
CONFIG_CRYPTO_MD5=y
|
||||
CONFIG_CRYPTO_RNG=y
|
||||
CONFIG_CRYPTO_RNG2=y
|
||||
CONFIG_CRYPTO_SHA1=y
|
||||
CONFIG_CRYPTO_SHA256=y
|
||||
CONFIG_CRYPTO_SHA512=y
|
||||
CONFIG_CRYPTO_SIG2=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC=y
|
||||
CONFIG_DMA_DIRECT_REMAP=y
|
||||
CONFIG_DMA_ENGINE=y
|
||||
CONFIG_DMA_NEED_SYNC=y
|
||||
CONFIG_DMA_OF=y
|
||||
CONFIG_DMA_SUN6I=y
|
||||
CONFIG_DMA_VIRTUAL_CHANNELS=y
|
||||
CONFIG_DMI=y
|
||||
CONFIG_DMIID=y
|
||||
# CONFIG_DMI_SYSFS is not set
|
||||
CONFIG_DTC=y
|
||||
CONFIG_DWMAC_GENERIC=y
|
||||
CONFIG_DWMAC_SUN8I=y
|
||||
CONFIG_DWMAC_SUNXI=y
|
||||
CONFIG_DYNAMIC_SIGFRAME=y
|
||||
CONFIG_EDAC_SUPPORT=y
|
||||
CONFIG_EFI=y
|
||||
CONFIG_EFIVAR_FS=m
|
||||
# CONFIG_EFI_BOOTLOADER_CONTROL is not set
|
||||
# CONFIG_EFI_CAPSULE_LOADER is not set
|
||||
# CONFIG_EFI_COCO_SECRET is not set
|
||||
# CONFIG_EFI_DISABLE_PCI_DMA is not set
|
||||
# CONFIG_EFI_DISABLE_RUNTIME is not set
|
||||
CONFIG_EFI_EARLYCON=y
|
||||
CONFIG_EFI_ESRT=y
|
||||
CONFIG_EFI_GENERIC_STUB=y
|
||||
CONFIG_EFI_PARAMS_FROM_FDT=y
|
||||
CONFIG_EFI_RUNTIME_WRAPPERS=y
|
||||
CONFIG_EFI_STUB=y
|
||||
# CONFIG_EFI_TEST is not set
|
||||
# CONFIG_EFI_ZBOOT is not set
|
||||
CONFIG_ELF_CORE=y
|
||||
# CONFIG_ERRATA_ANDES is not set
|
||||
# CONFIG_ERRATA_SIFIVE is not set
|
||||
CONFIG_ERRATA_THEAD=y
|
||||
CONFIG_ERRATA_THEAD_CMO=y
|
||||
CONFIG_ERRATA_THEAD_MAE=y
|
||||
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_EXTCON=y
|
||||
CONFIG_FAILOVER=y
|
||||
CONFIG_FHANDLE=y
|
||||
CONFIG_FIXED_PHY=y
|
||||
CONFIG_FIX_EARLYCON_MEM=y
|
||||
CONFIG_FONT_8x16=y
|
||||
CONFIG_FONT_AUTOSELECT=y
|
||||
CONFIG_FONT_SUPPORT=y
|
||||
CONFIG_FPU=y
|
||||
CONFIG_FRAME_POINTER=y
|
||||
CONFIG_FS_IOMAP=y
|
||||
CONFIG_FS_MBCACHE=y
|
||||
CONFIG_FUNCTION_ALIGNMENT=0
|
||||
CONFIG_FWNODE_MDIO=y
|
||||
CONFIG_FW_LOADER_PAGED_BUF=y
|
||||
CONFIG_FW_LOADER_SYSFS=y
|
||||
CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE=y
|
||||
CONFIG_GENERIC_ALLOCATOR=y
|
||||
CONFIG_GENERIC_ARCH_TOPOLOGY=y
|
||||
CONFIG_GENERIC_BUG=y
|
||||
CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
|
||||
CONFIG_GENERIC_CPU_DEVICES=y
|
||||
CONFIG_GENERIC_CPU_VULNERABILITIES=y
|
||||
CONFIG_GENERIC_CSUM=y
|
||||
CONFIG_GENERIC_EARLY_IOREMAP=y
|
||||
CONFIG_GENERIC_ENTRY=y
|
||||
CONFIG_GENERIC_GETTIMEOFDAY=y
|
||||
CONFIG_GENERIC_IDLE_POLL_SETUP=y
|
||||
CONFIG_GENERIC_IOREMAP=y
|
||||
CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
|
||||
CONFIG_GENERIC_IRQ_IPI_MUX=y
|
||||
CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y
|
||||
CONFIG_GENERIC_IRQ_MULTI_HANDLER=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
|
||||
CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y
|
||||
CONFIG_GENERIC_MSI_IRQ=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GENERIC_PHY=y
|
||||
CONFIG_GENERIC_PINCONF=y
|
||||
CONFIG_GENERIC_PINCTRL_GROUPS=y
|
||||
CONFIG_GENERIC_PINMUX_FUNCTIONS=y
|
||||
CONFIG_GENERIC_SCHED_CLOCK=y
|
||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
||||
CONFIG_GENERIC_STRNCPY_FROM_USER=y
|
||||
CONFIG_GENERIC_STRNLEN_USER=y
|
||||
CONFIG_GENERIC_TIME_VSYSCALL=y
|
||||
CONFIG_GPIOLIB_IRQCHIP=y
|
||||
CONFIG_GPIO_CDEV=y
|
||||
CONFIG_GPIO_PCF857X=y
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT=y
|
||||
CONFIG_HAS_IOPORT_MAP=y
|
||||
CONFIG_HW_RANDOM=y
|
||||
CONFIG_HZ_PERIODIC=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_BOARDINFO=y
|
||||
CONFIG_I2C_HELPER_AUTO=y
|
||||
CONFIG_I2C_MV64XXX=y
|
||||
CONFIG_I2C_OCORES=y
|
||||
CONFIG_IIO=y
|
||||
CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_INPUT=y
|
||||
CONFIG_INPUT_KEYBOARD=y
|
||||
# CONFIG_IOMMUFD is not set
|
||||
CONFIG_IOMMU_API=y
|
||||
# CONFIG_IOMMU_DEBUGFS is not set
|
||||
# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set
|
||||
CONFIG_IOMMU_DEFAULT_DMA_STRICT=y
|
||||
# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set
|
||||
CONFIG_IOMMU_SUPPORT=y
|
||||
CONFIG_IO_URING=y
|
||||
CONFIG_IRQCHIP=y
|
||||
CONFIG_IRQ_DOMAIN=y
|
||||
CONFIG_IRQ_DOMAIN_HIERARCHY=y
|
||||
CONFIG_IRQ_FORCED_THREADING=y
|
||||
CONFIG_IRQ_STACKS=y
|
||||
CONFIG_IRQ_WORK=y
|
||||
CONFIG_JBD2=y
|
||||
CONFIG_KALLSYMS=y
|
||||
# CONFIG_KERNEL_UNCOMPRESSED is not set
|
||||
# CONFIG_KEYBOARD_SUN4I_LRADC is not set
|
||||
CONFIG_LEDS_SUN50I_A100=y
|
||||
CONFIG_LEGACY_PTYS=y
|
||||
CONFIG_LEGACY_PTY_COUNT=256
|
||||
CONFIG_LIBFDT=y
|
||||
CONFIG_LOCALVERSION_AUTO=y
|
||||
CONFIG_LOCK_DEBUGGING_SUPPORT=y
|
||||
CONFIG_LOCK_SPIN_ON_OWNER=y
|
||||
CONFIG_MAILBOX=y
|
||||
# CONFIG_MAILBOX_TEST is not set
|
||||
CONFIG_MDIO_BUS=y
|
||||
CONFIG_MDIO_BUS_MUX=y
|
||||
CONFIG_MDIO_DEVICE=y
|
||||
CONFIG_MDIO_DEVRES=y
|
||||
# CONFIG_MDIO_SUN4I is not set
|
||||
CONFIG_MFD_AXP20X=y
|
||||
CONFIG_MFD_AXP20X_I2C=y
|
||||
CONFIG_MFD_CORE=y
|
||||
# CONFIG_MFD_SUN4I_GPADC is not set
|
||||
CONFIG_MFD_SUN6I_PRCM=y
|
||||
CONFIG_MFD_SYSCON=y
|
||||
CONFIG_MIGRATION=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_BLOCK=y
|
||||
CONFIG_MMC_SUNXI=y
|
||||
CONFIG_MMIOWB=y
|
||||
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
|
||||
CONFIG_MODULES_USE_ELF_RELA=y
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y
|
||||
# CONFIG_MUSB_PIO_ONLY is not set
|
||||
CONFIG_MUTEX_SPIN_ON_OWNER=y
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_NET_EGRESS=y
|
||||
CONFIG_NET_INGRESS=y
|
||||
CONFIG_NET_PTP_CLASSIFY=y
|
||||
CONFIG_NET_SELFTESTS=y
|
||||
CONFIG_NET_VENDOR_ALLWINNER=y
|
||||
CONFIG_NET_XGRESS=y
|
||||
CONFIG_NLS=y
|
||||
# CONFIG_NONPORTABLE is not set
|
||||
CONFIG_NOP_USB_XCEIV=y
|
||||
CONFIG_NR_CPUS=8
|
||||
CONFIG_NVMEM=y
|
||||
CONFIG_NVMEM_LAYOUTS=y
|
||||
CONFIG_NVMEM_SUNXI_SID=y
|
||||
CONFIG_NVMEM_SYSFS=y
|
||||
CONFIG_OF=y
|
||||
CONFIG_OF_ADDRESS=y
|
||||
CONFIG_OF_EARLY_FLATTREE=y
|
||||
CONFIG_OF_FLATTREE=y
|
||||
CONFIG_OF_GPIO=y
|
||||
CONFIG_OF_IOMMU=y
|
||||
CONFIG_OF_IRQ=y
|
||||
CONFIG_OF_KOBJ=y
|
||||
CONFIG_OF_MDIO=y
|
||||
CONFIG_OID_REGISTRY=y
|
||||
CONFIG_PADATA=y
|
||||
CONFIG_PAGE_OFFSET=0xff60000000000000
|
||||
CONFIG_PAGE_POOL=y
|
||||
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
|
||||
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
|
||||
CONFIG_PANIC_TIMEOUT=0
|
||||
CONFIG_PCS_XPCS=y
|
||||
CONFIG_PER_VMA_LOCK=y
|
||||
CONFIG_PGTABLE_LEVELS=5
|
||||
CONFIG_PHYLIB=y
|
||||
CONFIG_PHYLIB_LEDS=y
|
||||
CONFIG_PHYLINK=y
|
||||
CONFIG_PHYS_ADDR_T_64BIT=y
|
||||
CONFIG_PHY_SUN4I_USB=y
|
||||
CONFIG_PHY_SUN50I_USB3=y
|
||||
# CONFIG_PHY_SUN6I_MIPI_DPHY is not set
|
||||
# CONFIG_PHY_SUN9I_USB is not set
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_PINCTRL_SUN20I_D1=y
|
||||
# CONFIG_PINCTRL_SUN4I_A10 is not set
|
||||
# CONFIG_PINCTRL_SUN50I_A100 is not set
|
||||
# CONFIG_PINCTRL_SUN50I_A100_R is not set
|
||||
# CONFIG_PINCTRL_SUN50I_A64 is not set
|
||||
# CONFIG_PINCTRL_SUN50I_A64_R is not set
|
||||
# CONFIG_PINCTRL_SUN50I_H5 is not set
|
||||
# CONFIG_PINCTRL_SUN50I_H6 is not set
|
||||
# CONFIG_PINCTRL_SUN50I_H616 is not set
|
||||
# CONFIG_PINCTRL_SUN50I_H616_R is not set
|
||||
# CONFIG_PINCTRL_SUN50I_H6_R is not set
|
||||
# CONFIG_PINCTRL_SUN5I is not set
|
||||
# CONFIG_PINCTRL_SUN6I_A31 is not set
|
||||
# CONFIG_PINCTRL_SUN6I_A31_R is not set
|
||||
# CONFIG_PINCTRL_SUN8I_A23 is not set
|
||||
# CONFIG_PINCTRL_SUN8I_A23_R is not set
|
||||
# CONFIG_PINCTRL_SUN8I_A33 is not set
|
||||
# CONFIG_PINCTRL_SUN8I_A83T is not set
|
||||
# CONFIG_PINCTRL_SUN8I_A83T_R is not set
|
||||
# CONFIG_PINCTRL_SUN8I_H3 is not set
|
||||
# CONFIG_PINCTRL_SUN8I_H3_R is not set
|
||||
# CONFIG_PINCTRL_SUN8I_V3S is not set
|
||||
# CONFIG_PINCTRL_SUN9I_A80 is not set
|
||||
# CONFIG_PINCTRL_SUN9I_A80_R is not set
|
||||
CONFIG_PINCTRL_SUNXI=y
|
||||
CONFIG_PM=y
|
||||
CONFIG_PM_CLK=y
|
||||
CONFIG_PM_GENERIC_DOMAINS=y
|
||||
CONFIG_PM_GENERIC_DOMAINS_OF=y
|
||||
CONFIG_PORTABLE=y
|
||||
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_SYSCON=y
|
||||
CONFIG_POWER_RESET_SYSCON_POWEROFF=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_PPS=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_PTP_1588_CLOCK=y
|
||||
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
|
||||
CONFIG_PWM=y
|
||||
# CONFIG_PWM_SIFIVE is not set
|
||||
# CONFIG_PWM_SUN4I is not set
|
||||
CONFIG_QUEUED_RWLOCKS=y
|
||||
CONFIG_RANDSTRUCT_NONE=y
|
||||
CONFIG_RATIONAL=y
|
||||
CONFIG_RCU_TRACE=y
|
||||
CONFIG_REALTEK_PHY=y
|
||||
CONFIG_REGMAP=y
|
||||
CONFIG_REGMAP_I2C=y
|
||||
CONFIG_REGMAP_IRQ=y
|
||||
CONFIG_REGMAP_MMIO=y
|
||||
CONFIG_REGMAP_SPI=y
|
||||
CONFIG_REGULATOR=y
|
||||
# CONFIG_REGULATOR_AXP20X is not set
|
||||
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
CONFIG_REGULATOR_SUN20I=y
|
||||
# CONFIG_RESET_ATTACK_MITIGATION is not set
|
||||
CONFIG_RESET_CONTROLLER=y
|
||||
CONFIG_RESET_SIMPLE=y
|
||||
CONFIG_RESET_SUNXI=y
|
||||
CONFIG_RISCV=y
|
||||
CONFIG_RISCV_ALTERNATIVE=y
|
||||
CONFIG_RISCV_ALTERNATIVE_EARLY=y
|
||||
CONFIG_RISCV_APLIC=y
|
||||
CONFIG_RISCV_APLIC_MSI=y
|
||||
CONFIG_RISCV_BOOT_SPINWAIT=y
|
||||
CONFIG_RISCV_DMA_NONCOHERENT=y
|
||||
# CONFIG_RISCV_EMULATED_UNALIGNED_ACCESS is not set
|
||||
CONFIG_RISCV_IMSIC=y
|
||||
CONFIG_RISCV_INTC=y
|
||||
CONFIG_RISCV_ISA_C=y
|
||||
CONFIG_RISCV_ISA_FALLBACK=y
|
||||
CONFIG_RISCV_ISA_SVNAPOT=y
|
||||
CONFIG_RISCV_ISA_SVPBMT=y
|
||||
CONFIG_RISCV_ISA_V=y
|
||||
CONFIG_RISCV_ISA_VENDOR_EXT=y
|
||||
CONFIG_RISCV_ISA_VENDOR_EXT_ANDES=y
|
||||
CONFIG_RISCV_ISA_V_DEFAULT_ENABLE=y
|
||||
CONFIG_RISCV_ISA_V_UCOPY_THRESHOLD=768
|
||||
CONFIG_RISCV_ISA_ZAWRS=y
|
||||
CONFIG_RISCV_ISA_ZBA=y
|
||||
CONFIG_RISCV_ISA_ZBB=y
|
||||
CONFIG_RISCV_ISA_ZBC=y
|
||||
CONFIG_RISCV_ISA_ZICBOM=y
|
||||
CONFIG_RISCV_ISA_ZICBOZ=y
|
||||
CONFIG_RISCV_MISALIGNED=y
|
||||
CONFIG_RISCV_NONSTANDARD_CACHE_OPS=y
|
||||
CONFIG_RISCV_PROBE_UNALIGNED_ACCESS=y
|
||||
CONFIG_RISCV_SBI=y
|
||||
CONFIG_RISCV_SBI_V01=y
|
||||
CONFIG_RISCV_TIMER=y
|
||||
CONFIG_RISCV_USE_LINKER_RELAXATION=y
|
||||
# CONFIG_RPS is not set
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_GOLDFISH=y
|
||||
CONFIG_RTC_DRV_SUN6I=y
|
||||
CONFIG_RTC_I2C_AND_SPI=y
|
||||
# CONFIG_RUNTIME_KERNEL_TESTING_MENU is not set
|
||||
CONFIG_RWSEM_SPIN_ON_OWNER=y
|
||||
CONFIG_SCHED_DEBUG=y
|
||||
CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
|
||||
CONFIG_SERIAL_8250_DW=y
|
||||
CONFIG_SERIAL_8250_DWLIB=y
|
||||
CONFIG_SERIAL_8250_NR_UARTS=4
|
||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
|
||||
CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
|
||||
CONFIG_SERIAL_MCTRL_GPIO=y
|
||||
CONFIG_SERIAL_OF_PLATFORM=y
|
||||
CONFIG_SERIO=y
|
||||
CONFIG_SERIO_SERPORT=y
|
||||
CONFIG_SGL_ALLOC=y
|
||||
CONFIG_SIFIVE_PLIC=y
|
||||
CONFIG_SLUB_DEBUG=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_SND=y
|
||||
CONFIG_SND_COMPRESS_OFFLOAD=y
|
||||
CONFIG_SND_JACK=y
|
||||
CONFIG_SND_JACK_INPUT_DEV=y
|
||||
CONFIG_SND_PCM=y
|
||||
CONFIG_SND_SOC=y
|
||||
CONFIG_SND_SOC_I2C_AND_SPI=y
|
||||
# CONFIG_SND_SUN20I_D1_CODEC_ANALOG is not set
|
||||
# CONFIG_SND_SUN4I_I2S is not set
|
||||
# CONFIG_SND_SUN4I_SPDIF is not set
|
||||
# CONFIG_SND_SUN50I_DMIC is not set
|
||||
CONFIG_SOCK_RX_QUEUE_MAPPING=y
|
||||
# CONFIG_SOC_STARFIVE is not set
|
||||
CONFIG_SOFTIRQ_ON_OWN_STACK=y
|
||||
CONFIG_SOUND=y
|
||||
CONFIG_SOUND_OSS_CORE=y
|
||||
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
|
||||
CONFIG_SPARSE_IRQ=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_BITBANG=y
|
||||
CONFIG_SPI_MASTER=y
|
||||
CONFIG_SPI_MEM=y
|
||||
# CONFIG_SPI_SUN4I is not set
|
||||
CONFIG_SPI_SUN6I=y
|
||||
CONFIG_SPLIT_PMD_PTLOCKS=y
|
||||
CONFIG_SPLIT_PTE_PTLOCKS=y
|
||||
CONFIG_STACKDEPOT=y
|
||||
CONFIG_STACKTRACE=y
|
||||
CONFIG_STMMAC_ETH=y
|
||||
CONFIG_STMMAC_PLATFORM=y
|
||||
CONFIG_SUN20I_D1_CCU=y
|
||||
CONFIG_SUN20I_D1_R_CCU=y
|
||||
CONFIG_SUN20I_GPADC=y
|
||||
CONFIG_SUN20I_PPU=y
|
||||
# CONFIG_SUN4I_EMAC is not set
|
||||
CONFIG_SUN4I_TIMER=y
|
||||
CONFIG_SUN50I_IOMMU=y
|
||||
CONFIG_SUN6I_MSGBOX=y
|
||||
CONFIG_SUN6I_RTC_CCU=y
|
||||
CONFIG_SUN8I_DE2_CCU=y
|
||||
CONFIG_SUN8I_THERMAL=y
|
||||
CONFIG_SUNXI_CCU=y
|
||||
# CONFIG_SUNXI_RSB is not set
|
||||
CONFIG_SUNXI_SRAM=y
|
||||
CONFIG_SUNXI_WATCHDOG=y
|
||||
CONFIG_SWIOTLB=y
|
||||
CONFIG_SWPHY=y
|
||||
CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW=y
|
||||
CONFIG_SYSCTL_EXCEPTION_TRACE=y
|
||||
# CONFIG_SYSFB_SIMPLEFB is not set
|
||||
CONFIG_SYSFS_SYSCALL=y
|
||||
CONFIG_THERMAL=y
|
||||
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
|
||||
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
|
||||
CONFIG_THERMAL_GOV_STEP_WISE=y
|
||||
CONFIG_THERMAL_OF=y
|
||||
CONFIG_THREAD_INFO_IN_TASK=y
|
||||
CONFIG_THREAD_SIZE_ORDER=2
|
||||
CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
CONFIG_TIMER_OF=y
|
||||
CONFIG_TIMER_PROBE=y
|
||||
CONFIG_TOOLCHAIN_HAS_V=y
|
||||
CONFIG_TOOLCHAIN_HAS_VECTOR_CRYPTO=y
|
||||
CONFIG_TOOLCHAIN_HAS_ZBB=y
|
||||
CONFIG_TOOLCHAIN_HAS_ZBC=y
|
||||
CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_TREE_RCU=y
|
||||
CONFIG_TREE_SRCU=y
|
||||
CONFIG_TUNE_GENERIC=y
|
||||
CONFIG_UCS2_STRING=y
|
||||
CONFIG_UEVENT_HELPER_PATH=""
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_COMMON=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_EHCI_HCD_PLATFORM=y
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_USB_MUSB_DUAL_ROLE=y
|
||||
CONFIG_USB_MUSB_HDRC=y
|
||||
CONFIG_USB_MUSB_SUNXI=y
|
||||
CONFIG_USB_OHCI_HCD=y
|
||||
CONFIG_USB_OHCI_HCD_PLATFORM=y
|
||||
CONFIG_USB_PHY=y
|
||||
CONFIG_USB_SUPPORT=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
# CONFIG_USB_XHCI_PLATFORM is not set
|
||||
CONFIG_USER_STACKTRACE_SUPPORT=y
|
||||
# CONFIG_VHOST_MENU is not set
|
||||
# CONFIG_VIRTIO_MENU is not set
|
||||
CONFIG_VMAP_STACK=y
|
||||
CONFIG_VM_EVENT_COUNTERS=y
|
||||
CONFIG_WATCHDOG_CORE=y
|
||||
CONFIG_XPS=y
|
||||
CONFIG_ZONE_DMA32=y
|
||||
@@ -128,6 +128,7 @@ CONFIG_FONT_AUTOSELECT=y
|
||||
CONFIG_FONT_SUPPORT=y
|
||||
CONFIG_FPU=y
|
||||
CONFIG_FRAME_POINTER=y
|
||||
CONFIG_F2FS_FS=y
|
||||
CONFIG_FS_IOMAP=y
|
||||
CONFIG_FS_MBCACHE=y
|
||||
CONFIG_FUNCTION_ALIGNMENT=0
|
||||
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
From 3341f884d75929a009801d4299d219e64c64a33c Mon Sep 17 00:00:00 2001
|
||||
From: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
Date: Sat, 5 Aug 2023 21:05:01 +0300
|
||||
Subject: [PATCH 07/14] ASoC: dt-bindings: sun4i-a10-codec: Add binding for
|
||||
Allwinner D1 SoC
|
||||
|
||||
The Allwinner D1 SoC has a internal audio codec that similar to previous
|
||||
ones, but it contains a three ADC channels instead of two, and also has
|
||||
a separate clocks for ADC and DAC modules.
|
||||
|
||||
Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
Reviewed-by: Rob Herring <robh@kernel.org>
|
||||
---
|
||||
.../sound/allwinner,sun4i-a10-codec.yaml | 64 ++++++++++++++++---
|
||||
1 file changed, 56 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml
|
||||
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml
|
||||
@@ -22,6 +22,7 @@ properties:
|
||||
- allwinner,sun8i-a23-codec
|
||||
- allwinner,sun8i-h3-codec
|
||||
- allwinner,sun8i-v3s-codec
|
||||
+ - allwinner,sun20i-d1-codec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -29,15 +30,9 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
- clocks:
|
||||
- items:
|
||||
- - description: Bus Clock
|
||||
- - description: Module Clock
|
||||
+ clocks: true
|
||||
|
||||
- clock-names:
|
||||
- items:
|
||||
- - const: apb
|
||||
- - const: codec
|
||||
+ clock-names: true
|
||||
|
||||
dmas:
|
||||
items:
|
||||
@@ -106,11 +101,42 @@ allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
+ const: allwinner,sun20i-d1-codec
|
||||
+ then:
|
||||
+ properties:
|
||||
+ clocks:
|
||||
+ items:
|
||||
+ - description: Bus Clock
|
||||
+ - description: ADC Module Clock
|
||||
+ - description: DAC Module Clock
|
||||
+
|
||||
+ clock-names:
|
||||
+ items:
|
||||
+ - const: apb
|
||||
+ - const: adc
|
||||
+ - const: dac
|
||||
+
|
||||
+ else:
|
||||
+ properties:
|
||||
+ clocks:
|
||||
+ items:
|
||||
+ - description: Bus Clock
|
||||
+ - description: Module Clock
|
||||
+
|
||||
+ clock-names:
|
||||
+ items:
|
||||
+ - const: apb
|
||||
+ - const: codec
|
||||
+
|
||||
+ - if:
|
||||
+ properties:
|
||||
+ compatible:
|
||||
enum:
|
||||
- allwinner,sun6i-a31-codec
|
||||
- allwinner,sun8i-a23-codec
|
||||
- allwinner,sun8i-h3-codec
|
||||
- allwinner,sun8i-v3s-codec
|
||||
+ - allwinner,sun20i-d1-codec
|
||||
|
||||
then:
|
||||
if:
|
||||
@@ -225,6 +251,28 @@ allOf:
|
||||
- Headphone
|
||||
- Headset Mic
|
||||
- Line In
|
||||
+ - Line Out
|
||||
+ - Mic
|
||||
+ - Speaker
|
||||
+
|
||||
+ - if:
|
||||
+ properties:
|
||||
+ compatible:
|
||||
+ enum:
|
||||
+ - allwinner,sun20i-d1-codec
|
||||
+
|
||||
+ then:
|
||||
+ properties:
|
||||
+ allwinner,audio-routing:
|
||||
+ items:
|
||||
+ enum:
|
||||
+ - HP
|
||||
+ - LINEIN
|
||||
+ - MIC3
|
||||
+ - MBIAS
|
||||
+ - Headphone
|
||||
+ - Headset Mic
|
||||
+ - Line In
|
||||
- Line Out
|
||||
- Mic
|
||||
- Speaker
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
From 64efc9cc704d27c60dc9c96a02d842f22dbdfeae Mon Sep 17 00:00:00 2001
|
||||
From: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
Date: Sat, 5 Aug 2023 21:05:02 +0300
|
||||
Subject: [PATCH 08/14] ASoC: dt-bindings: Add schema for
|
||||
"allwinner,sun20i-d1-codec-analog"
|
||||
|
||||
Add a DT schema to describe the analog part of the Allwinner D1/T113s
|
||||
internal audio codec.
|
||||
|
||||
Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
---
|
||||
.../allwinner,sun20i-d1-codec-analog.yaml | 33 +++++++++++++++++++
|
||||
1 file changed, 33 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/sound/allwinner,sun20i-d1-codec-analog.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun20i-d1-codec-analog.yaml
|
||||
@@ -0,0 +1,33 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/sound/allwinner,sun20i-d1-codec-analog.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: Allwinner D1 Analog Codec
|
||||
+
|
||||
+maintainers:
|
||||
+ - Maksim Kiselev <bigunclemax@gmail.com>
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ const: allwinner,sun20i-d1-codec-analog
|
||||
+
|
||||
+ reg:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - reg
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ codec_analog: codec-analog@2030300 {
|
||||
+ compatible = "allwinner,sun20i-d1-codec-analog";
|
||||
+ reg = <0x02030300 0xd00>;
|
||||
+ };
|
||||
+
|
||||
+...
|
||||
+
|
||||
-615
@@ -1,615 +0,0 @@
|
||||
From 0963766bc665769aebf370d44ee3a97facfbca57 Mon Sep 17 00:00:00 2001
|
||||
From: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
Date: Sat, 5 Aug 2023 21:05:03 +0300
|
||||
Subject: [PATCH 09/14] ASoC: sunxi: sun4i-codec: add basic support for D1
|
||||
audio codec
|
||||
|
||||
Allwinner D1 has an audio codec similar to earlier ones, but it comes
|
||||
with 3 channel ADC instead of 2, and many registers are moved.
|
||||
|
||||
Add basic support for it.
|
||||
|
||||
Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
---
|
||||
sound/soc/sunxi/sun4i-codec.c | 364 ++++++++++++++++++++++++++++------
|
||||
1 file changed, 300 insertions(+), 64 deletions(-)
|
||||
|
||||
--- a/sound/soc/sunxi/sun4i-codec.c
|
||||
+++ b/sound/soc/sunxi/sun4i-codec.c
|
||||
@@ -229,16 +229,66 @@
|
||||
|
||||
/* TODO H3 DAP (Digital Audio Processing) bits */
|
||||
|
||||
+/*
|
||||
+ * sun20i D1 and similar codecs specific registers
|
||||
+ *
|
||||
+ * Almost all registers moved on D1, including ADC digital controls,
|
||||
+ * FIFO and RX data registers. Only DAC control are at the same offset.
|
||||
+ */
|
||||
+
|
||||
+#define SUN20I_D1_CODEC_DAC_VOL_CTRL (0x04)
|
||||
+#define SUN20I_D1_CODEC_DAC_VOL_SEL (16)
|
||||
+#define SUN20I_D1_CODEC_DAC_VOL_L (8)
|
||||
+#define SUN20I_D1_CODEC_DAC_VOL_R (0)
|
||||
+#define SUN20I_D1_CODEC_DAC_FIFOC (0x10)
|
||||
+#define SUN20I_D1_CODEC_ADC_FIFOC (0x30)
|
||||
+#define SUN20I_D1_CODEC_ADC_FIFOC_EN_AD (28)
|
||||
+#define SUN20I_D1_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (16)
|
||||
+#define SUN20I_D1_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (4)
|
||||
+#define SUN20I_D1_CODEC_ADC_FIFOC_ADC_DRQ_EN (3)
|
||||
+#define SUN20I_D1_CODEC_ADC_VOL_CTRL1 (0x34)
|
||||
+#define SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC3_VOL (16)
|
||||
+#define SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC2_VOL (8)
|
||||
+#define SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC1_VOL (0)
|
||||
+#define SUN20I_D1_CODEC_ADC_RXDATA (0x40)
|
||||
+#define SUN20I_D1_CODEC_ADC_DIG_CTRL (0x50)
|
||||
+#define SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC3_CH_EN (2)
|
||||
+#define SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC2_CH_EN (1)
|
||||
+#define SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC1_CH_EN (0)
|
||||
+#define SUN20I_D1_CODEC_VRA1SPEEDUP_DOWN_CTRL (0x54)
|
||||
+
|
||||
+/* TODO D1 DAP (Digital Audio Processing) bits */
|
||||
+
|
||||
+struct sun4i_codec;
|
||||
+
|
||||
+struct sun4i_codec_quirks {
|
||||
+ const struct regmap_config *regmap_config;
|
||||
+ const struct snd_soc_component_driver *codec;
|
||||
+ struct snd_soc_card * (*create_card)(struct device *dev);
|
||||
+ struct reg_field reg_dac_fifoc; /* used for regmap_field */
|
||||
+ struct reg_field reg_adc_fifoc; /* used for regmap_field */
|
||||
+ unsigned int adc_drq_en;
|
||||
+ unsigned int rx_sample_bits;
|
||||
+ unsigned int rx_trig_level;
|
||||
+ unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
|
||||
+ unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */
|
||||
+ bool has_reset;
|
||||
+ bool has_dual_clock;
|
||||
+};
|
||||
+
|
||||
struct sun4i_codec {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk_apb;
|
||||
- struct clk *clk_module;
|
||||
+ struct clk *clk_module; /* used for ADC if clocks are separate */
|
||||
+ struct clk *clk_module_dac;
|
||||
struct reset_control *rst;
|
||||
struct gpio_desc *gpio_pa;
|
||||
struct gpio_desc *gpio_hp;
|
||||
+ const struct sun4i_codec_quirks *quirks;
|
||||
|
||||
- /* ADC_FIFOC register is at different offset on different SoCs */
|
||||
+ /* DAC/ADC FIFOC registers are at different offset on different SoCs */
|
||||
+ struct regmap_field *reg_dac_fifoc;
|
||||
struct regmap_field *reg_adc_fifoc;
|
||||
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
@@ -248,33 +298,33 @@ struct sun4i_codec {
|
||||
static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
|
||||
{
|
||||
/* Flush TX FIFO */
|
||||
- regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
|
||||
+ regmap_field_set_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
|
||||
|
||||
/* Enable DAC DRQ */
|
||||
- regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
|
||||
+ regmap_field_set_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
|
||||
}
|
||||
|
||||
static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
|
||||
{
|
||||
/* Disable DAC DRQ */
|
||||
- regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
|
||||
+ regmap_field_clear_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
|
||||
}
|
||||
|
||||
static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
|
||||
{
|
||||
/* Enable ADC DRQ */
|
||||
regmap_field_set_bits(scodec->reg_adc_fifoc,
|
||||
- BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
|
||||
+ BIT(scodec->quirks->adc_drq_en));
|
||||
}
|
||||
|
||||
static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
|
||||
{
|
||||
/* Disable ADC DRQ */
|
||||
regmap_field_clear_bits(scodec->reg_adc_fifoc,
|
||||
- BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
|
||||
+ BIT(scodec->quirks->adc_drq_en));
|
||||
}
|
||||
|
||||
static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
@@ -323,8 +373,8 @@ static int sun4i_codec_prepare_capture(s
|
||||
|
||||
/* Set RX FIFO trigger level */
|
||||
regmap_field_update_bits(scodec->reg_adc_fifoc,
|
||||
- 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
|
||||
- 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
|
||||
+ 0xf << scodec->quirks->rx_trig_level,
|
||||
+ 0x7 << scodec->quirks->rx_trig_level);
|
||||
|
||||
/*
|
||||
* FIXME: Undocumented in the datasheet, but
|
||||
@@ -358,13 +408,13 @@ static int sun4i_codec_prepare_playback(
|
||||
u32 val;
|
||||
|
||||
/* Flush the TX FIFO */
|
||||
- regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
|
||||
+ regmap_field_set_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
|
||||
|
||||
/* Set TX FIFO Empty Trigger Level */
|
||||
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
|
||||
- 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
|
||||
+ regmap_field_update_bits(scodec->reg_dac_fifoc,
|
||||
+ 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
|
||||
+ 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
|
||||
|
||||
if (substream->runtime->rate > 32000)
|
||||
/* Use 64 bits FIR filter */
|
||||
@@ -373,13 +423,12 @@ static int sun4i_codec_prepare_playback(
|
||||
/* Use 32 bits FIR filter */
|
||||
val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
|
||||
|
||||
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
|
||||
- val);
|
||||
+ regmap_field_update_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION), val);
|
||||
|
||||
/* Send zeros when we have an underrun */
|
||||
- regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT));
|
||||
+ regmap_field_clear_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT));
|
||||
|
||||
return 0;
|
||||
};
|
||||
@@ -474,30 +523,32 @@ static int sun4i_codec_hw_params_capture
|
||||
7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
|
||||
hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
|
||||
|
||||
- /* Set the number of channels we want to use */
|
||||
- if (params_channels(params) == 1)
|
||||
- regmap_field_set_bits(scodec->reg_adc_fifoc,
|
||||
- BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
|
||||
- else
|
||||
- regmap_field_clear_bits(scodec->reg_adc_fifoc,
|
||||
- BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
|
||||
+ if (!scodec->quirks->has_dual_clock) {
|
||||
+ /* Set the number of channels we want to use */
|
||||
+ if (params_channels(params) == 1)
|
||||
+ regmap_field_set_bits(scodec->reg_adc_fifoc,
|
||||
+ BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
|
||||
+ else
|
||||
+ regmap_field_clear_bits(scodec->reg_adc_fifoc,
|
||||
+ BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
|
||||
+ }
|
||||
|
||||
/* Set the number of sample bits to either 16 or 24 bits */
|
||||
if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
|
||||
regmap_field_set_bits(scodec->reg_adc_fifoc,
|
||||
- BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));
|
||||
+ BIT(scodec->quirks->rx_sample_bits));
|
||||
|
||||
regmap_field_clear_bits(scodec->reg_adc_fifoc,
|
||||
- BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
|
||||
+ BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
|
||||
|
||||
scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
} else {
|
||||
regmap_field_clear_bits(scodec->reg_adc_fifoc,
|
||||
- BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS));
|
||||
+ BIT(scodec->quirks->rx_sample_bits));
|
||||
|
||||
/* Fill most significant bits with valid data MSB */
|
||||
regmap_field_set_bits(scodec->reg_adc_fifoc,
|
||||
- BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
|
||||
+ BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
|
||||
|
||||
scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
}
|
||||
@@ -512,9 +563,9 @@ static int sun4i_codec_hw_params_playbac
|
||||
u32 val;
|
||||
|
||||
/* Set DAC sample rate */
|
||||
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
|
||||
- hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
|
||||
+ regmap_field_update_bits(scodec->reg_dac_fifoc,
|
||||
+ 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
|
||||
+ hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
|
||||
|
||||
/* Set the number of channels we want to use */
|
||||
if (params_channels(params) == 1)
|
||||
@@ -522,27 +573,26 @@ static int sun4i_codec_hw_params_playbac
|
||||
else
|
||||
val = 0;
|
||||
|
||||
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
|
||||
- val);
|
||||
+ regmap_field_update_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN), val);
|
||||
|
||||
/* Set the number of sample bits to either 16 or 24 bits */
|
||||
if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
|
||||
- regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
|
||||
+ regmap_field_set_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
|
||||
|
||||
/* Set TX FIFO mode to padding the LSBs with 0 */
|
||||
- regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
|
||||
+ regmap_field_clear_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
|
||||
|
||||
scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
} else {
|
||||
- regmap_clear_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
|
||||
+ regmap_field_clear_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
|
||||
|
||||
/* Set TX FIFO mode to repeat the MSB */
|
||||
- regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
|
||||
+ regmap_field_set_bits(scodec->reg_dac_fifoc,
|
||||
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
|
||||
|
||||
scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
}
|
||||
@@ -563,7 +613,11 @@ static int sun4i_codec_hw_params(struct
|
||||
if (!clk_freq)
|
||||
return -EINVAL;
|
||||
|
||||
- ret = clk_set_rate(scodec->clk_module, clk_freq);
|
||||
+ if (scodec->clk_module_dac &&
|
||||
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
+ ret = clk_set_rate(scodec->clk_module_dac, clk_freq);
|
||||
+ else
|
||||
+ ret = clk_set_rate(scodec->clk_module, clk_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -589,10 +643,14 @@ static int sun4i_codec_startup(struct sn
|
||||
* Stop issuing DRQ when we have room for less than 16 samples
|
||||
* in our TX FIFO
|
||||
*/
|
||||
- regmap_set_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
|
||||
- 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
|
||||
+ regmap_field_set_bits(scodec->reg_dac_fifoc,
|
||||
+ 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
|
||||
|
||||
- return clk_prepare_enable(scodec->clk_module);
|
||||
+ if (scodec->clk_module_dac &&
|
||||
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
+ return clk_prepare_enable(scodec->clk_module_dac);
|
||||
+ else
|
||||
+ return clk_prepare_enable(scodec->clk_module);
|
||||
}
|
||||
|
||||
static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
|
||||
@@ -601,7 +659,11 @@ static void sun4i_codec_shutdown(struct
|
||||
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
|
||||
struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
|
||||
|
||||
- clk_disable_unprepare(scodec->clk_module);
|
||||
+ if (scodec->clk_module_dac &&
|
||||
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
+ clk_disable_unprepare(scodec->clk_module_dac);
|
||||
+ else
|
||||
+ clk_disable_unprepare(scodec->clk_module);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
|
||||
@@ -1218,6 +1280,55 @@ static const struct snd_soc_component_dr
|
||||
.endianness = 1,
|
||||
};
|
||||
|
||||
+/* sun20i D1 codec */
|
||||
+static const DECLARE_TLV_DB_SCALE(sun20i_d1_codec_dvol_scale, -12000, 75, 1);
|
||||
+
|
||||
+static const struct snd_kcontrol_new sun20i_d1_codec_codec_controls[] = {
|
||||
+ SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
|
||||
+ SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
|
||||
+ sun6i_codec_dvol_scale),
|
||||
+ SOC_DOUBLE_TLV("DAC Front Playback Volume", SUN20I_D1_CODEC_DAC_VOL_CTRL,
|
||||
+ SUN20I_D1_CODEC_DAC_VOL_L, SUN20I_D1_CODEC_DAC_VOL_R,
|
||||
+ 0xFF, 0, sun20i_d1_codec_dvol_scale),
|
||||
+
|
||||
+ SOC_SINGLE_TLV("ADC1 Capture Volume", SUN20I_D1_CODEC_ADC_VOL_CTRL1,
|
||||
+ SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC1_VOL, 0xff, 0,
|
||||
+ sun20i_d1_codec_dvol_scale),
|
||||
+ SOC_SINGLE_TLV("ADC2 Capture Volume", SUN20I_D1_CODEC_ADC_VOL_CTRL1,
|
||||
+ SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC2_VOL, 0xff, 0,
|
||||
+ sun20i_d1_codec_dvol_scale),
|
||||
+ SOC_SINGLE_TLV("ADC3 Capture Volume", SUN20I_D1_CODEC_ADC_VOL_CTRL1,
|
||||
+ SUN20I_D1_CODEC_ADC_VOL_CTRL1_ADC3_VOL, 0xff, 0,
|
||||
+ sun20i_d1_codec_dvol_scale),
|
||||
+};
|
||||
+
|
||||
+static const struct snd_soc_dapm_widget sun20i_d1_codec_codec_widgets[] = {
|
||||
+ /* Digital parts of the ADCs */
|
||||
+ SND_SOC_DAPM_SUPPLY("ADC Enable", SUN20I_D1_CODEC_ADC_FIFOC,
|
||||
+ SUN20I_D1_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0),
|
||||
+ SND_SOC_DAPM_SUPPLY("ADC1 CH Enable", SUN20I_D1_CODEC_ADC_DIG_CTRL,
|
||||
+ SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC1_CH_EN, 0, NULL, 0),
|
||||
+ SND_SOC_DAPM_SUPPLY("ADC2 CH Enable", SUN20I_D1_CODEC_ADC_DIG_CTRL,
|
||||
+ SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC2_CH_EN, 0, NULL, 0),
|
||||
+ SND_SOC_DAPM_SUPPLY("ADC3 CH Enable", SUN20I_D1_CODEC_ADC_DIG_CTRL,
|
||||
+ SUN20I_D1_CODEC_ADC_DIG_CTRL_ADC3_CH_EN, 0, NULL, 0),
|
||||
+ /* Digital parts of the DACs */
|
||||
+ SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
|
||||
+ SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0),
|
||||
+ SND_SOC_DAPM_SUPPLY("DAC VOL_SEL Enable", SUN20I_D1_CODEC_DAC_VOL_CTRL,
|
||||
+ SUN20I_D1_CODEC_DAC_VOL_SEL, 0, NULL, 0),
|
||||
+};
|
||||
+
|
||||
+static const struct snd_soc_component_driver sun20i_d1_codec_codec = {
|
||||
+ .controls = sun20i_d1_codec_codec_controls,
|
||||
+ .num_controls = ARRAY_SIZE(sun20i_d1_codec_codec_controls),
|
||||
+ .dapm_widgets = sun20i_d1_codec_codec_widgets,
|
||||
+ .num_dapm_widgets = ARRAY_SIZE(sun20i_d1_codec_codec_widgets),
|
||||
+ .idle_bias_on = 1,
|
||||
+ .use_pmdown_time = 1,
|
||||
+ .endianness = 1,
|
||||
+};
|
||||
+
|
||||
static const struct snd_soc_component_driver sun4i_codec_component = {
|
||||
.name = "sun4i-codec",
|
||||
.legacy_dai_naming = 1,
|
||||
@@ -1564,6 +1675,66 @@ static struct snd_soc_card *sun8i_v3s_co
|
||||
return card;
|
||||
};
|
||||
|
||||
+static const struct snd_soc_dapm_route sun20i_d1_codec_card_routes[] = {
|
||||
+ /* ADC Routes */
|
||||
+ { "ADC1", NULL, "ADC Enable" },
|
||||
+ { "ADC2", NULL, "ADC Enable" },
|
||||
+ { "ADC3", NULL, "ADC Enable" },
|
||||
+ { "ADC1", NULL, "ADC1 CH Enable" },
|
||||
+ { "ADC2", NULL, "ADC2 CH Enable" },
|
||||
+ { "ADC3", NULL, "ADC3 CH Enable" },
|
||||
+ { "Codec Capture", NULL, "ADC1" },
|
||||
+ { "Codec Capture", NULL, "ADC2" },
|
||||
+ { "Codec Capture", NULL, "ADC3" },
|
||||
+
|
||||
+ /* DAC Routes */
|
||||
+ { "Left DAC", NULL, "DAC Enable" },
|
||||
+ { "Right DAC", NULL, "DAC Enable" },
|
||||
+ { "Left DAC", NULL, "DAC VOL_SEL Enable" },
|
||||
+ { "Right DAC", NULL, "DAC VOL_SEL Enable" },
|
||||
+ { "Left DAC", NULL, "Codec Playback" },
|
||||
+ { "Right DAC", NULL, "Codec Playback" },
|
||||
+};
|
||||
+
|
||||
+static struct snd_soc_card *sun20i_d1_codec_create_card(struct device *dev)
|
||||
+{
|
||||
+ struct snd_soc_card *card;
|
||||
+ int ret;
|
||||
+
|
||||
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
|
||||
+ if (!card)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ aux_dev.dlc.of_node = of_parse_phandle(dev->of_node,
|
||||
+ "allwinner,codec-analog-controls",
|
||||
+ 0);
|
||||
+ if (!aux_dev.dlc.of_node) {
|
||||
+ dev_err(dev, "Can't find analog controls for codec.\n");
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+ }
|
||||
+
|
||||
+ card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
|
||||
+ if (!card->dai_link)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ card->dev = dev;
|
||||
+ card->owner = THIS_MODULE;
|
||||
+ card->name = "D1 Audio Codec";
|
||||
+ card->dapm_widgets = sun6i_codec_card_dapm_widgets;
|
||||
+ card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
|
||||
+ card->dapm_routes = sun20i_d1_codec_card_routes;
|
||||
+ card->num_dapm_routes = ARRAY_SIZE(sun20i_d1_codec_card_routes);
|
||||
+ card->aux_dev = &aux_dev;
|
||||
+ card->num_aux_devs = 1;
|
||||
+ card->fully_routed = true;
|
||||
+
|
||||
+ ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
|
||||
+ if (ret)
|
||||
+ dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
|
||||
+
|
||||
+ return card;
|
||||
+};
|
||||
+
|
||||
static const struct regmap_config sun4i_codec_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
@@ -1606,21 +1777,22 @@ static const struct regmap_config sun8i_
|
||||
.max_register = SUN8I_H3_CODEC_ADC_DBG,
|
||||
};
|
||||
|
||||
-struct sun4i_codec_quirks {
|
||||
- const struct regmap_config *regmap_config;
|
||||
- const struct snd_soc_component_driver *codec;
|
||||
- struct snd_soc_card * (*create_card)(struct device *dev);
|
||||
- struct reg_field reg_adc_fifoc; /* used for regmap_field */
|
||||
- unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
|
||||
- unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */
|
||||
- bool has_reset;
|
||||
+static const struct regmap_config sun20i_d1_codec_regmap_config = {
|
||||
+ .reg_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+ .val_bits = 32,
|
||||
+ .max_register = SUN20I_D1_CODEC_VRA1SPEEDUP_DOWN_CTRL,
|
||||
};
|
||||
|
||||
static const struct sun4i_codec_quirks sun4i_codec_quirks = {
|
||||
.regmap_config = &sun4i_codec_regmap_config,
|
||||
.codec = &sun4i_codec_codec,
|
||||
.create_card = sun4i_codec_create_card,
|
||||
+ .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
|
||||
.reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
|
||||
+ .adc_drq_en = SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
|
||||
+ .rx_sample_bits = SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
|
||||
+ .rx_trig_level = SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
|
||||
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
|
||||
.reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
|
||||
};
|
||||
@@ -1629,7 +1801,11 @@ static const struct sun4i_codec_quirks s
|
||||
.regmap_config = &sun6i_codec_regmap_config,
|
||||
.codec = &sun6i_codec_codec,
|
||||
.create_card = sun6i_codec_create_card,
|
||||
+ .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
|
||||
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
|
||||
+ .adc_drq_en = SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
|
||||
+ .rx_sample_bits = SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
|
||||
+ .rx_trig_level = SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
|
||||
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
|
||||
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
|
||||
.has_reset = true,
|
||||
@@ -1639,7 +1815,11 @@ static const struct sun4i_codec_quirks s
|
||||
.regmap_config = &sun7i_codec_regmap_config,
|
||||
.codec = &sun7i_codec_codec,
|
||||
.create_card = sun4i_codec_create_card,
|
||||
+ .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
|
||||
.reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31),
|
||||
+ .adc_drq_en = SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
|
||||
+ .rx_sample_bits = SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
|
||||
+ .rx_trig_level = SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
|
||||
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
|
||||
.reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
|
||||
};
|
||||
@@ -1648,7 +1828,11 @@ static const struct sun4i_codec_quirks s
|
||||
.regmap_config = &sun8i_a23_codec_regmap_config,
|
||||
.codec = &sun8i_a23_codec_codec,
|
||||
.create_card = sun8i_a23_codec_create_card,
|
||||
+ .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
|
||||
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
|
||||
+ .adc_drq_en = SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
|
||||
+ .rx_sample_bits = SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
|
||||
+ .rx_trig_level = SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
|
||||
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
|
||||
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
|
||||
.has_reset = true,
|
||||
@@ -1663,7 +1847,11 @@ static const struct sun4i_codec_quirks s
|
||||
*/
|
||||
.codec = &sun8i_a23_codec_codec,
|
||||
.create_card = sun8i_h3_codec_create_card,
|
||||
+ .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
|
||||
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
|
||||
+ .adc_drq_en = SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
|
||||
+ .rx_sample_bits = SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
|
||||
+ .rx_trig_level = SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
|
||||
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
|
||||
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
|
||||
.has_reset = true,
|
||||
@@ -1677,12 +1865,31 @@ static const struct sun4i_codec_quirks s
|
||||
*/
|
||||
.codec = &sun8i_a23_codec_codec,
|
||||
.create_card = sun8i_v3s_codec_create_card,
|
||||
+ .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
|
||||
.reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31),
|
||||
+ .adc_drq_en = SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN,
|
||||
+ .rx_sample_bits = SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
|
||||
+ .rx_trig_level = SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
|
||||
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
|
||||
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
|
||||
.has_reset = true,
|
||||
};
|
||||
|
||||
+static const struct sun4i_codec_quirks sun20i_d1_codec_quirks = {
|
||||
+ .regmap_config = &sun20i_d1_codec_regmap_config,
|
||||
+ .codec = &sun20i_d1_codec_codec,
|
||||
+ .create_card = sun20i_d1_codec_create_card,
|
||||
+ .reg_dac_fifoc = REG_FIELD(SUN20I_D1_CODEC_DAC_FIFOC, 0, 31),
|
||||
+ .reg_adc_fifoc = REG_FIELD(SUN20I_D1_CODEC_ADC_FIFOC, 0, 31),
|
||||
+ .adc_drq_en = SUN20I_D1_CODEC_ADC_FIFOC_ADC_DRQ_EN,
|
||||
+ .rx_sample_bits = SUN20I_D1_CODEC_ADC_FIFOC_RX_SAMPLE_BITS,
|
||||
+ .rx_trig_level = SUN20I_D1_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
|
||||
+ .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
|
||||
+ .reg_adc_rxdata = SUN20I_D1_CODEC_ADC_RXDATA,
|
||||
+ .has_reset = true,
|
||||
+ .has_dual_clock = true,
|
||||
+};
|
||||
+
|
||||
static const struct of_device_id sun4i_codec_of_match[] = {
|
||||
{
|
||||
.compatible = "allwinner,sun4i-a10-codec",
|
||||
@@ -1708,6 +1915,10 @@ static const struct of_device_id sun4i_c
|
||||
.compatible = "allwinner,sun8i-v3s-codec",
|
||||
.data = &sun8i_v3s_codec_quirks,
|
||||
},
|
||||
+ {
|
||||
+ .compatible = "allwinner,sun20i-d1-codec",
|
||||
+ .data = &sun20i_d1_codec_quirks,
|
||||
+ },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
|
||||
@@ -1736,6 +1947,7 @@ static int sun4i_codec_probe(struct plat
|
||||
dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
+ scodec->quirks = quirks;
|
||||
|
||||
scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
quirks->regmap_config);
|
||||
@@ -1751,10 +1963,24 @@ static int sun4i_codec_probe(struct plat
|
||||
return PTR_ERR(scodec->clk_apb);
|
||||
}
|
||||
|
||||
- scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
|
||||
- if (IS_ERR(scodec->clk_module)) {
|
||||
- dev_err(&pdev->dev, "Failed to get the module clock\n");
|
||||
- return PTR_ERR(scodec->clk_module);
|
||||
+ if (quirks->has_dual_clock) {
|
||||
+ scodec->clk_module = devm_clk_get(&pdev->dev, "adc");
|
||||
+ if (IS_ERR(scodec->clk_module)) {
|
||||
+ dev_err(&pdev->dev, "Failed to get the ADC module clock\n");
|
||||
+ return PTR_ERR(scodec->clk_module);
|
||||
+ }
|
||||
+
|
||||
+ scodec->clk_module_dac = devm_clk_get(&pdev->dev, "dac");
|
||||
+ if (IS_ERR(scodec->clk_module_dac)) {
|
||||
+ dev_err(&pdev->dev, "Failed to get the DAC module clock\n");
|
||||
+ return PTR_ERR(scodec->clk_module_dac);
|
||||
+ }
|
||||
+ } else {
|
||||
+ scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
|
||||
+ if (IS_ERR(scodec->clk_module)) {
|
||||
+ dev_err(&pdev->dev, "Failed to get the module clock\n");
|
||||
+ return PTR_ERR(scodec->clk_module);
|
||||
+ }
|
||||
}
|
||||
|
||||
if (quirks->has_reset) {
|
||||
@@ -1790,6 +2016,16 @@ static int sun4i_codec_probe(struct plat
|
||||
dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
+ }
|
||||
+
|
||||
+ scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev,
|
||||
+ scodec->regmap,
|
||||
+ quirks->reg_dac_fifoc);
|
||||
+ if (IS_ERR(scodec->reg_dac_fifoc)) {
|
||||
+ ret = PTR_ERR(scodec->reg_dac_fifoc);
|
||||
+ dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
|
||||
+ ret);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/* Enable the bus clock */
|
||||
-274
@@ -1,274 +0,0 @@
|
||||
From c8c3c516ca5c38e7858055ce0137efde17a07190 Mon Sep 17 00:00:00 2001
|
||||
From: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
Date: Sat, 5 Aug 2023 21:05:04 +0300
|
||||
Subject: [PATCH 10/14] ASoC: sunxi: Add new driver for Allwinner D1/T113s
|
||||
codec's analog path controls
|
||||
|
||||
The internal codec on D1/T113s is split into 2 parts like the previous
|
||||
ones. But now analog path controls registers are mapped directly
|
||||
on the bus, right after the registers of the digital part.
|
||||
|
||||
Add an ASoC component driver for it. This should be tied to the codec
|
||||
audio card as an auxiliary device.
|
||||
|
||||
Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
---
|
||||
sound/soc/sunxi/Kconfig | 11 ++
|
||||
sound/soc/sunxi/Makefile | 1 +
|
||||
sound/soc/sunxi/sun20i-d1-codec-analog.c | 220 +++++++++++++++++++++++
|
||||
3 files changed, 232 insertions(+)
|
||||
create mode 100644 sound/soc/sunxi/sun20i-d1-codec-analog.c
|
||||
|
||||
--- a/sound/soc/sunxi/Kconfig
|
||||
+++ b/sound/soc/sunxi/Kconfig
|
||||
@@ -38,6 +38,17 @@ config SND_SUN50I_CODEC_ANALOG
|
||||
Say Y or M if you want to add support for the analog controls for
|
||||
the codec embedded in Allwinner A64 SoC.
|
||||
|
||||
+config SND_SUN20I_D1_CODEC_ANALOG
|
||||
+ tristate "Allwinner D1 Codec Analog Controls Support"
|
||||
+ depends on ARCH_SUNXI || COMPILE_TEST
|
||||
+ select REGMAP_MMIO
|
||||
+ help
|
||||
+ This option enables the analog controls part of the internal audio
|
||||
+ codec for Allwinner D1/T113s SoCs family.
|
||||
+
|
||||
+ Say Y or M if you want to add support for the analog part of
|
||||
+ the D1/T113s audio codec.
|
||||
+
|
||||
config SND_SUN4I_I2S
|
||||
tristate "Allwinner A10 I2S Support"
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
--- a/sound/soc/sunxi/Makefile
|
||||
+++ b/sound/soc/sunxi/Makefile
|
||||
@@ -4,6 +4,7 @@ obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s
|
||||
obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
|
||||
obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
|
||||
obj-$(CONFIG_SND_SUN50I_CODEC_ANALOG) += sun50i-codec-analog.o
|
||||
+obj-$(CONFIG_SND_SUN20I_D1_CODEC_ANALOG) += sun20i-d1-codec-analog.o
|
||||
obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o
|
||||
obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o
|
||||
obj-$(CONFIG_SND_SUN50I_DMIC) += sun50i-dmic.o
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/sunxi/sun20i-d1-codec-analog.c
|
||||
@@ -0,0 +1,220 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0+
|
||||
+/*
|
||||
+ * This driver supports the analog controls for the internal codec
|
||||
+ * found in Allwinner's D1/T113s SoCs family.
|
||||
+ *
|
||||
+ * Based on sun50i-codec-analog.c
|
||||
+ */
|
||||
+
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+#include <sound/soc.h>
|
||||
+#include <sound/soc-dapm.h>
|
||||
+#include <sound/tlv.h>
|
||||
+
|
||||
+/* Codec analog control register offsets and bit fields */
|
||||
+#define SUN20I_D1_ADDA_ADC1 (0x00)
|
||||
+#define SUN20I_D1_ADDA_ADC2 (0x04)
|
||||
+#define SUN20I_D1_ADDA_ADC3 (0x08)
|
||||
+#define SUN20I_D1_ADDA_ADC_EN (31)
|
||||
+#define SUN20I_D1_ADDA_ADC_PGA_EN (30)
|
||||
+#define SUN20I_D1_ADDA_ADC_MIC_SIN_EN (28)
|
||||
+#define SUN20I_D1_ADDA_ADC_LINEINLEN (23)
|
||||
+#define SUN20I_D1_ADDA_ADC_PGA_GAIN (8)
|
||||
+
|
||||
+#define SUN20I_D1_ADDA_DAC (0x10)
|
||||
+#define SUN20I_D1_ADDA_DAC_DACL_EN (15)
|
||||
+#define SUN20I_D1_ADDA_DAC_DACR_EN (14)
|
||||
+
|
||||
+#define SUN20I_D1_ADDA_MICBIAS (0x18)
|
||||
+#define SUN20I_D1_ADDA_MICBIAS_MMICBIASEN (7)
|
||||
+
|
||||
+#define SUN20I_D1_ADDA_RAMP (0x1C)
|
||||
+#define SUN20I_D1_ADDA_RAMP_RD_EN (0)
|
||||
+
|
||||
+#define SUN20I_D1_ADDA_HP2 (0x40)
|
||||
+#define SUN20I_D1_ADDA_HP2_HEADPHONE_GAIN (28)
|
||||
+
|
||||
+#define SUN20I_D1_ADDA_ADC_CUR_REG (0x4C)
|
||||
+
|
||||
+static const DECLARE_TLV_DB_RANGE(sun20i_d1_codec_adc_gain_scale,
|
||||
+ 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
|
||||
+ 1, 3, TLV_DB_SCALE_ITEM(600, 0, 0),
|
||||
+ 4, 4, TLV_DB_SCALE_ITEM(900, 0, 0),
|
||||
+ 5, 31, TLV_DB_SCALE_ITEM(1000, 100, 0),
|
||||
+);
|
||||
+
|
||||
+static const DECLARE_TLV_DB_SCALE(sun20i_d1_codec_hp_vol_scale, -4200, 600, 0);
|
||||
+
|
||||
+/* volume controls */
|
||||
+static const struct snd_kcontrol_new sun20i_d1_codec_controls[] = {
|
||||
+ SOC_SINGLE_TLV("Headphone Playback Volume",
|
||||
+ SUN20I_D1_ADDA_HP2,
|
||||
+ SUN20I_D1_ADDA_HP2_HEADPHONE_GAIN, 0x7, 1,
|
||||
+ sun20i_d1_codec_hp_vol_scale),
|
||||
+ SOC_SINGLE_TLV("ADC1 Gain Capture Volume",
|
||||
+ SUN20I_D1_ADDA_ADC1,
|
||||
+ SUN20I_D1_ADDA_ADC_PGA_GAIN, 0x1f, 0,
|
||||
+ sun20i_d1_codec_adc_gain_scale),
|
||||
+ SOC_SINGLE_TLV("ADC2 Gain Capture Volume",
|
||||
+ SUN20I_D1_ADDA_ADC2,
|
||||
+ SUN20I_D1_ADDA_ADC_PGA_GAIN, 0x1f, 0,
|
||||
+ sun20i_d1_codec_adc_gain_scale),
|
||||
+ SOC_SINGLE_TLV("ADC3 Gain Capture Volume",
|
||||
+ SUN20I_D1_ADDA_ADC3,
|
||||
+ SUN20I_D1_ADDA_ADC_PGA_GAIN, 0x1f, 0,
|
||||
+ sun20i_d1_codec_adc_gain_scale),
|
||||
+};
|
||||
+
|
||||
+/* ADC mixer controls */
|
||||
+static const struct snd_kcontrol_new sun20i_d1_codec_mixer_controls[] = {
|
||||
+ SOC_DAPM_DOUBLE_R("Line In Switch",
|
||||
+ SUN20I_D1_ADDA_ADC1,
|
||||
+ SUN20I_D1_ADDA_ADC2,
|
||||
+ SUN20I_D1_ADDA_ADC_LINEINLEN, 1, 0),
|
||||
+};
|
||||
+
|
||||
+static const char * const sun20i_d1_codec_mic3_src_enum_text[] = {
|
||||
+ "Differential", "Single",
|
||||
+};
|
||||
+
|
||||
+static SOC_ENUM_SINGLE_DECL(sun20i_d1_codec_mic3_src_enum,
|
||||
+ SUN20I_D1_ADDA_ADC3,
|
||||
+ SUN20I_D1_ADDA_ADC_MIC_SIN_EN,
|
||||
+ sun20i_d1_codec_mic3_src_enum_text);
|
||||
+
|
||||
+static const struct snd_kcontrol_new sun20i_d1_codec_mic3_input_src[] = {
|
||||
+ SOC_DAPM_ENUM("MIC3 Source Capture Route",
|
||||
+ sun20i_d1_codec_mic3_src_enum),
|
||||
+};
|
||||
+
|
||||
+static const struct snd_soc_dapm_widget sun20i_d1_codec_widgets[] = {
|
||||
+ /* DAC */
|
||||
+ SND_SOC_DAPM_DAC("Left DAC", NULL, SUN20I_D1_ADDA_DAC,
|
||||
+ SUN20I_D1_ADDA_DAC_DACL_EN, 0),
|
||||
+ SND_SOC_DAPM_DAC("Right DAC", NULL, SUN20I_D1_ADDA_DAC,
|
||||
+ SUN20I_D1_ADDA_DAC_DACR_EN, 0),
|
||||
+ /* ADC */
|
||||
+ SND_SOC_DAPM_ADC("ADC1", NULL, SUN20I_D1_ADDA_ADC1,
|
||||
+ SUN20I_D1_ADDA_ADC_EN, 0),
|
||||
+ SND_SOC_DAPM_ADC("ADC2", NULL, SUN20I_D1_ADDA_ADC2,
|
||||
+ SUN20I_D1_ADDA_ADC_EN, 0),
|
||||
+ SND_SOC_DAPM_ADC("ADC3", NULL, SUN20I_D1_ADDA_ADC3,
|
||||
+ SUN20I_D1_ADDA_ADC_EN, 0),
|
||||
+
|
||||
+ /* ADC Mixers */
|
||||
+ SND_SOC_DAPM_MIXER("ADC1 Mixer", SND_SOC_NOPM, 0, 0,
|
||||
+ sun20i_d1_codec_mixer_controls,
|
||||
+ ARRAY_SIZE(sun20i_d1_codec_mixer_controls)),
|
||||
+ SND_SOC_DAPM_MIXER("ADC2 Mixer", SND_SOC_NOPM, 0, 0,
|
||||
+ sun20i_d1_codec_mixer_controls,
|
||||
+ ARRAY_SIZE(sun20i_d1_codec_mixer_controls)),
|
||||
+
|
||||
+ /* Headphone */
|
||||
+ SND_SOC_DAPM_OUTPUT("HP"),
|
||||
+ SND_SOC_DAPM_SUPPLY("RAMP Enable", SUN20I_D1_ADDA_RAMP,
|
||||
+ SUN20I_D1_ADDA_RAMP_RD_EN, 0, NULL, 0),
|
||||
+
|
||||
+ /* Line input */
|
||||
+ SND_SOC_DAPM_INPUT("LINEIN"),
|
||||
+
|
||||
+ /* Microphone input */
|
||||
+ SND_SOC_DAPM_INPUT("MIC3"),
|
||||
+
|
||||
+ /* Microphone input path */
|
||||
+ SND_SOC_DAPM_MUX("MIC3 Source Capture Route", SND_SOC_NOPM, 0, 0,
|
||||
+ sun20i_d1_codec_mic3_input_src),
|
||||
+
|
||||
+ SND_SOC_DAPM_PGA("Mic3 Amplifier", SUN20I_D1_ADDA_ADC3,
|
||||
+ SUN20I_D1_ADDA_ADC_PGA_EN, 0, NULL, 0),
|
||||
+
|
||||
+ /* Microphone Bias */
|
||||
+ SND_SOC_DAPM_SUPPLY("MBIAS", SUN20I_D1_ADDA_MICBIAS,
|
||||
+ SUN20I_D1_ADDA_MICBIAS_MMICBIASEN, 0, NULL, 0),
|
||||
+};
|
||||
+
|
||||
+static const struct snd_soc_dapm_route sun20i_d1_codec_routes[] = {
|
||||
+ /* Headphone Routes */
|
||||
+ { "HP", NULL, "Left DAC" },
|
||||
+ { "HP", NULL, "Right DAC" },
|
||||
+ { "HP", NULL, "RAMP Enable" },
|
||||
+
|
||||
+ /* Line input Routes */
|
||||
+ { "ADC1", NULL, "ADC1 Mixer" },
|
||||
+ { "ADC2", NULL, "ADC2 Mixer" },
|
||||
+ { "ADC1 Mixer", "Line In Switch", "LINEIN" },
|
||||
+ { "ADC2 Mixer", "Line In Switch", "LINEIN" },
|
||||
+
|
||||
+ /* Microphone Routes */
|
||||
+ { "MIC3 Source Capture Route", "Differential", "MIC3" },
|
||||
+ { "MIC3 Source Capture Route", "Single", "MIC3" },
|
||||
+ { "Mic3 Amplifier", NULL, "MIC3 Source Capture Route" },
|
||||
+ { "ADC3", NULL, "Mic3 Amplifier" },
|
||||
+};
|
||||
+
|
||||
+static const struct snd_soc_component_driver sun20i_d1_codec_analog_cmpnt_drv = {
|
||||
+ .controls = sun20i_d1_codec_controls,
|
||||
+ .num_controls = ARRAY_SIZE(sun20i_d1_codec_controls),
|
||||
+ .dapm_widgets = sun20i_d1_codec_widgets,
|
||||
+ .num_dapm_widgets = ARRAY_SIZE(sun20i_d1_codec_widgets),
|
||||
+ .dapm_routes = sun20i_d1_codec_routes,
|
||||
+ .num_dapm_routes = ARRAY_SIZE(sun20i_d1_codec_routes),
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id sun20i_d1_codec_analog_of_match[] = {
|
||||
+ {
|
||||
+ .compatible = "allwinner,sun20i-d1-codec-analog",
|
||||
+ },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, sun20i_d1_codec_analog_of_match);
|
||||
+
|
||||
+static const struct regmap_config sun20i_d1_codec_regmap_config = {
|
||||
+ .reg_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+ .val_bits = 32,
|
||||
+ .max_register = SUN20I_D1_ADDA_ADC_CUR_REG,
|
||||
+};
|
||||
+
|
||||
+static int sun20i_d1_codec_analog_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct regmap *regmap;
|
||||
+ void __iomem *base;
|
||||
+
|
||||
+ base = devm_platform_ioremap_resource(pdev, 0);
|
||||
+ if (IS_ERR(base)) {
|
||||
+ dev_err(&pdev->dev, "Failed to map the registers\n");
|
||||
+ return PTR_ERR(base);
|
||||
+ }
|
||||
+
|
||||
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
+ &sun20i_d1_codec_regmap_config);
|
||||
+ if (IS_ERR(regmap)) {
|
||||
+ dev_err(&pdev->dev, "Failed to create regmap\n");
|
||||
+ return PTR_ERR(regmap);
|
||||
+ }
|
||||
+
|
||||
+ return devm_snd_soc_register_component(&pdev->dev,
|
||||
+ &sun20i_d1_codec_analog_cmpnt_drv,
|
||||
+ NULL, 0);
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver sun20i_d1_codec_analog_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "sun20i-d1-codec-analog",
|
||||
+ .of_match_table = sun20i_d1_codec_analog_of_match,
|
||||
+ },
|
||||
+ .probe = sun20i_d1_codec_analog_probe,
|
||||
+};
|
||||
+module_platform_driver(sun20i_d1_codec_analog_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Allwinner internal codec analog controls driver for D1");
|
||||
+MODULE_AUTHOR("Maksim Kiselev <bigunclemax@gmail.com>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_ALIAS("platform:sun20i-d1-codec-analog");
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
From edebcc9d47f0bfe9bd769a2c578dda16acbfbef2 Mon Sep 17 00:00:00 2001
|
||||
From: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
Date: Sat, 5 Aug 2023 21:05:05 +0300
|
||||
Subject: [PATCH 14/14] riscv: dts: allwinner: d1: Add device nodes for
|
||||
internal audio codec
|
||||
|
||||
Add DT nodes for the internal D1/T113s audio codec and its analog part.
|
||||
|
||||
Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com>
|
||||
---
|
||||
.../boot/dts/allwinner/sunxi-d1s-t113.dtsi | 22 +++++++++++++++++++
|
||||
1 file changed, 22 insertions(+)
|
||||
|
||||
--- a/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
|
||||
+++ b/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
|
||||
@@ -166,6 +166,28 @@
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
|
||||
+ codec: codec@2030000 {
|
||||
+ #sound-dai-cells = <0>;
|
||||
+ compatible = "allwinner,sun20i-d1-codec";
|
||||
+ reg = <0x02030000 0x300>;
|
||||
+ interrupts = <SOC_PERIPHERAL_IRQ(25) IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ clocks = <&ccu CLK_BUS_AUDIO>,
|
||||
+ <&ccu CLK_AUDIO_ADC>,
|
||||
+ <&ccu CLK_AUDIO_DAC>;
|
||||
+ clock-names = "apb", "adc", "dac";
|
||||
+ resets = <&ccu RST_BUS_AUDIO>;
|
||||
+ dmas = <&dma 7>, <&dma 7>;
|
||||
+ dma-names = "rx", "tx";
|
||||
+ allwinner,codec-analog-controls = <&codec_analog>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ codec_analog: codec-analog@2030300 {
|
||||
+ compatible = "allwinner,sun20i-d1-codec-analog";
|
||||
+ reg = <0x02030300 0xd00>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
dmic: dmic@2031000 {
|
||||
compatible = "allwinner,sun20i-d1-dmic",
|
||||
"allwinner,sun50i-h6-dmic";
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
From: Samuel Holland <samuel@sholland.org>
|
||||
Subject: riscv: dts: allwinner: d1: Add LED controller node
|
||||
Date: Sun, 29 Oct 2023 16:26:58 -0500
|
||||
|
||||
Allwinner D1 contains an LED controller. Add its devicetree node, as
|
||||
well as the pinmux used by the reference board design.
|
||||
|
||||
Acked-by: Guo Ren <guoren@kernel.org>
|
||||
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
|
||||
Tested-by: Trevor Woerner <twoerner@gmail.com>
|
||||
Signed-off-by: Samuel Holland <samuel@sholland.org>
|
||||
---
|
||||
(no changes since v5)
|
||||
|
||||
Changes in v5:
|
||||
- New patch for v5
|
||||
|
||||
arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 6 ++++++
|
||||
arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi | 15 +++++++++++++++
|
||||
2 files changed, 21 insertions(+)
|
||||
|
||||
--- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi
|
||||
+++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi
|
||||
@@ -59,6 +59,12 @@
|
||||
};
|
||||
|
||||
/omit-if-no-ref/
|
||||
+ ledc_pc0_pin: ledc-pc0-pin {
|
||||
+ pins = "PC0";
|
||||
+ function = "ledc";
|
||||
+ };
|
||||
+
|
||||
+ /omit-if-no-ref/
|
||||
uart0_pb8_pins: uart0-pb8-pins {
|
||||
pins = "PB8", "PB9";
|
||||
function = "uart0";
|
||||
--- a/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
|
||||
+++ b/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
|
||||
@@ -156,6 +156,21 @@
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
+ ledc: led-controller@2008000 {
|
||||
+ compatible = "allwinner,sun20i-d1-ledc",
|
||||
+ "allwinner,sun50i-a100-ledc";
|
||||
+ reg = <0x2008000 0x400>;
|
||||
+ interrupts = <SOC_PERIPHERAL_IRQ(20) IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ clocks = <&ccu CLK_BUS_LEDC>, <&ccu CLK_LEDC>;
|
||||
+ clock-names = "bus", "mod";
|
||||
+ resets = <&ccu RST_BUS_LEDC>;
|
||||
+ dmas = <&dma 42>;
|
||||
+ dma-names = "tx";
|
||||
+ status = "disabled";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+ };
|
||||
+
|
||||
gpadc: adc@2009000 {
|
||||
compatible = "allwinner,sun20i-d1-gpadc";
|
||||
reg = <0x2009000 0x400>;
|
||||
@@ -1,70 +0,0 @@
|
||||
From: Samuel Holland <samuel@sholland.org>
|
||||
Subject: riscv: dts: allwinner: d1: Add RGB LEDs to boards
|
||||
Date: Sun, 29 Oct 2023 16:26:59 -0500
|
||||
|
||||
Some D1-based boards feature an onboard RGB LED. Enable them.
|
||||
|
||||
Acked-by: Guo Ren <guoren@kernel.org>
|
||||
Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
|
||||
Tested-by: Trevor Woerner <twoerner@gmail.com>
|
||||
Signed-off-by: Samuel Holland <samuel@sholland.org>
|
||||
---
|
||||
(no changes since v5)
|
||||
|
||||
Changes in v5:
|
||||
- New patch for v5
|
||||
|
||||
.../boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts | 12 ++++++++++++
|
||||
arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts | 13 +++++++++++++
|
||||
2 files changed, 25 insertions(+)
|
||||
|
||||
--- a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts
|
||||
+++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts
|
||||
@@ -59,6 +59,18 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
+&ledc {
|
||||
+ pinctrl-0 = <&ledc_pc0_pin>;
|
||||
+ pinctrl-names = "default";
|
||||
+ status = "okay";
|
||||
+
|
||||
+ multi-led@0 {
|
||||
+ reg = <0x0>;
|
||||
+ color = <LED_COLOR_ID_RGB>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ };
|
||||
+};
|
||||
+
|
||||
&mmc1 {
|
||||
bus-width = <4>;
|
||||
mmc-pwrseq = <&wifi_pwrseq>;
|
||||
--- a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts
|
||||
+++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
+#include <dt-bindings/leds/common.h>
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
@@ -121,6 +122,18 @@
|
||||
};
|
||||
};
|
||||
|
||||
+&ledc {
|
||||
+ pinctrl-0 = <&ledc_pc0_pin>;
|
||||
+ pinctrl-names = "default";
|
||||
+ status = "okay";
|
||||
+
|
||||
+ multi-led@0 {
|
||||
+ reg = <0x0>;
|
||||
+ color = <LED_COLOR_ID_RGB>;
|
||||
+ function = LED_FUNCTION_STATUS;
|
||||
+ };
|
||||
+};
|
||||
+
|
||||
&mdio {
|
||||
ext_rgmii_phy: ethernet-phy@1 {
|
||||
compatible = "ethernet-phy-ieee802.3-c22";
|
||||
+1
-2
@@ -29,7 +29,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
||||
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
||||
@@ -298,6 +298,15 @@ config DWMAC_LOONGSON
|
||||
@@ -298,6 +298,14 @@ config DWMAC_LOONGSON
|
||||
This selects the LOONGSON PCI bus support for the stmmac driver,
|
||||
Support for ethernet controller on Loongson-2K1000 SoC and LS7A1000 bridge.
|
||||
|
||||
@@ -37,7 +37,6 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
+ tristate "Motorcomm PCI DWMAC support"
|
||||
+ depends on PCI
|
||||
+ select MOTORCOMM_PHY
|
||||
+ select STMMAC_LIBPCI
|
||||
+ help
|
||||
+ This enables glue driver for Motorcomm DWMAC-based PCI Ethernet
|
||||
+ controllers. Currently only YT6801 is supported.
|
||||
|
||||
+6
-3
@@ -1,13 +1,16 @@
|
||||
From 3fd163f5bb88de426ca9847549f94b4296170ef0 Mon Sep 17 00:00:00 2001
|
||||
From e9abf1da0af3f787a03b249945e5ca726c1b8013 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Mon, 30 Mar 2026 23:40:53 +0100
|
||||
Date: Mon, 30 Mar 2026 23:52:09 +0100
|
||||
Subject: [PATCH] net: dsa: mxl862xx: cancel pending work on probe error
|
||||
|
||||
Call mxl862xx_host_shutdown() in case dsa_register_switch() returns
|
||||
an error, so any still pending crc_err_work get canceled.
|
||||
|
||||
Fixes: a319d0c8c8ced ("net: dsa: mxl862xx: add CRC for MDIO communication)"
|
||||
Fixes: a319d0c8c8ce ("net: dsa: mxl862xx: add CRC for MDIO communication")
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://patch.msgid.link/3fd163f5bb88de426ca9847549f94b4296170ef0.1774911025.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
+6
-4
@@ -1,7 +1,7 @@
|
||||
From cd698f1ae94c16499e2714b31dd6048e6f9f068d Mon Sep 17 00:00:00 2001
|
||||
From b0a79590d10847f190ed377d2664377d7068191d Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 17:54:11 +0000
|
||||
Subject: [PATCH 01/26] net: dsa: move dsa_bridge_ports() helper to dsa.h
|
||||
Date: Wed, 1 Apr 2026 14:34:30 +0100
|
||||
Subject: [PATCH 1/4] net: dsa: move dsa_bridge_ports() helper to dsa.h
|
||||
|
||||
The yt921x driver contains a helper to create a bitmap of ports
|
||||
which are members of a bridge.
|
||||
@@ -10,8 +10,10 @@ Move the helper as static inline function into dsa.h, so other driver
|
||||
can make use of it as well.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/4f8bbfce3e4e3a02064fc4dc366263136c6e0383.1775049897.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
include/net/dsa.h | 13 +++++++++++++
|
||||
include/net/dsa.h | 13 +++++++++++++
|
||||
2 files changed, 13 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/include/net/dsa.h
|
||||
+5
-3
@@ -1,7 +1,7 @@
|
||||
From c161533e1605a7282563c139323a3913890fdb72 Mon Sep 17 00:00:00 2001
|
||||
From f259e08494c47c614ce7b6d3079d1e0d3f30ae66 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 17:54:41 +0000
|
||||
Subject: [PATCH 02/26] net: dsa: add bridge member iteration macro
|
||||
Date: Wed, 1 Apr 2026 14:34:42 +0100
|
||||
Subject: [PATCH 2/4] net: dsa: add bridge member iteration macro
|
||||
|
||||
Drivers that offload bridges need to iterate over the ports that are
|
||||
members of a given bridge, for example to rebuild per-port forwarding
|
||||
@@ -15,6 +15,8 @@ directly, and use it for the existing dsa_bridge_ports() inline
|
||||
helper.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/e7136aaa26773f39e805a00fe4ecf13cd2b83fc0.1775049897.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
include/net/dsa.h | 9 ++++++---
|
||||
1 file changed, 6 insertions(+), 3 deletions(-)
|
||||
+5
-3
@@ -1,7 +1,7 @@
|
||||
From 753efe27a9afee52c4ad42098a9b9278366d63cc Mon Sep 17 00:00:00 2001
|
||||
From 4250ff1640ea1ede99bfe02ca949acbcc6c0927f Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 17:54:52 +0000
|
||||
Subject: [PATCH 03/26] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
|
||||
Date: Wed, 1 Apr 2026 14:34:52 +0100
|
||||
Subject: [PATCH 3/4] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
|
||||
|
||||
The MxL862xx offloads bridge forwarding in hardware, so set
|
||||
dsa_default_offload_fwd_mark() to avoid duplicate forwarding of
|
||||
@@ -11,6 +11,8 @@ Link-local frames are directly trapped to the CPU port only, so don't
|
||||
set dsa_default_offload_fwd_mark() on those.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/e1161c90894ddc519c57dc0224b3a0f6bfa1d2d6.1775049897.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
net/dsa/tag_mxl862xx.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
+160
-179
@@ -1,7 +1,7 @@
|
||||
From ce0664ff8f75c3ab01101c3f0f8569924d948775 Mon Sep 17 00:00:00 2001
|
||||
From 340bdf984613c4a9241d678915e513824f5a9b19 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 17:55:08 +0000
|
||||
Subject: [PATCH 04/26] net: dsa: mxl862xx: implement bridge offloading
|
||||
Date: Wed, 1 Apr 2026 14:35:01 +0100
|
||||
Subject: [PATCH 4/4] net: dsa: mxl862xx: implement bridge offloading
|
||||
|
||||
Implement joining and leaving bridges as well as add, delete and dump
|
||||
operations on isolated FDBs, port MDB membership management, and
|
||||
@@ -24,9 +24,17 @@ dsa_switch_for_each_bridge_member().
|
||||
|
||||
As there are now more users of the BRIDGEPORT_CONFIG_SET API and the
|
||||
state of each port is cached locally, introduce a helper function
|
||||
mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) which is
|
||||
then used to replace the direct calls to the API in
|
||||
mxl862xx_setup_cpu_bridge() and mxl862xx_add_single_port_bridge().
|
||||
mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) which
|
||||
applies the cached per-port state to hardware. For standalone user
|
||||
ports (dp->bridge == NULL), it additionally resets the port to
|
||||
single-port bridge state: CPU-only portmap, learning and flooding
|
||||
disabled. The CPU port path sets its state explicitly before calling
|
||||
this helper and is therefore not affected by the reset.
|
||||
|
||||
Note that MASK_VLAN_BASED_MAC_LEARNING is intentionally absent from
|
||||
the firmware write mask. After mxl862xx_reset(), the firmware
|
||||
initialises all VLAN-based MAC learning fields to 0 (disabled), so
|
||||
SVL is the active mode by default without having to set it explicitly.
|
||||
|
||||
Note that there is no convenient way to control flooding on per-port
|
||||
level, so the driver is using a 0-rate QoS meter setup as a stopper in
|
||||
@@ -36,12 +44,14 @@ registers -- without that at least one 64-byte packet could still
|
||||
pass before the meter would change from 'yellow' into 'red' state.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/dd079180e2098e5f9626fcd149b9bad9a1b5a1b2.1775049897.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 ++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 +++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 20 +-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 743 ++++++++++++++++++++++--
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 133 +++++
|
||||
4 files changed, 1076 insertions(+), 45 deletions(-)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 736 ++++++++++++++++++++++--
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 99 ++++
|
||||
4 files changed, 1022 insertions(+), 58 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -384,7 +394,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
|
||||
int port,
|
||||
enum dsa_tag_protocol m)
|
||||
@@ -168,6 +182,213 @@ static int mxl862xx_setup_mdio(struct ds
|
||||
@@ -168,6 +182,199 @@ static int mxl862xx_setup_mdio(struct ds
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -479,11 +489,40 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
+ u16 bridge_id = dp->bridge ?
|
||||
+ priv->bridges[dp->bridge->num] : p->fid;
|
||||
+ struct dsa_port *member_dp;
|
||||
+ u16 bridge_id;
|
||||
+ bool enable;
|
||||
+ int i, idx;
|
||||
+
|
||||
+ if (!p->setup_done)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (dsa_port_is_cpu(dp)) {
|
||||
+ dsa_switch_for_each_user_port(member_dp, ds) {
|
||||
+ if (member_dp->cpu_dp->index != port)
|
||||
+ continue;
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ member_dp->index);
|
||||
+ }
|
||||
+ } else if (dp->bridge) {
|
||||
+ dsa_switch_for_each_bridge_member(member_dp, ds,
|
||||
+ dp->bridge->dev) {
|
||||
+ if (member_dp->index == port)
|
||||
+ continue;
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ member_dp->index);
|
||||
+ }
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ dp->cpu_dp->index);
|
||||
+ } else {
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ dp->cpu_dp->index);
|
||||
+ p->flood_block = 0;
|
||||
+ p->learning = false;
|
||||
+ }
|
||||
+
|
||||
+ bridge_id = dp->bridge ? priv->bridges[dp->bridge->num] : p->fid;
|
||||
+
|
||||
+ br_port_cfg.bridge_port_id = cpu_to_le16(port);
|
||||
+ br_port_cfg.bridge_id = cpu_to_le16(bridge_id);
|
||||
+ br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
|
||||
@@ -492,8 +531,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER);
|
||||
+ br_port_cfg.src_mac_learning_disable = !p->learning;
|
||||
+
|
||||
+ mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
|
||||
+ idx = mxl862xx_flood_meters[i];
|
||||
+ enable = !!(p->flood_block & BIT(idx));
|
||||
@@ -510,24 +547,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+static int mxl862xx_sync_bridge_members(struct dsa_switch *ds,
|
||||
+ const struct dsa_bridge *bridge)
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct dsa_port *dp, *member_dp;
|
||||
+ int port, err, ret = 0;
|
||||
+ struct dsa_port *dp;
|
||||
+ int ret = 0, err;
|
||||
+
|
||||
+ dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) {
|
||||
+ port = dp->index;
|
||||
+
|
||||
+ bitmap_zero(priv->ports[port].portmap,
|
||||
+ MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+
|
||||
+ dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) {
|
||||
+ if (member_dp->index != port)
|
||||
+ __set_bit(member_dp->index,
|
||||
+ priv->ports[port].portmap);
|
||||
+ }
|
||||
+ __set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
|
||||
+
|
||||
+ err = mxl862xx_set_bridge_port(ds, port);
|
||||
+ err = mxl862xx_set_bridge_port(ds, dp->index);
|
||||
+ if (err)
|
||||
+ ret = err;
|
||||
+ }
|
||||
@@ -535,7 +559,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
|
||||
+static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv)
|
||||
+{
|
||||
+ struct mxl862xx_bridge_alloc br_alloc = {};
|
||||
+ int ret;
|
||||
@@ -544,8 +568,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ *bridge_id = le16_to_cpu(br_alloc.bridge_id);
|
||||
+ return 0;
|
||||
+ return le16_to_cpu(br_alloc.bridge_id);
|
||||
+}
|
||||
+
|
||||
+static void mxl862xx_free_bridge(struct dsa_switch *ds,
|
||||
@@ -567,38 +590,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+
|
||||
+ priv->bridges[bridge->num] = 0;
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
|
||||
+{
|
||||
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = mxl862xx_allocate_bridge(priv, &priv->ports[port].fid);
|
||||
+ if (ret) {
|
||||
+ dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ priv->ports[port].learning = false;
|
||||
+ bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ __set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
|
||||
+
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Standalone ports should not flood unknown unicast or multicast
|
||||
+ * towards the CPU by default; only broadcast is needed initially.
|
||||
+ */
|
||||
+ return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
|
||||
+ false, false, true);
|
||||
+}
|
||||
+
|
||||
static int mxl862xx_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
@@ -181,6 +402,10 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -181,6 +388,10 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -609,66 +605,49 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return mxl862xx_setup_mdio(ds);
|
||||
}
|
||||
|
||||
@@ -260,66 +485,87 @@ static int mxl862xx_configure_sp_tag_pro
|
||||
@@ -260,99 +471,137 @@ static int mxl862xx_configure_sp_tag_pro
|
||||
|
||||
static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
|
||||
{
|
||||
- struct mxl862xx_bridge_port_config br_port_cfg = {};
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
- u16 bridge_port_map = 0;
|
||||
struct dsa_port *dp;
|
||||
- struct dsa_port *dp;
|
||||
|
||||
- /* CPU port bridge setup */
|
||||
- br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
|
||||
- MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING |
|
||||
- MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING);
|
||||
-
|
||||
+ priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
|
||||
+ priv->ports[port].learning = true;
|
||||
|
||||
- br_port_cfg.bridge_port_id = cpu_to_le16(port);
|
||||
- br_port_cfg.src_mac_learning_disable = false;
|
||||
- br_port_cfg.vlan_src_mac_vid_enable = true;
|
||||
- br_port_cfg.vlan_dst_mac_vid_enable = true;
|
||||
+ priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
|
||||
+ priv->ports[port].learning = true;
|
||||
|
||||
/* include all assigned user ports in the CPU portmap */
|
||||
+ bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
dsa_switch_for_each_user_port(dp, ds) {
|
||||
/* it's safe to rely on cpu_dp being valid for user ports */
|
||||
if (dp->cpu_dp->index != port)
|
||||
continue;
|
||||
|
||||
- bridge_port_map |= BIT(dp->index);
|
||||
+ __set_bit(dp->index, priv->ports[port].portmap);
|
||||
}
|
||||
- br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map);
|
||||
|
||||
- return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg);
|
||||
+ return mxl862xx_set_bridge_port(ds, port);
|
||||
}
|
||||
+}
|
||||
|
||||
-static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
|
||||
- /* include all assigned user ports in the CPU portmap */
|
||||
- dsa_switch_for_each_user_port(dp, ds) {
|
||||
- /* it's safe to rely on cpu_dp being valid for user ports */
|
||||
- if (dp->cpu_dp->index != port)
|
||||
- continue;
|
||||
+static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
|
||||
+ const struct dsa_bridge bridge,
|
||||
+ bool *tx_fwd_offload,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
{
|
||||
- struct mxl862xx_bridge_port_config br_port_cfg = {};
|
||||
- struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
- struct mxl862xx_bridge_alloc br_alloc = {};
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ u16 fw_id;
|
||||
int ret;
|
||||
+ int ret;
|
||||
|
||||
- ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc);
|
||||
- if (ret) {
|
||||
- dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
|
||||
- return ret;
|
||||
- bridge_port_map |= BIT(dp->index);
|
||||
+ if (!priv->bridges[bridge.num]) {
|
||||
+ ret = mxl862xx_allocate_bridge(priv, &fw_id);
|
||||
+ if (ret)
|
||||
+ ret = mxl862xx_allocate_bridge(priv);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ priv->bridges[bridge.num] = fw_id;
|
||||
+ priv->bridges[bridge.num] = ret;
|
||||
+
|
||||
+ /* Free bridge here on error, DSA rollback won't. */
|
||||
+ ret = mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
@@ -679,6 +658,32 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
- br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map);
|
||||
|
||||
- return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg);
|
||||
+ return mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
}
|
||||
|
||||
-static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
|
||||
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||
+ const struct dsa_bridge bridge)
|
||||
{
|
||||
- struct mxl862xx_bridge_port_config br_port_cfg = {};
|
||||
- struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
- struct mxl862xx_bridge_alloc br_alloc = {};
|
||||
- int ret;
|
||||
+ int err;
|
||||
|
||||
- ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc);
|
||||
- if (ret) {
|
||||
- dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
|
||||
- return ret;
|
||||
- }
|
||||
+ err = mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
+ if (err)
|
||||
+ dev_err(ds->dev,
|
||||
+ "failed to sync bridge members after port %d left: %pe\n",
|
||||
+ port, ERR_PTR(err));
|
||||
|
||||
- br_port_cfg.bridge_id = br_alloc.bridge_id;
|
||||
- br_port_cfg.bridge_port_id = cpu_to_le16(port);
|
||||
@@ -691,30 +696,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
- br_port_cfg.vlan_dst_mac_vid_enable = false;
|
||||
- /* As this function is only called for user ports it is safe to rely on
|
||||
- * cpu_dp being valid
|
||||
+ return mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
+}
|
||||
+
|
||||
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||
+ const struct dsa_bridge bridge)
|
||||
+{
|
||||
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
+ int err;
|
||||
+
|
||||
+ err = mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
+ if (err)
|
||||
+ dev_err(ds->dev,
|
||||
+ "failed to sync bridge members after port %d left: %pe\n",
|
||||
+ port, ERR_PTR(err));
|
||||
+
|
||||
+ /* Revert leaving port, omitted by the sync above, to its
|
||||
+ * single-port bridge
|
||||
*/
|
||||
- br_port_cfg.bridge_port_map[0] = cpu_to_le16(BIT(dp->cpu_dp->index));
|
||||
+ bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ __set_bit(dp->cpu_dp->index, p->portmap);
|
||||
+ p->flood_block = 0;
|
||||
+ err = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (err)
|
||||
+ dev_err(ds->dev,
|
||||
@@ -732,12 +717,54 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
bool is_cpu_port = dsa_port_is_cpu(dp);
|
||||
int ret;
|
||||
@@ -352,7 +598,31 @@ static int mxl862xx_port_setup(struct ds
|
||||
|
||||
- /* disable port and flush MAC entries */
|
||||
ret = mxl862xx_port_state(ds, port, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mxl862xx_port_fast_age(ds, port);
|
||||
|
||||
- /* skip setup for unused and DSA ports */
|
||||
if (dsa_port_is_unused(dp) ||
|
||||
dsa_port_is_dsa(dp))
|
||||
return 0;
|
||||
|
||||
- /* configure tag protocol */
|
||||
ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- /* assign CTP port IDs */
|
||||
ret = mxl862xx_configure_ctp_port(ds, port, port,
|
||||
is_cpu_port ? 32 - port : 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (is_cpu_port)
|
||||
- /* assign user ports to CPU port bridge */
|
||||
return mxl862xx_setup_cpu_bridge(ds, port);
|
||||
|
||||
/* setup single-port bridge for user ports */
|
||||
- /* setup single-port bridge for user ports */
|
||||
- return mxl862xx_add_single_port_bridge(ds, port);
|
||||
+ ret = mxl862xx_add_single_port_bridge(ds, port);
|
||||
+ /* setup single-port bridge for user ports.
|
||||
+ * If this fails, the FID is leaked -- but the port then transitions
|
||||
+ * to unused, and the FID pool is sized to tolerate this.
|
||||
+ */
|
||||
+ ret = mxl862xx_allocate_bridge(priv);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ priv->ports[port].fid = ret;
|
||||
+ /* Standalone ports should not flood unknown unicast or multicast
|
||||
+ * towards the CPU by default; only broadcast is needed initially.
|
||||
+ */
|
||||
+ ret = mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
|
||||
+ false, false, true);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
@@ -765,7 +792,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
}
|
||||
|
||||
static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
|
||||
@@ -365,14 +635,383 @@ static void mxl862xx_phylink_get_caps(st
|
||||
@@ -365,14 +614,371 @@ static void mxl862xx_phylink_get_caps(st
|
||||
config->supported_interfaces);
|
||||
}
|
||||
|
||||
@@ -876,7 +903,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (fid < 0)
|
||||
+ return fid;
|
||||
+
|
||||
+ /* Look up existing entry by {MAC, FID, TCI} */
|
||||
+ ether_addr_copy(qparam.mac, mdb->addr);
|
||||
+ qparam.fid = cpu_to_le16(fid);
|
||||
+ qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
|
||||
@@ -892,7 +918,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ aparam.static_entry = true;
|
||||
+ aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG);
|
||||
+
|
||||
+ /* Merge with existing portmap if entry already exists */
|
||||
+ if (qparam.found)
|
||||
+ memcpy(aparam.port_map, qparam.port_map,
|
||||
+ sizeof(aparam.port_map));
|
||||
@@ -915,7 +940,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (fid < 0)
|
||||
+ return fid;
|
||||
+
|
||||
+ /* Look up existing entry */
|
||||
+ qparam.fid = cpu_to_le16(fid);
|
||||
+ qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
|
||||
+ ether_addr_copy(qparam.mac, mdb->addr);
|
||||
@@ -930,7 +954,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ mxl862xx_fw_portmap_clear_bit(qparam.port_map, port);
|
||||
+
|
||||
+ if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) {
|
||||
+ /* No ports left -- remove the entry entirely */
|
||||
+ rparam.fid = cpu_to_le16(fid);
|
||||
+ rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
|
||||
+ ether_addr_copy(rparam.mac, mdb->addr);
|
||||
@@ -1007,8 +1030,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * LEARNING or FORWARDING state (per 802.1D defaults).
|
||||
+ * Re-apply the driver's intended learning and metering config so that
|
||||
+ * standalone ports keep learning disabled.
|
||||
+ * This is likely to get fixed in future firmware releases, however,
|
||||
+ * the additional API call even then doesn't hurt much.
|
||||
+ */
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
@@ -1031,8 +1052,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ host_flood_work);
|
||||
+ struct mxl862xx_priv *priv = p->priv;
|
||||
+ struct dsa_switch *ds = priv->ds;
|
||||
+ int port = p - priv->ports;
|
||||
+ bool uc, mc;
|
||||
+
|
||||
+ rtnl_lock();
|
||||
+
|
||||
@@ -1042,17 +1061,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ uc = p->host_flood_uc;
|
||||
+ mc = p->host_flood_mc;
|
||||
+
|
||||
+ /* The hardware controls unknown-unicast/multicast forwarding per FID
|
||||
+ * (bridge), not per source port. For bridged ports all members share
|
||||
+ * one FID, so we cannot selectively suppress flooding to the CPU for
|
||||
+ * one source port while allowing it for another. Silently ignore the
|
||||
+ * request -- the excess flooding towards the CPU is harmless.
|
||||
+ /* Always write to the standalone FID. When standalone it takes effect
|
||||
+ * immediately; when bridged the port uses the shared bridge FID so the
|
||||
+ * write is a no-op for current forwarding, but the state is preserved
|
||||
+ * in hardware and is ready once the port returns to standalone.
|
||||
+ */
|
||||
+ if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
|
||||
+ mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true);
|
||||
+ mxl862xx_bridge_config_fwd(ds, p->fid, p->host_flood_uc,
|
||||
+ p->host_flood_mc, true);
|
||||
+
|
||||
+ rtnl_unlock();
|
||||
+}
|
||||
@@ -1115,7 +1130,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (flags.mask & BR_LEARNING)
|
||||
+ priv->ports[port].learning = !!(flags.val & BR_LEARNING);
|
||||
+
|
||||
+ if ((block != old_block) || (flags.mask & BR_LEARNING)) {
|
||||
+ if (block != old_block || (flags.mask & BR_LEARNING)) {
|
||||
+ priv->ports[port].flood_block = block;
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
@@ -1149,7 +1164,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
};
|
||||
|
||||
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
|
||||
@@ -407,7 +1046,7 @@ static int mxl862xx_probe(struct mdio_de
|
||||
@@ -407,7 +1013,7 @@ static int mxl862xx_probe(struct mdio_de
|
||||
struct device *dev = &mdiodev->dev;
|
||||
struct mxl862xx_priv *priv;
|
||||
struct dsa_switch *ds;
|
||||
@@ -1158,7 +1173,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@@ -425,14 +1064,25 @@ static int mxl862xx_probe(struct mdio_de
|
||||
@@ -425,14 +1031,25 @@ static int mxl862xx_probe(struct mdio_de
|
||||
ds->ops = &mxl862xx_switch_ops;
|
||||
ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops;
|
||||
ds->num_ports = MXL862XX_MAX_PORTS;
|
||||
@@ -1186,7 +1201,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -440,6 +1090,7 @@ static void mxl862xx_remove(struct mdio_
|
||||
@@ -440,6 +1057,7 @@ static void mxl862xx_remove(struct mdio_
|
||||
{
|
||||
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
|
||||
struct mxl862xx_priv *priv;
|
||||
@@ -1194,7 +1209,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
if (!ds)
|
||||
return;
|
||||
@@ -449,12 +1100,21 @@ static void mxl862xx_remove(struct mdio_
|
||||
@@ -449,12 +1067,21 @@ static void mxl862xx_remove(struct mdio_
|
||||
dsa_unregister_switch(ds);
|
||||
|
||||
mxl862xx_host_shutdown(priv);
|
||||
@@ -1216,7 +1231,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
if (!ds)
|
||||
return;
|
||||
@@ -465,6 +1125,9 @@ static void mxl862xx_shutdown(struct mdi
|
||||
@@ -465,6 +1092,9 @@ static void mxl862xx_shutdown(struct mdi
|
||||
|
||||
mxl862xx_host_shutdown(priv);
|
||||
|
||||
@@ -1228,7 +1243,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -4,15 +4,148 @@
|
||||
@@ -4,15 +4,114 @@
|
||||
#define __MXL862XX_H
|
||||
|
||||
#include <linux/mdio.h>
|
||||
@@ -1246,37 +1261,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+#define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_fw_portmap_from_bitmap - convert a kernel bitmap to a firmware
|
||||
+ * portmap (__le16[8])
|
||||
+ * @dst: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
|
||||
+ * @src: kernel bitmap of at least MXL862XX_MAX_BRIDGE_PORTS bits
|
||||
+ */
|
||||
+static inline void
|
||||
+mxl862xx_fw_portmap_from_bitmap(__le16 *dst, const unsigned long *src)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++)
|
||||
+ dst[i] = cpu_to_le16(bitmap_read(src, i * 16, 16));
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_fw_portmap_to_bitmap - convert a firmware portmap (__le16[8]) to
|
||||
+ * a kernel bitmap
|
||||
+ * @dst: kernel bitmap of at least MXL862XX_MAX_BRIDGE_PORTS bits
|
||||
+ * @src: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
|
||||
+ */
|
||||
+static inline void
|
||||
+mxl862xx_fw_portmap_to_bitmap(unsigned long *dst, const __le16 *src)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ bitmap_zero(dst, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++)
|
||||
+ bitmap_write(dst, le16_to_cpu(src[i]), i * 16, 16);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_fw_portmap_set_bit - set a single port bit in a firmware portmap
|
||||
+ * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
|
||||
+ * @port: port index (0..MXL862XX_MAX_BRIDGE_PORTS-1)
|
||||
@@ -1320,8 +1304,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * @fid: firmware FID for the permanent single-port bridge;
|
||||
+ * kept alive for the lifetime of the port so traffic is
|
||||
+ * never forwarded while the port is unbridged
|
||||
+ * @portmap: bitmap of switch port indices that share the current
|
||||
+ * bridge with this port
|
||||
+ * @flood_block: bitmask of firmware meter indices that are currently
|
||||
+ * rate-limiting flood traffic on this port (zero-rate
|
||||
+ * meters used to block flooding)
|
||||
@@ -1343,7 +1325,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+struct mxl862xx_port {
|
||||
+ struct mxl862xx_priv *priv;
|
||||
+ u16 fid;
|
||||
+ DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ unsigned long flood_block;
|
||||
+ bool learning;
|
||||
+ bool setup_done;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
From 3a4056ec7ec8f71ae9722f86d3cfbc4589deeac4 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 7 Apr 2026 18:30:27 +0100
|
||||
Subject: [PATCH 1/2] net: dsa: mxl862xx: reject DSA_PORT_TYPE_DSA
|
||||
|
||||
DSA links aren't supported by the mxl862xx driver.
|
||||
|
||||
Instead of returning early from .port_setup when called for
|
||||
DSA_PORT_TYPE_DSA ports rather return -EOPNOTSUPP and show an error
|
||||
message.
|
||||
|
||||
The desired side-effect is that the framework will switch the port to
|
||||
DSA_PORT_TYPE_UNUSED, so we can stop caring about DSA_PORT_TYPE_DSA in
|
||||
all other places.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/b686f3a22d8a6e7d470e7aa98da811a996a229b9.1775581804.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -544,10 +544,14 @@ static int mxl862xx_port_setup(struct ds
|
||||
|
||||
mxl862xx_port_fast_age(ds, port);
|
||||
|
||||
- if (dsa_port_is_unused(dp) ||
|
||||
- dsa_port_is_dsa(dp))
|
||||
+ if (dsa_port_is_unused(dp))
|
||||
return 0;
|
||||
|
||||
+ if (dsa_port_is_dsa(dp)) {
|
||||
+ dev_err(ds->dev, "port %d: DSA links not supported\n", port);
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+
|
||||
ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -591,7 +595,7 @@ static void mxl862xx_port_teardown(struc
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
|
||||
- if (dsa_port_is_unused(dp) || dsa_port_is_dsa(dp))
|
||||
+ if (dsa_port_is_unused(dp))
|
||||
return;
|
||||
|
||||
/* Prevent deferred host_flood_work from acting on stale state.
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
From 71934b9e6f36b1786bd969c0e1d2de8f9bd65f0f Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 7 Apr 2026 18:30:35 +0100
|
||||
Subject: [PATCH 2/2] net: dsa: mxl862xx: don't skip early bridge port
|
||||
configuration
|
||||
|
||||
mxl862xx_bridge_port_set() is currently guarded by the
|
||||
mxl8622_port->setup_done flag, as the early call to
|
||||
mxl862xx_bridge_port_set() from mxl862xx_port_stp_state_set() would
|
||||
otherwise cause a NULL-pointer dereference on unused ports which don't
|
||||
have dp->cpu_dp despite not being a CPU port.
|
||||
|
||||
Using the setup_done flag (which is never set for unused ports),
|
||||
however, also prevents mxl862xx_bridge_port_set() from configuring
|
||||
user ports' single-port bridges early, which was unintended.
|
||||
|
||||
Fix this by returning early from mxl862xx_bridge_port_set() in case
|
||||
dsa_port_is_unused().
|
||||
|
||||
Fixes: 340bdf984613c ("net: dsa: mxl862xx: implement bridge offloading")
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/15962aac29ebe0a6eb77565451acff880c41ef33.1775581804.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -278,7 +278,7 @@ static int mxl862xx_set_bridge_port(stru
|
||||
bool enable;
|
||||
int i, idx;
|
||||
|
||||
- if (!p->setup_done)
|
||||
+ if (dsa_port_is_unused(dp))
|
||||
return 0;
|
||||
|
||||
if (dsa_port_is_cpu(dp)) {
|
||||
+116
-255
@@ -1,7 +1,7 @@
|
||||
From 0d88d02cc9dccad01ff88f54e1beee867107b942 Mon Sep 17 00:00:00 2001
|
||||
From d587f9b6dcc98c1e8aeb5c189a7bfac60d6d29ac Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 10 Mar 2026 02:36:00 +0000
|
||||
Subject: [PATCH 05/26] net: dsa: mxl862xx: implement VLAN functionality
|
||||
Date: Tue, 7 Apr 2026 18:31:01 +0100
|
||||
Subject: [PATCH] net: dsa: mxl862xx: implement VLAN functionality
|
||||
|
||||
Add VLAN support using both the Extended VLAN (EVLAN) engine and the
|
||||
VLAN Filter (VF) engine in a hybrid architecture that allows a higher
|
||||
@@ -16,8 +16,8 @@ egress rules at all, so they consume only a VF entry.
|
||||
Both engines draw from shared 1024-entry hardware pools. The VF pool
|
||||
is divided equally among user ports for VID membership, while the
|
||||
EVLAN pool is partitioned into small fixed-size ingress blocks (7
|
||||
entries of catchall rules per port) and variable-size egress blocks
|
||||
for tag stripping.
|
||||
entries of catchall rules per port) and fixed-size egress blocks for
|
||||
tag stripping.
|
||||
|
||||
With 5 user ports this yields up to 204 VIDs per port (limited by VF),
|
||||
of which up to 98 can be untagged (limited by EVLAN egress budget).
|
||||
@@ -34,12 +34,14 @@ rather than worst-case pre-allocation, or by sharing EVLAN egress and
|
||||
VLAN Filter blocks across ports with identical VID sets.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/9be29637675342b109a85fa08f5378800d9f7b78.1775581804.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 +++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 ++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 12 +
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 915 +++++++++++++++++++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 104 ++-
|
||||
4 files changed, 1344 insertions(+), 16 deletions(-)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 781 +++++++++++++++++++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 103 +++-
|
||||
4 files changed, 1211 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -101,14 +103,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+/**
|
||||
+ * enum mxl862xx_extended_vlan_treatment_priority - Treatment priority mode
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL: Use explicit value
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY: Copy from inner tag
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY: Copy from outer tag
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY: Copy from inner tag
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY: Copy from outer tag
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP: Derive from DSCP
|
||||
+ */
|
||||
+enum mxl862xx_extended_vlan_treatment_priority {
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY = 1,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY = 2,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3,
|
||||
+};
|
||||
+
|
||||
@@ -409,16 +411,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -50,6 +50,88 @@ static const int mxl862xx_flood_meters[]
|
||||
@@ -50,6 +50,85 @@ static const int mxl862xx_flood_meters[]
|
||||
MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST,
|
||||
};
|
||||
|
||||
+enum mxl862xx_evlan_action {
|
||||
+ EVLAN_ACCEPT, /* pass-through, no tag removal */
|
||||
+ EVLAN_STRIP_IF_UNTAGGED, /* remove 1 tag if entry's untagged flag set */
|
||||
+ EVLAN_DISCARD, /* discard upstream */
|
||||
+ EVLAN_PVID_OR_DISCARD, /* insert PVID tag or discard if no PVID */
|
||||
+ EVLAN_PVID_OR_PASS, /* insert PVID tag or pass-through */
|
||||
+ EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */
|
||||
+};
|
||||
+
|
||||
@@ -457,7 +457,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * regardless of TPID, so without the ACCEPT guard, it would also
|
||||
+ * catch standard 802.1Q VID>0 frames and corrupt them. With the
|
||||
+ * guard, 802.1Q VID>0 frames match the ACCEPT rules first and
|
||||
+ * pass through untouched; only non-8021Q TPID frames fall through
|
||||
+ * pass through untouched; only non-8021Q TPID frames pass through
|
||||
+ * to the NO_FILTER catchalls.
|
||||
+ */
|
||||
+static const struct mxl862xx_evlan_rule_desc ingress_aware_final[] = {
|
||||
@@ -486,27 +486,26 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * VID-specific accept rules for VLAN-unaware egress.
|
||||
+ * The HW sees the MxL tag as outer, real VLAN tag as inner.
|
||||
+ * match on inner VID with outer=NO_FILTER.
|
||||
+ * Egress tag-stripping rules for VLAN-unaware mode (2 per untagged VID).
|
||||
+ * The HW sees the MxL tag as outer; the real VLAN tag, if any, is inner.
|
||||
+ */
|
||||
+static const struct mxl862xx_evlan_rule_desc vid_accept_egress_unaware[] = {
|
||||
+ { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
|
||||
+ { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED },
|
||||
+ { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
|
||||
+ { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_STRIP_IF_UNTAGGED },
|
||||
+};
|
||||
+
|
||||
static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
|
||||
int port,
|
||||
enum dsa_tag_protocol m)
|
||||
@@ -275,6 +357,7 @@ static int mxl862xx_set_bridge_port(stru
|
||||
@@ -275,6 +354,7 @@ static int mxl862xx_set_bridge_port(stru
|
||||
struct mxl862xx_port *p = &priv->ports[port];
|
||||
u16 bridge_id = dp->bridge ?
|
||||
priv->bridges[dp->bridge->num] : p->fid;
|
||||
struct dsa_port *member_dp;
|
||||
u16 bridge_id;
|
||||
+ u16 vf_scan;
|
||||
bool enable;
|
||||
int i, idx;
|
||||
|
||||
@@ -283,9 +366,69 @@ static int mxl862xx_set_bridge_port(stru
|
||||
@@ -312,9 +392,69 @@ static int mxl862xx_set_bridge_port(stru
|
||||
br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
|
||||
MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
|
||||
MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING |
|
||||
@@ -574,23 +573,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ br_port_cfg.vlan_src_mac_vid_enable = p->vlan_filtering;
|
||||
+ br_port_cfg.vlan_dst_mac_vid_enable = p->vlan_filtering;
|
||||
+
|
||||
mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
|
||||
@@ -329,6 +472,91 @@ static int mxl862xx_sync_bridge_members(
|
||||
idx = mxl862xx_flood_meters[i];
|
||||
enable = !!(p->flood_block & BIT(idx));
|
||||
@@ -343,6 +483,72 @@ static int mxl862xx_sync_bridge_members(
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static void mxl862xx_evlan_block_init(struct mxl862xx_evlan_block *blk,
|
||||
+ u16 size)
|
||||
+{
|
||||
+ blk->allocated = false;
|
||||
+ blk->in_use = false;
|
||||
+ blk->block_id = 0;
|
||||
+ blk->block_size = size;
|
||||
+ blk->n_active = 0;
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_evlan_block_alloc(struct mxl862xx_priv *priv,
|
||||
+ struct mxl862xx_evlan_block *blk)
|
||||
+{
|
||||
@@ -609,15 +598,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mxl862xx_vf_init(struct mxl862xx_vf_block *vf, u16 size)
|
||||
+{
|
||||
+ vf->allocated = false;
|
||||
+ vf->block_id = 0;
|
||||
+ vf->block_size = size;
|
||||
+ vf->active_count = 0;
|
||||
+ INIT_LIST_HEAD(&vf->vids);
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv,
|
||||
+ u16 size, u16 *block_id)
|
||||
+{
|
||||
@@ -666,10 +646,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
|
||||
+}
|
||||
+
|
||||
static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
|
||||
static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv)
|
||||
{
|
||||
struct mxl862xx_bridge_alloc br_alloc = {};
|
||||
@@ -392,6 +620,9 @@ static int mxl862xx_add_single_port_brid
|
||||
@@ -378,6 +584,9 @@ static void mxl862xx_free_bridge(struct
|
||||
static int mxl862xx_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
@@ -679,7 +659,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
int ret;
|
||||
|
||||
ret = mxl862xx_reset(priv);
|
||||
@@ -402,6 +633,50 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -388,6 +597,50 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -695,7 +675,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * through without EVLAN processing.
|
||||
+ *
|
||||
+ * Total EVLAN budget:
|
||||
+ * n_user_ports * (ingress + egress) ≤ 1024.
|
||||
+ * n_user_ports * (ingress + egress) <= 1024.
|
||||
+ * Ingress blocks are small (7 entries), so almost all capacity
|
||||
+ * goes to egress VID rules.
|
||||
+ */
|
||||
@@ -730,23 +710,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
ret = mxl862xx_setup_drop_meter(ds);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -483,27 +758,616 @@ static int mxl862xx_configure_sp_tag_pro
|
||||
@@ -469,12 +722,509 @@ static int mxl862xx_configure_sp_tag_pro
|
||||
return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
|
||||
+ * @priv: driver private data
|
||||
+ * @block_id: HW Extended VLAN block ID
|
||||
+ * @entry_index: entry index within the block
|
||||
+ * @desc: rule descriptor (filter type + action)
|
||||
+ * @vid: VLAN ID for VID-specific rules (ignored when !desc->match_vid)
|
||||
+ * @untagged: strip tag on egress for EVLAN_STRIP_IF_UNTAGGED action
|
||||
+ * @pvid: port VLAN ID for PVID insertion rules (0 = no PVID)
|
||||
+ *
|
||||
+ * Translates a compact rule descriptor into a full firmware
|
||||
+ * mxl862xx_extendedvlan_config struct and writes it via the API.
|
||||
+ */
|
||||
+static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
|
||||
+ u16 block_id, u16 entry_index,
|
||||
+ const struct mxl862xx_evlan_rule_desc *desc,
|
||||
@@ -788,11 +755,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
|
||||
+ break;
|
||||
+
|
||||
+ case EVLAN_DISCARD:
|
||||
+ cfg.treatment.remove_tag =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
|
||||
+ break;
|
||||
+
|
||||
+ case EVLAN_PVID_OR_DISCARD:
|
||||
+ if (pvid) {
|
||||
+ cfg.treatment.remove_tag =
|
||||
@@ -809,22 +771,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case EVLAN_PVID_OR_PASS:
|
||||
+ if (pvid) {
|
||||
+ cfg.treatment.remove_tag =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
|
||||
+ cfg.treatment.add_outer_vlan = 1;
|
||||
+ cfg.treatment.outer_vlan.vid_mode =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
|
||||
+ cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
|
||||
+ cfg.treatment.outer_vlan.tpid =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
|
||||
+ } else {
|
||||
+ cfg.treatment.remove_tag =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case EVLAN_STRIP1_AND_PVID_OR_DISCARD:
|
||||
+ if (pvid) {
|
||||
+ cfg.treatment.remove_tag =
|
||||
@@ -845,15 +791,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_evlan_deactivate_entry - Reset an Extended VLAN entry to no-op
|
||||
+ * @priv: driver private data
|
||||
+ * @block_id: HW Extended VLAN block ID
|
||||
+ * @entry_index: entry index within the block
|
||||
+ *
|
||||
+ * Writes a zeroed-out config to the firmware, which deactivates the
|
||||
+ * rule (making it transparent / no-op).
|
||||
+ */
|
||||
+static int mxl862xx_evlan_deactivate_entry(struct mxl862xx_priv *priv,
|
||||
+ u16 block_id, u16 entry_index)
|
||||
+{
|
||||
@@ -876,19 +813,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_evlan_write_final_rules - Write catchall rules to the ingress block
|
||||
+ * @priv: driver private data
|
||||
+ * @blk: Extended VLAN block (already allocated)
|
||||
+ * @rules: array of rule descriptors for the final rules
|
||||
+ * @n_rules: number of final rules
|
||||
+ * @pvid: port VLAN ID (for PVID insertion rules)
|
||||
+ *
|
||||
+ * Writes final catchall rules starting at block_size - n_rules. With
|
||||
+ * VLAN Filter handling VID membership, only the ingress block uses
|
||||
+ * finals, and the block is sized to exactly fit them (no VID entries),
|
||||
+ * so the rules fill the entire block.
|
||||
+ */
|
||||
+static int mxl862xx_evlan_write_final_rules(struct mxl862xx_priv *priv,
|
||||
+ struct mxl862xx_evlan_block *blk,
|
||||
+ const struct mxl862xx_evlan_rule_desc *rules,
|
||||
@@ -908,15 +832,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_vf_entry_set - Write a single VLAN Filter entry
|
||||
+ * @priv: driver private data
|
||||
+ * @block_id: HW VLAN Filter block ID
|
||||
+ * @index: entry index within the block
|
||||
+ * @vid: VLAN ID to allow
|
||||
+ *
|
||||
+ * Writes an ALLOW entry (discard_matched=false) for the given VID.
|
||||
+ */
|
||||
+static int mxl862xx_vf_entry_set(struct mxl862xx_priv *priv,
|
||||
+ u16 block_id, u16 index, u16 vid)
|
||||
+{
|
||||
@@ -931,13 +846,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_vf_find_vid - Find a VID entry in a VF block
|
||||
+ * @vf: VLAN Filter block to search
|
||||
+ * @vid: VLAN ID to find
|
||||
+ */
|
||||
+static struct mxl862xx_vf_vid *
|
||||
+mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf, u16 vid)
|
||||
+static struct mxl862xx_vf_vid *mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf,
|
||||
+ u16 vid)
|
||||
+{
|
||||
+ struct mxl862xx_vf_vid *ve;
|
||||
+
|
||||
@@ -948,17 +858,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_vf_add_vid - Add a VID to a port's VLAN Filter block
|
||||
+ * @priv: driver private data
|
||||
+ * @vf: VLAN Filter block
|
||||
+ * @vid: VLAN ID to add
|
||||
+ * @untagged: whether this VID should strip tags on egress
|
||||
+ *
|
||||
+ * Idempotent. Writes an ALLOW entry at active_count and increments
|
||||
+ * active_count. If the VID already exists, only the untagged flag
|
||||
+ * is updated. The HW block must be allocated before calling this.
|
||||
+ */
|
||||
+static int mxl862xx_vf_add_vid(struct mxl862xx_priv *priv,
|
||||
+ struct mxl862xx_vf_block *vf,
|
||||
+ u16 vid, bool untagged)
|
||||
@@ -995,17 +894,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_vf_del_vid - Remove a VID from a port's VLAN Filter block
|
||||
+ * @priv: driver private data
|
||||
+ * @vf: VLAN Filter block
|
||||
+ * @vid: VLAN ID to remove
|
||||
+ *
|
||||
+ * Swap-compacts: the last active entry is moved into the gap,
|
||||
+ * active_count is decremented, and the old last slot is plugged
|
||||
+ * with DISCARD. When active_count drops to 0, a DISCARD sentinel
|
||||
+ * is restored at index 0.
|
||||
+ */
|
||||
+static int mxl862xx_vf_del_vid(struct mxl862xx_priv *priv,
|
||||
+ struct mxl862xx_vf_block *vf, u16 vid)
|
||||
+{
|
||||
@@ -1035,12 +923,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return ret;
|
||||
+ } else if (gap < last) {
|
||||
+ /* Swap: move the last ALLOW entry into the gap */
|
||||
+ last_ve = NULL;
|
||||
+ list_for_each_entry(last_ve, &vf->vids, list)
|
||||
+ if (last_ve->index == last)
|
||||
+ break;
|
||||
+
|
||||
+ if (WARN_ON(!last_ve || last_ve->index != last))
|
||||
+ if (WARN_ON(list_entry_is_head(last_ve, &vf->vids, list)))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = mxl862xx_vf_entry_set(priv, vf->block_id,
|
||||
@@ -1049,16 +936,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return ret;
|
||||
+
|
||||
+ last_ve->index = gap;
|
||||
+
|
||||
+ /* Plug the old last slot with DISCARD */
|
||||
+ ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ } else {
|
||||
+ /* Deleting the last entry -- just plug it */
|
||||
+ ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ list_del(&ve->list);
|
||||
@@ -1068,20 +945,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
|
||||
+ * @priv: driver private data
|
||||
+ * @port: port number
|
||||
+ *
|
||||
+ * In VLAN-aware mode the ingress EVLAN block handles PVID insertion for
|
||||
+ * untagged/priority-tagged frames, passes through standard 802.1Q
|
||||
+ * tagged frames for VF membership checking, and treats non-8021Q TPID
|
||||
+ * frames as untagged. The block is sized to exactly fit the 7 catchall
|
||||
+ * rules and is rewritten whenever PVID changes.
|
||||
+ *
|
||||
+ * In VLAN-unaware mode the firmware passes frames through unchanged when
|
||||
+ * no ingress block is assigned, so nothing is programmed.
|
||||
+ */
|
||||
+static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port)
|
||||
+{
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
@@ -1099,19 +962,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ p->pvid);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_evlan_program_egress - Reprogram all egress tag-stripping rules
|
||||
+ * @priv: driver private data
|
||||
+ * @port: port number
|
||||
+ *
|
||||
+ * Walks the port's VF VID list and writes 2 EVLAN rules per VID that
|
||||
+ * needs egress tag stripping. In VLAN-aware mode only untagged VIDs
|
||||
+ * need rules (tagged VIDs pass through EVLAN untouched). In unaware
|
||||
+ * mode every VID gets rules.
|
||||
+ *
|
||||
+ * Entries are packed starting at index 0, and the scan window
|
||||
+ * (n_active) is narrowed so stale entries beyond it are never matched.
|
||||
+ */
|
||||
+static int mxl862xx_evlan_program_egress(struct mxl862xx_priv *priv, int port)
|
||||
+{
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
@@ -1131,10 +981,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ }
|
||||
+
|
||||
+ list_for_each_entry(vfv, &p->vf.vids, list) {
|
||||
+ /* In VLAN-aware mode tagged-only VIDs need no EVLAN
|
||||
+ * rules -- VLAN Filter handles membership.
|
||||
+ */
|
||||
+ if (p->vlan_filtering && !vfv->untagged)
|
||||
+ if (!vfv->untagged)
|
||||
+ continue;
|
||||
+
|
||||
+ if (idx + n_vid > blk->block_size)
|
||||
@@ -1182,12 +1029,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
+ bool old_vlan_filtering = p->vlan_filtering;
|
||||
+ bool old_in_use = p->ingress_evlan.in_use;
|
||||
+ bool changed = (p->vlan_filtering != vlan_filtering);
|
||||
+ int ret;
|
||||
+
|
||||
+ p->vlan_filtering = vlan_filtering;
|
||||
+
|
||||
+ /* Reprogram Extended VLAN rules if filtering mode changed */
|
||||
+ if (changed) {
|
||||
+ /* When leaving VLAN-aware mode, release the ingress HW
|
||||
+ * block. The firmware passes frames through unchanged
|
||||
@@ -1199,17 +1047,20 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+
|
||||
+ ret = mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_restore;
|
||||
+
|
||||
+ ret = mxl862xx_evlan_program_egress(priv, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_restore;
|
||||
+ }
|
||||
+
|
||||
+ /* Push VLAN-based MAC learning flags and (possibly newly
|
||||
+ * allocated) ingress block to hardware.
|
||||
+ */
|
||||
+ return mxl862xx_set_bridge_port(ds, port);
|
||||
+
|
||||
+ /* No HW rollback -- restoring SW state is sufficient for a correct retry. */
|
||||
+err_restore:
|
||||
+ p->vlan_filtering = old_vlan_filtering;
|
||||
+ p->ingress_evlan.in_use = old_in_use;
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
|
||||
@@ -1255,21 +1106,32 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (pvid_changed) {
|
||||
+ ret = mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ if (ret)
|
||||
+ goto err_pvid;
|
||||
+ goto err_rollback;
|
||||
+ }
|
||||
+
|
||||
+ /* Reprogram egress tag-stripping rules (walks VF VID list) */
|
||||
+ ret = mxl862xx_evlan_program_egress(priv, port);
|
||||
+ if (ret)
|
||||
+ goto err_pvid;
|
||||
+ goto err_rollback;
|
||||
+
|
||||
+ /* Apply VLAN block IDs and MAC learning flags to bridge port */
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
+ goto err_pvid;
|
||||
+ goto err_rollback;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_rollback:
|
||||
+ /* Best-effort: undo VF add and restore consistent hardware state.
|
||||
+ * A retry of port_vlan_add will converge since vf_add_vid is
|
||||
+ * idempotent.
|
||||
+ */
|
||||
+ p->pvid = old_pvid;
|
||||
+ mxl862xx_vf_del_vid(priv, &p->vf, vid);
|
||||
+ mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ mxl862xx_evlan_program_egress(priv, port);
|
||||
+ mxl862xx_set_bridge_port(ds, port);
|
||||
+ return ret;
|
||||
+err_pvid:
|
||||
+ p->pvid = old_pvid;
|
||||
+ return ret;
|
||||
@@ -1280,13 +1142,22 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
+ u16 vid = vlan->vid;
|
||||
+ struct mxl862xx_vf_vid *ve;
|
||||
+ bool pvid_changed = false;
|
||||
+ u16 vid = vlan->vid;
|
||||
+ bool old_untagged;
|
||||
+ u16 old_pvid;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (dsa_is_cpu_port(ds, port))
|
||||
+ return 0;
|
||||
+
|
||||
+ ve = mxl862xx_vf_find_vid(&p->vf, vid);
|
||||
+ if (!ve)
|
||||
+ return 0;
|
||||
+ old_untagged = ve->untagged;
|
||||
+ old_pvid = p->pvid;
|
||||
+
|
||||
+ /* Clear PVID if we're deleting it */
|
||||
+ if (p->pvid == vid) {
|
||||
+ p->pvid = 0;
|
||||
@@ -1299,28 +1170,45 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ */
|
||||
+ ret = mxl862xx_vf_del_vid(priv, &p->vf, vid);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_pvid;
|
||||
+
|
||||
+ /* Reprogram egress tag-stripping rules (VID is now gone) */
|
||||
+ ret = mxl862xx_evlan_program_egress(priv, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_rollback;
|
||||
+
|
||||
+ /* If PVID changed, reprogram ingress finals */
|
||||
+ if (pvid_changed) {
|
||||
+ ret = mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_rollback;
|
||||
+ }
|
||||
+
|
||||
+ return mxl862xx_set_bridge_port(ds, port);
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
+ goto err_rollback;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_rollback:
|
||||
+ /* Best-effort: re-add the VID and restore consistent hardware
|
||||
+ * state. A retry of port_vlan_del will converge.
|
||||
+ */
|
||||
+ p->pvid = old_pvid;
|
||||
+ mxl862xx_vf_add_vid(priv, &p->vf, vid, old_untagged);
|
||||
+ mxl862xx_evlan_program_egress(priv, port);
|
||||
+ mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ mxl862xx_set_bridge_port(ds, port);
|
||||
+ return ret;
|
||||
+err_pvid:
|
||||
+ p->pvid = old_pvid;
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
struct dsa_port *dp;
|
||||
|
||||
- priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
|
||||
- priv->ports[port].learning = true;
|
||||
@@ -1332,41 +1220,21 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * assignment need to be configured.
|
||||
+ */
|
||||
|
||||
/* include all assigned user ports in the CPU portmap */
|
||||
- bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
dsa_switch_for_each_user_port(dp, ds) {
|
||||
/* it's safe to rely on cpu_dp being valid for user ports */
|
||||
if (dp->cpu_dp->index != port)
|
||||
continue;
|
||||
|
||||
- __set_bit(dp->index, priv->ports[port].portmap);
|
||||
+ __set_bit(dp->index, p->portmap);
|
||||
}
|
||||
|
||||
return mxl862xx_set_bridge_port(ds, port);
|
||||
}
|
||||
@@ -510,6 +1260,8 @@ static int mxl862xx_port_bridge_join(str
|
||||
static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||
const struct dsa_bridge bridge)
|
||||
{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
int err;
|
||||
|
||||
+
|
||||
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
|
||||
const struct dsa_bridge bridge,
|
||||
bool *tx_fwd_offload,
|
||||
@@ -553,6 +1417,22 @@ static void mxl862xx_port_bridge_leave(s
|
||||
bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
__set_bit(dp->cpu_dp->index, p->portmap);
|
||||
p->flood_block = 0;
|
||||
+
|
||||
+ /* Detach EVLAN and VF blocks from the bridge port BEFORE freeing
|
||||
+ * them. The firmware tracks a usage count per block and rejects
|
||||
+ * FREE while the count is non-zero.
|
||||
+ *
|
||||
+ * For EVLAN: setting in_use=false makes set_bridge_port send
|
||||
+ * enable=false, which decrements the firmware refcount.
|
||||
+ *
|
||||
+ * For VF: set_bridge_port sees dp->bridge == NULL (DSA already
|
||||
+ * cleared it) and sends vlan_filter_enable=0, which decrements
|
||||
+ * the firmware VF refcount.
|
||||
+ */
|
||||
err = mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
@@ -521,6 +1273,10 @@ static void mxl862xx_port_bridge_leave(s
|
||||
/* Revert leaving port, omitted by the sync above, to its
|
||||
* single-port bridge
|
||||
*/
|
||||
+ p->pvid = 0;
|
||||
+ p->ingress_evlan.in_use = false;
|
||||
+ p->egress_evlan.in_use = false;
|
||||
@@ -1374,28 +1242,22 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
err = mxl862xx_set_bridge_port(ds, port);
|
||||
if (err)
|
||||
dev_err(ds->dev,
|
||||
@@ -602,6 +1482,28 @@ static int mxl862xx_port_setup(struct ds
|
||||
@@ -585,6 +1341,22 @@ static int mxl862xx_port_setup(struct ds
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ /* Initialize and pre-allocate per-port EVLAN and VF blocks for
|
||||
+ * user ports. CPU ports do not use EVLAN or VF -- frames pass
|
||||
+ * through without processing. Pre-allocation avoids firmware
|
||||
+ * EVLAN table fragmentation and simplifies control flow.
|
||||
+ */
|
||||
+ mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan,
|
||||
+ priv->evlan_ingress_size);
|
||||
+ priv->ports[port].ingress_evlan.block_size = priv->evlan_ingress_size;
|
||||
+ ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan,
|
||||
+ priv->evlan_egress_size);
|
||||
+ priv->ports[port].egress_evlan.block_size = priv->evlan_egress_size;
|
||||
+ ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ mxl862xx_vf_init(&priv->ports[port].vf, priv->vf_block_size);
|
||||
+ priv->ports[port].vf.block_size = priv->vf_block_size;
|
||||
+ INIT_LIST_HEAD(&priv->ports[port].vf.vids);
|
||||
+ ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
@@ -1403,7 +1265,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
priv->ports[port].setup_done = true;
|
||||
|
||||
return 0;
|
||||
@@ -1012,6 +1914,9 @@ static const struct dsa_switch_ops mxl86
|
||||
@@ -983,6 +1755,9 @@ static const struct dsa_switch_ops mxl86
|
||||
.port_fdb_dump = mxl862xx_port_fdb_dump,
|
||||
.port_mdb_add = mxl862xx_port_mdb_add,
|
||||
.port_mdb_del = mxl862xx_port_mdb_del,
|
||||
@@ -1424,7 +1286,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
/* Number of __le16 words in a firmware portmap (128-bit bitmap). */
|
||||
#define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
|
||||
@@ -86,6 +88,66 @@ static inline bool mxl862xx_fw_portmap_i
|
||||
@@ -55,6 +57,66 @@ static inline bool mxl862xx_fw_portmap_i
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1491,7 +1353,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* struct mxl862xx_port - per-port state tracked by the driver
|
||||
* @priv: back-pointer to switch private data; needed by
|
||||
* deferred work handlers to access ds and priv
|
||||
@@ -101,6 +163,11 @@ static inline bool mxl862xx_fw_portmap_i
|
||||
@@ -68,6 +130,11 @@ static inline bool mxl862xx_fw_portmap_i
|
||||
* @setup_done: set at end of port_setup, cleared at start of
|
||||
* port_teardown; guards deferred work against
|
||||
* acting on torn-down state
|
||||
@@ -1503,11 +1365,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @host_flood_uc: desired host unicast flood state (true = flood);
|
||||
* updated atomically by port_set_host_flood, consumed
|
||||
* by the deferred host_flood_work
|
||||
@@ -119,6 +186,12 @@ struct mxl862xx_port {
|
||||
@@ -85,6 +152,11 @@ struct mxl862xx_port {
|
||||
unsigned long flood_block;
|
||||
bool learning;
|
||||
bool setup_done;
|
||||
+ /* VLAN state */
|
||||
+ u16 pvid;
|
||||
+ bool vlan_filtering;
|
||||
+ struct mxl862xx_vf_block vf;
|
||||
@@ -1516,7 +1377,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
bool host_flood_uc;
|
||||
bool host_flood_mc;
|
||||
struct work_struct host_flood_work;
|
||||
@@ -126,17 +199,23 @@ struct mxl862xx_port {
|
||||
@@ -92,17 +164,23 @@ struct mxl862xx_port {
|
||||
|
||||
/**
|
||||
* struct mxl862xx_priv - driver private data for an MxL862xx switch
|
||||
@@ -1551,7 +1412,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
*/
|
||||
struct mxl862xx_priv {
|
||||
struct dsa_switch *ds;
|
||||
@@ -146,6 +225,9 @@ struct mxl862xx_priv {
|
||||
@@ -112,6 +190,9 @@ struct mxl862xx_priv {
|
||||
u16 drop_meter;
|
||||
struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
|
||||
u16 bridges[MXL862XX_MAX_BRIDGES + 1];
|
||||
+56
-49
@@ -1,7 +1,7 @@
|
||||
From 0067d79d10becfc5779fb50d5c0ac152cc5dc303 Mon Sep 17 00:00:00 2001
|
||||
From e6295d124644b14a12b55edf5d3e89cf86a4a2ce Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Sun, 22 Mar 2026 00:57:33 +0000
|
||||
Subject: [PATCH 06/26] net: dsa: mxl862xx: add ethtool statistics support
|
||||
Date: Sun, 12 Apr 2026 01:01:57 +0100
|
||||
Subject: [PATCH 1/2] net: dsa: mxl862xx: add ethtool statistics support
|
||||
|
||||
The MxL862xx firmware exposes per-port RMON counters through the
|
||||
RMON_PORT_GET command, covering standard IEEE 802.3 MAC statistics
|
||||
@@ -16,11 +16,13 @@ legacy ethtool -S support. Implement .get_eth_mac_stats,
|
||||
IEEE 802.3 statistics interface.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/480be14d5ed51f3db7b1681b298044dbf8e87494.1775951347.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 142 ++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 142 +++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 3 +
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 168 ++++++++++++++++++++++++
|
||||
3 files changed, 313 insertions(+)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 173 ++++++++++++++++++++++++
|
||||
3 files changed, 318 insertions(+)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -120,7 +122,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * @tx_good_bytes: Transmitted good byte count (64-bit)
|
||||
+ */
|
||||
+struct mxl862xx_rmon_port_cnt {
|
||||
+ enum mxl862xx_port_type port_type;
|
||||
+ __le32 port_type; /* enum mxl862xx_port_type */
|
||||
+ __le16 port_id;
|
||||
+ __le16 sub_if_id_group;
|
||||
+ u8 pce_bypass;
|
||||
@@ -192,7 +194,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -30,6 +30,64 @@
|
||||
@@ -30,6 +30,38 @@
|
||||
#define MXL862XX_API_READ_QUIET(dev, cmd, data) \
|
||||
mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true)
|
||||
|
||||
@@ -209,60 +211,34 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ .offset = offsetof(struct mxl862xx_rmon_port_cnt, _element) \
|
||||
+}
|
||||
+
|
||||
+/* Hardware-specific counters not covered by any standardized stats callback. */
|
||||
+static const struct mxl862xx_mib_desc mxl862xx_mib[] = {
|
||||
+ MIB_DESC(1, "TxGoodPkts", tx_good_pkts),
|
||||
+ MIB_DESC(1, "TxUnicastPkts", tx_unicast_pkts),
|
||||
+ MIB_DESC(1, "TxBroadcastPkts", tx_broadcast_pkts),
|
||||
+ MIB_DESC(1, "TxMulticastPkts", tx_multicast_pkts),
|
||||
+ MIB_DESC(1, "Tx64BytePkts", tx64byte_pkts),
|
||||
+ MIB_DESC(1, "Tx127BytePkts", tx127byte_pkts),
|
||||
+ MIB_DESC(1, "Tx255BytePkts", tx255byte_pkts),
|
||||
+ MIB_DESC(1, "Tx511BytePkts", tx511byte_pkts),
|
||||
+ MIB_DESC(1, "Tx1023BytePkts", tx1023byte_pkts),
|
||||
+ MIB_DESC(1, "TxMaxBytePkts", tx_max_byte_pkts),
|
||||
+ MIB_DESC(1, "TxDroppedPkts", tx_dropped_pkts),
|
||||
+ MIB_DESC(1, "TxAcmDroppedPkts", tx_acm_dropped_pkts),
|
||||
+ MIB_DESC(2, "TxGoodBytes", tx_good_bytes),
|
||||
+ MIB_DESC(1, "TxSingleCollCount", tx_single_coll_count),
|
||||
+ MIB_DESC(1, "TxMultCollCount", tx_mult_coll_count),
|
||||
+ MIB_DESC(1, "TxLateCollCount", tx_late_coll_count),
|
||||
+ MIB_DESC(1, "TxExcessCollCount", tx_excess_coll_count),
|
||||
+ MIB_DESC(1, "TxCollCount", tx_coll_count),
|
||||
+ MIB_DESC(1, "TxPauseCount", tx_pause_count),
|
||||
+ MIB_DESC(1, "RxGoodPkts", rx_good_pkts),
|
||||
+ MIB_DESC(1, "RxUnicastPkts", rx_unicast_pkts),
|
||||
+ MIB_DESC(1, "RxBroadcastPkts", rx_broadcast_pkts),
|
||||
+ MIB_DESC(1, "RxMulticastPkts", rx_multicast_pkts),
|
||||
+ MIB_DESC(1, "RxFCSErrorPkts", rx_fcserror_pkts),
|
||||
+ MIB_DESC(1, "RxUnderSizeGoodPkts", rx_under_size_good_pkts),
|
||||
+ MIB_DESC(1, "RxOversizeGoodPkts", rx_oversize_good_pkts),
|
||||
+ MIB_DESC(1, "RxUnderSizeErrorPkts", rx_under_size_error_pkts),
|
||||
+ MIB_DESC(1, "RxOversizeErrorPkts", rx_oversize_error_pkts),
|
||||
+ MIB_DESC(1, "RxFilteredPkts", rx_filtered_pkts),
|
||||
+ MIB_DESC(1, "Rx64BytePkts", rx64byte_pkts),
|
||||
+ MIB_DESC(1, "Rx127BytePkts", rx127byte_pkts),
|
||||
+ MIB_DESC(1, "Rx255BytePkts", rx255byte_pkts),
|
||||
+ MIB_DESC(1, "Rx511BytePkts", rx511byte_pkts),
|
||||
+ MIB_DESC(1, "Rx1023BytePkts", rx1023byte_pkts),
|
||||
+ MIB_DESC(1, "RxMaxBytePkts", rx_max_byte_pkts),
|
||||
+ MIB_DESC(1, "RxDroppedPkts", rx_dropped_pkts),
|
||||
+ MIB_DESC(1, "RxExtendedVlanDiscardPkts", rx_extended_vlan_discard_pkts),
|
||||
+ MIB_DESC(1, "MtuExceedDiscardPkts", mtu_exceed_discard_pkts),
|
||||
+ MIB_DESC(2, "RxGoodBytes", rx_good_bytes),
|
||||
+ MIB_DESC(2, "RxBadBytes", rx_bad_bytes),
|
||||
+ MIB_DESC(1, "RxGoodPausePkts", rx_good_pause_pkts),
|
||||
+ MIB_DESC(1, "RxAlignErrorPkts", rx_align_error_pkts),
|
||||
+};
|
||||
+
|
||||
+static const struct ethtool_rmon_hist_range mxl862xx_rmon_ranges[] = {
|
||||
+ { 0, 64 },
|
||||
+ { 65, 127 },
|
||||
+ { 128, 255 },
|
||||
+ { 256, 511 },
|
||||
+ { 512, 1023 },
|
||||
+ { 1024, 10240 },
|
||||
+ {}
|
||||
+};
|
||||
+
|
||||
#define MXL862XX_SDMA_PCTRLP(p) (0xbc0 + ((p) * 0x6))
|
||||
#define MXL862XX_SDMA_PCTRL_EN BIT(0)
|
||||
|
||||
@@ -1893,6 +1951,110 @@ static int mxl862xx_port_bridge_flags(st
|
||||
@@ -1734,6 +1766,140 @@ static int mxl862xx_port_bridge_flags(st
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void mxl862xx_get_strings(struct dsa_switch *ds, int port,
|
||||
+ u32 stringset, u8 *data)
|
||||
+ u32 stringset, u8 *data)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
@@ -285,7 +261,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ struct mxl862xx_rmon_port_cnt *cnt)
|
||||
+{
|
||||
+ memset(cnt, 0, sizeof(*cnt));
|
||||
+ cnt->port_type = MXL862XX_CTP_PORT;
|
||||
+ cnt->port_type = cpu_to_le32(MXL862XX_CTP_PORT);
|
||||
+ cnt->port_id = cpu_to_le16(port);
|
||||
+
|
||||
+ return MXL862XX_API_READ(ds->priv, MXL862XX_RMON_PORT_GET, *cnt);
|
||||
@@ -365,10 +341,40 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts);
|
||||
+}
|
||||
+
|
||||
+static void mxl862xx_get_rmon_stats(struct dsa_switch *ds, int port,
|
||||
+ struct ethtool_rmon_stats *rmon_stats,
|
||||
+ const struct ethtool_rmon_hist_range **ranges)
|
||||
+{
|
||||
+ struct mxl862xx_rmon_port_cnt cnt;
|
||||
+
|
||||
+ if (mxl862xx_read_rmon(ds, port, &cnt))
|
||||
+ return;
|
||||
+
|
||||
+ rmon_stats->undersize_pkts = le32_to_cpu(cnt.rx_under_size_good_pkts);
|
||||
+ rmon_stats->oversize_pkts = le32_to_cpu(cnt.rx_oversize_good_pkts);
|
||||
+ rmon_stats->fragments = le32_to_cpu(cnt.rx_under_size_error_pkts);
|
||||
+ rmon_stats->jabbers = le32_to_cpu(cnt.rx_oversize_error_pkts);
|
||||
+
|
||||
+ rmon_stats->hist[0] = le32_to_cpu(cnt.rx64byte_pkts);
|
||||
+ rmon_stats->hist[1] = le32_to_cpu(cnt.rx127byte_pkts);
|
||||
+ rmon_stats->hist[2] = le32_to_cpu(cnt.rx255byte_pkts);
|
||||
+ rmon_stats->hist[3] = le32_to_cpu(cnt.rx511byte_pkts);
|
||||
+ rmon_stats->hist[4] = le32_to_cpu(cnt.rx1023byte_pkts);
|
||||
+ rmon_stats->hist[5] = le32_to_cpu(cnt.rx_max_byte_pkts);
|
||||
+
|
||||
+ rmon_stats->hist_tx[0] = le32_to_cpu(cnt.tx64byte_pkts);
|
||||
+ rmon_stats->hist_tx[1] = le32_to_cpu(cnt.tx127byte_pkts);
|
||||
+ rmon_stats->hist_tx[2] = le32_to_cpu(cnt.tx255byte_pkts);
|
||||
+ rmon_stats->hist_tx[3] = le32_to_cpu(cnt.tx511byte_pkts);
|
||||
+ rmon_stats->hist_tx[4] = le32_to_cpu(cnt.tx1023byte_pkts);
|
||||
+ rmon_stats->hist_tx[5] = le32_to_cpu(cnt.tx_max_byte_pkts);
|
||||
+
|
||||
+ *ranges = mxl862xx_rmon_ranges;
|
||||
+}
|
||||
static const struct dsa_switch_ops mxl862xx_switch_ops = {
|
||||
.get_tag_protocol = mxl862xx_get_tag_protocol,
|
||||
.setup = mxl862xx_setup,
|
||||
@@ -1917,6 +2079,12 @@ static const struct dsa_switch_ops mxl86
|
||||
@@ -1758,6 +1924,13 @@ static const struct dsa_switch_ops mxl86
|
||||
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
|
||||
.port_vlan_add = mxl862xx_port_vlan_add,
|
||||
.port_vlan_del = mxl862xx_port_vlan_del,
|
||||
@@ -378,6 +384,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ .get_eth_mac_stats = mxl862xx_get_eth_mac_stats,
|
||||
+ .get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
|
||||
+ .get_pause_stats = mxl862xx_get_pause_stats,
|
||||
+ .get_rmon_stats = mxl862xx_get_rmon_stats,
|
||||
};
|
||||
|
||||
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
|
||||
+60
-26
@@ -1,20 +1,22 @@
|
||||
From bab5a69e3872a693069e430a1fa0d2825ea83b4f Mon Sep 17 00:00:00 2001
|
||||
From a21d33a5265f0b31d935a8b9b2b6faefb5185911 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 04:14:38 +0000
|
||||
Subject: [PATCH 07/26] net: dsa: mxl862xx: implement .get_stats64
|
||||
Date: Sun, 12 Apr 2026 01:02:05 +0100
|
||||
Subject: [PATCH 2/2] net: dsa: mxl862xx: implement .get_stats64
|
||||
|
||||
Poll free-running firmware RMON counters every 2 seconds and accumulate
|
||||
deltas into 64-bit per-port statistics. 32-bit packet counters wrap
|
||||
in ~880s at 2.5 Gbps line rate; the 2s polling interval provides a
|
||||
comfortable margin. The .get_stats64 callback forces a fresh poll so
|
||||
that counters are always up to date when queried.
|
||||
in ~220s at 10 Gbps line rate with minimum-size frames; the 2s polling
|
||||
interval provides a comfortable margin. The .get_stats64 callback
|
||||
forces a fresh poll so that counters are always up to date when queried.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/fa38548ba05866879e8912721edc91947ce4ff12.1775951347.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-host.c | 8 +-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 174 +++++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 63 +++++++-
|
||||
3 files changed, 238 insertions(+), 7 deletions(-)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 175 +++++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 94 +++++++++++-
|
||||
3 files changed, 270 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
|
||||
@@ -69,7 +71,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
struct mxl862xx_mib_desc {
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
@@ -739,6 +745,9 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -677,6 +683,9 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -79,10 +81,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return mxl862xx_setup_mdio(ds);
|
||||
}
|
||||
|
||||
@@ -2055,6 +2064,158 @@ static void mxl862xx_get_pause_stats(str
|
||||
pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts);
|
||||
}
|
||||
@@ -1900,6 +1909,159 @@ static void mxl862xx_get_rmon_stats(stru
|
||||
|
||||
*ranges = mxl862xx_rmon_ranges;
|
||||
}
|
||||
+
|
||||
+/* Compute the delta between two 32-bit free-running counter snapshots,
|
||||
+ * handling a single wrap-around correctly via unsigned subtraction.
|
||||
+ */
|
||||
@@ -238,15 +241,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static const struct dsa_switch_ops mxl862xx_switch_ops = {
|
||||
.get_tag_protocol = mxl862xx_get_tag_protocol,
|
||||
.setup = mxl862xx_setup,
|
||||
@@ -2085,6 +2246,7 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_eth_mac_stats = mxl862xx_get_eth_mac_stats,
|
||||
@@ -1931,6 +2093,7 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
|
||||
.get_pause_stats = mxl862xx_get_pause_stats,
|
||||
.get_rmon_stats = mxl862xx_get_rmon_stats,
|
||||
+ .get_stats64 = mxl862xx_get_stats64,
|
||||
};
|
||||
|
||||
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
|
||||
@@ -2146,16 +2308,22 @@ static int mxl862xx_probe(struct mdio_de
|
||||
@@ -1992,16 +2155,22 @@ static int mxl862xx_probe(struct mdio_de
|
||||
priv->ports[i].priv = priv;
|
||||
INIT_WORK(&priv->ports[i].host_flood_work,
|
||||
mxl862xx_host_flood_work_fn);
|
||||
@@ -269,7 +272,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2170,6 +2338,9 @@ static void mxl862xx_remove(struct mdio_
|
||||
@@ -2016,6 +2185,9 @@ static void mxl862xx_remove(struct mdio_
|
||||
|
||||
priv = ds->priv;
|
||||
|
||||
@@ -279,7 +282,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
dsa_unregister_switch(ds);
|
||||
|
||||
mxl862xx_host_shutdown(priv);
|
||||
@@ -2196,6 +2367,9 @@ static void mxl862xx_shutdown(struct mdi
|
||||
@@ -2042,6 +2214,9 @@ static void mxl862xx_shutdown(struct mdi
|
||||
|
||||
dsa_switch_shutdown(ds);
|
||||
|
||||
@@ -291,11 +294,45 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
for (i = 0; i < MXL862XX_MAX_PORTS; i++)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -148,6 +148,47 @@ struct mxl862xx_evlan_block {
|
||||
@@ -117,6 +117,79 @@ struct mxl862xx_evlan_block {
|
||||
};
|
||||
|
||||
/**
|
||||
+ * struct mxl862xx_port_stats - 64-bit accumulated hardware port statistics
|
||||
+ * @rx_packets: total received packets
|
||||
+ * @tx_packets: total transmitted packets
|
||||
+ * @rx_bytes: total received bytes
|
||||
+ * @tx_bytes: total transmitted bytes
|
||||
+ * @rx_errors: total receive errors
|
||||
+ * @tx_errors: total transmit errors
|
||||
+ * @rx_dropped: total received packets dropped
|
||||
+ * @tx_dropped: total transmitted packets dropped
|
||||
+ * @multicast: total received multicast packets
|
||||
+ * @collisions: total transmit collisions
|
||||
+ * @rx_length_errors: received length errors (undersize + oversize)
|
||||
+ * @rx_crc_errors: received FCS errors
|
||||
+ * @rx_frame_errors: received alignment errors
|
||||
+ * @prev_rx_good_pkts: previous snapshot of rx good packet counter
|
||||
+ * @prev_tx_good_pkts: previous snapshot of tx good packet counter
|
||||
+ * @prev_rx_good_bytes: previous snapshot of rx good byte counter
|
||||
+ * @prev_tx_good_bytes: previous snapshot of tx good byte counter
|
||||
+ * @prev_rx_fcserror_pkts: previous snapshot of rx FCS error counter
|
||||
+ * @prev_rx_under_size_error_pkts: previous snapshot of rx undersize
|
||||
+ * error counter
|
||||
+ * @prev_rx_oversize_error_pkts: previous snapshot of rx oversize
|
||||
+ * error counter
|
||||
+ * @prev_rx_align_error_pkts: previous snapshot of rx alignment
|
||||
+ * error counter
|
||||
+ * @prev_tx_dropped_pkts: previous snapshot of tx dropped counter
|
||||
+ * @prev_rx_dropped_pkts: previous snapshot of rx dropped counter
|
||||
+ * @prev_rx_evlan_discard_pkts: previous snapshot of extended VLAN
|
||||
+ * discard counter
|
||||
+ * @prev_mtu_exceed_discard_pkts: previous snapshot of MTU exceed
|
||||
+ * discard counter
|
||||
+ * @prev_tx_acm_dropped_pkts: previous snapshot of tx ACM dropped
|
||||
+ * counter
|
||||
+ * @prev_rx_multicast_pkts: previous snapshot of rx multicast counter
|
||||
+ * @prev_tx_coll_count: previous snapshot of tx collision counter
|
||||
+ *
|
||||
+ * The firmware RMON counters are 32-bit free-running (64-bit for byte
|
||||
+ * counters). This structure holds 64-bit accumulators alongside the
|
||||
@@ -303,7 +340,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * handling 32-bit wrap correctly via unsigned subtraction.
|
||||
+ */
|
||||
+struct mxl862xx_port_stats {
|
||||
+ /* 64-bit accumulators */
|
||||
+ u64 rx_packets;
|
||||
+ u64 tx_packets;
|
||||
+ u64 rx_bytes;
|
||||
@@ -317,7 +353,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ u64 rx_length_errors;
|
||||
+ u64 rx_crc_errors;
|
||||
+ u64 rx_frame_errors;
|
||||
+ /* Previous raw RMON values for delta computation */
|
||||
+ u32 prev_rx_good_pkts;
|
||||
+ u32 prev_tx_good_pkts;
|
||||
+ u64 prev_rx_good_bytes;
|
||||
@@ -339,7 +374,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* struct mxl862xx_port - per-port state tracked by the driver
|
||||
* @priv: back-pointer to switch private data; needed by
|
||||
* deferred work handlers to access ds and priv
|
||||
@@ -178,6 +219,10 @@ struct mxl862xx_evlan_block {
|
||||
@@ -145,6 +218,10 @@ struct mxl862xx_evlan_block {
|
||||
* The worker acquires rtnl_lock() to serialize with
|
||||
* DSA callbacks and checks @setup_done to avoid
|
||||
* acting on torn-down ports.
|
||||
@@ -350,13 +385,12 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
*/
|
||||
struct mxl862xx_port {
|
||||
struct mxl862xx_priv *priv;
|
||||
@@ -195,16 +240,25 @@ struct mxl862xx_port {
|
||||
@@ -160,16 +237,24 @@ struct mxl862xx_port {
|
||||
bool host_flood_uc;
|
||||
bool host_flood_mc;
|
||||
struct work_struct host_flood_work;
|
||||
+ /* Hardware stats accumulation */
|
||||
+ struct mxl862xx_port_stats stats;
|
||||
+ spinlock_t stats_lock;
|
||||
+ spinlock_t stats_lock; /* protects stats accumulators */
|
||||
};
|
||||
|
||||
+/* Bit indices for struct mxl862xx_priv::flags */
|
||||
@@ -378,7 +412,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @drop_meter: index of the single shared zero-rate firmware meter
|
||||
* used to unconditionally drop traffic (used to block
|
||||
* flooding)
|
||||
@@ -216,18 +270,21 @@ struct mxl862xx_port {
|
||||
@@ -181,18 +266,21 @@ struct mxl862xx_port {
|
||||
* @evlan_ingress_size: per-port ingress Extended VLAN block size
|
||||
* @evlan_egress_size: per-port egress Extended VLAN block size
|
||||
* @vf_block_size: per-port VLAN Filter block size
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
From 0e4d7df2f3b2e59c1bccc09ea099b7a6c2dda886 Mon Sep 17 00:00:00 2001
|
||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||
Date: Wed, 28 Jan 2026 10:51:56 +0000
|
||||
Subject: [PATCH] net: phylink: fix NULL pointer deref in
|
||||
phylink_major_config()
|
||||
|
||||
When a MAC driver returns a PCS for an interface mode, and then we
|
||||
attempt to switch to a different mode that doesn't require a PCS,
|
||||
this causes phylink to oops:
|
||||
|
||||
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010
|
||||
Mem abort info:
|
||||
ESR = 0x0000000096000044
|
||||
EC = 0x25: DABT (current EL), IL = 32 bits
|
||||
SET = 0, FnV = 0
|
||||
EA = 0, S1PTW = 0
|
||||
FSC = 0x04: level 0 translation fault
|
||||
Data abort info:
|
||||
ISV = 0, ISS = 0x00000044, ISS2 = 0x00000000
|
||||
CM = 0, WnR = 1, TnD = 0, TagAccess = 0
|
||||
GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
|
||||
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000137f96000
|
||||
[0000000000000010] pgd=0000000000000000, p4d=0000000000000000
|
||||
Internal error: Oops: 0000000096000044 [#1] SMP
|
||||
Modules linked in: --
|
||||
CPU: 1 UID: 0 PID: 55 Comm: kworker/u33:0 Not tainted 6.19.0-rc5-00581-g73cb8467a63e #1 PREEMPT
|
||||
Hardware name: Qualcomm Technologies, Inc. Lemans Ride Rev3 (DT)
|
||||
Workqueue: events_power_efficient phylink_resolve
|
||||
pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS +BTYPE=--)
|
||||
pc : phylink_major_config+0x408/0x948
|
||||
lr : phylink_major_config+0x3fc/0x948
|
||||
sp : ffff800080353c60
|
||||
x29: ffff800080353cb0 x28: ffffb305068a8a00 x27: ffffb305068a8000
|
||||
x26: ffff000080092100 x25: 0000000000000000 x24: 0000000000000000
|
||||
x23: 0000000000000001 x22: 0000000000000000 x21: ffffb3050555b3d0
|
||||
x20: ffff800080353d10 x19: ffff0000b6059400 x18: 00000000ffffffff
|
||||
x17: 74756f2f79687020 x16: ffffb305045e4f18 x15: 6769666e6f632072
|
||||
x14: 6f6a616d203a3168 x13: 782d657361623030 x12: ffffb305068c6a98
|
||||
x11: 0000000000000583 x10: 0000000000000018 x9 : ffffb305068c6a98
|
||||
x8 : 0000000100006583 x7 : 0000000000000000 x6 : ffff00008083cc40
|
||||
x5 : ffff00008083cc40 x4 : 0000000000000001 x3 : 0000000000000001
|
||||
x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff0000b269e5a8
|
||||
Call trace:
|
||||
phylink_major_config+0x408/0x948 (P)
|
||||
phylink_resolve+0x294/0x6e4
|
||||
process_one_work+0x148/0x28c
|
||||
worker_thread+0x2d8/0x3d8
|
||||
kthread+0x134/0x208
|
||||
ret_from_fork+0x10/0x20
|
||||
Code: d63f0020 f9400e60 b4000040 f900081f (f9000ad3)
|
||||
---[ end trace 0000000000000000 ]---
|
||||
|
||||
This is caused by "pcs" being NULL when we attempt to execute:
|
||||
|
||||
pcs->phylink = pl;
|
||||
|
||||
Make this conditional on pcs being non-null.
|
||||
|
||||
Fixes: 486dc391ef43 ("net: phylink: allow mac_select_pcs() to remove a PCS")
|
||||
Reported-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
|
||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://patch.msgid.link/E1vl39Q-00000006utm-229h@rmk-PC.armlinux.org.uk
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -1287,7 +1287,8 @@ static void phylink_major_config(struct
|
||||
if (pl->pcs)
|
||||
pl->pcs->phylink = NULL;
|
||||
|
||||
- pcs->phylink = pl;
|
||||
+ if (pcs)
|
||||
+ pcs->phylink = pl;
|
||||
|
||||
pl->pcs = pcs;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
From 7bf588dc62a05c1866efe098e1b188fd879aa2cf Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Mon, 19 Jan 2026 14:19:51 +0200
|
||||
Subject: [PATCH] net: phylink: simplify phylink_resolve() ->
|
||||
phylink_major_config() path
|
||||
|
||||
This is a trivial change with no functional effect which replaces the
|
||||
pattern:
|
||||
|
||||
if (a) {
|
||||
if (b) {
|
||||
do_stuff();
|
||||
}
|
||||
}
|
||||
|
||||
with:
|
||||
|
||||
if (a && b) {
|
||||
do_stuff();
|
||||
};
|
||||
|
||||
The purpose is to reduce the delta of a subsequent functional change.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://patch.msgid.link/20260119121954.1624535-2-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 20 +++++++++-----------
|
||||
1 file changed, 9 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -1681,18 +1681,16 @@ static void phylink_resolve(struct work_
|
||||
if (pl->act_link_an_mode != MLO_AN_FIXED)
|
||||
phylink_apply_manual_flow(pl, &link_state);
|
||||
|
||||
- if (mac_config) {
|
||||
- if (link_state.interface != pl->link_config.interface) {
|
||||
- /* The interface has changed, force the link down and
|
||||
- * then reconfigure.
|
||||
- */
|
||||
- if (cur_link_state) {
|
||||
- phylink_link_down(pl);
|
||||
- cur_link_state = false;
|
||||
- }
|
||||
- phylink_major_config(pl, false, &link_state);
|
||||
- pl->link_config.interface = link_state.interface;
|
||||
+ if (mac_config && link_state.interface != pl->link_config.interface) {
|
||||
+ /* The interface has changed, so force the link down and then
|
||||
+ * reconfigure.
|
||||
+ */
|
||||
+ if (cur_link_state) {
|
||||
+ phylink_link_down(pl);
|
||||
+ cur_link_state = false;
|
||||
}
|
||||
+ phylink_major_config(pl, false, &link_state);
|
||||
+ pl->link_config.interface = link_state.interface;
|
||||
}
|
||||
|
||||
/* If configuration of the interface failed, force the link down
|
||||
+1
-2
@@ -29,7 +29,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
||||
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
||||
@@ -358,6 +358,15 @@ config DWMAC_LOONGSON
|
||||
@@ -358,6 +358,14 @@ config DWMAC_LOONGSON
|
||||
This selects the LOONGSON PCI bus support for the stmmac driver,
|
||||
Support for ethernet controller on Loongson-2K1000 SoC and LS7A1000 bridge.
|
||||
|
||||
@@ -37,7 +37,6 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
+ tristate "Motorcomm PCI DWMAC support"
|
||||
+ depends on PCI
|
||||
+ select MOTORCOMM_PHY
|
||||
+ select STMMAC_LIBPCI
|
||||
+ help
|
||||
+ This enables glue driver for Motorcomm DWMAC-based PCI Ethernet
|
||||
+ controllers. Currently only YT6801 is supported.
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
From 911e2c050963ccf239faec6ae9dee0f5e8f1cc5c Mon Sep 17 00:00:00 2001
|
||||
From: Jan Hoffmann <jan@3e8.eu>
|
||||
Date: Sun, 29 Mar 2026 21:11:11 +0200
|
||||
Subject: [PATCH] net: sfp: add quirk for ZOERAX SFP-2.5G-T
|
||||
|
||||
This is a 2.5G copper module which appears to be based on a Motorcomm
|
||||
YT8821 PHY. There doesn't seem to be a usable way to to access the PHY
|
||||
(I2C address 0x56 provides only read-only C22 access, and Rollball is
|
||||
also not working).
|
||||
|
||||
The module does not report the correct extended compliance code for
|
||||
2.5GBase-T, and instead claims to support SONET OC-48 and Fibre Channel:
|
||||
|
||||
Identifier : 0x03 (SFP)
|
||||
Extended identifier : 0x04 (GBIC/SFP defined by 2-wire interface ID)
|
||||
Connector : 0x07 (LC)
|
||||
Transceiver codes : 0x00 0x01 0x00 0x00 0x40 0x40 0x04 0x00 0x00
|
||||
Transceiver type : FC: Multimode, 50um (M5)
|
||||
Encoding : 0x05 (SONET Scrambled)
|
||||
BR Nominal : 2500MBd
|
||||
|
||||
Despite this, the kernel still enables the correct 2500Base-X interface
|
||||
mode. However, for the module to actually work, it is also necessary to
|
||||
disable inband auto-negotiation.
|
||||
|
||||
Enable the existing "sfp_quirk_oem_2_5g" for this module, which handles
|
||||
that and also sets the bit for 2500Base-T link mode.
|
||||
|
||||
Signed-off-by: Jan Hoffmann <jan@3e8.eu>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Link: https://patch.msgid.link/20260329191304.720160-1-jan@3e8.eu
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/sfp.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/sfp.c
|
||||
+++ b/drivers/net/phy/sfp.c
|
||||
@@ -567,6 +567,8 @@ static const struct sfp_quirk sfp_quirks
|
||||
SFP_QUIRK_F("Turris", "RTSFP-2.5G", sfp_fixup_rollball),
|
||||
SFP_QUIRK_F("Turris", "RTSFP-10", sfp_fixup_rollball),
|
||||
SFP_QUIRK_F("Turris", "RTSFP-10G", sfp_fixup_rollball),
|
||||
+
|
||||
+ SFP_QUIRK_S("ZOERAX", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
|
||||
};
|
||||
|
||||
static size_t sfp_strlen(const char *str, size_t maxlen)
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
From 95aca8602ef70ffd3d971675751c81826e124f90 Mon Sep 17 00:00:00 2001
|
||||
From: John Pavlick <jspavlick@posteo.net>
|
||||
Date: Mon, 6 Apr 2026 13:23:33 +0000
|
||||
Subject: [PATCH] net: sfp: add quirks for Hisense and HSGQ GPON ONT SFP
|
||||
modules
|
||||
|
||||
Several GPON ONT SFP sticks based on Realtek RTL960x report
|
||||
1000BASE-LX at 1300MBd in their EEPROM but can operate at 2500base-X.
|
||||
On hosts capable of 2500base-X (e.g. Banana Pi R3 / MT7986), the
|
||||
kernel negotiates only 1G because it trusts the incorrect EEPROM data.
|
||||
|
||||
Add quirks for:
|
||||
- Hisense-Leox LXT-010S-H
|
||||
- Hisense ZNID-GPON-2311NA
|
||||
- HSGQ HSGQ-XPON-Stick
|
||||
|
||||
Each quirk advertises 2500base-X and ignores TX_FAULT during the
|
||||
module's ~40s Linux boot time.
|
||||
|
||||
Tested on Banana Pi R3 (MT7986) with OpenWrt 25.12.1, confirmed
|
||||
2.5Gbps link and full throughput with flow offloading.
|
||||
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Suggested-by: Marcin Nita <marcin.nita@leolabs.pl>
|
||||
Signed-off-by: John Pavlick <jspavlick@posteo.net>
|
||||
Link: https://patch.msgid.link/20260406132321.72563-1-jspavlick@posteo.net
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/sfp.c | 16 ++++++++++++++++
|
||||
1 file changed, 16 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/sfp.c
|
||||
+++ b/drivers/net/phy/sfp.c
|
||||
@@ -538,6 +538,22 @@ static const struct sfp_quirk sfp_quirks
|
||||
SFP_QUIRK("HUAWEI", "MA5671A", sfp_quirk_2500basex,
|
||||
sfp_fixup_ignore_tx_fault_and_los),
|
||||
|
||||
+ // Hisense LXT-010S-H is a GPON ONT SFP (sold as LEOX LXT-010S-H) that
|
||||
+ // can operate at 2500base-X, but reports 1000BASE-LX / 1300MBd in its
|
||||
+ // EEPROM
|
||||
+ SFP_QUIRK("Hisense-Leox", "LXT-010S-H", sfp_quirk_2500basex,
|
||||
+ sfp_fixup_ignore_tx_fault),
|
||||
+
|
||||
+ // Hisense ZNID-GPON-2311NA can operate at 2500base-X, but reports
|
||||
+ // 1000BASE-LX / 1300MBd in its EEPROM
|
||||
+ SFP_QUIRK("Hisense", "ZNID-GPON-2311NA", sfp_quirk_2500basex,
|
||||
+ sfp_fixup_ignore_tx_fault),
|
||||
+
|
||||
+ // HSGQ HSGQ-XPON-Stick can operate at 2500base-X, but reports
|
||||
+ // 1000BASE-LX / 1300MBd in its EEPROM
|
||||
+ SFP_QUIRK("HSGQ", "HSGQ-XPON-Stick", sfp_quirk_2500basex,
|
||||
+ sfp_fixup_ignore_tx_fault),
|
||||
+
|
||||
// Lantech 8330-262D-E and 8330-265D can operate at 2500base-X, but
|
||||
// incorrectly report 2500MBd NRZ in their EEPROM.
|
||||
// Some 8330-265D modules have inverted LOS, while all of them report
|
||||
+6
-3
@@ -1,13 +1,16 @@
|
||||
From 3fd163f5bb88de426ca9847549f94b4296170ef0 Mon Sep 17 00:00:00 2001
|
||||
From e9abf1da0af3f787a03b249945e5ca726c1b8013 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Mon, 30 Mar 2026 23:40:53 +0100
|
||||
Date: Mon, 30 Mar 2026 23:52:09 +0100
|
||||
Subject: [PATCH] net: dsa: mxl862xx: cancel pending work on probe error
|
||||
|
||||
Call mxl862xx_host_shutdown() in case dsa_register_switch() returns
|
||||
an error, so any still pending crc_err_work get canceled.
|
||||
|
||||
Fixes: a319d0c8c8ced ("net: dsa: mxl862xx: add CRC for MDIO communication)"
|
||||
Fixes: a319d0c8c8ce ("net: dsa: mxl862xx: add CRC for MDIO communication")
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://patch.msgid.link/3fd163f5bb88de426ca9847549f94b4296170ef0.1774911025.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
+7
-5
@@ -1,7 +1,7 @@
|
||||
From cd698f1ae94c16499e2714b31dd6048e6f9f068d Mon Sep 17 00:00:00 2001
|
||||
From b0a79590d10847f190ed377d2664377d7068191d Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 17:54:11 +0000
|
||||
Subject: [PATCH 01/26] net: dsa: move dsa_bridge_ports() helper to dsa.h
|
||||
Date: Wed, 1 Apr 2026 14:34:30 +0100
|
||||
Subject: [PATCH 1/4] net: dsa: move dsa_bridge_ports() helper to dsa.h
|
||||
|
||||
The yt921x driver contains a helper to create a bitmap of ports
|
||||
which are members of a bridge.
|
||||
@@ -10,9 +10,11 @@ Move the helper as static inline function into dsa.h, so other driver
|
||||
can make use of it as well.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/4f8bbfce3e4e3a02064fc4dc366263136c6e0383.1775049897.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
include/net/dsa.h | 13 +++++++++++++
|
||||
1 file changed, 13 insertions(+)
|
||||
include/net/dsa.h | 13 +++++++++++++
|
||||
2 files changed, 13 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/include/net/dsa.h
|
||||
+++ b/include/net/dsa.h
|
||||
+5
-3
@@ -1,7 +1,7 @@
|
||||
From c161533e1605a7282563c139323a3913890fdb72 Mon Sep 17 00:00:00 2001
|
||||
From f259e08494c47c614ce7b6d3079d1e0d3f30ae66 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 17:54:41 +0000
|
||||
Subject: [PATCH 02/26] net: dsa: add bridge member iteration macro
|
||||
Date: Wed, 1 Apr 2026 14:34:42 +0100
|
||||
Subject: [PATCH 2/4] net: dsa: add bridge member iteration macro
|
||||
|
||||
Drivers that offload bridges need to iterate over the ports that are
|
||||
members of a given bridge, for example to rebuild per-port forwarding
|
||||
@@ -15,6 +15,8 @@ directly, and use it for the existing dsa_bridge_ports() inline
|
||||
helper.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/e7136aaa26773f39e805a00fe4ecf13cd2b83fc0.1775049897.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
include/net/dsa.h | 9 ++++++---
|
||||
1 file changed, 6 insertions(+), 3 deletions(-)
|
||||
+5
-3
@@ -1,7 +1,7 @@
|
||||
From 753efe27a9afee52c4ad42098a9b9278366d63cc Mon Sep 17 00:00:00 2001
|
||||
From 4250ff1640ea1ede99bfe02ca949acbcc6c0927f Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 17:54:52 +0000
|
||||
Subject: [PATCH 03/26] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
|
||||
Date: Wed, 1 Apr 2026 14:34:52 +0100
|
||||
Subject: [PATCH 3/4] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
|
||||
|
||||
The MxL862xx offloads bridge forwarding in hardware, so set
|
||||
dsa_default_offload_fwd_mark() to avoid duplicate forwarding of
|
||||
@@ -11,6 +11,8 @@ Link-local frames are directly trapped to the CPU port only, so don't
|
||||
set dsa_default_offload_fwd_mark() on those.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/e1161c90894ddc519c57dc0224b3a0f6bfa1d2d6.1775049897.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
net/dsa/tag_mxl862xx.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
+160
-179
@@ -1,7 +1,7 @@
|
||||
From ce0664ff8f75c3ab01101c3f0f8569924d948775 Mon Sep 17 00:00:00 2001
|
||||
From 340bdf984613c4a9241d678915e513824f5a9b19 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 17:55:08 +0000
|
||||
Subject: [PATCH 04/26] net: dsa: mxl862xx: implement bridge offloading
|
||||
Date: Wed, 1 Apr 2026 14:35:01 +0100
|
||||
Subject: [PATCH 4/4] net: dsa: mxl862xx: implement bridge offloading
|
||||
|
||||
Implement joining and leaving bridges as well as add, delete and dump
|
||||
operations on isolated FDBs, port MDB membership management, and
|
||||
@@ -24,9 +24,17 @@ dsa_switch_for_each_bridge_member().
|
||||
|
||||
As there are now more users of the BRIDGEPORT_CONFIG_SET API and the
|
||||
state of each port is cached locally, introduce a helper function
|
||||
mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) which is
|
||||
then used to replace the direct calls to the API in
|
||||
mxl862xx_setup_cpu_bridge() and mxl862xx_add_single_port_bridge().
|
||||
mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) which
|
||||
applies the cached per-port state to hardware. For standalone user
|
||||
ports (dp->bridge == NULL), it additionally resets the port to
|
||||
single-port bridge state: CPU-only portmap, learning and flooding
|
||||
disabled. The CPU port path sets its state explicitly before calling
|
||||
this helper and is therefore not affected by the reset.
|
||||
|
||||
Note that MASK_VLAN_BASED_MAC_LEARNING is intentionally absent from
|
||||
the firmware write mask. After mxl862xx_reset(), the firmware
|
||||
initialises all VLAN-based MAC learning fields to 0 (disabled), so
|
||||
SVL is the active mode by default without having to set it explicitly.
|
||||
|
||||
Note that there is no convenient way to control flooding on per-port
|
||||
level, so the driver is using a 0-rate QoS meter setup as a stopper in
|
||||
@@ -36,12 +44,14 @@ registers -- without that at least one 64-byte packet could still
|
||||
pass before the meter would change from 'yellow' into 'red' state.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/dd079180e2098e5f9626fcd149b9bad9a1b5a1b2.1775049897.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 ++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 +++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 20 +-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 743 ++++++++++++++++++++++--
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 133 +++++
|
||||
4 files changed, 1076 insertions(+), 45 deletions(-)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 736 ++++++++++++++++++++++--
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 99 ++++
|
||||
4 files changed, 1022 insertions(+), 58 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -384,7 +394,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
|
||||
int port,
|
||||
enum dsa_tag_protocol m)
|
||||
@@ -168,6 +182,213 @@ static int mxl862xx_setup_mdio(struct ds
|
||||
@@ -168,6 +182,199 @@ static int mxl862xx_setup_mdio(struct ds
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -479,11 +489,40 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
+ u16 bridge_id = dp->bridge ?
|
||||
+ priv->bridges[dp->bridge->num] : p->fid;
|
||||
+ struct dsa_port *member_dp;
|
||||
+ u16 bridge_id;
|
||||
+ bool enable;
|
||||
+ int i, idx;
|
||||
+
|
||||
+ if (!p->setup_done)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (dsa_port_is_cpu(dp)) {
|
||||
+ dsa_switch_for_each_user_port(member_dp, ds) {
|
||||
+ if (member_dp->cpu_dp->index != port)
|
||||
+ continue;
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ member_dp->index);
|
||||
+ }
|
||||
+ } else if (dp->bridge) {
|
||||
+ dsa_switch_for_each_bridge_member(member_dp, ds,
|
||||
+ dp->bridge->dev) {
|
||||
+ if (member_dp->index == port)
|
||||
+ continue;
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ member_dp->index);
|
||||
+ }
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ dp->cpu_dp->index);
|
||||
+ } else {
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ dp->cpu_dp->index);
|
||||
+ p->flood_block = 0;
|
||||
+ p->learning = false;
|
||||
+ }
|
||||
+
|
||||
+ bridge_id = dp->bridge ? priv->bridges[dp->bridge->num] : p->fid;
|
||||
+
|
||||
+ br_port_cfg.bridge_port_id = cpu_to_le16(port);
|
||||
+ br_port_cfg.bridge_id = cpu_to_le16(bridge_id);
|
||||
+ br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
|
||||
@@ -492,8 +531,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER);
|
||||
+ br_port_cfg.src_mac_learning_disable = !p->learning;
|
||||
+
|
||||
+ mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
|
||||
+ idx = mxl862xx_flood_meters[i];
|
||||
+ enable = !!(p->flood_block & BIT(idx));
|
||||
@@ -510,24 +547,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+static int mxl862xx_sync_bridge_members(struct dsa_switch *ds,
|
||||
+ const struct dsa_bridge *bridge)
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct dsa_port *dp, *member_dp;
|
||||
+ int port, err, ret = 0;
|
||||
+ struct dsa_port *dp;
|
||||
+ int ret = 0, err;
|
||||
+
|
||||
+ dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) {
|
||||
+ port = dp->index;
|
||||
+
|
||||
+ bitmap_zero(priv->ports[port].portmap,
|
||||
+ MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+
|
||||
+ dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) {
|
||||
+ if (member_dp->index != port)
|
||||
+ __set_bit(member_dp->index,
|
||||
+ priv->ports[port].portmap);
|
||||
+ }
|
||||
+ __set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
|
||||
+
|
||||
+ err = mxl862xx_set_bridge_port(ds, port);
|
||||
+ err = mxl862xx_set_bridge_port(ds, dp->index);
|
||||
+ if (err)
|
||||
+ ret = err;
|
||||
+ }
|
||||
@@ -535,7 +559,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
|
||||
+static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv)
|
||||
+{
|
||||
+ struct mxl862xx_bridge_alloc br_alloc = {};
|
||||
+ int ret;
|
||||
@@ -544,8 +568,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ *bridge_id = le16_to_cpu(br_alloc.bridge_id);
|
||||
+ return 0;
|
||||
+ return le16_to_cpu(br_alloc.bridge_id);
|
||||
+}
|
||||
+
|
||||
+static void mxl862xx_free_bridge(struct dsa_switch *ds,
|
||||
@@ -567,38 +590,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+
|
||||
+ priv->bridges[bridge->num] = 0;
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
|
||||
+{
|
||||
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = mxl862xx_allocate_bridge(priv, &priv->ports[port].fid);
|
||||
+ if (ret) {
|
||||
+ dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ priv->ports[port].learning = false;
|
||||
+ bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ __set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
|
||||
+
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Standalone ports should not flood unknown unicast or multicast
|
||||
+ * towards the CPU by default; only broadcast is needed initially.
|
||||
+ */
|
||||
+ return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
|
||||
+ false, false, true);
|
||||
+}
|
||||
+
|
||||
static int mxl862xx_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
@@ -181,6 +402,10 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -181,6 +388,10 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -609,66 +605,49 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return mxl862xx_setup_mdio(ds);
|
||||
}
|
||||
|
||||
@@ -260,66 +485,87 @@ static int mxl862xx_configure_sp_tag_pro
|
||||
@@ -260,99 +471,137 @@ static int mxl862xx_configure_sp_tag_pro
|
||||
|
||||
static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
|
||||
{
|
||||
- struct mxl862xx_bridge_port_config br_port_cfg = {};
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
- u16 bridge_port_map = 0;
|
||||
struct dsa_port *dp;
|
||||
- struct dsa_port *dp;
|
||||
|
||||
- /* CPU port bridge setup */
|
||||
- br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
|
||||
- MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING |
|
||||
- MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING);
|
||||
-
|
||||
+ priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
|
||||
+ priv->ports[port].learning = true;
|
||||
|
||||
- br_port_cfg.bridge_port_id = cpu_to_le16(port);
|
||||
- br_port_cfg.src_mac_learning_disable = false;
|
||||
- br_port_cfg.vlan_src_mac_vid_enable = true;
|
||||
- br_port_cfg.vlan_dst_mac_vid_enable = true;
|
||||
+ priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
|
||||
+ priv->ports[port].learning = true;
|
||||
|
||||
/* include all assigned user ports in the CPU portmap */
|
||||
+ bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
dsa_switch_for_each_user_port(dp, ds) {
|
||||
/* it's safe to rely on cpu_dp being valid for user ports */
|
||||
if (dp->cpu_dp->index != port)
|
||||
continue;
|
||||
|
||||
- bridge_port_map |= BIT(dp->index);
|
||||
+ __set_bit(dp->index, priv->ports[port].portmap);
|
||||
}
|
||||
- br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map);
|
||||
|
||||
- return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg);
|
||||
+ return mxl862xx_set_bridge_port(ds, port);
|
||||
}
|
||||
+}
|
||||
|
||||
-static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
|
||||
- /* include all assigned user ports in the CPU portmap */
|
||||
- dsa_switch_for_each_user_port(dp, ds) {
|
||||
- /* it's safe to rely on cpu_dp being valid for user ports */
|
||||
- if (dp->cpu_dp->index != port)
|
||||
- continue;
|
||||
+static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
|
||||
+ const struct dsa_bridge bridge,
|
||||
+ bool *tx_fwd_offload,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
{
|
||||
- struct mxl862xx_bridge_port_config br_port_cfg = {};
|
||||
- struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
- struct mxl862xx_bridge_alloc br_alloc = {};
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ u16 fw_id;
|
||||
int ret;
|
||||
+ int ret;
|
||||
|
||||
- ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc);
|
||||
- if (ret) {
|
||||
- dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
|
||||
- return ret;
|
||||
- bridge_port_map |= BIT(dp->index);
|
||||
+ if (!priv->bridges[bridge.num]) {
|
||||
+ ret = mxl862xx_allocate_bridge(priv, &fw_id);
|
||||
+ if (ret)
|
||||
+ ret = mxl862xx_allocate_bridge(priv);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ priv->bridges[bridge.num] = fw_id;
|
||||
+ priv->bridges[bridge.num] = ret;
|
||||
+
|
||||
+ /* Free bridge here on error, DSA rollback won't. */
|
||||
+ ret = mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
@@ -679,6 +658,32 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
- br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map);
|
||||
|
||||
- return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg);
|
||||
+ return mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
}
|
||||
|
||||
-static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
|
||||
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||
+ const struct dsa_bridge bridge)
|
||||
{
|
||||
- struct mxl862xx_bridge_port_config br_port_cfg = {};
|
||||
- struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
- struct mxl862xx_bridge_alloc br_alloc = {};
|
||||
- int ret;
|
||||
+ int err;
|
||||
|
||||
- ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc);
|
||||
- if (ret) {
|
||||
- dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
|
||||
- return ret;
|
||||
- }
|
||||
+ err = mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
+ if (err)
|
||||
+ dev_err(ds->dev,
|
||||
+ "failed to sync bridge members after port %d left: %pe\n",
|
||||
+ port, ERR_PTR(err));
|
||||
|
||||
- br_port_cfg.bridge_id = br_alloc.bridge_id;
|
||||
- br_port_cfg.bridge_port_id = cpu_to_le16(port);
|
||||
@@ -691,30 +696,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
- br_port_cfg.vlan_dst_mac_vid_enable = false;
|
||||
- /* As this function is only called for user ports it is safe to rely on
|
||||
- * cpu_dp being valid
|
||||
+ return mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
+}
|
||||
+
|
||||
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||
+ const struct dsa_bridge bridge)
|
||||
+{
|
||||
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
+ int err;
|
||||
+
|
||||
+ err = mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
+ if (err)
|
||||
+ dev_err(ds->dev,
|
||||
+ "failed to sync bridge members after port %d left: %pe\n",
|
||||
+ port, ERR_PTR(err));
|
||||
+
|
||||
+ /* Revert leaving port, omitted by the sync above, to its
|
||||
+ * single-port bridge
|
||||
*/
|
||||
- br_port_cfg.bridge_port_map[0] = cpu_to_le16(BIT(dp->cpu_dp->index));
|
||||
+ bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ __set_bit(dp->cpu_dp->index, p->portmap);
|
||||
+ p->flood_block = 0;
|
||||
+ err = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (err)
|
||||
+ dev_err(ds->dev,
|
||||
@@ -732,12 +717,54 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
bool is_cpu_port = dsa_port_is_cpu(dp);
|
||||
int ret;
|
||||
@@ -352,7 +598,31 @@ static int mxl862xx_port_setup(struct ds
|
||||
|
||||
- /* disable port and flush MAC entries */
|
||||
ret = mxl862xx_port_state(ds, port, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mxl862xx_port_fast_age(ds, port);
|
||||
|
||||
- /* skip setup for unused and DSA ports */
|
||||
if (dsa_port_is_unused(dp) ||
|
||||
dsa_port_is_dsa(dp))
|
||||
return 0;
|
||||
|
||||
- /* configure tag protocol */
|
||||
ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- /* assign CTP port IDs */
|
||||
ret = mxl862xx_configure_ctp_port(ds, port, port,
|
||||
is_cpu_port ? 32 - port : 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (is_cpu_port)
|
||||
- /* assign user ports to CPU port bridge */
|
||||
return mxl862xx_setup_cpu_bridge(ds, port);
|
||||
|
||||
/* setup single-port bridge for user ports */
|
||||
- /* setup single-port bridge for user ports */
|
||||
- return mxl862xx_add_single_port_bridge(ds, port);
|
||||
+ ret = mxl862xx_add_single_port_bridge(ds, port);
|
||||
+ /* setup single-port bridge for user ports.
|
||||
+ * If this fails, the FID is leaked -- but the port then transitions
|
||||
+ * to unused, and the FID pool is sized to tolerate this.
|
||||
+ */
|
||||
+ ret = mxl862xx_allocate_bridge(priv);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ priv->ports[port].fid = ret;
|
||||
+ /* Standalone ports should not flood unknown unicast or multicast
|
||||
+ * towards the CPU by default; only broadcast is needed initially.
|
||||
+ */
|
||||
+ ret = mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
|
||||
+ false, false, true);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
@@ -765,7 +792,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
}
|
||||
|
||||
static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
|
||||
@@ -365,14 +635,383 @@ static void mxl862xx_phylink_get_caps(st
|
||||
@@ -365,14 +614,371 @@ static void mxl862xx_phylink_get_caps(st
|
||||
config->supported_interfaces);
|
||||
}
|
||||
|
||||
@@ -876,7 +903,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (fid < 0)
|
||||
+ return fid;
|
||||
+
|
||||
+ /* Look up existing entry by {MAC, FID, TCI} */
|
||||
+ ether_addr_copy(qparam.mac, mdb->addr);
|
||||
+ qparam.fid = cpu_to_le16(fid);
|
||||
+ qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
|
||||
@@ -892,7 +918,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ aparam.static_entry = true;
|
||||
+ aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG);
|
||||
+
|
||||
+ /* Merge with existing portmap if entry already exists */
|
||||
+ if (qparam.found)
|
||||
+ memcpy(aparam.port_map, qparam.port_map,
|
||||
+ sizeof(aparam.port_map));
|
||||
@@ -915,7 +940,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (fid < 0)
|
||||
+ return fid;
|
||||
+
|
||||
+ /* Look up existing entry */
|
||||
+ qparam.fid = cpu_to_le16(fid);
|
||||
+ qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
|
||||
+ ether_addr_copy(qparam.mac, mdb->addr);
|
||||
@@ -930,7 +954,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ mxl862xx_fw_portmap_clear_bit(qparam.port_map, port);
|
||||
+
|
||||
+ if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) {
|
||||
+ /* No ports left -- remove the entry entirely */
|
||||
+ rparam.fid = cpu_to_le16(fid);
|
||||
+ rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
|
||||
+ ether_addr_copy(rparam.mac, mdb->addr);
|
||||
@@ -1007,8 +1030,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * LEARNING or FORWARDING state (per 802.1D defaults).
|
||||
+ * Re-apply the driver's intended learning and metering config so that
|
||||
+ * standalone ports keep learning disabled.
|
||||
+ * This is likely to get fixed in future firmware releases, however,
|
||||
+ * the additional API call even then doesn't hurt much.
|
||||
+ */
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
@@ -1031,8 +1052,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ host_flood_work);
|
||||
+ struct mxl862xx_priv *priv = p->priv;
|
||||
+ struct dsa_switch *ds = priv->ds;
|
||||
+ int port = p - priv->ports;
|
||||
+ bool uc, mc;
|
||||
+
|
||||
+ rtnl_lock();
|
||||
+
|
||||
@@ -1042,17 +1061,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ uc = p->host_flood_uc;
|
||||
+ mc = p->host_flood_mc;
|
||||
+
|
||||
+ /* The hardware controls unknown-unicast/multicast forwarding per FID
|
||||
+ * (bridge), not per source port. For bridged ports all members share
|
||||
+ * one FID, so we cannot selectively suppress flooding to the CPU for
|
||||
+ * one source port while allowing it for another. Silently ignore the
|
||||
+ * request -- the excess flooding towards the CPU is harmless.
|
||||
+ /* Always write to the standalone FID. When standalone it takes effect
|
||||
+ * immediately; when bridged the port uses the shared bridge FID so the
|
||||
+ * write is a no-op for current forwarding, but the state is preserved
|
||||
+ * in hardware and is ready once the port returns to standalone.
|
||||
+ */
|
||||
+ if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
|
||||
+ mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true);
|
||||
+ mxl862xx_bridge_config_fwd(ds, p->fid, p->host_flood_uc,
|
||||
+ p->host_flood_mc, true);
|
||||
+
|
||||
+ rtnl_unlock();
|
||||
+}
|
||||
@@ -1115,7 +1130,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (flags.mask & BR_LEARNING)
|
||||
+ priv->ports[port].learning = !!(flags.val & BR_LEARNING);
|
||||
+
|
||||
+ if ((block != old_block) || (flags.mask & BR_LEARNING)) {
|
||||
+ if (block != old_block || (flags.mask & BR_LEARNING)) {
|
||||
+ priv->ports[port].flood_block = block;
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
@@ -1149,7 +1164,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
};
|
||||
|
||||
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
|
||||
@@ -407,7 +1046,7 @@ static int mxl862xx_probe(struct mdio_de
|
||||
@@ -407,7 +1013,7 @@ static int mxl862xx_probe(struct mdio_de
|
||||
struct device *dev = &mdiodev->dev;
|
||||
struct mxl862xx_priv *priv;
|
||||
struct dsa_switch *ds;
|
||||
@@ -1158,7 +1173,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@@ -425,14 +1064,25 @@ static int mxl862xx_probe(struct mdio_de
|
||||
@@ -425,14 +1031,25 @@ static int mxl862xx_probe(struct mdio_de
|
||||
ds->ops = &mxl862xx_switch_ops;
|
||||
ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops;
|
||||
ds->num_ports = MXL862XX_MAX_PORTS;
|
||||
@@ -1186,7 +1201,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -440,6 +1090,7 @@ static void mxl862xx_remove(struct mdio_
|
||||
@@ -440,6 +1057,7 @@ static void mxl862xx_remove(struct mdio_
|
||||
{
|
||||
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
|
||||
struct mxl862xx_priv *priv;
|
||||
@@ -1194,7 +1209,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
if (!ds)
|
||||
return;
|
||||
@@ -449,12 +1100,21 @@ static void mxl862xx_remove(struct mdio_
|
||||
@@ -449,12 +1067,21 @@ static void mxl862xx_remove(struct mdio_
|
||||
dsa_unregister_switch(ds);
|
||||
|
||||
mxl862xx_host_shutdown(priv);
|
||||
@@ -1216,7 +1231,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
if (!ds)
|
||||
return;
|
||||
@@ -465,6 +1125,9 @@ static void mxl862xx_shutdown(struct mdi
|
||||
@@ -465,6 +1092,9 @@ static void mxl862xx_shutdown(struct mdi
|
||||
|
||||
mxl862xx_host_shutdown(priv);
|
||||
|
||||
@@ -1228,7 +1243,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -4,15 +4,148 @@
|
||||
@@ -4,15 +4,114 @@
|
||||
#define __MXL862XX_H
|
||||
|
||||
#include <linux/mdio.h>
|
||||
@@ -1246,37 +1261,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+#define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_fw_portmap_from_bitmap - convert a kernel bitmap to a firmware
|
||||
+ * portmap (__le16[8])
|
||||
+ * @dst: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
|
||||
+ * @src: kernel bitmap of at least MXL862XX_MAX_BRIDGE_PORTS bits
|
||||
+ */
|
||||
+static inline void
|
||||
+mxl862xx_fw_portmap_from_bitmap(__le16 *dst, const unsigned long *src)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++)
|
||||
+ dst[i] = cpu_to_le16(bitmap_read(src, i * 16, 16));
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_fw_portmap_to_bitmap - convert a firmware portmap (__le16[8]) to
|
||||
+ * a kernel bitmap
|
||||
+ * @dst: kernel bitmap of at least MXL862XX_MAX_BRIDGE_PORTS bits
|
||||
+ * @src: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
|
||||
+ */
|
||||
+static inline void
|
||||
+mxl862xx_fw_portmap_to_bitmap(unsigned long *dst, const __le16 *src)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ bitmap_zero(dst, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++)
|
||||
+ bitmap_write(dst, le16_to_cpu(src[i]), i * 16, 16);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_fw_portmap_set_bit - set a single port bit in a firmware portmap
|
||||
+ * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
|
||||
+ * @port: port index (0..MXL862XX_MAX_BRIDGE_PORTS-1)
|
||||
@@ -1320,8 +1304,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * @fid: firmware FID for the permanent single-port bridge;
|
||||
+ * kept alive for the lifetime of the port so traffic is
|
||||
+ * never forwarded while the port is unbridged
|
||||
+ * @portmap: bitmap of switch port indices that share the current
|
||||
+ * bridge with this port
|
||||
+ * @flood_block: bitmask of firmware meter indices that are currently
|
||||
+ * rate-limiting flood traffic on this port (zero-rate
|
||||
+ * meters used to block flooding)
|
||||
@@ -1343,7 +1325,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+struct mxl862xx_port {
|
||||
+ struct mxl862xx_priv *priv;
|
||||
+ u16 fid;
|
||||
+ DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ unsigned long flood_block;
|
||||
+ bool learning;
|
||||
+ bool setup_done;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
From 3a4056ec7ec8f71ae9722f86d3cfbc4589deeac4 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 7 Apr 2026 18:30:27 +0100
|
||||
Subject: [PATCH 1/2] net: dsa: mxl862xx: reject DSA_PORT_TYPE_DSA
|
||||
|
||||
DSA links aren't supported by the mxl862xx driver.
|
||||
|
||||
Instead of returning early from .port_setup when called for
|
||||
DSA_PORT_TYPE_DSA ports rather return -EOPNOTSUPP and show an error
|
||||
message.
|
||||
|
||||
The desired side-effect is that the framework will switch the port to
|
||||
DSA_PORT_TYPE_UNUSED, so we can stop caring about DSA_PORT_TYPE_DSA in
|
||||
all other places.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/b686f3a22d8a6e7d470e7aa98da811a996a229b9.1775581804.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -544,10 +544,14 @@ static int mxl862xx_port_setup(struct ds
|
||||
|
||||
mxl862xx_port_fast_age(ds, port);
|
||||
|
||||
- if (dsa_port_is_unused(dp) ||
|
||||
- dsa_port_is_dsa(dp))
|
||||
+ if (dsa_port_is_unused(dp))
|
||||
return 0;
|
||||
|
||||
+ if (dsa_port_is_dsa(dp)) {
|
||||
+ dev_err(ds->dev, "port %d: DSA links not supported\n", port);
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+
|
||||
ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -591,7 +595,7 @@ static void mxl862xx_port_teardown(struc
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
|
||||
- if (dsa_port_is_unused(dp) || dsa_port_is_dsa(dp))
|
||||
+ if (dsa_port_is_unused(dp))
|
||||
return;
|
||||
|
||||
/* Prevent deferred host_flood_work from acting on stale state.
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
From 71934b9e6f36b1786bd969c0e1d2de8f9bd65f0f Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 7 Apr 2026 18:30:35 +0100
|
||||
Subject: [PATCH 2/2] net: dsa: mxl862xx: don't skip early bridge port
|
||||
configuration
|
||||
|
||||
mxl862xx_bridge_port_set() is currently guarded by the
|
||||
mxl8622_port->setup_done flag, as the early call to
|
||||
mxl862xx_bridge_port_set() from mxl862xx_port_stp_state_set() would
|
||||
otherwise cause a NULL-pointer dereference on unused ports which don't
|
||||
have dp->cpu_dp despite not being a CPU port.
|
||||
|
||||
Using the setup_done flag (which is never set for unused ports),
|
||||
however, also prevents mxl862xx_bridge_port_set() from configuring
|
||||
user ports' single-port bridges early, which was unintended.
|
||||
|
||||
Fix this by returning early from mxl862xx_bridge_port_set() in case
|
||||
dsa_port_is_unused().
|
||||
|
||||
Fixes: 340bdf984613c ("net: dsa: mxl862xx: implement bridge offloading")
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/15962aac29ebe0a6eb77565451acff880c41ef33.1775581804.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -278,7 +278,7 @@ static int mxl862xx_set_bridge_port(stru
|
||||
bool enable;
|
||||
int i, idx;
|
||||
|
||||
- if (!p->setup_done)
|
||||
+ if (dsa_port_is_unused(dp))
|
||||
return 0;
|
||||
|
||||
if (dsa_port_is_cpu(dp)) {
|
||||
+116
-255
@@ -1,7 +1,7 @@
|
||||
From 0d88d02cc9dccad01ff88f54e1beee867107b942 Mon Sep 17 00:00:00 2001
|
||||
From d587f9b6dcc98c1e8aeb5c189a7bfac60d6d29ac Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 10 Mar 2026 02:36:00 +0000
|
||||
Subject: [PATCH 05/26] net: dsa: mxl862xx: implement VLAN functionality
|
||||
Date: Tue, 7 Apr 2026 18:31:01 +0100
|
||||
Subject: [PATCH] net: dsa: mxl862xx: implement VLAN functionality
|
||||
|
||||
Add VLAN support using both the Extended VLAN (EVLAN) engine and the
|
||||
VLAN Filter (VF) engine in a hybrid architecture that allows a higher
|
||||
@@ -16,8 +16,8 @@ egress rules at all, so they consume only a VF entry.
|
||||
Both engines draw from shared 1024-entry hardware pools. The VF pool
|
||||
is divided equally among user ports for VID membership, while the
|
||||
EVLAN pool is partitioned into small fixed-size ingress blocks (7
|
||||
entries of catchall rules per port) and variable-size egress blocks
|
||||
for tag stripping.
|
||||
entries of catchall rules per port) and fixed-size egress blocks for
|
||||
tag stripping.
|
||||
|
||||
With 5 user ports this yields up to 204 VIDs per port (limited by VF),
|
||||
of which up to 98 can be untagged (limited by EVLAN egress budget).
|
||||
@@ -34,12 +34,14 @@ rather than worst-case pre-allocation, or by sharing EVLAN egress and
|
||||
VLAN Filter blocks across ports with identical VID sets.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/9be29637675342b109a85fa08f5378800d9f7b78.1775581804.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 +++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 ++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 12 +
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 915 +++++++++++++++++++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 104 ++-
|
||||
4 files changed, 1344 insertions(+), 16 deletions(-)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 781 +++++++++++++++++++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 103 +++-
|
||||
4 files changed, 1211 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -101,14 +103,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+/**
|
||||
+ * enum mxl862xx_extended_vlan_treatment_priority - Treatment priority mode
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL: Use explicit value
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY: Copy from inner tag
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY: Copy from outer tag
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY: Copy from inner tag
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY: Copy from outer tag
|
||||
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP: Derive from DSCP
|
||||
+ */
|
||||
+enum mxl862xx_extended_vlan_treatment_priority {
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY = 1,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY = 2,
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3,
|
||||
+};
|
||||
+
|
||||
@@ -409,16 +411,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -50,6 +50,88 @@ static const int mxl862xx_flood_meters[]
|
||||
@@ -50,6 +50,85 @@ static const int mxl862xx_flood_meters[]
|
||||
MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST,
|
||||
};
|
||||
|
||||
+enum mxl862xx_evlan_action {
|
||||
+ EVLAN_ACCEPT, /* pass-through, no tag removal */
|
||||
+ EVLAN_STRIP_IF_UNTAGGED, /* remove 1 tag if entry's untagged flag set */
|
||||
+ EVLAN_DISCARD, /* discard upstream */
|
||||
+ EVLAN_PVID_OR_DISCARD, /* insert PVID tag or discard if no PVID */
|
||||
+ EVLAN_PVID_OR_PASS, /* insert PVID tag or pass-through */
|
||||
+ EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */
|
||||
+};
|
||||
+
|
||||
@@ -457,7 +457,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * regardless of TPID, so without the ACCEPT guard, it would also
|
||||
+ * catch standard 802.1Q VID>0 frames and corrupt them. With the
|
||||
+ * guard, 802.1Q VID>0 frames match the ACCEPT rules first and
|
||||
+ * pass through untouched; only non-8021Q TPID frames fall through
|
||||
+ * pass through untouched; only non-8021Q TPID frames pass through
|
||||
+ * to the NO_FILTER catchalls.
|
||||
+ */
|
||||
+static const struct mxl862xx_evlan_rule_desc ingress_aware_final[] = {
|
||||
@@ -486,27 +486,26 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * VID-specific accept rules for VLAN-unaware egress.
|
||||
+ * The HW sees the MxL tag as outer, real VLAN tag as inner.
|
||||
+ * match on inner VID with outer=NO_FILTER.
|
||||
+ * Egress tag-stripping rules for VLAN-unaware mode (2 per untagged VID).
|
||||
+ * The HW sees the MxL tag as outer; the real VLAN tag, if any, is inner.
|
||||
+ */
|
||||
+static const struct mxl862xx_evlan_rule_desc vid_accept_egress_unaware[] = {
|
||||
+ { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
|
||||
+ { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED },
|
||||
+ { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
|
||||
+ { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_STRIP_IF_UNTAGGED },
|
||||
+};
|
||||
+
|
||||
static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
|
||||
int port,
|
||||
enum dsa_tag_protocol m)
|
||||
@@ -275,6 +357,7 @@ static int mxl862xx_set_bridge_port(stru
|
||||
@@ -275,6 +354,7 @@ static int mxl862xx_set_bridge_port(stru
|
||||
struct mxl862xx_port *p = &priv->ports[port];
|
||||
u16 bridge_id = dp->bridge ?
|
||||
priv->bridges[dp->bridge->num] : p->fid;
|
||||
struct dsa_port *member_dp;
|
||||
u16 bridge_id;
|
||||
+ u16 vf_scan;
|
||||
bool enable;
|
||||
int i, idx;
|
||||
|
||||
@@ -283,9 +366,69 @@ static int mxl862xx_set_bridge_port(stru
|
||||
@@ -312,9 +392,69 @@ static int mxl862xx_set_bridge_port(stru
|
||||
br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
|
||||
MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
|
||||
MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING |
|
||||
@@ -574,23 +573,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ br_port_cfg.vlan_src_mac_vid_enable = p->vlan_filtering;
|
||||
+ br_port_cfg.vlan_dst_mac_vid_enable = p->vlan_filtering;
|
||||
+
|
||||
mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
|
||||
@@ -329,6 +472,91 @@ static int mxl862xx_sync_bridge_members(
|
||||
idx = mxl862xx_flood_meters[i];
|
||||
enable = !!(p->flood_block & BIT(idx));
|
||||
@@ -343,6 +483,72 @@ static int mxl862xx_sync_bridge_members(
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static void mxl862xx_evlan_block_init(struct mxl862xx_evlan_block *blk,
|
||||
+ u16 size)
|
||||
+{
|
||||
+ blk->allocated = false;
|
||||
+ blk->in_use = false;
|
||||
+ blk->block_id = 0;
|
||||
+ blk->block_size = size;
|
||||
+ blk->n_active = 0;
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_evlan_block_alloc(struct mxl862xx_priv *priv,
|
||||
+ struct mxl862xx_evlan_block *blk)
|
||||
+{
|
||||
@@ -609,15 +598,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mxl862xx_vf_init(struct mxl862xx_vf_block *vf, u16 size)
|
||||
+{
|
||||
+ vf->allocated = false;
|
||||
+ vf->block_id = 0;
|
||||
+ vf->block_size = size;
|
||||
+ vf->active_count = 0;
|
||||
+ INIT_LIST_HEAD(&vf->vids);
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv,
|
||||
+ u16 size, u16 *block_id)
|
||||
+{
|
||||
@@ -666,10 +646,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
|
||||
+}
|
||||
+
|
||||
static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
|
||||
static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv)
|
||||
{
|
||||
struct mxl862xx_bridge_alloc br_alloc = {};
|
||||
@@ -392,6 +620,9 @@ static int mxl862xx_add_single_port_brid
|
||||
@@ -378,6 +584,9 @@ static void mxl862xx_free_bridge(struct
|
||||
static int mxl862xx_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
@@ -679,7 +659,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
int ret;
|
||||
|
||||
ret = mxl862xx_reset(priv);
|
||||
@@ -402,6 +633,50 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -388,6 +597,50 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -695,7 +675,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * through without EVLAN processing.
|
||||
+ *
|
||||
+ * Total EVLAN budget:
|
||||
+ * n_user_ports * (ingress + egress) ≤ 1024.
|
||||
+ * n_user_ports * (ingress + egress) <= 1024.
|
||||
+ * Ingress blocks are small (7 entries), so almost all capacity
|
||||
+ * goes to egress VID rules.
|
||||
+ */
|
||||
@@ -730,23 +710,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
ret = mxl862xx_setup_drop_meter(ds);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -483,27 +758,616 @@ static int mxl862xx_configure_sp_tag_pro
|
||||
@@ -469,12 +722,509 @@ static int mxl862xx_configure_sp_tag_pro
|
||||
return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
|
||||
+ * @priv: driver private data
|
||||
+ * @block_id: HW Extended VLAN block ID
|
||||
+ * @entry_index: entry index within the block
|
||||
+ * @desc: rule descriptor (filter type + action)
|
||||
+ * @vid: VLAN ID for VID-specific rules (ignored when !desc->match_vid)
|
||||
+ * @untagged: strip tag on egress for EVLAN_STRIP_IF_UNTAGGED action
|
||||
+ * @pvid: port VLAN ID for PVID insertion rules (0 = no PVID)
|
||||
+ *
|
||||
+ * Translates a compact rule descriptor into a full firmware
|
||||
+ * mxl862xx_extendedvlan_config struct and writes it via the API.
|
||||
+ */
|
||||
+static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
|
||||
+ u16 block_id, u16 entry_index,
|
||||
+ const struct mxl862xx_evlan_rule_desc *desc,
|
||||
@@ -788,11 +755,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
|
||||
+ break;
|
||||
+
|
||||
+ case EVLAN_DISCARD:
|
||||
+ cfg.treatment.remove_tag =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
|
||||
+ break;
|
||||
+
|
||||
+ case EVLAN_PVID_OR_DISCARD:
|
||||
+ if (pvid) {
|
||||
+ cfg.treatment.remove_tag =
|
||||
@@ -809,22 +771,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case EVLAN_PVID_OR_PASS:
|
||||
+ if (pvid) {
|
||||
+ cfg.treatment.remove_tag =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
|
||||
+ cfg.treatment.add_outer_vlan = 1;
|
||||
+ cfg.treatment.outer_vlan.vid_mode =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
|
||||
+ cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
|
||||
+ cfg.treatment.outer_vlan.tpid =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
|
||||
+ } else {
|
||||
+ cfg.treatment.remove_tag =
|
||||
+ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case EVLAN_STRIP1_AND_PVID_OR_DISCARD:
|
||||
+ if (pvid) {
|
||||
+ cfg.treatment.remove_tag =
|
||||
@@ -845,15 +791,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_evlan_deactivate_entry - Reset an Extended VLAN entry to no-op
|
||||
+ * @priv: driver private data
|
||||
+ * @block_id: HW Extended VLAN block ID
|
||||
+ * @entry_index: entry index within the block
|
||||
+ *
|
||||
+ * Writes a zeroed-out config to the firmware, which deactivates the
|
||||
+ * rule (making it transparent / no-op).
|
||||
+ */
|
||||
+static int mxl862xx_evlan_deactivate_entry(struct mxl862xx_priv *priv,
|
||||
+ u16 block_id, u16 entry_index)
|
||||
+{
|
||||
@@ -876,19 +813,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_evlan_write_final_rules - Write catchall rules to the ingress block
|
||||
+ * @priv: driver private data
|
||||
+ * @blk: Extended VLAN block (already allocated)
|
||||
+ * @rules: array of rule descriptors for the final rules
|
||||
+ * @n_rules: number of final rules
|
||||
+ * @pvid: port VLAN ID (for PVID insertion rules)
|
||||
+ *
|
||||
+ * Writes final catchall rules starting at block_size - n_rules. With
|
||||
+ * VLAN Filter handling VID membership, only the ingress block uses
|
||||
+ * finals, and the block is sized to exactly fit them (no VID entries),
|
||||
+ * so the rules fill the entire block.
|
||||
+ */
|
||||
+static int mxl862xx_evlan_write_final_rules(struct mxl862xx_priv *priv,
|
||||
+ struct mxl862xx_evlan_block *blk,
|
||||
+ const struct mxl862xx_evlan_rule_desc *rules,
|
||||
@@ -908,15 +832,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_vf_entry_set - Write a single VLAN Filter entry
|
||||
+ * @priv: driver private data
|
||||
+ * @block_id: HW VLAN Filter block ID
|
||||
+ * @index: entry index within the block
|
||||
+ * @vid: VLAN ID to allow
|
||||
+ *
|
||||
+ * Writes an ALLOW entry (discard_matched=false) for the given VID.
|
||||
+ */
|
||||
+static int mxl862xx_vf_entry_set(struct mxl862xx_priv *priv,
|
||||
+ u16 block_id, u16 index, u16 vid)
|
||||
+{
|
||||
@@ -931,13 +846,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_vf_find_vid - Find a VID entry in a VF block
|
||||
+ * @vf: VLAN Filter block to search
|
||||
+ * @vid: VLAN ID to find
|
||||
+ */
|
||||
+static struct mxl862xx_vf_vid *
|
||||
+mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf, u16 vid)
|
||||
+static struct mxl862xx_vf_vid *mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf,
|
||||
+ u16 vid)
|
||||
+{
|
||||
+ struct mxl862xx_vf_vid *ve;
|
||||
+
|
||||
@@ -948,17 +858,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_vf_add_vid - Add a VID to a port's VLAN Filter block
|
||||
+ * @priv: driver private data
|
||||
+ * @vf: VLAN Filter block
|
||||
+ * @vid: VLAN ID to add
|
||||
+ * @untagged: whether this VID should strip tags on egress
|
||||
+ *
|
||||
+ * Idempotent. Writes an ALLOW entry at active_count and increments
|
||||
+ * active_count. If the VID already exists, only the untagged flag
|
||||
+ * is updated. The HW block must be allocated before calling this.
|
||||
+ */
|
||||
+static int mxl862xx_vf_add_vid(struct mxl862xx_priv *priv,
|
||||
+ struct mxl862xx_vf_block *vf,
|
||||
+ u16 vid, bool untagged)
|
||||
@@ -995,17 +894,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_vf_del_vid - Remove a VID from a port's VLAN Filter block
|
||||
+ * @priv: driver private data
|
||||
+ * @vf: VLAN Filter block
|
||||
+ * @vid: VLAN ID to remove
|
||||
+ *
|
||||
+ * Swap-compacts: the last active entry is moved into the gap,
|
||||
+ * active_count is decremented, and the old last slot is plugged
|
||||
+ * with DISCARD. When active_count drops to 0, a DISCARD sentinel
|
||||
+ * is restored at index 0.
|
||||
+ */
|
||||
+static int mxl862xx_vf_del_vid(struct mxl862xx_priv *priv,
|
||||
+ struct mxl862xx_vf_block *vf, u16 vid)
|
||||
+{
|
||||
@@ -1035,12 +923,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return ret;
|
||||
+ } else if (gap < last) {
|
||||
+ /* Swap: move the last ALLOW entry into the gap */
|
||||
+ last_ve = NULL;
|
||||
+ list_for_each_entry(last_ve, &vf->vids, list)
|
||||
+ if (last_ve->index == last)
|
||||
+ break;
|
||||
+
|
||||
+ if (WARN_ON(!last_ve || last_ve->index != last))
|
||||
+ if (WARN_ON(list_entry_is_head(last_ve, &vf->vids, list)))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = mxl862xx_vf_entry_set(priv, vf->block_id,
|
||||
@@ -1049,16 +936,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return ret;
|
||||
+
|
||||
+ last_ve->index = gap;
|
||||
+
|
||||
+ /* Plug the old last slot with DISCARD */
|
||||
+ ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ } else {
|
||||
+ /* Deleting the last entry -- just plug it */
|
||||
+ ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ list_del(&ve->list);
|
||||
@@ -1068,20 +945,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
|
||||
+ * @priv: driver private data
|
||||
+ * @port: port number
|
||||
+ *
|
||||
+ * In VLAN-aware mode the ingress EVLAN block handles PVID insertion for
|
||||
+ * untagged/priority-tagged frames, passes through standard 802.1Q
|
||||
+ * tagged frames for VF membership checking, and treats non-8021Q TPID
|
||||
+ * frames as untagged. The block is sized to exactly fit the 7 catchall
|
||||
+ * rules and is rewritten whenever PVID changes.
|
||||
+ *
|
||||
+ * In VLAN-unaware mode the firmware passes frames through unchanged when
|
||||
+ * no ingress block is assigned, so nothing is programmed.
|
||||
+ */
|
||||
+static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port)
|
||||
+{
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
@@ -1099,19 +962,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ p->pvid);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * mxl862xx_evlan_program_egress - Reprogram all egress tag-stripping rules
|
||||
+ * @priv: driver private data
|
||||
+ * @port: port number
|
||||
+ *
|
||||
+ * Walks the port's VF VID list and writes 2 EVLAN rules per VID that
|
||||
+ * needs egress tag stripping. In VLAN-aware mode only untagged VIDs
|
||||
+ * need rules (tagged VIDs pass through EVLAN untouched). In unaware
|
||||
+ * mode every VID gets rules.
|
||||
+ *
|
||||
+ * Entries are packed starting at index 0, and the scan window
|
||||
+ * (n_active) is narrowed so stale entries beyond it are never matched.
|
||||
+ */
|
||||
+static int mxl862xx_evlan_program_egress(struct mxl862xx_priv *priv, int port)
|
||||
+{
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
@@ -1131,10 +981,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ }
|
||||
+
|
||||
+ list_for_each_entry(vfv, &p->vf.vids, list) {
|
||||
+ /* In VLAN-aware mode tagged-only VIDs need no EVLAN
|
||||
+ * rules -- VLAN Filter handles membership.
|
||||
+ */
|
||||
+ if (p->vlan_filtering && !vfv->untagged)
|
||||
+ if (!vfv->untagged)
|
||||
+ continue;
|
||||
+
|
||||
+ if (idx + n_vid > blk->block_size)
|
||||
@@ -1182,12 +1029,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
+ bool old_vlan_filtering = p->vlan_filtering;
|
||||
+ bool old_in_use = p->ingress_evlan.in_use;
|
||||
+ bool changed = (p->vlan_filtering != vlan_filtering);
|
||||
+ int ret;
|
||||
+
|
||||
+ p->vlan_filtering = vlan_filtering;
|
||||
+
|
||||
+ /* Reprogram Extended VLAN rules if filtering mode changed */
|
||||
+ if (changed) {
|
||||
+ /* When leaving VLAN-aware mode, release the ingress HW
|
||||
+ * block. The firmware passes frames through unchanged
|
||||
@@ -1199,17 +1047,20 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+
|
||||
+ ret = mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_restore;
|
||||
+
|
||||
+ ret = mxl862xx_evlan_program_egress(priv, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_restore;
|
||||
+ }
|
||||
+
|
||||
+ /* Push VLAN-based MAC learning flags and (possibly newly
|
||||
+ * allocated) ingress block to hardware.
|
||||
+ */
|
||||
+ return mxl862xx_set_bridge_port(ds, port);
|
||||
+
|
||||
+ /* No HW rollback -- restoring SW state is sufficient for a correct retry. */
|
||||
+err_restore:
|
||||
+ p->vlan_filtering = old_vlan_filtering;
|
||||
+ p->ingress_evlan.in_use = old_in_use;
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
|
||||
@@ -1255,21 +1106,32 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ if (pvid_changed) {
|
||||
+ ret = mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ if (ret)
|
||||
+ goto err_pvid;
|
||||
+ goto err_rollback;
|
||||
+ }
|
||||
+
|
||||
+ /* Reprogram egress tag-stripping rules (walks VF VID list) */
|
||||
+ ret = mxl862xx_evlan_program_egress(priv, port);
|
||||
+ if (ret)
|
||||
+ goto err_pvid;
|
||||
+ goto err_rollback;
|
||||
+
|
||||
+ /* Apply VLAN block IDs and MAC learning flags to bridge port */
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
+ goto err_pvid;
|
||||
+ goto err_rollback;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_rollback:
|
||||
+ /* Best-effort: undo VF add and restore consistent hardware state.
|
||||
+ * A retry of port_vlan_add will converge since vf_add_vid is
|
||||
+ * idempotent.
|
||||
+ */
|
||||
+ p->pvid = old_pvid;
|
||||
+ mxl862xx_vf_del_vid(priv, &p->vf, vid);
|
||||
+ mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ mxl862xx_evlan_program_egress(priv, port);
|
||||
+ mxl862xx_set_bridge_port(ds, port);
|
||||
+ return ret;
|
||||
+err_pvid:
|
||||
+ p->pvid = old_pvid;
|
||||
+ return ret;
|
||||
@@ -1280,13 +1142,22 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
+ u16 vid = vlan->vid;
|
||||
+ struct mxl862xx_vf_vid *ve;
|
||||
+ bool pvid_changed = false;
|
||||
+ u16 vid = vlan->vid;
|
||||
+ bool old_untagged;
|
||||
+ u16 old_pvid;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (dsa_is_cpu_port(ds, port))
|
||||
+ return 0;
|
||||
+
|
||||
+ ve = mxl862xx_vf_find_vid(&p->vf, vid);
|
||||
+ if (!ve)
|
||||
+ return 0;
|
||||
+ old_untagged = ve->untagged;
|
||||
+ old_pvid = p->pvid;
|
||||
+
|
||||
+ /* Clear PVID if we're deleting it */
|
||||
+ if (p->pvid == vid) {
|
||||
+ p->pvid = 0;
|
||||
@@ -1299,28 +1170,45 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ */
|
||||
+ ret = mxl862xx_vf_del_vid(priv, &p->vf, vid);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_pvid;
|
||||
+
|
||||
+ /* Reprogram egress tag-stripping rules (VID is now gone) */
|
||||
+ ret = mxl862xx_evlan_program_egress(priv, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_rollback;
|
||||
+
|
||||
+ /* If PVID changed, reprogram ingress finals */
|
||||
+ if (pvid_changed) {
|
||||
+ ret = mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ goto err_rollback;
|
||||
+ }
|
||||
+
|
||||
+ return mxl862xx_set_bridge_port(ds, port);
|
||||
+ ret = mxl862xx_set_bridge_port(ds, port);
|
||||
+ if (ret)
|
||||
+ goto err_rollback;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_rollback:
|
||||
+ /* Best-effort: re-add the VID and restore consistent hardware
|
||||
+ * state. A retry of port_vlan_del will converge.
|
||||
+ */
|
||||
+ p->pvid = old_pvid;
|
||||
+ mxl862xx_vf_add_vid(priv, &p->vf, vid, old_untagged);
|
||||
+ mxl862xx_evlan_program_egress(priv, port);
|
||||
+ mxl862xx_evlan_program_ingress(priv, port);
|
||||
+ mxl862xx_set_bridge_port(ds, port);
|
||||
+ return ret;
|
||||
+err_pvid:
|
||||
+ p->pvid = old_pvid;
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
struct dsa_port *dp;
|
||||
|
||||
- priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
|
||||
- priv->ports[port].learning = true;
|
||||
@@ -1332,41 +1220,21 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * assignment need to be configured.
|
||||
+ */
|
||||
|
||||
/* include all assigned user ports in the CPU portmap */
|
||||
- bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
dsa_switch_for_each_user_port(dp, ds) {
|
||||
/* it's safe to rely on cpu_dp being valid for user ports */
|
||||
if (dp->cpu_dp->index != port)
|
||||
continue;
|
||||
|
||||
- __set_bit(dp->index, priv->ports[port].portmap);
|
||||
+ __set_bit(dp->index, p->portmap);
|
||||
}
|
||||
|
||||
return mxl862xx_set_bridge_port(ds, port);
|
||||
}
|
||||
@@ -510,6 +1260,8 @@ static int mxl862xx_port_bridge_join(str
|
||||
static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||
const struct dsa_bridge bridge)
|
||||
{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p = &priv->ports[port];
|
||||
int err;
|
||||
|
||||
+
|
||||
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
|
||||
const struct dsa_bridge bridge,
|
||||
bool *tx_fwd_offload,
|
||||
@@ -553,6 +1417,22 @@ static void mxl862xx_port_bridge_leave(s
|
||||
bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
__set_bit(dp->cpu_dp->index, p->portmap);
|
||||
p->flood_block = 0;
|
||||
+
|
||||
+ /* Detach EVLAN and VF blocks from the bridge port BEFORE freeing
|
||||
+ * them. The firmware tracks a usage count per block and rejects
|
||||
+ * FREE while the count is non-zero.
|
||||
+ *
|
||||
+ * For EVLAN: setting in_use=false makes set_bridge_port send
|
||||
+ * enable=false, which decrements the firmware refcount.
|
||||
+ *
|
||||
+ * For VF: set_bridge_port sees dp->bridge == NULL (DSA already
|
||||
+ * cleared it) and sends vlan_filter_enable=0, which decrements
|
||||
+ * the firmware VF refcount.
|
||||
+ */
|
||||
err = mxl862xx_sync_bridge_members(ds, &bridge);
|
||||
@@ -521,6 +1273,10 @@ static void mxl862xx_port_bridge_leave(s
|
||||
/* Revert leaving port, omitted by the sync above, to its
|
||||
* single-port bridge
|
||||
*/
|
||||
+ p->pvid = 0;
|
||||
+ p->ingress_evlan.in_use = false;
|
||||
+ p->egress_evlan.in_use = false;
|
||||
@@ -1374,28 +1242,22 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
err = mxl862xx_set_bridge_port(ds, port);
|
||||
if (err)
|
||||
dev_err(ds->dev,
|
||||
@@ -602,6 +1482,28 @@ static int mxl862xx_port_setup(struct ds
|
||||
@@ -585,6 +1341,22 @@ static int mxl862xx_port_setup(struct ds
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ /* Initialize and pre-allocate per-port EVLAN and VF blocks for
|
||||
+ * user ports. CPU ports do not use EVLAN or VF -- frames pass
|
||||
+ * through without processing. Pre-allocation avoids firmware
|
||||
+ * EVLAN table fragmentation and simplifies control flow.
|
||||
+ */
|
||||
+ mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan,
|
||||
+ priv->evlan_ingress_size);
|
||||
+ priv->ports[port].ingress_evlan.block_size = priv->evlan_ingress_size;
|
||||
+ ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan,
|
||||
+ priv->evlan_egress_size);
|
||||
+ priv->ports[port].egress_evlan.block_size = priv->evlan_egress_size;
|
||||
+ ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ mxl862xx_vf_init(&priv->ports[port].vf, priv->vf_block_size);
|
||||
+ priv->ports[port].vf.block_size = priv->vf_block_size;
|
||||
+ INIT_LIST_HEAD(&priv->ports[port].vf.vids);
|
||||
+ ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
@@ -1403,7 +1265,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
priv->ports[port].setup_done = true;
|
||||
|
||||
return 0;
|
||||
@@ -1012,6 +1914,9 @@ static const struct dsa_switch_ops mxl86
|
||||
@@ -983,6 +1755,9 @@ static const struct dsa_switch_ops mxl86
|
||||
.port_fdb_dump = mxl862xx_port_fdb_dump,
|
||||
.port_mdb_add = mxl862xx_port_mdb_add,
|
||||
.port_mdb_del = mxl862xx_port_mdb_del,
|
||||
@@ -1424,7 +1286,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
/* Number of __le16 words in a firmware portmap (128-bit bitmap). */
|
||||
#define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
|
||||
@@ -86,6 +88,66 @@ static inline bool mxl862xx_fw_portmap_i
|
||||
@@ -55,6 +57,66 @@ static inline bool mxl862xx_fw_portmap_i
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1491,7 +1353,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* struct mxl862xx_port - per-port state tracked by the driver
|
||||
* @priv: back-pointer to switch private data; needed by
|
||||
* deferred work handlers to access ds and priv
|
||||
@@ -101,6 +163,11 @@ static inline bool mxl862xx_fw_portmap_i
|
||||
@@ -68,6 +130,11 @@ static inline bool mxl862xx_fw_portmap_i
|
||||
* @setup_done: set at end of port_setup, cleared at start of
|
||||
* port_teardown; guards deferred work against
|
||||
* acting on torn-down state
|
||||
@@ -1503,11 +1365,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @host_flood_uc: desired host unicast flood state (true = flood);
|
||||
* updated atomically by port_set_host_flood, consumed
|
||||
* by the deferred host_flood_work
|
||||
@@ -119,6 +186,12 @@ struct mxl862xx_port {
|
||||
@@ -85,6 +152,11 @@ struct mxl862xx_port {
|
||||
unsigned long flood_block;
|
||||
bool learning;
|
||||
bool setup_done;
|
||||
+ /* VLAN state */
|
||||
+ u16 pvid;
|
||||
+ bool vlan_filtering;
|
||||
+ struct mxl862xx_vf_block vf;
|
||||
@@ -1516,7 +1377,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
bool host_flood_uc;
|
||||
bool host_flood_mc;
|
||||
struct work_struct host_flood_work;
|
||||
@@ -126,17 +199,23 @@ struct mxl862xx_port {
|
||||
@@ -92,17 +164,23 @@ struct mxl862xx_port {
|
||||
|
||||
/**
|
||||
* struct mxl862xx_priv - driver private data for an MxL862xx switch
|
||||
@@ -1551,7 +1412,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
*/
|
||||
struct mxl862xx_priv {
|
||||
struct dsa_switch *ds;
|
||||
@@ -146,6 +225,9 @@ struct mxl862xx_priv {
|
||||
@@ -112,6 +190,9 @@ struct mxl862xx_priv {
|
||||
u16 drop_meter;
|
||||
struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
|
||||
u16 bridges[MXL862XX_MAX_BRIDGES + 1];
|
||||
+56
-49
@@ -1,7 +1,7 @@
|
||||
From 0067d79d10becfc5779fb50d5c0ac152cc5dc303 Mon Sep 17 00:00:00 2001
|
||||
From e6295d124644b14a12b55edf5d3e89cf86a4a2ce Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Sun, 22 Mar 2026 00:57:33 +0000
|
||||
Subject: [PATCH 06/26] net: dsa: mxl862xx: add ethtool statistics support
|
||||
Date: Sun, 12 Apr 2026 01:01:57 +0100
|
||||
Subject: [PATCH 1/2] net: dsa: mxl862xx: add ethtool statistics support
|
||||
|
||||
The MxL862xx firmware exposes per-port RMON counters through the
|
||||
RMON_PORT_GET command, covering standard IEEE 802.3 MAC statistics
|
||||
@@ -16,11 +16,13 @@ legacy ethtool -S support. Implement .get_eth_mac_stats,
|
||||
IEEE 802.3 statistics interface.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/480be14d5ed51f3db7b1681b298044dbf8e87494.1775951347.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 142 ++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 142 +++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 3 +
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 168 ++++++++++++++++++++++++
|
||||
3 files changed, 313 insertions(+)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 173 ++++++++++++++++++++++++
|
||||
3 files changed, 318 insertions(+)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -120,7 +122,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * @tx_good_bytes: Transmitted good byte count (64-bit)
|
||||
+ */
|
||||
+struct mxl862xx_rmon_port_cnt {
|
||||
+ enum mxl862xx_port_type port_type;
|
||||
+ __le32 port_type; /* enum mxl862xx_port_type */
|
||||
+ __le16 port_id;
|
||||
+ __le16 sub_if_id_group;
|
||||
+ u8 pce_bypass;
|
||||
@@ -192,7 +194,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -30,6 +30,64 @@
|
||||
@@ -30,6 +30,38 @@
|
||||
#define MXL862XX_API_READ_QUIET(dev, cmd, data) \
|
||||
mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true)
|
||||
|
||||
@@ -209,60 +211,34 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ .offset = offsetof(struct mxl862xx_rmon_port_cnt, _element) \
|
||||
+}
|
||||
+
|
||||
+/* Hardware-specific counters not covered by any standardized stats callback. */
|
||||
+static const struct mxl862xx_mib_desc mxl862xx_mib[] = {
|
||||
+ MIB_DESC(1, "TxGoodPkts", tx_good_pkts),
|
||||
+ MIB_DESC(1, "TxUnicastPkts", tx_unicast_pkts),
|
||||
+ MIB_DESC(1, "TxBroadcastPkts", tx_broadcast_pkts),
|
||||
+ MIB_DESC(1, "TxMulticastPkts", tx_multicast_pkts),
|
||||
+ MIB_DESC(1, "Tx64BytePkts", tx64byte_pkts),
|
||||
+ MIB_DESC(1, "Tx127BytePkts", tx127byte_pkts),
|
||||
+ MIB_DESC(1, "Tx255BytePkts", tx255byte_pkts),
|
||||
+ MIB_DESC(1, "Tx511BytePkts", tx511byte_pkts),
|
||||
+ MIB_DESC(1, "Tx1023BytePkts", tx1023byte_pkts),
|
||||
+ MIB_DESC(1, "TxMaxBytePkts", tx_max_byte_pkts),
|
||||
+ MIB_DESC(1, "TxDroppedPkts", tx_dropped_pkts),
|
||||
+ MIB_DESC(1, "TxAcmDroppedPkts", tx_acm_dropped_pkts),
|
||||
+ MIB_DESC(2, "TxGoodBytes", tx_good_bytes),
|
||||
+ MIB_DESC(1, "TxSingleCollCount", tx_single_coll_count),
|
||||
+ MIB_DESC(1, "TxMultCollCount", tx_mult_coll_count),
|
||||
+ MIB_DESC(1, "TxLateCollCount", tx_late_coll_count),
|
||||
+ MIB_DESC(1, "TxExcessCollCount", tx_excess_coll_count),
|
||||
+ MIB_DESC(1, "TxCollCount", tx_coll_count),
|
||||
+ MIB_DESC(1, "TxPauseCount", tx_pause_count),
|
||||
+ MIB_DESC(1, "RxGoodPkts", rx_good_pkts),
|
||||
+ MIB_DESC(1, "RxUnicastPkts", rx_unicast_pkts),
|
||||
+ MIB_DESC(1, "RxBroadcastPkts", rx_broadcast_pkts),
|
||||
+ MIB_DESC(1, "RxMulticastPkts", rx_multicast_pkts),
|
||||
+ MIB_DESC(1, "RxFCSErrorPkts", rx_fcserror_pkts),
|
||||
+ MIB_DESC(1, "RxUnderSizeGoodPkts", rx_under_size_good_pkts),
|
||||
+ MIB_DESC(1, "RxOversizeGoodPkts", rx_oversize_good_pkts),
|
||||
+ MIB_DESC(1, "RxUnderSizeErrorPkts", rx_under_size_error_pkts),
|
||||
+ MIB_DESC(1, "RxOversizeErrorPkts", rx_oversize_error_pkts),
|
||||
+ MIB_DESC(1, "RxFilteredPkts", rx_filtered_pkts),
|
||||
+ MIB_DESC(1, "Rx64BytePkts", rx64byte_pkts),
|
||||
+ MIB_DESC(1, "Rx127BytePkts", rx127byte_pkts),
|
||||
+ MIB_DESC(1, "Rx255BytePkts", rx255byte_pkts),
|
||||
+ MIB_DESC(1, "Rx511BytePkts", rx511byte_pkts),
|
||||
+ MIB_DESC(1, "Rx1023BytePkts", rx1023byte_pkts),
|
||||
+ MIB_DESC(1, "RxMaxBytePkts", rx_max_byte_pkts),
|
||||
+ MIB_DESC(1, "RxDroppedPkts", rx_dropped_pkts),
|
||||
+ MIB_DESC(1, "RxExtendedVlanDiscardPkts", rx_extended_vlan_discard_pkts),
|
||||
+ MIB_DESC(1, "MtuExceedDiscardPkts", mtu_exceed_discard_pkts),
|
||||
+ MIB_DESC(2, "RxGoodBytes", rx_good_bytes),
|
||||
+ MIB_DESC(2, "RxBadBytes", rx_bad_bytes),
|
||||
+ MIB_DESC(1, "RxGoodPausePkts", rx_good_pause_pkts),
|
||||
+ MIB_DESC(1, "RxAlignErrorPkts", rx_align_error_pkts),
|
||||
+};
|
||||
+
|
||||
+static const struct ethtool_rmon_hist_range mxl862xx_rmon_ranges[] = {
|
||||
+ { 0, 64 },
|
||||
+ { 65, 127 },
|
||||
+ { 128, 255 },
|
||||
+ { 256, 511 },
|
||||
+ { 512, 1023 },
|
||||
+ { 1024, 10240 },
|
||||
+ {}
|
||||
+};
|
||||
+
|
||||
#define MXL862XX_SDMA_PCTRLP(p) (0xbc0 + ((p) * 0x6))
|
||||
#define MXL862XX_SDMA_PCTRL_EN BIT(0)
|
||||
|
||||
@@ -1893,6 +1951,110 @@ static int mxl862xx_port_bridge_flags(st
|
||||
@@ -1734,6 +1766,140 @@ static int mxl862xx_port_bridge_flags(st
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void mxl862xx_get_strings(struct dsa_switch *ds, int port,
|
||||
+ u32 stringset, u8 *data)
|
||||
+ u32 stringset, u8 *data)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
@@ -285,7 +261,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ struct mxl862xx_rmon_port_cnt *cnt)
|
||||
+{
|
||||
+ memset(cnt, 0, sizeof(*cnt));
|
||||
+ cnt->port_type = MXL862XX_CTP_PORT;
|
||||
+ cnt->port_type = cpu_to_le32(MXL862XX_CTP_PORT);
|
||||
+ cnt->port_id = cpu_to_le16(port);
|
||||
+
|
||||
+ return MXL862XX_API_READ(ds->priv, MXL862XX_RMON_PORT_GET, *cnt);
|
||||
@@ -365,10 +341,40 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts);
|
||||
+}
|
||||
+
|
||||
+static void mxl862xx_get_rmon_stats(struct dsa_switch *ds, int port,
|
||||
+ struct ethtool_rmon_stats *rmon_stats,
|
||||
+ const struct ethtool_rmon_hist_range **ranges)
|
||||
+{
|
||||
+ struct mxl862xx_rmon_port_cnt cnt;
|
||||
+
|
||||
+ if (mxl862xx_read_rmon(ds, port, &cnt))
|
||||
+ return;
|
||||
+
|
||||
+ rmon_stats->undersize_pkts = le32_to_cpu(cnt.rx_under_size_good_pkts);
|
||||
+ rmon_stats->oversize_pkts = le32_to_cpu(cnt.rx_oversize_good_pkts);
|
||||
+ rmon_stats->fragments = le32_to_cpu(cnt.rx_under_size_error_pkts);
|
||||
+ rmon_stats->jabbers = le32_to_cpu(cnt.rx_oversize_error_pkts);
|
||||
+
|
||||
+ rmon_stats->hist[0] = le32_to_cpu(cnt.rx64byte_pkts);
|
||||
+ rmon_stats->hist[1] = le32_to_cpu(cnt.rx127byte_pkts);
|
||||
+ rmon_stats->hist[2] = le32_to_cpu(cnt.rx255byte_pkts);
|
||||
+ rmon_stats->hist[3] = le32_to_cpu(cnt.rx511byte_pkts);
|
||||
+ rmon_stats->hist[4] = le32_to_cpu(cnt.rx1023byte_pkts);
|
||||
+ rmon_stats->hist[5] = le32_to_cpu(cnt.rx_max_byte_pkts);
|
||||
+
|
||||
+ rmon_stats->hist_tx[0] = le32_to_cpu(cnt.tx64byte_pkts);
|
||||
+ rmon_stats->hist_tx[1] = le32_to_cpu(cnt.tx127byte_pkts);
|
||||
+ rmon_stats->hist_tx[2] = le32_to_cpu(cnt.tx255byte_pkts);
|
||||
+ rmon_stats->hist_tx[3] = le32_to_cpu(cnt.tx511byte_pkts);
|
||||
+ rmon_stats->hist_tx[4] = le32_to_cpu(cnt.tx1023byte_pkts);
|
||||
+ rmon_stats->hist_tx[5] = le32_to_cpu(cnt.tx_max_byte_pkts);
|
||||
+
|
||||
+ *ranges = mxl862xx_rmon_ranges;
|
||||
+}
|
||||
static const struct dsa_switch_ops mxl862xx_switch_ops = {
|
||||
.get_tag_protocol = mxl862xx_get_tag_protocol,
|
||||
.setup = mxl862xx_setup,
|
||||
@@ -1917,6 +2079,12 @@ static const struct dsa_switch_ops mxl86
|
||||
@@ -1758,6 +1924,13 @@ static const struct dsa_switch_ops mxl86
|
||||
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
|
||||
.port_vlan_add = mxl862xx_port_vlan_add,
|
||||
.port_vlan_del = mxl862xx_port_vlan_del,
|
||||
@@ -378,6 +384,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ .get_eth_mac_stats = mxl862xx_get_eth_mac_stats,
|
||||
+ .get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
|
||||
+ .get_pause_stats = mxl862xx_get_pause_stats,
|
||||
+ .get_rmon_stats = mxl862xx_get_rmon_stats,
|
||||
};
|
||||
|
||||
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
|
||||
+60
-26
@@ -1,20 +1,22 @@
|
||||
From bab5a69e3872a693069e430a1fa0d2825ea83b4f Mon Sep 17 00:00:00 2001
|
||||
From a21d33a5265f0b31d935a8b9b2b6faefb5185911 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 04:14:38 +0000
|
||||
Subject: [PATCH 07/26] net: dsa: mxl862xx: implement .get_stats64
|
||||
Date: Sun, 12 Apr 2026 01:02:05 +0100
|
||||
Subject: [PATCH 2/2] net: dsa: mxl862xx: implement .get_stats64
|
||||
|
||||
Poll free-running firmware RMON counters every 2 seconds and accumulate
|
||||
deltas into 64-bit per-port statistics. 32-bit packet counters wrap
|
||||
in ~880s at 2.5 Gbps line rate; the 2s polling interval provides a
|
||||
comfortable margin. The .get_stats64 callback forces a fresh poll so
|
||||
that counters are always up to date when queried.
|
||||
in ~220s at 10 Gbps line rate with minimum-size frames; the 2s polling
|
||||
interval provides a comfortable margin. The .get_stats64 callback
|
||||
forces a fresh poll so that counters are always up to date when queried.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Link: https://patch.msgid.link/fa38548ba05866879e8912721edc91947ce4ff12.1775951347.git.daniel@makrotopia.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-host.c | 8 +-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 174 +++++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 63 +++++++-
|
||||
3 files changed, 238 insertions(+), 7 deletions(-)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 175 +++++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 94 +++++++++++-
|
||||
3 files changed, 270 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
|
||||
@@ -69,7 +71,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
struct mxl862xx_mib_desc {
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
@@ -739,6 +745,9 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -677,6 +683,9 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -79,10 +81,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return mxl862xx_setup_mdio(ds);
|
||||
}
|
||||
|
||||
@@ -2055,6 +2064,158 @@ static void mxl862xx_get_pause_stats(str
|
||||
pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts);
|
||||
}
|
||||
@@ -1900,6 +1909,159 @@ static void mxl862xx_get_rmon_stats(stru
|
||||
|
||||
*ranges = mxl862xx_rmon_ranges;
|
||||
}
|
||||
+
|
||||
+/* Compute the delta between two 32-bit free-running counter snapshots,
|
||||
+ * handling a single wrap-around correctly via unsigned subtraction.
|
||||
+ */
|
||||
@@ -238,15 +241,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static const struct dsa_switch_ops mxl862xx_switch_ops = {
|
||||
.get_tag_protocol = mxl862xx_get_tag_protocol,
|
||||
.setup = mxl862xx_setup,
|
||||
@@ -2085,6 +2246,7 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_eth_mac_stats = mxl862xx_get_eth_mac_stats,
|
||||
@@ -1931,6 +2093,7 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
|
||||
.get_pause_stats = mxl862xx_get_pause_stats,
|
||||
.get_rmon_stats = mxl862xx_get_rmon_stats,
|
||||
+ .get_stats64 = mxl862xx_get_stats64,
|
||||
};
|
||||
|
||||
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
|
||||
@@ -2146,16 +2308,22 @@ static int mxl862xx_probe(struct mdio_de
|
||||
@@ -1992,16 +2155,22 @@ static int mxl862xx_probe(struct mdio_de
|
||||
priv->ports[i].priv = priv;
|
||||
INIT_WORK(&priv->ports[i].host_flood_work,
|
||||
mxl862xx_host_flood_work_fn);
|
||||
@@ -269,7 +272,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2170,6 +2338,9 @@ static void mxl862xx_remove(struct mdio_
|
||||
@@ -2016,6 +2185,9 @@ static void mxl862xx_remove(struct mdio_
|
||||
|
||||
priv = ds->priv;
|
||||
|
||||
@@ -279,7 +282,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
dsa_unregister_switch(ds);
|
||||
|
||||
mxl862xx_host_shutdown(priv);
|
||||
@@ -2196,6 +2367,9 @@ static void mxl862xx_shutdown(struct mdi
|
||||
@@ -2042,6 +2214,9 @@ static void mxl862xx_shutdown(struct mdi
|
||||
|
||||
dsa_switch_shutdown(ds);
|
||||
|
||||
@@ -291,11 +294,45 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
for (i = 0; i < MXL862XX_MAX_PORTS; i++)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -148,6 +148,47 @@ struct mxl862xx_evlan_block {
|
||||
@@ -117,6 +117,79 @@ struct mxl862xx_evlan_block {
|
||||
};
|
||||
|
||||
/**
|
||||
+ * struct mxl862xx_port_stats - 64-bit accumulated hardware port statistics
|
||||
+ * @rx_packets: total received packets
|
||||
+ * @tx_packets: total transmitted packets
|
||||
+ * @rx_bytes: total received bytes
|
||||
+ * @tx_bytes: total transmitted bytes
|
||||
+ * @rx_errors: total receive errors
|
||||
+ * @tx_errors: total transmit errors
|
||||
+ * @rx_dropped: total received packets dropped
|
||||
+ * @tx_dropped: total transmitted packets dropped
|
||||
+ * @multicast: total received multicast packets
|
||||
+ * @collisions: total transmit collisions
|
||||
+ * @rx_length_errors: received length errors (undersize + oversize)
|
||||
+ * @rx_crc_errors: received FCS errors
|
||||
+ * @rx_frame_errors: received alignment errors
|
||||
+ * @prev_rx_good_pkts: previous snapshot of rx good packet counter
|
||||
+ * @prev_tx_good_pkts: previous snapshot of tx good packet counter
|
||||
+ * @prev_rx_good_bytes: previous snapshot of rx good byte counter
|
||||
+ * @prev_tx_good_bytes: previous snapshot of tx good byte counter
|
||||
+ * @prev_rx_fcserror_pkts: previous snapshot of rx FCS error counter
|
||||
+ * @prev_rx_under_size_error_pkts: previous snapshot of rx undersize
|
||||
+ * error counter
|
||||
+ * @prev_rx_oversize_error_pkts: previous snapshot of rx oversize
|
||||
+ * error counter
|
||||
+ * @prev_rx_align_error_pkts: previous snapshot of rx alignment
|
||||
+ * error counter
|
||||
+ * @prev_tx_dropped_pkts: previous snapshot of tx dropped counter
|
||||
+ * @prev_rx_dropped_pkts: previous snapshot of rx dropped counter
|
||||
+ * @prev_rx_evlan_discard_pkts: previous snapshot of extended VLAN
|
||||
+ * discard counter
|
||||
+ * @prev_mtu_exceed_discard_pkts: previous snapshot of MTU exceed
|
||||
+ * discard counter
|
||||
+ * @prev_tx_acm_dropped_pkts: previous snapshot of tx ACM dropped
|
||||
+ * counter
|
||||
+ * @prev_rx_multicast_pkts: previous snapshot of rx multicast counter
|
||||
+ * @prev_tx_coll_count: previous snapshot of tx collision counter
|
||||
+ *
|
||||
+ * The firmware RMON counters are 32-bit free-running (64-bit for byte
|
||||
+ * counters). This structure holds 64-bit accumulators alongside the
|
||||
@@ -303,7 +340,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ * handling 32-bit wrap correctly via unsigned subtraction.
|
||||
+ */
|
||||
+struct mxl862xx_port_stats {
|
||||
+ /* 64-bit accumulators */
|
||||
+ u64 rx_packets;
|
||||
+ u64 tx_packets;
|
||||
+ u64 rx_bytes;
|
||||
@@ -317,7 +353,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ u64 rx_length_errors;
|
||||
+ u64 rx_crc_errors;
|
||||
+ u64 rx_frame_errors;
|
||||
+ /* Previous raw RMON values for delta computation */
|
||||
+ u32 prev_rx_good_pkts;
|
||||
+ u32 prev_tx_good_pkts;
|
||||
+ u64 prev_rx_good_bytes;
|
||||
@@ -339,7 +374,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* struct mxl862xx_port - per-port state tracked by the driver
|
||||
* @priv: back-pointer to switch private data; needed by
|
||||
* deferred work handlers to access ds and priv
|
||||
@@ -178,6 +219,10 @@ struct mxl862xx_evlan_block {
|
||||
@@ -145,6 +218,10 @@ struct mxl862xx_evlan_block {
|
||||
* The worker acquires rtnl_lock() to serialize with
|
||||
* DSA callbacks and checks @setup_done to avoid
|
||||
* acting on torn-down ports.
|
||||
@@ -350,13 +385,12 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
*/
|
||||
struct mxl862xx_port {
|
||||
struct mxl862xx_priv *priv;
|
||||
@@ -195,16 +240,25 @@ struct mxl862xx_port {
|
||||
@@ -160,16 +237,24 @@ struct mxl862xx_port {
|
||||
bool host_flood_uc;
|
||||
bool host_flood_mc;
|
||||
struct work_struct host_flood_work;
|
||||
+ /* Hardware stats accumulation */
|
||||
+ struct mxl862xx_port_stats stats;
|
||||
+ spinlock_t stats_lock;
|
||||
+ spinlock_t stats_lock; /* protects stats accumulators */
|
||||
};
|
||||
|
||||
+/* Bit indices for struct mxl862xx_priv::flags */
|
||||
@@ -378,7 +412,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @drop_meter: index of the single shared zero-rate firmware meter
|
||||
* used to unconditionally drop traffic (used to block
|
||||
* flooding)
|
||||
@@ -216,18 +270,21 @@ struct mxl862xx_port {
|
||||
@@ -181,18 +266,21 @@ struct mxl862xx_port {
|
||||
* @evlan_ingress_size: per-port ingress Extended VLAN block size
|
||||
* @evlan_egress_size: per-port egress Extended VLAN block size
|
||||
* @vf_block_size: per-port VLAN Filter block size
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
From b7b4dcd96e3dfbb955d152c9ce4b490498b0f4b4 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Sun, 11 Jan 2026 11:39:30 +0200
|
||||
Subject: [PATCH 1/5] dt-bindings: phy: rename transmit-amplitude.yaml to
|
||||
phy-common-props.yaml
|
||||
|
||||
I would like to add more properties similar to tx-p2p-microvolt, and I
|
||||
don't think it makes sense to create one schema for each such property
|
||||
(transmit-amplitude.yaml, lane-polarity.yaml, transmit-equalization.yaml
|
||||
etc).
|
||||
|
||||
Instead, let's rename to phy-common-props.yaml, which makes it a more
|
||||
adequate host schema for all the above properties.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Acked-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Link: https://patch.msgid.link/20260111093940.975359-2-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Vinod Koul <vkoul@kernel.org>
|
||||
---
|
||||
.../{transmit-amplitude.yaml => phy-common-props.yaml} | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
rename Documentation/devicetree/bindings/phy/{transmit-amplitude.yaml => phy-common-props.yaml} (90%)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/phy/transmit-amplitude.yaml
|
||||
+++ /dev/null
|
||||
@@ -1,103 +0,0 @@
|
||||
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
-%YAML 1.2
|
||||
----
|
||||
-$id: http://devicetree.org/schemas/phy/transmit-amplitude.yaml#
|
||||
-$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
-
|
||||
-title: Common PHY and network PCS transmit amplitude property
|
||||
-
|
||||
-description:
|
||||
- Binding describing the peak-to-peak transmit amplitude for common PHYs
|
||||
- and network PCSes.
|
||||
-
|
||||
-maintainers:
|
||||
- - Marek Behún <kabel@kernel.org>
|
||||
-
|
||||
-properties:
|
||||
- tx-p2p-microvolt:
|
||||
- description:
|
||||
- Transmit amplitude voltages in microvolts, peak-to-peak. If this property
|
||||
- contains multiple values for various PHY modes, the
|
||||
- 'tx-p2p-microvolt-names' property must be provided and contain
|
||||
- corresponding mode names.
|
||||
-
|
||||
- tx-p2p-microvolt-names:
|
||||
- description: |
|
||||
- Names of the modes corresponding to voltages in the 'tx-p2p-microvolt'
|
||||
- property. Required only if multiple voltages are provided.
|
||||
-
|
||||
- If a value of 'default' is provided, the system should use it for any PHY
|
||||
- mode that is otherwise not defined here. If 'default' is not provided, the
|
||||
- system should use manufacturer default value.
|
||||
- minItems: 1
|
||||
- maxItems: 16
|
||||
- items:
|
||||
- enum:
|
||||
- - default
|
||||
-
|
||||
- # ethernet modes
|
||||
- - sgmii
|
||||
- - qsgmii
|
||||
- - xgmii
|
||||
- - 1000base-x
|
||||
- - 2500base-x
|
||||
- - 5gbase-r
|
||||
- - rxaui
|
||||
- - xaui
|
||||
- - 10gbase-kr
|
||||
- - usxgmii
|
||||
- - 10gbase-r
|
||||
- - 25gbase-r
|
||||
-
|
||||
- # PCIe modes
|
||||
- - pcie
|
||||
- - pcie1
|
||||
- - pcie2
|
||||
- - pcie3
|
||||
- - pcie4
|
||||
- - pcie5
|
||||
- - pcie6
|
||||
-
|
||||
- # USB modes
|
||||
- - usb
|
||||
- - usb-ls
|
||||
- - usb-fs
|
||||
- - usb-hs
|
||||
- - usb-ss
|
||||
- - usb-ss+
|
||||
- - usb-4
|
||||
-
|
||||
- # storage modes
|
||||
- - sata
|
||||
- - ufs-hs
|
||||
- - ufs-hs-a
|
||||
- - ufs-hs-b
|
||||
-
|
||||
- # display modes
|
||||
- - lvds
|
||||
- - dp
|
||||
- - dp-rbr
|
||||
- - dp-hbr
|
||||
- - dp-hbr2
|
||||
- - dp-hbr3
|
||||
- - dp-uhbr-10
|
||||
- - dp-uhbr-13.5
|
||||
- - dp-uhbr-20
|
||||
-
|
||||
- # camera modes
|
||||
- - mipi-dphy
|
||||
- - mipi-dphy-univ
|
||||
- - mipi-dphy-v2.5-univ
|
||||
-
|
||||
-dependencies:
|
||||
- tx-p2p-microvolt-names: [ tx-p2p-microvolt ]
|
||||
-
|
||||
-additionalProperties: true
|
||||
-
|
||||
-examples:
|
||||
- - |
|
||||
- phy: phy {
|
||||
- #phy-cells = <1>;
|
||||
- tx-p2p-microvolt = <915000>, <1100000>, <1200000>;
|
||||
- tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss";
|
||||
- };
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
@@ -0,0 +1,103 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/phy/phy-common-props.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: Common PHY and network PCS properties
|
||||
+
|
||||
+description:
|
||||
+ Common PHY and network PCS properties, such as peak-to-peak transmit
|
||||
+ amplitude.
|
||||
+
|
||||
+maintainers:
|
||||
+ - Marek Behún <kabel@kernel.org>
|
||||
+
|
||||
+properties:
|
||||
+ tx-p2p-microvolt:
|
||||
+ description:
|
||||
+ Transmit amplitude voltages in microvolts, peak-to-peak. If this property
|
||||
+ contains multiple values for various PHY modes, the
|
||||
+ 'tx-p2p-microvolt-names' property must be provided and contain
|
||||
+ corresponding mode names.
|
||||
+
|
||||
+ tx-p2p-microvolt-names:
|
||||
+ description: |
|
||||
+ Names of the modes corresponding to voltages in the 'tx-p2p-microvolt'
|
||||
+ property. Required only if multiple voltages are provided.
|
||||
+
|
||||
+ If a value of 'default' is provided, the system should use it for any PHY
|
||||
+ mode that is otherwise not defined here. If 'default' is not provided, the
|
||||
+ system should use manufacturer default value.
|
||||
+ minItems: 1
|
||||
+ maxItems: 16
|
||||
+ items:
|
||||
+ enum:
|
||||
+ - default
|
||||
+
|
||||
+ # ethernet modes
|
||||
+ - sgmii
|
||||
+ - qsgmii
|
||||
+ - xgmii
|
||||
+ - 1000base-x
|
||||
+ - 2500base-x
|
||||
+ - 5gbase-r
|
||||
+ - rxaui
|
||||
+ - xaui
|
||||
+ - 10gbase-kr
|
||||
+ - usxgmii
|
||||
+ - 10gbase-r
|
||||
+ - 25gbase-r
|
||||
+
|
||||
+ # PCIe modes
|
||||
+ - pcie
|
||||
+ - pcie1
|
||||
+ - pcie2
|
||||
+ - pcie3
|
||||
+ - pcie4
|
||||
+ - pcie5
|
||||
+ - pcie6
|
||||
+
|
||||
+ # USB modes
|
||||
+ - usb
|
||||
+ - usb-ls
|
||||
+ - usb-fs
|
||||
+ - usb-hs
|
||||
+ - usb-ss
|
||||
+ - usb-ss+
|
||||
+ - usb-4
|
||||
+
|
||||
+ # storage modes
|
||||
+ - sata
|
||||
+ - ufs-hs
|
||||
+ - ufs-hs-a
|
||||
+ - ufs-hs-b
|
||||
+
|
||||
+ # display modes
|
||||
+ - lvds
|
||||
+ - dp
|
||||
+ - dp-rbr
|
||||
+ - dp-hbr
|
||||
+ - dp-hbr2
|
||||
+ - dp-hbr3
|
||||
+ - dp-uhbr-10
|
||||
+ - dp-uhbr-13.5
|
||||
+ - dp-uhbr-20
|
||||
+
|
||||
+ # camera modes
|
||||
+ - mipi-dphy
|
||||
+ - mipi-dphy-univ
|
||||
+ - mipi-dphy-v2.5-univ
|
||||
+
|
||||
+dependencies:
|
||||
+ tx-p2p-microvolt-names: [ tx-p2p-microvolt ]
|
||||
+
|
||||
+additionalProperties: true
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ phy: phy {
|
||||
+ #phy-cells = <1>;
|
||||
+ tx-p2p-microvolt = <915000>, <1100000>, <1200000>;
|
||||
+ tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss";
|
||||
+ };
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
From 33c79865c7d3cc84705ed133c101794902e60269 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Sun, 11 Jan 2026 11:39:31 +0200
|
||||
Subject: [PATCH 2/5] dt-bindings: phy-common-props: create a reusable
|
||||
"protocol-names" definition
|
||||
|
||||
Other properties also need to be defined per protocol than just
|
||||
tx-p2p-microvolt-names. Create a common definition to avoid copying a 55
|
||||
line property.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Link: https://patch.msgid.link/20260111093940.975359-3-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Vinod Koul <vkoul@kernel.org>
|
||||
---
|
||||
.../bindings/phy/phy-common-props.yaml | 34 +++++++++++--------
|
||||
1 file changed, 19 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
+++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
@@ -13,22 +13,12 @@ description:
|
||||
maintainers:
|
||||
- Marek Behún <kabel@kernel.org>
|
||||
|
||||
-properties:
|
||||
- tx-p2p-microvolt:
|
||||
+$defs:
|
||||
+ protocol-names:
|
||||
description:
|
||||
- Transmit amplitude voltages in microvolts, peak-to-peak. If this property
|
||||
- contains multiple values for various PHY modes, the
|
||||
- 'tx-p2p-microvolt-names' property must be provided and contain
|
||||
- corresponding mode names.
|
||||
-
|
||||
- tx-p2p-microvolt-names:
|
||||
- description: |
|
||||
- Names of the modes corresponding to voltages in the 'tx-p2p-microvolt'
|
||||
- property. Required only if multiple voltages are provided.
|
||||
-
|
||||
- If a value of 'default' is provided, the system should use it for any PHY
|
||||
- mode that is otherwise not defined here. If 'default' is not provided, the
|
||||
- system should use manufacturer default value.
|
||||
+ Names of the PHY modes. If a value of 'default' is provided, the system
|
||||
+ should use it for any PHY mode that is otherwise not defined here. If
|
||||
+ 'default' is not provided, the system should use manufacturer default value.
|
||||
minItems: 1
|
||||
maxItems: 16
|
||||
items:
|
||||
@@ -89,6 +79,20 @@ properties:
|
||||
- mipi-dphy-univ
|
||||
- mipi-dphy-v2.5-univ
|
||||
|
||||
+properties:
|
||||
+ tx-p2p-microvolt:
|
||||
+ description:
|
||||
+ Transmit amplitude voltages in microvolts, peak-to-peak. If this property
|
||||
+ contains multiple values for various PHY modes, the
|
||||
+ 'tx-p2p-microvolt-names' property must be provided and contain
|
||||
+ corresponding mode names.
|
||||
+
|
||||
+ tx-p2p-microvolt-names:
|
||||
+ description:
|
||||
+ Names of the modes corresponding to voltages in the 'tx-p2p-microvolt'
|
||||
+ property. Required only if multiple voltages are provided.
|
||||
+ $ref: "#/$defs/protocol-names"
|
||||
+
|
||||
dependencies:
|
||||
tx-p2p-microvolt-names: [ tx-p2p-microvolt ]
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
From 01fc2215940c20bbb22fa196a331ec9d50e45452 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Sun, 11 Jan 2026 11:39:32 +0200
|
||||
Subject: [PATCH 3/5] dt-bindings: phy-common-props: ensure protocol-names are
|
||||
unique
|
||||
|
||||
Rob Herring points out that "The default for .*-names is the entries
|
||||
don't have to be unique.":
|
||||
https://lore.kernel.org/linux-phy/20251204155219.GA1533839-robh@kernel.org/
|
||||
|
||||
Let's use uniqueItems: true to make sure the schema enforces this. It
|
||||
doesn't make sense in this case to have duplicate properties for the
|
||||
same SerDes protocol.
|
||||
|
||||
Note that this can only be done with the $defs + $ref pattern as
|
||||
established by the previous commit. When the tx-p2p-microvolt-names
|
||||
constraints were expressed directly under "properties", it would have
|
||||
been validated by the string-array meta-schema, which does not support
|
||||
the 'uniqueItems' keyword as can be seen below.
|
||||
|
||||
properties:tx-p2p-microvolt-names: Additional properties are not allowed ('uniqueItems' was unexpected)
|
||||
from schema $id: http://devicetree.org/meta-schemas/string-array.yaml
|
||||
|
||||
Suggested-by: Rob Herring <robh@kernel.org>
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Link: https://patch.msgid.link/20260111093940.975359-4-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Vinod Koul <vkoul@kernel.org>
|
||||
---
|
||||
Documentation/devicetree/bindings/phy/phy-common-props.yaml | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
+++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
@@ -21,6 +21,7 @@ $defs:
|
||||
'default' is not provided, the system should use manufacturer default value.
|
||||
minItems: 1
|
||||
maxItems: 16
|
||||
+ uniqueItems: true
|
||||
items:
|
||||
enum:
|
||||
- default
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
From fceb17ac05e772ffc82f1f008e876bf7752f0576 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Sun, 11 Jan 2026 11:39:33 +0200
|
||||
Subject: [PATCH 4/5] dt-bindings: phy-common-props: RX and TX lane polarity
|
||||
inversion
|
||||
|
||||
Differential signaling is a technique for high-speed protocols to be
|
||||
more resilient to noise. At the transmit side we have a positive and a
|
||||
negative signal which are mirror images of each other. At the receiver,
|
||||
if we subtract the negative signal (say of amplitude -A) from the
|
||||
positive signal (say +A), we recover the original single-ended signal at
|
||||
twice its original amplitude. But any noise, like one coming from EMI
|
||||
from outside sources, is supposed to have an almost equal impact upon
|
||||
the positive (A + E, E being for "error") and negative signal (-A + E).
|
||||
So (A + E) - (-A + E) eliminates this noise, and this is what makes
|
||||
differential signaling useful.
|
||||
|
||||
Except that in order to work, there must be strict requirements observed
|
||||
during PCB design and layout, like the signal traces needing to have the
|
||||
same length and be physically close to each other, and many others.
|
||||
|
||||
Sometimes it is not easy to fulfill all these requirements, a simple
|
||||
case to understand is when on chip A's pins, the positive pin is on the
|
||||
left and the negative is on the right, but on the chip B's pins (with
|
||||
which A tries to communicate), positive is on the right and negative on
|
||||
the left. The signals would need to cross, using vias and other ugly
|
||||
stuff that affects signal integrity (introduces impedance
|
||||
discontinuities which cause reflections, etc).
|
||||
|
||||
So sometimes, board designers intentionally connect differential lanes
|
||||
the wrong way, and expect somebody else to invert that signal to recover
|
||||
useful data. This is where RX and TX polarity inversion comes in as a
|
||||
generic concept that applies to any high-speed serial protocol as long
|
||||
as it uses differential signaling.
|
||||
|
||||
I've stopped two attempts to introduce more vendor-specific descriptions
|
||||
of this only in the past month:
|
||||
https://lore.kernel.org/linux-phy/20251110110536.2596490-1-horatiu.vultur@microchip.com/
|
||||
https://lore.kernel.org/netdev/20251028000959.3kiac5kwo5pcl4ft@skbuf/
|
||||
|
||||
and in the kernel we already have merged:
|
||||
- "st,px_rx_pol_inv"
|
||||
- "st,pcie-tx-pol-inv"
|
||||
- "st,sata-tx-pol-inv"
|
||||
- "mediatek,pnswap"
|
||||
- "airoha,pnswap-rx"
|
||||
- "airoha,pnswap-tx"
|
||||
|
||||
and maybe more. So it is pretty general.
|
||||
|
||||
One additional element of complexity is introduced by the fact that for
|
||||
some protocols, receivers can automatically detect and correct for an
|
||||
inverted lane polarity (example: the PCIe LTSSM does this in the
|
||||
Polling.Configuration state; the USB 3.1 Link Layer Test Specification
|
||||
says that the detection and correction of the lane polarity inversion in
|
||||
SuperSpeed operation shall be enabled in Polling.RxEQ.). Whereas for
|
||||
other protocols (SGMII, SATA, 10GBase-R, etc etc), the polarity is all
|
||||
manual and there is no detection mechanism mandated by their respective
|
||||
standards.
|
||||
|
||||
So why would one even describe rx-polarity and tx-polarity for protocols
|
||||
like PCIe, if it had to always be PHY_POL_AUTO?
|
||||
|
||||
Related question: why would we define the polarity as an array per
|
||||
protocol? Isn't the physical PCB layout protocol-agnostic, and aren't we
|
||||
describing the same physical reality from the lens of different protocols?
|
||||
|
||||
The answer to both questions is because multi-protocol PHYs exist
|
||||
(supporting e.g. USB2 and USB3, or SATA and PCIe, or PCIe and Ethernet
|
||||
over the same lane), one would need to manually set the polarity for
|
||||
SATA/Ethernet, while leaving it at auto for PCIe/USB 3.0+.
|
||||
|
||||
I also investigated from another angle: what if polarity inversion in
|
||||
the PHY is one layer, and then the PCIe/USB3 LTSSM polarity detection is
|
||||
another layer on top? Then rx-polarity = <PHY_POL_AUTO> doesn't make
|
||||
sense, it can still be rx-polarity = <PHY_POL_NORMAL> or <PHY_POL_INVERT>,
|
||||
and the link training state machine figures things out on top of that.
|
||||
This would radically simplify the design, as the elimination of
|
||||
PHY_POL_AUTO inherently means that the need for a property array per
|
||||
protocol also goes away.
|
||||
|
||||
I don't know how things are in the general case, but at least in the 10G
|
||||
and 28G Lynx SerDes blocks from NXP Layerscape devices, this isn't the
|
||||
case, and there's only a single level of RX polarity inversion: in the
|
||||
SerDes lane. In the case of PCIe, the controller is in charge of driving
|
||||
the RDAT_INV bit autonomously, and it is read-only to software.
|
||||
|
||||
So the existence of this kind of SerDes lane proves the need for
|
||||
PHY_POL_AUTO to be a third state.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Link: https://patch.msgid.link/20260111093940.975359-5-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Vinod Koul <vkoul@kernel.org>
|
||||
---
|
||||
.../bindings/phy/phy-common-props.yaml | 49 +++++++++++++++++++
|
||||
include/dt-bindings/phy/phy.h | 4 ++
|
||||
2 files changed, 53 insertions(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
+++ b/Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
@@ -94,15 +94,64 @@ properties:
|
||||
property. Required only if multiple voltages are provided.
|
||||
$ref: "#/$defs/protocol-names"
|
||||
|
||||
+ rx-polarity:
|
||||
+ description:
|
||||
+ An array of values indicating whether the differential receiver's
|
||||
+ polarity is inverted. Each value can be one of
|
||||
+ PHY_POL_NORMAL (0) which means the negative signal is decoded from the
|
||||
+ RXN input, and the positive signal from the RXP input;
|
||||
+ PHY_POL_INVERT (1) which means the negative signal is decoded from the
|
||||
+ RXP input, and the positive signal from the RXN input;
|
||||
+ PHY_POL_AUTO (2) which means the receiver performs automatic polarity
|
||||
+ detection and correction, which is a mandatory part of link training for
|
||||
+ some protocols (PCIe, USB SS).
|
||||
+
|
||||
+ The values are defined in <dt-bindings/phy/phy.h>. If the property is
|
||||
+ absent, the default value is undefined.
|
||||
+
|
||||
+ Note that the RXP and RXN inputs refer to the block that this property is
|
||||
+ under, and do not necessarily directly translate to external pins.
|
||||
+
|
||||
+ If this property contains multiple values for various protocols, the
|
||||
+ 'rx-polarity-names' property must be provided.
|
||||
+ $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
+ minItems: 1
|
||||
+ maxItems: 16
|
||||
+ items:
|
||||
+ enum: [0, 1, 2]
|
||||
+
|
||||
+ rx-polarity-names:
|
||||
+ $ref: '#/$defs/protocol-names'
|
||||
+
|
||||
+ tx-polarity:
|
||||
+ description:
|
||||
+ Like 'rx-polarity', except it applies to differential transmitters,
|
||||
+ and only the values of PHY_POL_NORMAL and PHY_POL_INVERT are possible.
|
||||
+ $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
+ minItems: 1
|
||||
+ maxItems: 16
|
||||
+ items:
|
||||
+ enum: [0, 1]
|
||||
+
|
||||
+ tx-polarity-names:
|
||||
+ $ref: '#/$defs/protocol-names'
|
||||
+
|
||||
dependencies:
|
||||
tx-p2p-microvolt-names: [ tx-p2p-microvolt ]
|
||||
+ rx-polarity-names: [ rx-polarity ]
|
||||
+ tx-polarity-names: [ tx-polarity ]
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
examples:
|
||||
- |
|
||||
+ #include <dt-bindings/phy/phy.h>
|
||||
+
|
||||
phy: phy {
|
||||
#phy-cells = <1>;
|
||||
tx-p2p-microvolt = <915000>, <1100000>, <1200000>;
|
||||
tx-p2p-microvolt-names = "2500base-x", "usb-hs", "usb-ss";
|
||||
+ rx-polarity = <PHY_POL_AUTO>, <PHY_POL_NORMAL>;
|
||||
+ rx-polarity-names = "usb-ss", "default";
|
||||
+ tx-polarity = <PHY_POL_INVERT>;
|
||||
};
|
||||
--- a/include/dt-bindings/phy/phy.h
|
||||
+++ b/include/dt-bindings/phy/phy.h
|
||||
@@ -24,4 +24,8 @@
|
||||
#define PHY_TYPE_CPHY 11
|
||||
#define PHY_TYPE_USXGMII 12
|
||||
|
||||
+#define PHY_POL_NORMAL 0
|
||||
+#define PHY_POL_INVERT 1
|
||||
+#define PHY_POL_AUTO 2
|
||||
+
|
||||
#endif /* _DT_BINDINGS_PHY */
|
||||
+758
@@ -0,0 +1,758 @@
|
||||
From e7556b59ba65179612bce3fa56bb53d1b4fb20db Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Sun, 11 Jan 2026 11:39:34 +0200
|
||||
Subject: [PATCH 5/5] phy: add phy_get_rx_polarity() and phy_get_tx_polarity()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add helpers in the generic PHY folder which can be used using 'select
|
||||
PHY_COMMON_PROPS' from Kconfig, without otherwise needing to
|
||||
enable GENERIC_PHY.
|
||||
|
||||
These helpers need to deal with the slight messiness of the fact that
|
||||
the polarity properties are arrays per protocol, and with the fact that
|
||||
there is no default value mandated by the standard properties, all
|
||||
default values depend on driver and protocol (PHY_POL_NORMAL may be a
|
||||
good default for SGMII, whereas PHY_POL_AUTO may be a good default for
|
||||
PCIe).
|
||||
|
||||
Push the supported mask of polarities to these helpers, to simplify
|
||||
drivers such that they don't need to validate what's in the device tree
|
||||
(or other firmware description).
|
||||
|
||||
Add a KUnit test suite to make sure that the API produces the expected
|
||||
results. The fact that we use fwnode structures means we can validate
|
||||
with software nodes, and as opposed to the device_property API, we can
|
||||
bypass the need to have a device structure.
|
||||
|
||||
Co-developed-by: Bjørn Mork <bjorn@mork.no>
|
||||
Signed-off-by: Bjørn Mork <bjorn@mork.no>
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Link: https://patch.msgid.link/20260111093940.975359-6-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Vinod Koul <vkoul@kernel.org>
|
||||
---
|
||||
drivers/phy/Kconfig | 22 ++
|
||||
drivers/phy/Makefile | 2 +
|
||||
drivers/phy/phy-common-props-test.c | 422 +++++++++++++++++++++++++++
|
||||
drivers/phy/phy-common-props.c | 209 +++++++++++++
|
||||
include/linux/phy/phy-common-props.h | 32 ++
|
||||
6 files changed, 697 insertions(+)
|
||||
create mode 100644 drivers/phy/phy-common-props-test.c
|
||||
create mode 100644 drivers/phy/phy-common-props.c
|
||||
create mode 100644 include/linux/phy/phy-common-props.h
|
||||
|
||||
--- a/drivers/phy/Kconfig
|
||||
+++ b/drivers/phy/Kconfig
|
||||
@@ -5,6 +5,28 @@
|
||||
|
||||
menu "PHY Subsystem"
|
||||
|
||||
+config PHY_COMMON_PROPS
|
||||
+ bool
|
||||
+ help
|
||||
+ This parses properties common between generic PHYs and Ethernet PHYs.
|
||||
+
|
||||
+ Select this from consumer drivers to gain access to helpers for
|
||||
+ parsing properties from the
|
||||
+ Documentation/devicetree/bindings/phy/phy-common-props.yaml schema.
|
||||
+
|
||||
+config PHY_COMMON_PROPS_TEST
|
||||
+ tristate "KUnit tests for PHY common props" if !KUNIT_ALL_TESTS
|
||||
+ select PHY_COMMON_PROPS
|
||||
+ depends on KUNIT
|
||||
+ default KUNIT_ALL_TESTS
|
||||
+ help
|
||||
+ This builds KUnit tests for the PHY common property API.
|
||||
+
|
||||
+ For more information on KUnit and unit tests in general,
|
||||
+ please refer to the KUnit documentation in Documentation/dev-tools/kunit/.
|
||||
+
|
||||
+ When in doubt, say N.
|
||||
+
|
||||
config GENERIC_PHY
|
||||
bool "PHY Core"
|
||||
help
|
||||
--- a/drivers/phy/Makefile
|
||||
+++ b/drivers/phy/Makefile
|
||||
@@ -3,6 +3,8 @@
|
||||
# Makefile for the phy drivers.
|
||||
#
|
||||
|
||||
+obj-$(CONFIG_PHY_COMMON_PROPS) += phy-common-props.o
|
||||
+obj-$(CONFIG_PHY_COMMON_PROPS_TEST) += phy-common-props-test.o
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o
|
||||
obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/phy/phy-common-props-test.c
|
||||
@@ -0,0 +1,422 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * phy-common-props-test.c -- Unit tests for PHY common properties API
|
||||
+ *
|
||||
+ * Copyright 2025-2026 NXP
|
||||
+ */
|
||||
+#include <kunit/test.h>
|
||||
+#include <linux/property.h>
|
||||
+#include <linux/phy/phy-common-props.h>
|
||||
+#include <dt-bindings/phy/phy.h>
|
||||
+
|
||||
+/* Test: rx-polarity property is missing */
|
||||
+static void phy_test_rx_polarity_is_missing(struct kunit *test)
|
||||
+{
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: rx-polarity has more values than rx-polarity-names */
|
||||
+static void phy_test_rx_polarity_more_values_than_names(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL };
|
||||
+ static const char * const rx_pol_names[] = { "sgmii", "2500base-x" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: rx-polarity has 1 value and rx-polarity-names does not exist */
|
||||
+static void phy_test_rx_polarity_single_value_no_names(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 rx_pol[] = { PHY_POL_INVERT };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: rx-polarity-names has more values than rx-polarity */
|
||||
+static void phy_test_rx_polarity_more_names_than_values(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
+ static const char * const rx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: rx-polarity and rx-polarity-names have same length, find the name */
|
||||
+static void phy_test_rx_polarity_find_by_name(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_AUTO };
|
||||
+ static const char * const rx_pol_names[] = { "sgmii", "2500base-x", "usb-ss" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "2500base-x", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
+
|
||||
+ ret = phy_get_rx_polarity(node, "usb-ss", BIT(PHY_POL_AUTO),
|
||||
+ PHY_POL_AUTO, &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_AUTO);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: same length, name not found, no "default" - error */
|
||||
+static void phy_test_rx_polarity_name_not_found_no_default(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
+ static const char * const rx_pol_names[] = { "2500base-x", "1000base-x" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: same length, name not found, but "default" exists */
|
||||
+static void phy_test_rx_polarity_name_not_found_with_default(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 rx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
+ static const char * const rx_pol_names[] = { "2500base-x", "default" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: polarity found but value is unsupported */
|
||||
+static void phy_test_rx_polarity_unsupported_value(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 rx_pol[] = { PHY_POL_AUTO };
|
||||
+ static const char * const rx_pol_names[] = { "sgmii" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("rx-polarity", rx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("rx-polarity-names", rx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_rx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, -EOPNOTSUPP);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: tx-polarity property is missing */
|
||||
+static void phy_test_tx_polarity_is_missing(struct kunit *test)
|
||||
+{
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: tx-polarity has more values than tx-polarity-names */
|
||||
+static void phy_test_tx_polarity_more_values_than_names(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL };
|
||||
+ static const char * const tx_pol_names[] = { "sgmii", "2500base-x" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: tx-polarity has 1 value and tx-polarity-names does not exist */
|
||||
+static void phy_test_tx_polarity_single_value_no_names(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 tx_pol[] = { PHY_POL_INVERT };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: tx-polarity-names has more values than tx-polarity */
|
||||
+static void phy_test_tx_polarity_more_names_than_values(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
+ static const char * const tx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: tx-polarity and tx-polarity-names have same length, find the name */
|
||||
+static void phy_test_tx_polarity_find_by_name(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT, PHY_POL_NORMAL };
|
||||
+ static const char * const tx_pol_names[] = { "sgmii", "2500base-x", "1000base-x" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "2500base-x", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "1000base-x", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_NORMAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: same length, name not found, no "default" - error */
|
||||
+static void phy_test_tx_polarity_name_not_found_no_default(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
+ static const char * const tx_pol_names[] = { "2500base-x", "1000base-x" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: same length, name not found, but "default" exists */
|
||||
+static void phy_test_tx_polarity_name_not_found_with_default(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 tx_pol[] = { PHY_POL_NORMAL, PHY_POL_INVERT };
|
||||
+ static const char * const tx_pol_names[] = { "2500base-x", "default" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
+ KUNIT_EXPECT_EQ(test, val, PHY_POL_INVERT);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+/* Test: polarity found but value is unsupported (AUTO for TX) */
|
||||
+static void phy_test_tx_polarity_unsupported_value(struct kunit *test)
|
||||
+{
|
||||
+ static const u32 tx_pol[] = { PHY_POL_AUTO };
|
||||
+ static const char * const tx_pol_names[] = { "sgmii" };
|
||||
+ static const struct property_entry entries[] = {
|
||||
+ PROPERTY_ENTRY_U32_ARRAY("tx-polarity", tx_pol),
|
||||
+ PROPERTY_ENTRY_STRING_ARRAY("tx-polarity-names", tx_pol_names),
|
||||
+ {}
|
||||
+ };
|
||||
+ struct fwnode_handle *node;
|
||||
+ unsigned int val;
|
||||
+ int ret;
|
||||
+
|
||||
+ node = fwnode_create_software_node(entries, NULL);
|
||||
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
|
||||
+
|
||||
+ ret = phy_get_manual_tx_polarity(node, "sgmii", &val);
|
||||
+ KUNIT_EXPECT_EQ(test, ret, -EOPNOTSUPP);
|
||||
+
|
||||
+ fwnode_remove_software_node(node);
|
||||
+}
|
||||
+
|
||||
+static struct kunit_case phy_common_props_test_cases[] = {
|
||||
+ KUNIT_CASE(phy_test_rx_polarity_is_missing),
|
||||
+ KUNIT_CASE(phy_test_rx_polarity_more_values_than_names),
|
||||
+ KUNIT_CASE(phy_test_rx_polarity_single_value_no_names),
|
||||
+ KUNIT_CASE(phy_test_rx_polarity_more_names_than_values),
|
||||
+ KUNIT_CASE(phy_test_rx_polarity_find_by_name),
|
||||
+ KUNIT_CASE(phy_test_rx_polarity_name_not_found_no_default),
|
||||
+ KUNIT_CASE(phy_test_rx_polarity_name_not_found_with_default),
|
||||
+ KUNIT_CASE(phy_test_rx_polarity_unsupported_value),
|
||||
+ KUNIT_CASE(phy_test_tx_polarity_is_missing),
|
||||
+ KUNIT_CASE(phy_test_tx_polarity_more_values_than_names),
|
||||
+ KUNIT_CASE(phy_test_tx_polarity_single_value_no_names),
|
||||
+ KUNIT_CASE(phy_test_tx_polarity_more_names_than_values),
|
||||
+ KUNIT_CASE(phy_test_tx_polarity_find_by_name),
|
||||
+ KUNIT_CASE(phy_test_tx_polarity_name_not_found_no_default),
|
||||
+ KUNIT_CASE(phy_test_tx_polarity_name_not_found_with_default),
|
||||
+ KUNIT_CASE(phy_test_tx_polarity_unsupported_value),
|
||||
+ {}
|
||||
+};
|
||||
+
|
||||
+static struct kunit_suite phy_common_props_test_suite = {
|
||||
+ .name = "phy-common-props",
|
||||
+ .test_cases = phy_common_props_test_cases,
|
||||
+};
|
||||
+
|
||||
+kunit_test_suite(phy_common_props_test_suite);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Test module for PHY common properties API");
|
||||
+MODULE_AUTHOR("Vladimir Oltean <vladimir.oltean@nxp.com>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
--- /dev/null
|
||||
+++ b/drivers/phy/phy-common-props.c
|
||||
@@ -0,0 +1,209 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * phy-common-props.c -- Common PHY properties
|
||||
+ *
|
||||
+ * Copyright 2025-2026 NXP
|
||||
+ */
|
||||
+#include <linux/export.h>
|
||||
+#include <linux/fwnode.h>
|
||||
+#include <linux/phy/phy-common-props.h>
|
||||
+#include <linux/printk.h>
|
||||
+#include <linux/property.h>
|
||||
+#include <linux/slab.h>
|
||||
+
|
||||
+/**
|
||||
+ * fwnode_get_u32_prop_for_name - Find u32 property by name, or default value
|
||||
+ * @fwnode: Pointer to firmware node, or NULL to use @default_val
|
||||
+ * @name: Property name used as lookup key in @names_title (must not be NULL)
|
||||
+ * @props_title: Name of u32 array property holding values
|
||||
+ * @names_title: Name of string array property holding lookup keys
|
||||
+ * @default_val: Default value if @fwnode is NULL or @props_title is empty
|
||||
+ * @val: Pointer to store the returned value
|
||||
+ *
|
||||
+ * This function retrieves a u32 value from @props_title based on a name lookup
|
||||
+ * in @names_title. The value stored in @val is determined as follows:
|
||||
+ *
|
||||
+ * - If @fwnode is NULL or @props_title is empty: @default_val is used
|
||||
+ * - If @props_title has exactly one element and @names_title is empty:
|
||||
+ * that element is used
|
||||
+ * - Otherwise: @val is set to the element at the same index where @name is
|
||||
+ * found in @names_title.
|
||||
+ * - If @name is not found, the function looks for a "default" entry in
|
||||
+ * @names_title and uses the corresponding value from @props_title
|
||||
+ *
|
||||
+ * When both @props_title and @names_title are present, they must have the
|
||||
+ * same number of elements (except when @props_title has exactly one element).
|
||||
+ *
|
||||
+ * Return: zero on success, negative error on failure.
|
||||
+ */
|
||||
+static int fwnode_get_u32_prop_for_name(struct fwnode_handle *fwnode,
|
||||
+ const char *name,
|
||||
+ const char *props_title,
|
||||
+ const char *names_title,
|
||||
+ unsigned int default_val,
|
||||
+ unsigned int *val)
|
||||
+{
|
||||
+ int err, n_props, n_names, idx;
|
||||
+ u32 *props;
|
||||
+
|
||||
+ if (!name) {
|
||||
+ pr_err("Lookup key inside \"%s\" is mandatory\n", names_title);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ n_props = fwnode_property_count_u32(fwnode, props_title);
|
||||
+ if (n_props <= 0) {
|
||||
+ /* fwnode is NULL, or is missing requested property */
|
||||
+ *val = default_val;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ n_names = fwnode_property_string_array_count(fwnode, names_title);
|
||||
+ if (n_names >= 0 && n_props != n_names) {
|
||||
+ pr_err("%pfw mismatch between \"%s\" and \"%s\" property count (%d vs %d)\n",
|
||||
+ fwnode, props_title, names_title, n_props, n_names);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ idx = fwnode_property_match_string(fwnode, names_title, name);
|
||||
+ if (idx < 0)
|
||||
+ idx = fwnode_property_match_string(fwnode, names_title, "default");
|
||||
+ /*
|
||||
+ * If the mode name is missing, it can only mean the specified property
|
||||
+ * is the default one for all modes, so reject any other property count
|
||||
+ * than 1.
|
||||
+ */
|
||||
+ if (idx < 0 && n_props != 1) {
|
||||
+ pr_err("%pfw \"%s \" property has %d elements, but cannot find \"%s\" in \"%s\" and there is no default value\n",
|
||||
+ fwnode, props_title, n_props, name, names_title);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (n_props == 1) {
|
||||
+ err = fwnode_property_read_u32(fwnode, props_title, val);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* We implicitly know idx >= 0 here */
|
||||
+ props = kcalloc(n_props, sizeof(*props), GFP_KERNEL);
|
||||
+ if (!props)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ err = fwnode_property_read_u32_array(fwnode, props_title, props, n_props);
|
||||
+ if (err >= 0)
|
||||
+ *val = props[idx];
|
||||
+
|
||||
+ kfree(props);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static int phy_get_polarity_for_mode(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name,
|
||||
+ unsigned int supported,
|
||||
+ unsigned int default_val,
|
||||
+ const char *polarity_prop,
|
||||
+ const char *names_prop,
|
||||
+ unsigned int *val)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ err = fwnode_get_u32_prop_for_name(fwnode, mode_name, polarity_prop,
|
||||
+ names_prop, default_val, val);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ if (!(supported & BIT(*val))) {
|
||||
+ pr_err("%d is not a supported value for %pfw '%s' element '%s'\n",
|
||||
+ *val, fwnode, polarity_prop, mode_name);
|
||||
+ err = -EOPNOTSUPP;
|
||||
+ }
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * phy_get_rx_polarity - Get RX polarity for PHY differential lane
|
||||
+ * @fwnode: Pointer to the PHY's firmware node.
|
||||
+ * @mode_name: The name of the PHY mode to look up.
|
||||
+ * @supported: Bit mask of PHY_POL_NORMAL, PHY_POL_INVERT and PHY_POL_AUTO
|
||||
+ * @default_val: Default polarity value if property is missing
|
||||
+ * @val: Pointer to returned polarity.
|
||||
+ *
|
||||
+ * Return: zero on success, negative error on failure.
|
||||
+ */
|
||||
+int __must_check phy_get_rx_polarity(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name,
|
||||
+ unsigned int supported,
|
||||
+ unsigned int default_val,
|
||||
+ unsigned int *val)
|
||||
+{
|
||||
+ return phy_get_polarity_for_mode(fwnode, mode_name, supported,
|
||||
+ default_val, "rx-polarity",
|
||||
+ "rx-polarity-names", val);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phy_get_rx_polarity);
|
||||
+
|
||||
+/**
|
||||
+ * phy_get_tx_polarity - Get TX polarity for PHY differential lane
|
||||
+ * @fwnode: Pointer to the PHY's firmware node.
|
||||
+ * @mode_name: The name of the PHY mode to look up.
|
||||
+ * @supported: Bit mask of PHY_POL_NORMAL and PHY_POL_INVERT
|
||||
+ * @default_val: Default polarity value if property is missing
|
||||
+ * @val: Pointer to returned polarity.
|
||||
+ *
|
||||
+ * Return: zero on success, negative error on failure.
|
||||
+ */
|
||||
+int __must_check phy_get_tx_polarity(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name, unsigned int supported,
|
||||
+ unsigned int default_val, unsigned int *val)
|
||||
+{
|
||||
+ return phy_get_polarity_for_mode(fwnode, mode_name, supported,
|
||||
+ default_val, "tx-polarity",
|
||||
+ "tx-polarity-names", val);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phy_get_tx_polarity);
|
||||
+
|
||||
+/**
|
||||
+ * phy_get_manual_rx_polarity - Get manual RX polarity for PHY differential lane
|
||||
+ * @fwnode: Pointer to the PHY's firmware node.
|
||||
+ * @mode_name: The name of the PHY mode to look up.
|
||||
+ * @val: Pointer to returned polarity.
|
||||
+ *
|
||||
+ * Helper for PHYs which do not support protocols with automatic RX polarity
|
||||
+ * detection and correction.
|
||||
+ *
|
||||
+ * Return: zero on success, negative error on failure.
|
||||
+ */
|
||||
+int __must_check phy_get_manual_rx_polarity(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name,
|
||||
+ unsigned int *val)
|
||||
+{
|
||||
+ return phy_get_rx_polarity(fwnode, mode_name,
|
||||
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
||||
+ PHY_POL_NORMAL, val);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phy_get_manual_rx_polarity);
|
||||
+
|
||||
+/**
|
||||
+ * phy_get_manual_tx_polarity - Get manual TX polarity for PHY differential lane
|
||||
+ * @fwnode: Pointer to the PHY's firmware node.
|
||||
+ * @mode_name: The name of the PHY mode to look up.
|
||||
+ * @val: Pointer to returned polarity.
|
||||
+ *
|
||||
+ * Helper for PHYs without any custom default value for the TX polarity.
|
||||
+ *
|
||||
+ * Return: zero on success, negative error on failure.
|
||||
+ */
|
||||
+int __must_check phy_get_manual_tx_polarity(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name,
|
||||
+ unsigned int *val)
|
||||
+{
|
||||
+ return phy_get_tx_polarity(fwnode, mode_name,
|
||||
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
||||
+ PHY_POL_NORMAL, val);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phy_get_manual_tx_polarity);
|
||||
--- /dev/null
|
||||
+++ b/include/linux/phy/phy-common-props.h
|
||||
@@ -0,0 +1,32 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
+/*
|
||||
+ * phy-common-props.h -- Common properties for generic PHYs
|
||||
+ *
|
||||
+ * Copyright 2025 NXP
|
||||
+ */
|
||||
+
|
||||
+#ifndef __PHY_COMMON_PROPS_H
|
||||
+#define __PHY_COMMON_PROPS_H
|
||||
+
|
||||
+#include <dt-bindings/phy/phy.h>
|
||||
+
|
||||
+struct fwnode_handle;
|
||||
+
|
||||
+int __must_check phy_get_rx_polarity(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name,
|
||||
+ unsigned int supported,
|
||||
+ unsigned int default_val,
|
||||
+ unsigned int *val);
|
||||
+int __must_check phy_get_tx_polarity(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name,
|
||||
+ unsigned int supported,
|
||||
+ unsigned int default_val,
|
||||
+ unsigned int *val);
|
||||
+int __must_check phy_get_manual_rx_polarity(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name,
|
||||
+ unsigned int *val);
|
||||
+int __must_check phy_get_manual_tx_polarity(struct fwnode_handle *fwnode,
|
||||
+ const char *mode_name,
|
||||
+ unsigned int *val);
|
||||
+
|
||||
+#endif /* __PHY_COMMON_PROPS_H */
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
From 44f62aa1b1209232cedcfb39097fc1bfbe75bbc7 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Mon, 19 Jan 2026 11:12:16 +0200
|
||||
Subject: [PATCH 1/2] dt-bindings: net: airoha,en8811h: deprecate
|
||||
"airoha,pnswap-rx" and "airoha,pnswap-tx"
|
||||
|
||||
Reference the common PHY properties, and update the example to use them.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Link: https://patch.msgid.link/20260119091220.1493761-2-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
.../devicetree/bindings/net/airoha,en8811h.yaml | 11 ++++++++++-
|
||||
1 file changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/net/airoha,en8811h.yaml
|
||||
+++ b/Documentation/devicetree/bindings/net/airoha,en8811h.yaml
|
||||
@@ -16,6 +16,7 @@ description:
|
||||
|
||||
allOf:
|
||||
- $ref: ethernet-phy.yaml#
|
||||
+ - $ref: /schemas/phy/phy-common-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@@ -30,12 +31,18 @@ properties:
|
||||
description:
|
||||
Reverse rx polarity of the SERDES. This is the receiving
|
||||
side of the lines from the MAC towards the EN881H.
|
||||
+ This property is deprecated, for details please refer to
|
||||
+ Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
+ deprecated: true
|
||||
|
||||
airoha,pnswap-tx:
|
||||
type: boolean
|
||||
description:
|
||||
Reverse tx polarity of SERDES. This is the transmitting
|
||||
side of the lines from EN8811H towards the MAC.
|
||||
+ This property is deprecated, for details please refer to
|
||||
+ Documentation/devicetree/bindings/phy/phy-common-props.yaml
|
||||
+ deprecated: true
|
||||
|
||||
required:
|
||||
- reg
|
||||
@@ -44,6 +51,8 @@ unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
+ #include <dt-bindings/phy/phy.h>
|
||||
+
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@@ -51,6 +60,6 @@ examples:
|
||||
ethernet-phy@1 {
|
||||
compatible = "ethernet-phy-id03a2.a411";
|
||||
reg = <1>;
|
||||
- airoha,pnswap-rx;
|
||||
+ rx-polarity = <PHY_POL_INVERT>;
|
||||
};
|
||||
};
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
From 66d8a334b57e64e43810623b3d88f0ce9745270b Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Mon, 19 Jan 2026 11:12:17 +0200
|
||||
Subject: [PATCH 2/2] net: phy: air_en8811h: deprecate "airoha,pnswap-rx" and
|
||||
"airoha,pnswap-tx"
|
||||
|
||||
Prefer the new "rx-polarity" and "tx-polarity" properties, and use the
|
||||
vendor specific ones as fallback if the standard description doesn't
|
||||
exist.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
Link: https://patch.msgid.link/20260119091220.1493761-3-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/Kconfig | 1 +
|
||||
drivers/net/phy/air_en8811h.c | 53 +++++++++++++++++++++++++----------
|
||||
2 files changed, 39 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/Kconfig
|
||||
+++ b/drivers/net/phy/Kconfig
|
||||
@@ -98,6 +98,7 @@ config AS21XXX_PHY
|
||||
|
||||
config AIR_EN8811H_PHY
|
||||
tristate "Airoha EN8811H 2.5 Gigabit PHY"
|
||||
+ select PHY_COMMON_PROPS
|
||||
help
|
||||
Currently supports the Airoha EN8811H PHY.
|
||||
|
||||
--- a/drivers/net/phy/air_en8811h.c
|
||||
+++ b/drivers/net/phy/air_en8811h.c
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/phy.h>
|
||||
+#include <linux/phy/phy-common-props.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/wordpart.h>
|
||||
@@ -966,11 +967,45 @@ static int en8811h_probe(struct phy_devi
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int en8811h_config_serdes_polarity(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ unsigned int pol, default_pol;
|
||||
+ u32 pbus_value = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ default_pol = PHY_POL_NORMAL;
|
||||
+ if (device_property_read_bool(dev, "airoha,pnswap-rx"))
|
||||
+ default_pol = PHY_POL_INVERT;
|
||||
+
|
||||
+ ret = phy_get_rx_polarity(dev_fwnode(dev), phy_modes(phydev->interface),
|
||||
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
||||
+ default_pol, &pol);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (pol == PHY_POL_INVERT)
|
||||
+ pbus_value |= EN8811H_POLARITY_RX_REVERSE;
|
||||
+
|
||||
+ default_pol = PHY_POL_NORMAL;
|
||||
+ if (device_property_read_bool(dev, "airoha,pnswap-tx"))
|
||||
+ default_pol = PHY_POL_INVERT;
|
||||
+
|
||||
+ ret = phy_get_tx_polarity(dev_fwnode(dev), phy_modes(phydev->interface),
|
||||
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
||||
+ default_pol, &pol);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (pol == PHY_POL_NORMAL)
|
||||
+ pbus_value |= EN8811H_POLARITY_TX_NORMAL;
|
||||
+
|
||||
+ return air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
|
||||
+ EN8811H_POLARITY_RX_REVERSE |
|
||||
+ EN8811H_POLARITY_TX_NORMAL, pbus_value);
|
||||
+}
|
||||
+
|
||||
static int en8811h_config_init(struct phy_device *phydev)
|
||||
{
|
||||
struct en8811h_priv *priv = phydev->priv;
|
||||
- struct device *dev = &phydev->mdio.dev;
|
||||
- u32 pbus_value;
|
||||
int ret;
|
||||
|
||||
/* If restart happened in .probe(), no need to restart now */
|
||||
@@ -1003,19 +1038,7 @@ static int en8811h_config_init(struct ph
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- /* Serdes polarity */
|
||||
- pbus_value = 0;
|
||||
- if (device_property_read_bool(dev, "airoha,pnswap-rx"))
|
||||
- pbus_value |= EN8811H_POLARITY_RX_REVERSE;
|
||||
- else
|
||||
- pbus_value &= ~EN8811H_POLARITY_RX_REVERSE;
|
||||
- if (device_property_read_bool(dev, "airoha,pnswap-tx"))
|
||||
- pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
|
||||
- else
|
||||
- pbus_value |= EN8811H_POLARITY_TX_NORMAL;
|
||||
- ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
|
||||
- EN8811H_POLARITY_RX_REVERSE |
|
||||
- EN8811H_POLARITY_TX_NORMAL, pbus_value);
|
||||
+ ret = en8811h_config_serdes_polarity(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
From 9f841922ebd0a34c78fc1984dc9abcb346704c58 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Mon, 19 Jan 2026 11:12:18 +0200
|
||||
Subject: [PATCH 1/3] dt-bindings: net: pcs: mediatek,sgmiisys: deprecate
|
||||
"mediatek,pnswap"
|
||||
|
||||
Reference the common PHY properties, and update the example to use them.
|
||||
Note that a PCS subnode exists, and it seems a better container of the
|
||||
polarity description than the SGMIISYS node that hosts "mediatek,pnswap".
|
||||
So use that.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Link: https://patch.msgid.link/20260119091220.1493761-4-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
.../devicetree/bindings/net/pcs/mediatek,sgmiisys.yaml | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/net/pcs/mediatek,sgmiisys.yaml
|
||||
+++ b/Documentation/devicetree/bindings/net/pcs/mediatek,sgmiisys.yaml
|
||||
@@ -39,12 +39,17 @@ properties:
|
||||
const: 1
|
||||
|
||||
mediatek,pnswap:
|
||||
- description: Invert polarity of the SGMII data lanes
|
||||
+ description:
|
||||
+ Invert polarity of the SGMII data lanes.
|
||||
+ This property is deprecated, for details please refer to
|
||||
+ Documentation/devicetree/bindings/phy/phy-common-props.yaml.
|
||||
type: boolean
|
||||
+ deprecated: true
|
||||
|
||||
pcs:
|
||||
type: object
|
||||
description: MediaTek LynxI HSGMII PCS
|
||||
+ $ref: /schemas/phy/phy-common-props.yaml#
|
||||
properties:
|
||||
compatible:
|
||||
const: mediatek,mt7988-sgmii
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
From bde1ae2d52ab3599e1c7ca68a90af8407d20f91d Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Mon, 19 Jan 2026 11:12:19 +0200
|
||||
Subject: [PATCH 2/3] net: pcs: pcs-mtk-lynxi: pass SGMIISYS OF node to PCS
|
||||
|
||||
The Mediatek LynxI PCS is used from the MT7530 DSA driver (where it does
|
||||
not have an OF presence) and from mtk_eth_soc, where it does
|
||||
(Documentation/devicetree/bindings/net/pcs/mediatek,sgmiisys.yaml
|
||||
informs of a combined clock provider + SGMII PCS "SGMIISYS" syscon
|
||||
block).
|
||||
|
||||
Currently, mtk_eth_soc parses the SGMIISYS OF node for the
|
||||
"mediatek,pnswap" property and sets a bit in the "flags" argument of
|
||||
mtk_pcs_lynxi_create() if set.
|
||||
|
||||
I'd like to deprecate "mediatek,pnswap" in favour of a property which
|
||||
takes the current phy-mode into consideration. But this is only known at
|
||||
mtk_pcs_lynxi_config() time, and not known at mtk_pcs_lynxi_create(),
|
||||
when the SGMIISYS OF node is parsed.
|
||||
|
||||
To achieve that, we must pass the OF node of the PCS, if it exists, to
|
||||
mtk_pcs_lynxi_create(), and let the PCS take a reference on it and
|
||||
handle property parsing whenever it wants.
|
||||
|
||||
Use the fwnode API which is more general than OF (in case we ever need
|
||||
to describe the PCS using some other format). This API should be NULL
|
||||
tolerant, so add no particular tests for the mt7530 case.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Link: https://patch.msgid.link/20260119091220.1493761-5-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/mt7530-mdio.c | 4 ++--
|
||||
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 19 ++++++++-----------
|
||||
drivers/net/pcs/pcs-mtk-lynxi.c | 15 ++++++++++-----
|
||||
include/linux/pcs/pcs-mtk-lynxi.h | 5 ++---
|
||||
4 files changed, 22 insertions(+), 21 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mt7530-mdio.c
|
||||
+++ b/drivers/net/dsa/mt7530-mdio.c
|
||||
@@ -113,8 +113,8 @@ mt7531_create_sgmii(struct mt7530_priv *
|
||||
ret = PTR_ERR(regmap);
|
||||
break;
|
||||
}
|
||||
- pcs = mtk_pcs_lynxi_create(priv->dev, regmap,
|
||||
- MT7531_PHYA_CTRL_SIGNAL3, 0);
|
||||
+ pcs = mtk_pcs_lynxi_create(priv->dev, NULL, regmap,
|
||||
+ MT7531_PHYA_CTRL_SIGNAL3);
|
||||
if (!pcs) {
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -5000,7 +5000,6 @@ static int mtk_sgmii_init(struct mtk_eth
|
||||
{
|
||||
struct device_node *np;
|
||||
struct regmap *regmap;
|
||||
- u32 flags;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MTK_MAX_DEVS; i++) {
|
||||
@@ -5009,18 +5008,16 @@ static int mtk_sgmii_init(struct mtk_eth
|
||||
break;
|
||||
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
- flags = 0;
|
||||
- if (of_property_read_bool(np, "mediatek,pnswap"))
|
||||
- flags |= MTK_SGMII_FLAG_PN_SWAP;
|
||||
-
|
||||
- of_node_put(np);
|
||||
-
|
||||
- if (IS_ERR(regmap))
|
||||
+ if (IS_ERR(regmap)) {
|
||||
+ of_node_put(np);
|
||||
return PTR_ERR(regmap);
|
||||
+ }
|
||||
|
||||
- eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, regmap,
|
||||
- eth->soc->ana_rgc3,
|
||||
- flags);
|
||||
+ eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev,
|
||||
+ of_fwnode_handle(np),
|
||||
+ regmap,
|
||||
+ eth->soc->ana_rgc3);
|
||||
+ of_node_put(np);
|
||||
}
|
||||
|
||||
return 0;
|
||||
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
@@ -81,6 +81,7 @@ struct mtk_pcs_lynxi {
|
||||
phy_interface_t interface;
|
||||
struct phylink_pcs pcs;
|
||||
u32 flags;
|
||||
+ struct fwnode_handle *fwnode;
|
||||
};
|
||||
|
||||
static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
|
||||
@@ -166,7 +167,7 @@ static int mtk_pcs_lynxi_config(struct p
|
||||
regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
|
||||
SGMII_SW_RESET);
|
||||
|
||||
- if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
|
||||
+ if (fwnode_property_read_bool(mpcs->fwnode, "mediatek,pnswap"))
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
|
||||
SGMII_PN_SWAP_MASK,
|
||||
SGMII_PN_SWAP_TX_RX);
|
||||
@@ -266,8 +267,8 @@ static const struct phylink_pcs_ops mtk_
|
||||
};
|
||||
|
||||
struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
|
||||
- struct regmap *regmap, u32 ana_rgc3,
|
||||
- u32 flags)
|
||||
+ struct fwnode_handle *fwnode,
|
||||
+ struct regmap *regmap, u32 ana_rgc3)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs;
|
||||
u32 id, ver;
|
||||
@@ -301,10 +302,10 @@ struct phylink_pcs *mtk_pcs_lynxi_create
|
||||
|
||||
mpcs->ana_rgc3 = ana_rgc3;
|
||||
mpcs->regmap = regmap;
|
||||
- mpcs->flags = flags;
|
||||
mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
|
||||
mpcs->pcs.poll = true;
|
||||
mpcs->interface = PHY_INTERFACE_MODE_NA;
|
||||
+ mpcs->fwnode = fwnode_handle_get(fwnode);
|
||||
|
||||
__set_bit(PHY_INTERFACE_MODE_SGMII, mpcs->pcs.supported_interfaces);
|
||||
__set_bit(PHY_INTERFACE_MODE_1000BASEX, mpcs->pcs.supported_interfaces);
|
||||
@@ -316,10 +317,14 @@ EXPORT_SYMBOL(mtk_pcs_lynxi_create);
|
||||
|
||||
void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
|
||||
{
|
||||
+ struct mtk_pcs_lynxi *mpcs;
|
||||
+
|
||||
if (!pcs)
|
||||
return;
|
||||
|
||||
- kfree(pcs_to_mtk_pcs_lynxi(pcs));
|
||||
+ mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
+ fwnode_handle_put(mpcs->fwnode);
|
||||
+ kfree(mpcs);
|
||||
}
|
||||
EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
|
||||
|
||||
--- a/include/linux/pcs/pcs-mtk-lynxi.h
|
||||
+++ b/include/linux/pcs/pcs-mtk-lynxi.h
|
||||
@@ -5,9 +5,8 @@
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
-#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
|
||||
struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
|
||||
- struct regmap *regmap,
|
||||
- u32 ana_rgc3, u32 flags);
|
||||
+ struct fwnode_handle *fwnode,
|
||||
+ struct regmap *regmap, u32 ana_rgc3);
|
||||
void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
|
||||
#endif
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
From 8871389da15165198c3407584d40e7295bceaca5 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Mon, 19 Jan 2026 11:12:20 +0200
|
||||
Subject: [PATCH 3/3] net: pcs: pcs-mtk-lynxi: deprecate "mediatek,pnswap"
|
||||
|
||||
Prefer the new "rx-polarity" and "tx-polarity" properties, which in this
|
||||
case have the advantage that polarity inversion can be specified per
|
||||
direction (and per protocol, although this isn't useful here).
|
||||
|
||||
We use the vendor specific ones as fallback if the standard description
|
||||
doesn't exist.
|
||||
|
||||
Daniel, referring to the Mediatek SDK, clarifies that the combined
|
||||
SGMII_PN_SWAP_TX_RX register field should be split like this: bit 0 is
|
||||
TX and bit 1 is RX:
|
||||
https://lore.kernel.org/linux-phy/aSW--slbJWpXK0nv@makrotopia.org/
|
||||
|
||||
Suggested-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Link: https://patch.msgid.link/20260119091220.1493761-6-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/pcs/Kconfig | 1 +
|
||||
drivers/net/pcs/pcs-mtk-lynxi.c | 50 +++++++++++++++++++++++++++++----
|
||||
2 files changed, 45 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/net/pcs/Kconfig
|
||||
+++ b/drivers/net/pcs/Kconfig
|
||||
@@ -20,6 +20,7 @@ config PCS_LYNX
|
||||
|
||||
config PCS_MTK_LYNXI
|
||||
tristate
|
||||
+ select PHY_COMMON_PROPS
|
||||
select REGMAP
|
||||
help
|
||||
This module provides helpers to phylink for managing the LynxI PCS
|
||||
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/mdio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pcs/pcs-mtk-lynxi.h>
|
||||
+#include <linux/phy/phy-common-props.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
@@ -62,8 +63,9 @@
|
||||
|
||||
/* Register to QPHY wrapper control */
|
||||
#define SGMSYS_QPHY_WRAP_CTRL 0xec
|
||||
-#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
|
||||
-#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
|
||||
+#define SGMII_PN_SWAP_RX BIT(1)
|
||||
+#define SGMII_PN_SWAP_TX BIT(0)
|
||||
+
|
||||
|
||||
/* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
|
||||
* data
|
||||
@@ -119,6 +121,42 @@ static void mtk_pcs_lynxi_get_state(stru
|
||||
FIELD_GET(SGMII_LPA, adv));
|
||||
}
|
||||
|
||||
+static int mtk_pcs_config_polarity(struct mtk_pcs_lynxi *mpcs,
|
||||
+ phy_interface_t interface)
|
||||
+{
|
||||
+ struct fwnode_handle *fwnode = mpcs->fwnode, *pcs_fwnode;
|
||||
+ unsigned int pol, default_pol = PHY_POL_NORMAL;
|
||||
+ unsigned int val = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (fwnode_property_read_bool(fwnode, "mediatek,pnswap"))
|
||||
+ default_pol = PHY_POL_INVERT;
|
||||
+
|
||||
+ pcs_fwnode = fwnode_get_named_child_node(fwnode, "pcs");
|
||||
+
|
||||
+ ret = phy_get_rx_polarity(pcs_fwnode, phy_modes(interface),
|
||||
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
||||
+ default_pol, &pol);
|
||||
+ if (ret) {
|
||||
+ fwnode_handle_put(pcs_fwnode);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ if (pol == PHY_POL_INVERT)
|
||||
+ val |= SGMII_PN_SWAP_RX;
|
||||
+
|
||||
+ ret = phy_get_tx_polarity(pcs_fwnode, phy_modes(interface),
|
||||
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
|
||||
+ default_pol, &pol);
|
||||
+ fwnode_handle_put(pcs_fwnode);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (pol == PHY_POL_INVERT)
|
||||
+ val |= SGMII_PN_SWAP_TX;
|
||||
+
|
||||
+ return regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
|
||||
+ SGMII_PN_SWAP_RX | SGMII_PN_SWAP_TX, val);
|
||||
+}
|
||||
+
|
||||
static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||
phy_interface_t interface,
|
||||
const unsigned long *advertising,
|
||||
@@ -128,6 +166,7 @@ static int mtk_pcs_lynxi_config(struct p
|
||||
bool mode_changed = false, changed;
|
||||
unsigned int rgc3, sgm_mode, bmcr;
|
||||
int advertise, link_timer;
|
||||
+ int ret;
|
||||
|
||||
advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
|
||||
advertising);
|
||||
@@ -167,10 +206,9 @@ static int mtk_pcs_lynxi_config(struct p
|
||||
regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
|
||||
SGMII_SW_RESET);
|
||||
|
||||
- if (fwnode_property_read_bool(mpcs->fwnode, "mediatek,pnswap"))
|
||||
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
|
||||
- SGMII_PN_SWAP_MASK,
|
||||
- SGMII_PN_SWAP_TX_RX);
|
||||
+ ret = mtk_pcs_config_polarity(mpcs, interface);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
||||
rgc3 = SGMII_PHY_SPEED_3_125G;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
From 3ddcd24b4d8454b2b9b2d013a0d61986ae8bbbe7 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Fri, 23 Jan 2026 13:06:00 +0200
|
||||
Subject: [PATCH] phy: enter drivers/phy/Makefile even without
|
||||
CONFIG_GENERIC_PHY
|
||||
|
||||
Kconfig option CONFIG_PHY_COMMON_PROPS, which builds
|
||||
drivers/phy/phy-common-props.c, was intended to be selectable
|
||||
independently of CONFIG_GENERIC_PHY. Yet it lives in drivers/phy/, which
|
||||
is entered by the Makefile only if CONFIG_GENERIC_PHY is set.
|
||||
|
||||
Allow the Makefile to enter one level deeper, but stop at drivers/phy/
|
||||
if CONFIG_GENERIC_PHY is unselected (i.e. do not enter vendor folders).
|
||||
The other stuff from drivers/phy/Makefile except for CONFIG_PHY_COMMON_PROPS,
|
||||
like CONFIG_PHY_NXP_PTN3222, all depends on CONFIG_GENERIC_PHY.
|
||||
|
||||
Fixes: e7556b59ba65 ("phy: add phy_get_rx_polarity() and phy_get_tx_polarity()")
|
||||
Closes: https://lore.kernel.org/lkml/43ea0202-891d-4582-980b-5cb557b41114@linux.ibm.com/
|
||||
Reported-by: Venkat Rao Bagalkote <venkat88@linux.ibm.com>
|
||||
Debugged-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
|
||||
Tested-by: Venkat Rao Bagalkote <venkat88@linux.ibm.com>
|
||||
Link: https://patch.msgid.link/20260123110600.3118561-1-vladimir.oltean@nxp.com
|
||||
Signed-off-by: Vinod Koul <vkoul@kernel.org>
|
||||
---
|
||||
drivers/Makefile | 2 +-
|
||||
drivers/phy/Makefile | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/Makefile
|
||||
+++ b/drivers/Makefile
|
||||
@@ -10,7 +10,7 @@ obj-y += cache/
|
||||
obj-y += irqchip/
|
||||
obj-y += bus/
|
||||
|
||||
-obj-$(CONFIG_GENERIC_PHY) += phy/
|
||||
+obj-y += phy/
|
||||
|
||||
# GPIO must come after pinctrl as gpios may need to mux pins etc
|
||||
obj-$(CONFIG_PINCTRL) += pinctrl/
|
||||
--- a/drivers/phy/Makefile
|
||||
+++ b/drivers/phy/Makefile
|
||||
@@ -15,7 +15,7 @@ obj-$(CONFIG_PHY_SNPS_EUSB2) += phy-snp
|
||||
obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o
|
||||
obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o
|
||||
obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o
|
||||
-obj-y += allwinner/ \
|
||||
+obj-$(CONFIG_GENERIC_PHY) += allwinner/ \
|
||||
amlogic/ \
|
||||
broadcom/ \
|
||||
cadence/ \
|
||||
@@ -1863,6 +1863,7 @@ CONFIG_DVB_MAX_ADAPTERS=16
|
||||
# CONFIG_DWMAC_LOONGSON is not set
|
||||
# CONFIG_DWMAC_LPC18XX is not set
|
||||
# CONFIG_DWMAC_MESON is not set
|
||||
# CONFIG_DWMAC_MOTORCOMM is not set
|
||||
# CONFIG_DWMAC_ROCKCHIP is not set
|
||||
# CONFIG_DWMAC_SOCFPGA is not set
|
||||
# CONFIG_DWMAC_STI is not set
|
||||
|
||||
@@ -1949,6 +1949,7 @@ CONFIG_DVB_MAX_ADAPTERS=16
|
||||
# CONFIG_DWMAC_LOONGSON is not set
|
||||
# CONFIG_DWMAC_LPC18XX is not set
|
||||
# CONFIG_DWMAC_MESON is not set
|
||||
# CONFIG_DWMAC_MOTORCOMM is not set
|
||||
# CONFIG_DWMAC_ROCKCHIP is not set
|
||||
# CONFIG_DWMAC_SOCFPGA is not set
|
||||
# CONFIG_DWMAC_STI is not set
|
||||
|
||||
@@ -17,6 +17,10 @@ CONFIG_PAHOLE_HAS_LANG_EXCLUDE=.*
|
||||
CONFIG_PAHOLE_HAS_SPLIT_BTF=.*
|
||||
CONFIG_PAHOLE_VERSION=.*
|
||||
CONFIG_PLUGIN_HOSTCC=".*"
|
||||
CONFIG_RUSTC_HAS_COERCE_POINTEE=.*
|
||||
CONFIG_RUSTC_HAS_FILE_AS_C_STR=.*
|
||||
CONFIG_RUSTC_HAS_FILE_WITH_NUL=.*
|
||||
CONFIG_RUSTC_HAS_SPAN_FILE=.*
|
||||
CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES=.*
|
||||
CONFIG_RUSTC_LLVM_VERSION=.*
|
||||
CONFIG_RUSTC_SUPPORTS_.*=.*
|
||||
|
||||
+3
-3
@@ -37,7 +37,7 @@ Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
|
||||
.glo_cfg = 0x4604,
|
||||
.rst_idx = 0x4608,
|
||||
.delay_irq = 0x460c,
|
||||
@@ -4176,6 +4179,56 @@ static void mtk_set_mcr_max_rx(struct mt
|
||||
@@ -4125,6 +4128,56 @@ static void mtk_set_mcr_max_rx(struct mt
|
||||
mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
|
||||
static void mtk_hw_reset(struct mtk_eth *eth)
|
||||
{
|
||||
u32 val;
|
||||
@@ -4655,6 +4708,8 @@ static void mtk_pending_work(struct work
|
||||
@@ -4604,6 +4657,8 @@ static void mtk_pending_work(struct work
|
||||
rtnl_lock();
|
||||
set_bit(MTK_RESETTING, ð->state);
|
||||
|
||||
@@ -105,7 +105,7 @@ Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
|
||||
/* Run again reset preliminary configuration in order to avoid any
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
||||
@@ -1193,6 +1193,7 @@ struct mtk_reg_map {
|
||||
@@ -1192,6 +1192,7 @@ struct mtk_reg_map {
|
||||
u32 rx_ptr; /* rx base pointer */
|
||||
u32 rx_cnt_cfg; /* rx max count configuration */
|
||||
u32 qcrx_ptr; /* rx cpu pointer */
|
||||
|
||||
@@ -7,7 +7,7 @@ There is no upstream driver yet. Merge the RTL SDK driver for now.
|
||||
Signed-off-by: John Crispin <john@phrozen.org>
|
||||
--- a/drivers/net/phy/Kconfig
|
||||
+++ b/drivers/net/phy/Kconfig
|
||||
@@ -458,6 +458,8 @@ config QSEMI_PHY
|
||||
@@ -459,6 +459,8 @@ config QSEMI_PHY
|
||||
|
||||
source "drivers/net/phy/realtek/Kconfig"
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
From 880d1311335120f64447ca9d11933872d734e19a Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Mon, 27 Mar 2023 18:41:54 +0100
|
||||
Subject: [PATCH] generic: pcs-mtk-lynxi: add hack to use 2500Base-X without AN
|
||||
|
||||
Using 2500Base-T SFP modules e.g. on the BananaPi R3 requires manually
|
||||
disabling auto-negotiation, e.g. using ethtool. While a proper fix
|
||||
using SFP quirks is being discussed upstream, bring a work-around to
|
||||
restore user experience to what it was before the switch to the
|
||||
dedicated SGMII PCS driver.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
@@ -129,15 +129,23 @@ static void mtk_pcs_lynxi_get_state(stru
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
- unsigned int bm, adv;
|
||||
+ unsigned int bm, bmsr, adv;
|
||||
|
||||
/* Read the BMSR and LPA */
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
|
||||
- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
|
||||
+ bmsr = FIELD_GET(SGMII_BMSR, bm);
|
||||
+
|
||||
+ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) {
|
||||
+ state->link = !!(bmsr & BMSR_LSTATUS);
|
||||
+ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
|
||||
+ state->speed = SPEED_2500;
|
||||
+ state->duplex = DUPLEX_FULL;
|
||||
+
|
||||
+ return;
|
||||
+ }
|
||||
|
||||
- phylink_mii_c22_pcs_decode_state(state, neg_mode,
|
||||
- FIELD_GET(SGMII_BMSR, bm),
|
||||
- FIELD_GET(SGMII_LPA, adv));
|
||||
+ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
|
||||
+ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, FIELD_GET(SGMII_LPA, adv));
|
||||
}
|
||||
|
||||
static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs)
|
||||
@@ -158,7 +166,7 @@ static int mtk_pcs_lynxi_config(struct p
|
||||
{
|
||||
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
|
||||
bool mode_changed = false, changed;
|
||||
- unsigned int rgc3, sgm_mode, bmcr;
|
||||
+ unsigned int rgc3, sgm_mode, bmcr = 0;
|
||||
int advertise, link_timer;
|
||||
|
||||
advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
|
||||
@@ -181,9 +189,8 @@ static int mtk_pcs_lynxi_config(struct p
|
||||
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII)
|
||||
sgm_mode |= SGMII_SPEED_DUPLEX_AN;
|
||||
- bmcr = BMCR_ANENABLE;
|
||||
- } else {
|
||||
- bmcr = 0;
|
||||
+ if (interface != PHY_INTERFACE_MODE_2500BASEX)
|
||||
+ bmcr = BMCR_ANENABLE;
|
||||
}
|
||||
|
||||
if (mpcs->interface != interface) {
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
From da12469e73282da814163125153f381823e33f20 Mon Sep 17 00:00:00 2001
|
||||
From 60a23e663e0c607ae4ed871aaa24d257051ad557 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 17:56:35 +0000
|
||||
Subject: [PATCH 08/26] net: dsa: mxl862xx: store firmware version for feature
|
||||
Subject: [PATCH 01/19] net: dsa: mxl862xx: store firmware version for feature
|
||||
gating
|
||||
|
||||
Query the firmware version at init (already done in wait_ready),
|
||||
@@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -286,6 +286,9 @@ static int mxl862xx_wait_ready(struct ds
|
||||
@@ -257,6 +257,9 @@ static int mxl862xx_wait_ready(struct ds
|
||||
ver.iv_major, ver.iv_minor,
|
||||
le16_to_cpu(ver.iv_revision),
|
||||
le32_to_cpu(ver.iv_build_num));
|
||||
@@ -40,8 +40,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#include <linux/mdio.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <net/dsa.h>
|
||||
@@ -245,6 +246,38 @@ struct mxl862xx_port {
|
||||
spinlock_t stats_lock;
|
||||
@@ -241,6 +242,38 @@ struct mxl862xx_port {
|
||||
spinlock_t stats_lock; /* protects stats accumulators */
|
||||
};
|
||||
|
||||
+/**
|
||||
@@ -79,7 +79,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
/* Bit indices for struct mxl862xx_priv::flags */
|
||||
#define MXL862XX_FLAG_CRC_ERR 0
|
||||
#define MXL862XX_FLAG_WORK_STOPPED 1
|
||||
@@ -262,6 +295,8 @@ struct mxl862xx_port {
|
||||
@@ -258,6 +291,8 @@ struct mxl862xx_port {
|
||||
* @drop_meter: index of the single shared zero-rate firmware meter
|
||||
* used to unconditionally drop traffic (used to block
|
||||
* flooding)
|
||||
@@ -88,7 +88,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @ports: per-port state, indexed by switch port number
|
||||
* @bridges: maps DSA bridge number to firmware bridge ID;
|
||||
* zero means no firmware bridge allocated for that
|
||||
@@ -279,6 +314,7 @@ struct mxl862xx_priv {
|
||||
@@ -275,6 +310,7 @@ struct mxl862xx_priv {
|
||||
struct work_struct crc_err_work;
|
||||
unsigned long flags;
|
||||
u16 drop_meter;
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
From f7606470d398e4091e1bc405bf2125dc5fc99919 Mon Sep 17 00:00:00 2001
|
||||
From cefa0447dc95a4ddd5093f7b8cf35e654870283f Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 21:39:30 +0000
|
||||
Subject: [PATCH 09/26] net: dsa: mxl862xx: move phylink stubs to
|
||||
Subject: [PATCH 02/19] net: dsa: mxl862xx: move phylink stubs to
|
||||
mxl862xx-phylink.c
|
||||
|
||||
Move the phylink MAC operations and get_caps callback from mxl862xx.c
|
||||
@@ -110,7 +110,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
#define MXL862XX_API_WRITE(dev, cmd, data) \
|
||||
mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false)
|
||||
@@ -1597,16 +1598,6 @@ static void mxl862xx_port_teardown(struc
|
||||
@@ -1424,16 +1425,6 @@ static void mxl862xx_port_teardown(struc
|
||||
priv->ports[port].setup_done = false;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db)
|
||||
{
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
@@ -2252,33 +2243,6 @@ static const struct dsa_switch_ops mxl86
|
||||
@@ -2099,33 +2090,6 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_stats64 = mxl862xx_get_stats64,
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
From e583eeeb907f0abeef2082162293a5d63b9fd6fa Mon Sep 17 00:00:00 2001
|
||||
From 3c1d77006daca1df20d612850535bc6050e266ee Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 26 Mar 2026 01:50:00 +0000
|
||||
Subject: [PATCH 10/26] net: dsa: mxl862xx: move API macros to mxl862xx-host.h
|
||||
Subject: [PATCH 03/19] net: dsa: mxl862xx: move API macros to mxl862xx-host.h
|
||||
|
||||
Move the MXL862XX_API_WRITE, MXL862XX_API_READ and
|
||||
MXL862XX_API_READ_QUIET convenience macros from mxl862xx.c to
|
||||
+5
-5
@@ -990,7 +990,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#endif /* __MXL862XX_PHYLINK_H */
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -684,7 +684,7 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -622,7 +622,7 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
int n_user_ports = 0, max_vlans;
|
||||
int ingress_finals, vid_rules;
|
||||
struct dsa_port *dp;
|
||||
@@ -999,7 +999,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
ret = mxl862xx_reset(priv);
|
||||
if (ret)
|
||||
@@ -694,6 +694,9 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -632,6 +632,9 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1011,7 +1011,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* Ingress: only final catchall rules (PVID insertion, 802.1Q
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -247,6 +247,22 @@ struct mxl862xx_port {
|
||||
@@ -243,6 +243,22 @@ struct mxl862xx_port {
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1034,7 +1034,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* union mxl862xx_fw_version - firmware version for comparison and display
|
||||
* @major: firmware major version
|
||||
* @minor: firmware minor version
|
||||
@@ -297,6 +313,8 @@ union mxl862xx_fw_version {
|
||||
@@ -293,6 +309,8 @@ union mxl862xx_fw_version {
|
||||
* flooding)
|
||||
* @fw_version: cached firmware version, populated at probe and
|
||||
* compared with MXL862XX_FW_VER_MIN()
|
||||
@@ -1043,7 +1043,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @ports: per-port state, indexed by switch port number
|
||||
* @bridges: maps DSA bridge number to firmware bridge ID;
|
||||
* zero means no firmware bridge allocated for that
|
||||
@@ -315,6 +333,7 @@ struct mxl862xx_priv {
|
||||
@@ -311,6 +329,7 @@ struct mxl862xx_priv {
|
||||
unsigned long flags;
|
||||
u16 drop_meter;
|
||||
union mxl862xx_fw_version fw_version;
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
From 24d752291784e30d7329bed15744bbbc6a3e2485 Mon Sep 17 00:00:00 2001
|
||||
From 3659914c43a587a1ca6418867834831aa518ac35 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 18:14:33 +0000
|
||||
Subject: [PATCH 12/26] net: dsa: mxl862xx: add SerDes ethtool statistics
|
||||
Subject: [PATCH 05/19] net: dsa: mxl862xx: add SerDes ethtool statistics
|
||||
|
||||
Expose SerDes equalization and signal detect parameters as ethtool
|
||||
statistics on ports 9-16 (XPCS-backed ports). Uses the XPCS EQ_GET
|
||||
@@ -239,7 +239,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#endif /* __MXL862XX_PHYLINK_H */
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -1960,6 +1960,8 @@ static void mxl862xx_get_strings(struct
|
||||
@@ -1775,6 +1775,8 @@ static void mxl862xx_get_strings(struct
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mxl862xx_mib); i++)
|
||||
ethtool_puts(&data, mxl862xx_mib[i].name);
|
||||
@@ -248,7 +248,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
}
|
||||
|
||||
static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset)
|
||||
@@ -1967,7 +1969,7 @@ static int mxl862xx_get_sset_count(struc
|
||||
@@ -1782,7 +1784,7 @@ static int mxl862xx_get_sset_count(struc
|
||||
if (sset != ETH_SS_STATS)
|
||||
return 0;
|
||||
|
||||
@@ -257,7 +257,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
}
|
||||
|
||||
static int mxl862xx_read_rmon(struct dsa_switch *ds, int port,
|
||||
@@ -2003,6 +2005,8 @@ static void mxl862xx_get_ethtool_stats(s
|
||||
@@ -1818,6 +1820,8 @@ static void mxl862xx_get_ethtool_stats(s
|
||||
else
|
||||
*data++ = le64_to_cpu(*(__le64 *)field);
|
||||
}
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
From ee227a5e4c74f599cc1b34578b32214d5873ad2f Mon Sep 17 00:00:00 2001
|
||||
From ce66c0be462c8500dfc483395e68be4326ebf296 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 18:15:32 +0000
|
||||
Subject: [PATCH 13/26] net: dsa: mxl862xx: add SerDes self-test via PRBS and
|
||||
Subject: [PATCH 06/19] net: dsa: mxl862xx: add SerDes self-test via PRBS and
|
||||
BERT
|
||||
|
||||
Implement the dsa_switch_ops.self_test callback for SerDes ports
|
||||
@@ -198,9 +198,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#endif /* __MXL862XX_PHYLINK_H */
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -2241,6 +2241,7 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
|
||||
@@ -2088,6 +2088,7 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_pause_stats = mxl862xx_get_pause_stats,
|
||||
.get_rmon_stats = mxl862xx_get_rmon_stats,
|
||||
.get_stats64 = mxl862xx_get_stats64,
|
||||
+ .self_test = mxl862xx_serdes_self_test,
|
||||
};
|
||||
+210
-40
@@ -1,14 +1,28 @@
|
||||
From 43eb3eed250ea4e7e83371fcbf2bfb8d626eade6 Mon Sep 17 00:00:00 2001
|
||||
From e6defbd42db6e64c2bb203f82b7d7f8c0691a052 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 18:51:13 +0000
|
||||
Subject: [PATCH 14/26] net: dsa: mxl862xx: trap link-local frames to the CPU
|
||||
port
|
||||
Subject: [PATCH 07/19] net: dsa: mxl862xx: trap link-local and multicast
|
||||
snooping frames to CPU
|
||||
|
||||
Install per-CTP PCE rules on each user port that trap IEEE 802.1D
|
||||
link-local frames (01:80:c2:00:00:0x) to the CPU port via an
|
||||
explicit forwarding portmap with cross-state enabled, ensuring the
|
||||
frames reach the host even when the bridge port is in BLOCKING or
|
||||
LEARNING state.
|
||||
link-local frames (01:80:c2:00:00:0x) and IP multicast snooping
|
||||
frames (IGMP, MLDv1, MLDv2) to the CPU port.
|
||||
|
||||
All trap rules share a common action helper,
|
||||
mxl862xx_fill_cpu_trap_action(), which sets PORTMAP_ALTERNATIVE to
|
||||
redirect frames to the CPU and enables cross-state forwarding so
|
||||
that frames reach the host even when the bridge port is in BLOCKING
|
||||
or LEARNING state.
|
||||
|
||||
A dedicated bridge FID (cpu_trap_fid) is allocated during setup with
|
||||
all flood modes enabled. Each trap rule points the bridge engine at
|
||||
this FID via bFidEnable so that IGMP and MLD frames are never
|
||||
silently dropped by the ingress port's private flood policy.
|
||||
|
||||
Three multicast snooping rules are installed per port:
|
||||
offset 2 -- IPv4 IGMP (IP protocol 2, all versions)
|
||||
offset 3 -- ICMPv6 types 130-132 (MLDv1 query, report, done)
|
||||
offset 4 -- ICMPv6 type 143 (MLDv2 Listener Report)
|
||||
|
||||
Add the PCE rule firmware API structures, command definitions, and
|
||||
the rule block allocation interface.
|
||||
@@ -17,8 +31,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 684 ++++++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 5 +
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 69 +++
|
||||
3 files changed, 758 insertions(+)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 186 +++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 8 +
|
||||
4 files changed, 883 insertions(+)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -736,7 +751,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#define MXL862XX_BRIDGE_CONFIGGET (MXL862XX_BRDG_MAGIC + 0x3)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -280,9 +280,11 @@ static int mxl862xx_wait_ready(struct ds
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/etherdevice.h>
|
||||
+#include <linux/icmpv6.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
@@ -251,9 +252,11 @@ static int mxl862xx_wait_ready(struct ds
|
||||
ver.iv_major, ver.iv_minor,
|
||||
le16_to_cpu(ver.iv_revision),
|
||||
le32_to_cpu(ver.iv_build_num));
|
||||
@@ -748,17 +771,54 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return 0;
|
||||
|
||||
not_ready_yet:
|
||||
@@ -410,6 +412,68 @@ static int mxl862xx_setup_drop_meter(str
|
||||
@@ -381,6 +384,158 @@ static int mxl862xx_setup_drop_meter(str
|
||||
return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg);
|
||||
}
|
||||
|
||||
+
|
||||
+/* Per-CTP offset used for the link-local trap rule. Each port's CTP
|
||||
+ * flow-table block is pre-allocated by the firmware during init (44
|
||||
+ * entries per port on a 10-port SKU, of which offset 0 is reserved
|
||||
+ * for flow-control marking). Offset 1 is the first unused slot.
|
||||
+/* Per-CTP offsets for protocol trap rules. Each port's CTP flow-table
|
||||
+ * block is pre-allocated by the firmware during init (44 entries per
|
||||
+ * port on a 10-port SKU, of which offset 0 is reserved for flow-control
|
||||
+ * marking). Offsets 1-4 are used for link-local and multicast snooping
|
||||
+ * traps; all others remain free.
|
||||
+ */
|
||||
+#define MXL862XX_LINK_LOCAL_CTP_OFFSET 1
|
||||
+#define MXL862XX_IGMP_CTP_OFFSET 2
|
||||
+#define MXL862XX_MLDV1_CTP_OFFSET 3
|
||||
+#define MXL862XX_MLDV2_CTP_OFFSET 4
|
||||
+
|
||||
+/* Fill the action fields of a PCE rule that traps ingress frames to
|
||||
+ * the CPU port. Used by both the link-local trap and the multicast
|
||||
+ * snooping traps. The caller must already have set the rule header
|
||||
+ * (logicalportid, subifidgroup, region) and the pattern fields.
|
||||
+ *
|
||||
+ * PORTMAP_ALTERNATIVE redirects the frame to the CPU port but does
|
||||
+ * not by itself bypass downstream flood gates. In SpTag mode the
|
||||
+ * ingress port's private FID may have forward_unknown_multicast=false,
|
||||
+ * which silently drops IGMP/MLD before they reach the CPU.
|
||||
+ * Setting bFidEnable to cpu_trap_fid (a dedicated bridge with all
|
||||
+ * flood modes enabled) overrides the FID used by the bridge engine,
|
||||
+ * so the frame is never classified as blocked unknown MC regardless
|
||||
+ * of the ingress port's standalone flood policy.
|
||||
+ *
|
||||
+ * Cross-state is enabled so trapped frames bypass STP port state.
|
||||
+ */
|
||||
+static void mxl862xx_fill_cpu_trap_action(struct dsa_switch *ds, int port,
|
||||
+ struct mxl862xx_pce_rule *rule)
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ int cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
|
||||
+
|
||||
+ rule->action.port_map_action =
|
||||
+ cpu_to_le32(MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE);
|
||||
+ mxl862xx_fw_portmap_set_bit(rule->action.forward_port_map, cpu_port);
|
||||
+
|
||||
+ rule->action.cross_state_action =
|
||||
+ cpu_to_le32(MXL862XX_PCE_ACTION_CROSS_STATE_CROSS);
|
||||
+
|
||||
+ rule->action.fid_enable = 1;
|
||||
+ rule->action.fid = priv->cpu_trap_fid;
|
||||
+}
|
||||
+
|
||||
+/* Install a PCE rule that traps IEEE 802.1D link-local frames
|
||||
+ * (01:80:c2:00:00:0x) to the CPU port for a single user port,
|
||||
@@ -778,54 +838,164 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ */
|
||||
+static int mxl862xx_setup_link_local_trap(struct dsa_switch *ds, int port)
|
||||
+{
|
||||
+ DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_pce_rule rule = {};
|
||||
+ int cpu_port = dp->cpu_dp->index;
|
||||
+ int i;
|
||||
+
|
||||
+ /* Address this port's CTP flow-table block */
|
||||
+ rule.logicalportid = port;
|
||||
+ rule.subifidgroup = 0;
|
||||
+ rule.region = cpu_to_le32(MXL862XX_PCE_RULE_CTP);
|
||||
+
|
||||
+ /* Pattern: link-local MAC on this specific ingress port */
|
||||
+ rule.pattern.index = cpu_to_le16(MXL862XX_LINK_LOCAL_CTP_OFFSET);
|
||||
+ rule.pattern.enable = 1;
|
||||
+ rule.pattern.mac_dst_enable = 1;
|
||||
+ memcpy(rule.pattern.mac_dst, eth_reserved_addr_base, ETH_ALEN);
|
||||
+ rule.pattern.mac_dst_mask = cpu_to_le16(0x0001);
|
||||
+
|
||||
+ /* Action: forward to the CPU port via explicit portmap */
|
||||
+ rule.action.port_map_action =
|
||||
+ cpu_to_le32(MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE);
|
||||
+ mxl862xx_fill_cpu_trap_action(ds, port, &rule);
|
||||
+
|
||||
+ bitmap_zero(portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
+ __set_bit(cpu_port, portmap);
|
||||
+ for (i = 0; i < ARRAY_SIZE(rule.action.forward_port_map); i++)
|
||||
+ rule.action.forward_port_map[i] =
|
||||
+ cpu_to_le16(bitmap_read(portmap, i * 16, 16));
|
||||
+ return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
|
||||
+}
|
||||
+
|
||||
+ /* Bypass STP port state */
|
||||
+ rule.action.cross_state_action =
|
||||
+ cpu_to_le32(MXL862XX_PCE_ACTION_CROSS_STATE_CROSS);
|
||||
+/* Install PCE rules that trap IGMP and MLD frames to the CPU port for
|
||||
+ * a single user port. PORTMAP_ALTERNATIVE overrides the bridge
|
||||
+ * forwarding portmap to the CPU port. bFidEnable points the bridge
|
||||
+ * engine at cpu_trap_fid (all flood modes enabled) so the frames are
|
||||
+ * never classified as blocked unknown MC regardless of the ingress
|
||||
+ * port's standalone flood policy.
|
||||
+ *
|
||||
+ * Three rules are installed per port:
|
||||
+ * offset 2 -- IPv4 IGMP (IP protocol 2, all versions)
|
||||
+ * offset 3 -- ICMPv6 types 130-132 (MLDv1 query, report, done)
|
||||
+ * offset 4 -- ICMPv6 type 143 (MLDv2 Listener Report)
|
||||
+ *
|
||||
+ * The MLDv1 rule uses range mode on the first two bytes after the IP
|
||||
+ * header (ICMPv6 type + code): lower bound 0x8200 (type 130, code 0)
|
||||
+ * to upper bound 0x84ff (type 132, code 255). The MLDv2 rule uses
|
||||
+ * nibble mask 0x3 to match type 143 with any code byte.
|
||||
+ */
|
||||
+static int mxl862xx_setup_snooping_traps(struct dsa_switch *ds, int port)
|
||||
+{
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_pce_rule rule = {};
|
||||
+ int ret;
|
||||
+
|
||||
+ return MXL862XX_API_WRITE(ds->priv, MXL862XX_TFLOW_PCERULEWRITE,
|
||||
+ rule);
|
||||
+ rule.logicalportid = port;
|
||||
+ rule.region = cpu_to_le32(MXL862XX_PCE_RULE_CTP);
|
||||
+ mxl862xx_fill_cpu_trap_action(ds, port, &rule);
|
||||
+
|
||||
+ /* IGMP: IPv4 protocol 2, all versions */
|
||||
+ rule.pattern.index = cpu_to_le16(MXL862XX_IGMP_CTP_OFFSET);
|
||||
+ rule.pattern.enable = 1;
|
||||
+ rule.pattern.protocol = IPPROTO_IGMP;
|
||||
+ rule.pattern.protocol_enable = 1;
|
||||
+
|
||||
+ ret = MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* MLDv1: ICMPv6 types 130 (query), 131 (report), 132 (done).
|
||||
+ * Range mode covers all three types with any code value.
|
||||
+ */
|
||||
+ memset(&rule.pattern, 0, sizeof(rule.pattern));
|
||||
+ rule.pattern.index = cpu_to_le16(MXL862XX_MLDV1_CTP_OFFSET);
|
||||
+ rule.pattern.enable = 1;
|
||||
+ rule.pattern.protocol = IPPROTO_ICMPV6;
|
||||
+ rule.pattern.protocol_enable = 1;
|
||||
+ rule.pattern.app_data_msb =
|
||||
+ cpu_to_le16((u16)ICMPV6_MGM_QUERY << 8);
|
||||
+ rule.pattern.app_mask_range_msb =
|
||||
+ cpu_to_le16(((u16)ICMPV6_MGM_REDUCTION << 8) | 0xff);
|
||||
+ rule.pattern.app_data_msb_enable = 1;
|
||||
+ rule.pattern.app_mask_range_msb_select = 1; /* range mode */
|
||||
+
|
||||
+ ret = MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* MLDv2: ICMPv6 type 143 (Listener Report v2), any code byte.
|
||||
+ * Nibble mask 0x3 masks nibbles 0-1 (lower byte = code field).
|
||||
+ */
|
||||
+ memset(&rule.pattern, 0, sizeof(rule.pattern));
|
||||
+ rule.pattern.index = cpu_to_le16(MXL862XX_MLDV2_CTP_OFFSET);
|
||||
+ rule.pattern.enable = 1;
|
||||
+ rule.pattern.protocol = IPPROTO_ICMPV6;
|
||||
+ rule.pattern.protocol_enable = 1;
|
||||
+ rule.pattern.app_data_msb = cpu_to_le16((u16)ICMPV6_MLD2_REPORT << 8);
|
||||
+ rule.pattern.app_mask_range_msb = cpu_to_le16(0x0003);
|
||||
+ rule.pattern.app_data_msb_enable = 1;
|
||||
+ /* app_mask_range_msb_select = 0: nibble mask mode (default) */
|
||||
+
|
||||
+ return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
|
||||
+}
|
||||
+
|
||||
static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct mxl862xx_bridge_port_config br_port_cfg = {};
|
||||
@@ -1549,6 +1613,11 @@ static int mxl862xx_port_setup(struct ds
|
||||
@@ -683,6 +838,28 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ /* install link-local trap for this user port */
|
||||
+
|
||||
+ /* Allocate a dedicated PCE snooping FID with all flood modes enabled.
|
||||
+ * Per-port PCE trap rules (link-local, IGMP, MLD) set bFidEnable to
|
||||
+ * this FID so that the bridge engine uses it for its flood-permission
|
||||
+ * check instead of the ingress port's private FID (which has
|
||||
+ * mc_flood=false to restrict unknown MC from reaching the CPU in the
|
||||
+ * normal path). The hardware PCE FID action field is 6 bits wide, so
|
||||
+ * the allocated ID must be in range 0..63.
|
||||
+ */
|
||||
+ ret = mxl862xx_allocate_bridge(priv);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (WARN_ON_ONCE(ret > 0x3F))
|
||||
+ return -ERANGE;
|
||||
+
|
||||
+ priv->cpu_trap_fid = ret;
|
||||
+
|
||||
+ ret = mxl862xx_bridge_config_fwd(ds, priv->cpu_trap_fid,
|
||||
+ true, true, true);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
schedule_delayed_work(&priv->stats_work,
|
||||
MXL862XX_STATS_POLL_INTERVAL);
|
||||
|
||||
@@ -1382,6 +1559,15 @@ static int mxl862xx_port_setup(struct ds
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ /* install link-local and multicast snooping traps */
|
||||
+ ret = mxl862xx_setup_link_local_trap(ds, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
/* Initialize and pre-allocate per-port EVLAN and VF blocks for
|
||||
* user ports. CPU ports do not use EVLAN or VF -- frames pass
|
||||
* through without processing. Pre-allocation avoids firmware
|
||||
+ ret = mxl862xx_setup_snooping_traps(ds, port);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
priv->ports[port].ingress_evlan.block_size = priv->evlan_ingress_size;
|
||||
ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
|
||||
if (ret)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -319,6 +319,13 @@ union mxl862xx_fw_version {
|
||||
* @evlan_ingress_size: per-port ingress Extended VLAN block size
|
||||
* @evlan_egress_size: per-port egress Extended VLAN block size
|
||||
* @vf_block_size: per-port VLAN Filter block size
|
||||
+ * @cpu_trap_fid: firmware bridge FID allocated for PCE-trapped frames;
|
||||
+ * configured with uc/mc/bc flood all enabled so that
|
||||
+ * IGMP, MLD, and link-local frames always reach the CPU
|
||||
+ * regardless of the ingress port's private FID flood
|
||||
+ * policy. Set once in setup() and referenced by
|
||||
+ * fill_cpu_trap_action() via bFidEnable. The PCE FID
|
||||
+ * action field is 6 bits, so this value must be <= 63.
|
||||
* @stats_work: periodic work item that polls RMON hardware counters
|
||||
* and accumulates them into 64-bit per-port stats
|
||||
*/
|
||||
@@ -335,6 +342,7 @@ struct mxl862xx_priv {
|
||||
u16 evlan_ingress_size;
|
||||
u16 evlan_egress_size;
|
||||
u16 vf_block_size;
|
||||
+ u16 cpu_trap_fid;
|
||||
struct delayed_work stats_work;
|
||||
};
|
||||
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
From e18f5b235d8df21209c73f4f0bbc00cc3a1973ba Mon Sep 17 00:00:00 2001
|
||||
From 264838ee8ee3ad611a84df96d889429f1ded2148 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 18:51:21 +0000
|
||||
Subject: [PATCH 15/26] net: dsa: mxl862xx: warn about old firmware default PCE
|
||||
Subject: [PATCH 08/19] net: dsa: mxl862xx: warn about old firmware default PCE
|
||||
rules
|
||||
|
||||
Firmware versions older than 1.0.80 install global PCE rules at
|
||||
@@ -19,14 +19,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -809,6 +809,10 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -860,6 +860,10 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
true, true, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+
|
||||
+ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80))
|
||||
+ dev_warn(ds->dev, "firmware < 1.0.80 installs global PCE rules "
|
||||
+ "that interfere with DSA operation, please update\n");
|
||||
+
|
||||
schedule_delayed_work(&priv->stats_work,
|
||||
MXL862XX_STATS_POLL_INTERVAL);
|
||||
|
||||
+346
-321
File diff suppressed because it is too large
Load Diff
+52
-63
@@ -1,7 +1,7 @@
|
||||
From 4e1d854199c166f617b93b7542e863e6a8ad2ccb Mon Sep 17 00:00:00 2001
|
||||
From a32f6a9c09b90e767a03ddac34d061545a0cf15e Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 03:44:41 +0000
|
||||
Subject: [PATCH 17/26] net: dsa: mxl862xx: add link aggregation support
|
||||
Subject: [PATCH 10/20] net: dsa: mxl862xx: add link aggregation support
|
||||
|
||||
Implement LAG offloading via the firmware's trunking engine. A
|
||||
dedicated firmware bridge port is allocated per LAG and remains
|
||||
@@ -33,9 +33,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 22 +
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 4 +
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 587 +++++++++++++++++++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 34 ++
|
||||
4 files changed, 630 insertions(+), 17 deletions(-)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 578 +++++++++++++++++++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 33 ++
|
||||
4 files changed, 621 insertions(+), 16 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -90,8 +90,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x1)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -620,7 +620,42 @@ static int mxl862xx_setup_link_local_tra
|
||||
rule);
|
||||
@@ -680,7 +680,42 @@ static int mxl862xx_setup_snooping_traps
|
||||
return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
|
||||
}
|
||||
|
||||
-static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
|
||||
@@ -134,16 +134,31 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
{
|
||||
struct mxl862xx_bridge_port_config br_port_cfg = {};
|
||||
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
@@ -632,7 +667,7 @@ static int mxl862xx_set_bridge_port(stru
|
||||
bool enable;
|
||||
int i, idx;
|
||||
@@ -713,8 +748,12 @@ static int mxl862xx_set_bridge_port(stru
|
||||
dp->bridge->dev) {
|
||||
if (member_dp->index == port)
|
||||
continue;
|
||||
- mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
- member_dp->index);
|
||||
+ if (!mxl862xx_is_lag_master(priv, member_dp->index))
|
||||
+ continue;
|
||||
+ mxl862xx_fw_portmap_set_bit(
|
||||
+ br_port_cfg.bridge_port_map,
|
||||
+ mxl862xx_lag_bridge_port(priv,
|
||||
+ member_dp->index));
|
||||
}
|
||||
mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
mxl862xx_cpu_bridge_port_id(ds, port));
|
||||
@@ -727,7 +766,7 @@ static int mxl862xx_set_bridge_port(stru
|
||||
|
||||
bridge_id = dp->bridge ? priv->bridges[dp->bridge->num] : p->fid;
|
||||
|
||||
- br_port_cfg.bridge_port_id = cpu_to_le16(port);
|
||||
+ br_port_cfg.bridge_port_id = cpu_to_le16(bp_id);
|
||||
br_port_cfg.bridge_id = cpu_to_le16(bridge_id);
|
||||
br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
|
||||
MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
|
||||
@@ -715,12 +750,38 @@ static int mxl862xx_set_bridge_port(stru
|
||||
@@ -808,11 +847,38 @@ static int mxl862xx_set_bridge_port(stru
|
||||
br_port_cfg);
|
||||
}
|
||||
|
||||
@@ -174,41 +189,16 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static int mxl862xx_sync_bridge_members(struct dsa_switch *ds,
|
||||
const struct dsa_bridge *bridge)
|
||||
{
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
struct dsa_port *dp, *member_dp;
|
||||
- int port, err, ret = 0;
|
||||
+ struct mxl862xx_priv *priv = ds->priv;
|
||||
+ struct mxl862xx_port *p;
|
||||
+ int port, member, err, ret = 0;
|
||||
+ u16 lag_bp, bp;
|
||||
struct dsa_port *dp;
|
||||
- int ret = 0, err;
|
||||
+ u16 lag_bp;
|
||||
+ int err, ret = 0;
|
||||
|
||||
dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) {
|
||||
port = dp->index;
|
||||
@@ -729,9 +790,21 @@ static int mxl862xx_sync_bridge_members(
|
||||
MXL862XX_MAX_BRIDGE_PORTS);
|
||||
|
||||
dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) {
|
||||
- if (member_dp->index != port)
|
||||
- __set_bit(member_dp->index,
|
||||
- priv->ports[port].portmap);
|
||||
+ member = member_dp->index;
|
||||
+
|
||||
+ /* For LAG members, only include the LAG's
|
||||
+ * dedicated bridge port in the portmap.
|
||||
+ * Non-master members are skipped to avoid
|
||||
+ * duplicates (they share the same LAG bridge
|
||||
+ * port).
|
||||
+ */
|
||||
+ if (!mxl862xx_is_lag_master(priv, member))
|
||||
+ continue;
|
||||
+ if (member != port) {
|
||||
+ bp = mxl862xx_lag_bridge_port(priv,
|
||||
+ member);
|
||||
+ __set_bit(bp, priv->ports[port].portmap);
|
||||
+ }
|
||||
}
|
||||
__set_bit(mxl862xx_cpu_bridge_port_id(ds, port),
|
||||
priv->ports[port].portmap);
|
||||
@@ -741,6 +814,25 @@ static int mxl862xx_sync_bridge_members(
|
||||
err = mxl862xx_set_bridge_port(ds, dp->index);
|
||||
@@ -820,6 +886,25 @@ static int mxl862xx_sync_bridge_members(
|
||||
ret = err;
|
||||
}
|
||||
|
||||
@@ -234,7 +224,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1881,6 +1973,408 @@ static int mxl862xx_setup_cpu_bridge(str
|
||||
@@ -1859,6 +1944,408 @@ static int mxl862xx_setup_cpu_bridge(str
|
||||
return mxl862xx_set_bridge_port(ds, port);
|
||||
}
|
||||
|
||||
@@ -643,7 +633,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
|
||||
const struct dsa_bridge bridge,
|
||||
bool *tx_fwd_offload,
|
||||
@@ -1907,7 +2401,18 @@ static int mxl862xx_port_bridge_join(str
|
||||
@@ -1884,7 +2371,18 @@ static int mxl862xx_port_bridge_join(str
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -663,7 +653,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
}
|
||||
|
||||
static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||
@@ -1966,6 +2471,17 @@ static void mxl862xx_port_bridge_leave(s
|
||||
@@ -1935,6 +2433,17 @@ static void mxl862xx_port_bridge_leave(s
|
||||
"failed to update CPU VBP for port %d: %pe\n", port,
|
||||
ERR_PTR(err));
|
||||
|
||||
@@ -681,7 +671,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
if (!dsa_bridge_ports(ds, bridge.dev))
|
||||
mxl862xx_free_bridge(ds, &bridge);
|
||||
}
|
||||
@@ -2591,18 +3107,17 @@ static int mxl862xx_get_fid(struct dsa_s
|
||||
@@ -2593,18 +3102,17 @@ static int mxl862xx_get_fid(struct dsa_s
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -707,7 +697,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
*/
|
||||
static int mxl862xx_fdb_bridge_port(struct dsa_switch *ds, int port,
|
||||
const struct dsa_db db)
|
||||
@@ -2618,7 +3133,7 @@ static int mxl862xx_fdb_bridge_port(stru
|
||||
@@ -2620,7 +3128,7 @@ static int mxl862xx_fdb_bridge_port(stru
|
||||
return bp_cpu;
|
||||
}
|
||||
|
||||
@@ -716,7 +706,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2862,11 +3377,43 @@ static int mxl862xx_port_fdb_del(struct
|
||||
@@ -2867,11 +3375,43 @@ static int mxl862xx_port_fdb_del(struct
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -760,7 +750,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
u32 entry_port_id;
|
||||
int ret;
|
||||
|
||||
@@ -2880,7 +3427,7 @@ static int mxl862xx_port_fdb_dump(struct
|
||||
@@ -2885,7 +3425,7 @@ static int mxl862xx_port_fdb_dump(struct
|
||||
|
||||
entry_port_id = le32_to_cpu(param.port_id);
|
||||
|
||||
@@ -769,7 +759,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
ret = cb(param.mac, FIELD_GET(MXL862XX_TCI_VLAN_ID,
|
||||
le16_to_cpu(param.tci)),
|
||||
param.static_entry, data);
|
||||
@@ -3521,6 +4068,11 @@ static const struct dsa_switch_ops mxl86
|
||||
@@ -3556,6 +4096,11 @@ static const struct dsa_switch_ops mxl86
|
||||
.port_fdb_dump = mxl862xx_port_fdb_dump,
|
||||
.port_mdb_add = mxl862xx_port_mdb_add,
|
||||
.port_mdb_del = mxl862xx_port_mdb_del,
|
||||
@@ -781,7 +771,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
|
||||
.port_vlan_add = mxl862xx_port_vlan_add,
|
||||
.port_vlan_del = mxl862xx_port_vlan_del,
|
||||
@@ -3561,6 +4113,7 @@ static int mxl862xx_probe(struct mdio_de
|
||||
@@ -3597,6 +4142,7 @@ static int mxl862xx_probe(struct mdio_de
|
||||
ds->num_ports = MXL862XX_MAX_PORTS;
|
||||
ds->fdb_isolation = true;
|
||||
ds->max_num_bridges = MXL862XX_MAX_BRIDGES;
|
||||
@@ -811,7 +801,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
/* Number of __le16 words in a firmware portmap (128-bit bitmap). */
|
||||
#define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
|
||||
@@ -228,6 +241,12 @@ struct mxl862xx_port_stats {
|
||||
@@ -227,6 +240,12 @@ struct mxl862xx_port_stats {
|
||||
* @stats_lock: protects accumulator reads in .get_stats64 against
|
||||
* concurrent updates from the polling work
|
||||
* @tag_8021q_vid: currently assigned tag_8021q management VID
|
||||
@@ -824,21 +814,20 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
*/
|
||||
struct mxl862xx_port {
|
||||
struct mxl862xx_priv *priv;
|
||||
@@ -250,6 +269,10 @@ struct mxl862xx_port {
|
||||
@@ -246,6 +265,9 @@ struct mxl862xx_port {
|
||||
struct work_struct host_flood_work;
|
||||
u16 tag_8021q_vid;
|
||||
struct mxl862xx_evlan_block cpu_egress_evlan;
|
||||
+ /* LAG state */
|
||||
+ struct dsa_lag *lag;
|
||||
+ bool lag_tx_enabled;
|
||||
+ u8 lag_hash_bits;
|
||||
/* Hardware stats accumulation */
|
||||
struct mxl862xx_port_stats stats;
|
||||
spinlock_t stats_lock;
|
||||
@@ -334,6 +357,15 @@ union mxl862xx_fw_version {
|
||||
* @evlan_egress_size: per-port egress Extended VLAN block size
|
||||
* @cpu_evlan_ingress_size: CPU port ingress EVLAN block size (tag_8021q)
|
||||
* @vf_block_size: per-port VLAN Filter block size
|
||||
spinlock_t stats_lock; /* protects stats accumulators */
|
||||
};
|
||||
@@ -336,6 +358,15 @@ union mxl862xx_fw_version {
|
||||
* policy. Set once in setup() and referenced by
|
||||
* fill_cpu_trap_action() via bFidEnable. The PCE FID
|
||||
* action field is 6 bits, so this value must be <= 63.
|
||||
+ * @lag_bridge_ports: maps DSA LAG ID to firmware bridge port ID;
|
||||
+ * zero means no bridge port allocated for that LAG.
|
||||
+ * Indexed by lag->id (entry 0 is unused).
|
||||
@@ -851,10 +840,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @stats_work: periodic work item that polls RMON hardware counters
|
||||
* and accumulates them into 64-bit per-port stats
|
||||
*/
|
||||
@@ -352,6 +384,8 @@ struct mxl862xx_priv {
|
||||
u16 evlan_egress_size;
|
||||
@@ -355,6 +386,8 @@ struct mxl862xx_priv {
|
||||
u16 cpu_evlan_ingress_size;
|
||||
u16 vf_block_size;
|
||||
u16 cpu_trap_fid;
|
||||
+ u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
|
||||
+ u8 trunk_hash;
|
||||
struct delayed_work stats_work;
|
||||
+16
-17
@@ -1,7 +1,7 @@
|
||||
From 5528f38c3d709417625eb7f36628be31727a8221 Mon Sep 17 00:00:00 2001
|
||||
From d919c2f8da9dbd4dda57ceebb5c8b103805b58d1 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 12:05:29 +0000
|
||||
Subject: [PATCH 18/26] net: dsa: mxl862xx: add support for mirror port
|
||||
Subject: [PATCH 11/19] net: dsa: mxl862xx: add support for mirror port
|
||||
|
||||
The MxL862xx hardware supports a single monitor port which can be
|
||||
configured to mirror any other port's ingress and/or egress traffic.
|
||||
@@ -14,8 +14,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 12 +++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 1 +
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 118 ++++++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 8 ++
|
||||
4 files changed, 139 insertions(+)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 7 ++
|
||||
4 files changed, 138 insertions(+)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
|
||||
@@ -50,7 +50,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -1084,6 +1084,8 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
@@ -1100,6 +1100,8 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
(n_user_ports + n_cpu_ports);
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
ret = mxl862xx_setup_drop_meter(ds);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1973,6 +1975,120 @@ static int mxl862xx_setup_cpu_bridge(str
|
||||
return mxl862xx_set_bridge_port(ds, port);
|
||||
@@ -3167,6 +3169,120 @@ static int mxl862xx_fdb_del_per_fid(stru
|
||||
return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, param);
|
||||
}
|
||||
|
||||
+static int mxl862xx_port_mirror_add(struct dsa_switch *ds, int port,
|
||||
@@ -178,9 +178,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* mxl862xx_lag_master_port - Find the LAG master (lowest-numbered member)
|
||||
* @ds: DSA switch
|
||||
@@ -4068,6 +4184,8 @@ static const struct dsa_switch_ops mxl86
|
||||
* mxl862xx_mac_portmap_add - Set port bits in a MAC table entry's portmap
|
||||
* @priv: driver private data
|
||||
@@ -4096,6 +4212,8 @@ static const struct dsa_switch_ops mxl86
|
||||
.port_fdb_dump = mxl862xx_port_fdb_dump,
|
||||
.port_mdb_add = mxl862xx_port_mdb_add,
|
||||
.port_mdb_del = mxl862xx_port_mdb_del,
|
||||
@@ -191,7 +191,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
.port_lag_change = mxl862xx_port_lag_change,
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -241,6 +241,8 @@ struct mxl862xx_port_stats {
|
||||
@@ -240,6 +240,8 @@ struct mxl862xx_port_stats {
|
||||
* @stats_lock: protects accumulator reads in .get_stats64 against
|
||||
* concurrent updates from the polling work
|
||||
* @tag_8021q_vid: currently assigned tag_8021q management VID
|
||||
@@ -200,17 +200,16 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @lag: non-NULL when port is member of a LAG group;
|
||||
* points to the DSA LAG structure
|
||||
* @lag_tx_enabled: true when this port is active for TX in its LAG
|
||||
@@ -269,6 +271,9 @@ struct mxl862xx_port {
|
||||
@@ -265,6 +267,8 @@ struct mxl862xx_port {
|
||||
struct work_struct host_flood_work;
|
||||
u16 tag_8021q_vid;
|
||||
struct mxl862xx_evlan_block cpu_egress_evlan;
|
||||
+ /* Mirror state */
|
||||
+ bool ingress_mirror;
|
||||
+ bool egress_mirror;
|
||||
/* LAG state */
|
||||
struct dsa_lag *lag;
|
||||
bool lag_tx_enabled;
|
||||
@@ -366,6 +371,8 @@ union mxl862xx_fw_version {
|
||||
u8 lag_hash_bits;
|
||||
@@ -367,6 +371,8 @@ union mxl862xx_fw_version {
|
||||
* @trunk_hash: current global hash field bitmask (6 bits,
|
||||
* MXL862XX_TRUNK_HASH_*); union of all active LAGs'
|
||||
* hash requirements
|
||||
@@ -219,8 +218,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @stats_work: periodic work item that polls RMON hardware counters
|
||||
* and accumulates them into 64-bit per-port stats
|
||||
*/
|
||||
@@ -386,6 +393,7 @@ struct mxl862xx_priv {
|
||||
u16 vf_block_size;
|
||||
@@ -388,6 +394,7 @@ struct mxl862xx_priv {
|
||||
u16 cpu_trap_fid;
|
||||
u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
|
||||
u8 trunk_hash;
|
||||
+ int mirror_dest;
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
From 4059d35a5bbf1901b2e0eb7126369cd713cacfce Mon Sep 17 00:00:00 2001
|
||||
From 64269d9d809962a0f7e68e9b618d81e561e3eb6f Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 16:30:08 +0000
|
||||
Subject: [PATCH 19/26] net: dsa: wire flash_update devlink callback to drivers
|
||||
Subject: [PATCH 12/19] net: dsa: wire flash_update devlink callback to drivers
|
||||
|
||||
Add a devlink_flash_update callback to dsa_switch_ops so that DSA
|
||||
drivers can support devlink dev flash without open-coding the devlink
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
From 0145151dc68aa318d8addb6fe7f12c0967f951da Mon Sep 17 00:00:00 2001
|
||||
From fed4225f75b6fe6898e48f472cbbee0aaa046760 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 16:30:17 +0000
|
||||
Subject: [PATCH 20/26] net: dsa: mxl862xx: add SMDIO clause-22 register access
|
||||
Subject: [PATCH 13/19] net: dsa: mxl862xx: add SMDIO clause-22 register access
|
||||
|
||||
Add mxl862xx_smdio_read() and mxl862xx_smdio_write() for clause-22
|
||||
SMDIO register access. MCUboot rescue mode only exposes clause-22
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
From bdbca48510e3e96ed9210f20fa4244dd6df5d44a Mon Sep 17 00:00:00 2001
|
||||
From fa186b09e346e0b7f2504232731bc9e4dee0c600 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 16:30:31 +0000
|
||||
Subject: [PATCH 21/26] net: dsa: mxl862xx: add devlink flash_update and
|
||||
Subject: [PATCH 14/19] net: dsa: mxl862xx: add devlink flash_update and
|
||||
info_get
|
||||
|
||||
Implement runtime firmware upgrade via "devlink dev flash" and version
|
||||
@@ -538,7 +538,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -22,6 +22,7 @@
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "mxl862xx.h"
|
||||
#include "mxl862xx-api.h"
|
||||
#include "mxl862xx-cmd.h"
|
||||
@@ -546,8 +546,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
#include "mxl862xx-host.h"
|
||||
#include "mxl862xx-phylink.h"
|
||||
|
||||
@@ -4204,6 +4205,9 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_pause_stats = mxl862xx_get_pause_stats,
|
||||
@@ -4233,6 +4234,9 @@ static const struct dsa_switch_ops mxl86
|
||||
.get_rmon_stats = mxl862xx_get_rmon_stats,
|
||||
.get_stats64 = mxl862xx_get_stats64,
|
||||
.self_test = mxl862xx_serdes_self_test,
|
||||
+ .devlink_info_get = mxl862xx_devlink_info_get,
|
||||
@@ -558,7 +558,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static int mxl862xx_probe(struct mdio_device *mdiodev)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -394,6 +394,8 @@ struct mxl862xx_priv {
|
||||
@@ -395,6 +395,8 @@ struct mxl862xx_priv {
|
||||
u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
|
||||
u8 trunk_hash;
|
||||
int mirror_dest;
|
||||
+13
-14
@@ -1,7 +1,7 @@
|
||||
From 8deb5be9638f7eb3009ed3eb619eedadee1df523 Mon Sep 17 00:00:00 2001
|
||||
From 05bc981690d6ef03143a094f28714aa0171ab571 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 23:42:18 +0000
|
||||
Subject: [PATCH 22/26] net: dsa: mxl862xx: implement port MTU configuration
|
||||
Date: Wed, 1 Apr 2026 13:43:08 +0100
|
||||
Subject: [PATCH 15/19] net: dsa: mxl862xx: implement port MTU configuration
|
||||
|
||||
The firmware exposes a global max_packet_len register via
|
||||
MXL862XX_COMMON_CFGSET. Since this is switch-wide rather than
|
||||
@@ -12,20 +12,20 @@ the effective maximum does not change.
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 50 +++++++++++++++++++++++++++++
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 4 +++
|
||||
2 files changed, 54 insertions(+)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 3 ++
|
||||
2 files changed, 53 insertions(+)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/delay.h>
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/if_bridge.h>
|
||||
+#include <linux/if_vlan.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_mdio.h>
|
||||
@@ -3723,6 +3724,53 @@ static int mxl862xx_set_ageing_time(stru
|
||||
@@ -3727,6 +3728,53 @@ static int mxl862xx_set_ageing_time(stru
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port,
|
||||
u8 state)
|
||||
{
|
||||
@@ -4174,6 +4222,8 @@ static const struct dsa_switch_ops mxl86
|
||||
@@ -4202,6 +4250,8 @@ static const struct dsa_switch_ops mxl86
|
||||
.port_disable = mxl862xx_port_disable,
|
||||
.port_fast_age = mxl862xx_port_fast_age,
|
||||
.set_ageing_time = mxl862xx_set_ageing_time,
|
||||
@@ -90,7 +90,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
.port_pre_bridge_flags = mxl862xx_port_pre_bridge_flags,
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -249,6 +249,8 @@ struct mxl862xx_port_stats {
|
||||
@@ -248,6 +248,8 @@ struct mxl862xx_port_stats {
|
||||
* @lag_hash_bits: hash field bitmask (MXL862XX_TRUNK_HASH_*) requested
|
||||
* when this port joined its LAG; used to recompute the
|
||||
* global trunk_hash when a LAG is destroyed
|
||||
@@ -99,12 +99,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
*/
|
||||
struct mxl862xx_port {
|
||||
struct mxl862xx_priv *priv;
|
||||
@@ -278,6 +280,8 @@ struct mxl862xx_port {
|
||||
@@ -272,6 +274,7 @@ struct mxl862xx_port {
|
||||
struct dsa_lag *lag;
|
||||
bool lag_tx_enabled;
|
||||
u8 lag_hash_bits;
|
||||
+ /* MTU */
|
||||
+ int mtu;
|
||||
/* Hardware stats accumulation */
|
||||
struct mxl862xx_port_stats stats;
|
||||
spinlock_t stats_lock;
|
||||
spinlock_t stats_lock; /* protects stats accumulators */
|
||||
};
|
||||
+24
-49
@@ -1,7 +1,7 @@
|
||||
From 13a4c918cd9ded7207f38033511ab13f7aff9bd2 Mon Sep 17 00:00:00 2001
|
||||
From b723f7484006aadbaa51e16b870f3c98390605b1 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 01:47:19 +0000
|
||||
Subject: [PATCH 23/26] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag
|
||||
Date: Wed, 1 Apr 2026 13:43:12 +0100
|
||||
Subject: [PATCH 16/19] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag
|
||||
|
||||
Implement hairpin mode by including the port's own bridge port ID in
|
||||
its forwarding portmap. When hairpin is enabled, bridged frames whose
|
||||
@@ -18,29 +18,24 @@ bridge member rebuild since only the calling port is affected.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 29 +++++++++++++++++++++++++++--
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 6 ++++++
|
||||
2 files changed, 33 insertions(+), 2 deletions(-)
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 12 ++++++++++--
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 5 +++++
|
||||
2 files changed, 15 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -811,6 +811,15 @@ static int mxl862xx_sync_bridge_members(
|
||||
__set_bit(mxl862xx_cpu_bridge_port_id(ds, port),
|
||||
priv->ports[port].portmap);
|
||||
|
||||
+ /* Hairpin: include the port's own bridge port so bridged
|
||||
+ * frames can egress the ingress port.
|
||||
+ * For LAG ports this adds the LAG bridge port, which
|
||||
+ * propagates to the LAG BP in the second loop below.
|
||||
+ */
|
||||
+ if (priv->ports[port].hairpin)
|
||||
+ __set_bit(mxl862xx_lag_bridge_port(priv, port),
|
||||
+ priv->ports[port].portmap);
|
||||
+
|
||||
err = mxl862xx_set_bridge_port(ds, port);
|
||||
if (err)
|
||||
ret = err;
|
||||
@@ -3898,7 +3907,7 @@ static int mxl862xx_port_pre_bridge_flag
|
||||
@@ -759,6 +759,10 @@ static int __mxl862xx_set_bridge_port(st
|
||||
}
|
||||
mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
mxl862xx_cpu_bridge_port_id(ds, port));
|
||||
+ if (p->hairpin)
|
||||
+ mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
+ mxl862xx_lag_bridge_port(priv,
|
||||
+ port));
|
||||
} else {
|
||||
mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
|
||||
mxl862xx_cpu_bridge_port_id(ds, port));
|
||||
@@ -3895,7 +3899,7 @@ static int mxl862xx_port_pre_bridge_flag
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
|
||||
@@ -49,33 +44,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
@@ -3912,6 +3921,7 @@ static int mxl862xx_port_bridge_flags(st
|
||||
unsigned long old_block = priv->ports[port].flood_block;
|
||||
unsigned long block = old_block;
|
||||
int ret;
|
||||
+ u16 bp;
|
||||
|
||||
if (flags.mask & BR_FLOOD) {
|
||||
if (flags.val & BR_FLOOD)
|
||||
@@ -3940,7 +3950,22 @@ static int mxl862xx_port_bridge_flags(st
|
||||
@@ -3937,7 +3941,11 @@ static int mxl862xx_port_bridge_flags(st
|
||||
if (flags.mask & BR_LEARNING)
|
||||
priv->ports[port].learning = !!(flags.val & BR_LEARNING);
|
||||
|
||||
- if ((block != old_block) || (flags.mask & BR_LEARNING)) {
|
||||
+ if (flags.mask & BR_HAIRPIN_MODE) {
|
||||
+ bp = mxl862xx_lag_bridge_port(priv, port);
|
||||
- if (block != old_block || (flags.mask & BR_LEARNING)) {
|
||||
+ if (flags.mask & BR_HAIRPIN_MODE)
|
||||
+ priv->ports[port].hairpin = !!(flags.val & BR_HAIRPIN_MODE);
|
||||
+
|
||||
+ /* Hairpin adds/removes the port's own bridge port from its
|
||||
+ * cached portmap. Only this port is affected -- push the
|
||||
+ * updated portmap directly.
|
||||
+ */
|
||||
+ if (flags.val & BR_HAIRPIN_MODE)
|
||||
+ __set_bit(bp, priv->ports[port].portmap);
|
||||
+ else
|
||||
+ __clear_bit(bp, priv->ports[port].portmap);
|
||||
+ }
|
||||
+
|
||||
+ if ((block != old_block) ||
|
||||
+ (flags.mask & (BR_LEARNING | BR_HAIRPIN_MODE))) {
|
||||
priv->ports[port].flood_block = block;
|
||||
@@ -83,7 +59,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
if (ret)
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -241,6 +241,10 @@ struct mxl862xx_port_stats {
|
||||
@@ -240,6 +240,10 @@ struct mxl862xx_port_stats {
|
||||
* @stats_lock: protects accumulator reads in .get_stats64 against
|
||||
* concurrent updates from the polling work
|
||||
* @tag_8021q_vid: currently assigned tag_8021q management VID
|
||||
@@ -94,12 +70,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @ingress_mirror: true when ingress mirroring is active on this port
|
||||
* @egress_mirror: true when egress mirroring is active on this port
|
||||
* @lag: non-NULL when port is member of a LAG group;
|
||||
@@ -273,6 +277,8 @@ struct mxl862xx_port {
|
||||
@@ -269,6 +273,7 @@ struct mxl862xx_port {
|
||||
struct work_struct host_flood_work;
|
||||
u16 tag_8021q_vid;
|
||||
struct mxl862xx_evlan_block cpu_egress_evlan;
|
||||
+ /* Hairpin state */
|
||||
+ bool hairpin;
|
||||
/* Mirror state */
|
||||
bool ingress_mirror;
|
||||
bool egress_mirror;
|
||||
struct dsa_lag *lag;
|
||||
+19
-25
@@ -1,7 +1,7 @@
|
||||
From d49d1f8bee29269def7593f980d0e08bfb5c3ef8 Mon Sep 17 00:00:00 2001
|
||||
From d8f20ba50ce0f93f34a41a9b833be76a5d1d2714 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 25 Mar 2026 01:51:33 +0000
|
||||
Subject: [PATCH 24/26] net: dsa: mxl862xx: support BR_ISOLATED bridge flag
|
||||
Subject: [PATCH 17/19] net: dsa: mxl862xx: support BR_ISOLATED bridge flag
|
||||
|
||||
Implement port isolation by excluding isolated ports from each other's
|
||||
forwarding portmaps in sync_bridge_members. Non-isolated ports can
|
||||
@@ -13,28 +13,22 @@ rebuilt via sync_bridge_members since multiple ports are affected.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 26 +++++++++++++++++++++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.c | 20 +++++++++++++++++++-
|
||||
drivers/net/dsa/mxl862xx/mxl862xx.h | 4 ++++
|
||||
2 files changed, 29 insertions(+), 1 deletion(-)
|
||||
2 files changed, 23 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -802,6 +802,14 @@ static int mxl862xx_sync_bridge_members(
|
||||
*/
|
||||
if (!mxl862xx_is_lag_master(priv, member))
|
||||
@@ -752,6 +752,8 @@ static int __mxl862xx_set_bridge_port(st
|
||||
continue;
|
||||
+
|
||||
+ /* Isolated ports cannot forward to each other.
|
||||
+ * Non-isolated ports can reach everyone.
|
||||
+ */
|
||||
+ if (priv->ports[port].isolated &&
|
||||
+ priv->ports[member].isolated)
|
||||
if (!mxl862xx_is_lag_master(priv, member_dp->index))
|
||||
continue;
|
||||
+ if (p->isolated && priv->ports[member_dp->index].isolated)
|
||||
+ continue;
|
||||
+
|
||||
if (member != port) {
|
||||
bp = mxl862xx_lag_bridge_port(priv,
|
||||
member);
|
||||
@@ -3907,7 +3915,7 @@ static int mxl862xx_port_pre_bridge_flag
|
||||
mxl862xx_fw_portmap_set_bit(
|
||||
br_port_cfg.bridge_port_map,
|
||||
mxl862xx_lag_bridge_port(priv,
|
||||
@@ -3899,7 +3901,7 @@ static int mxl862xx_port_pre_bridge_flag
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
|
||||
@@ -43,15 +37,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
@@ -3920,6 +3928,7 @@ static int mxl862xx_port_bridge_flags(st
|
||||
@@ -3912,6 +3914,7 @@ static int mxl862xx_port_bridge_flags(st
|
||||
struct mxl862xx_priv *priv = ds->priv;
|
||||
unsigned long old_block = priv->ports[port].flood_block;
|
||||
unsigned long block = old_block;
|
||||
+ struct dsa_port *dp;
|
||||
int ret;
|
||||
u16 bp;
|
||||
|
||||
@@ -3972,6 +3981,21 @@ static int mxl862xx_port_bridge_flags(st
|
||||
if (flags.mask & BR_FLOOD) {
|
||||
@@ -3952,6 +3955,21 @@ static int mxl862xx_port_bridge_flags(st
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -75,7 +69,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
|
||||
@@ -214,6 +214,9 @@ struct mxl862xx_port_stats {
|
||||
@@ -213,6 +213,9 @@ struct mxl862xx_port_stats {
|
||||
* @flood_block: bitmask of firmware meter indices that are currently
|
||||
* rate-limiting flood traffic on this port (zero-rate
|
||||
* meters used to block flooding)
|
||||
@@ -85,11 +79,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
* @learning: true when address learning is enabled on this port
|
||||
* @setup_done: set at end of port_setup, cleared at start of
|
||||
* port_teardown; guards deferred work against
|
||||
@@ -261,6 +264,7 @@ struct mxl862xx_port {
|
||||
@@ -259,6 +262,7 @@ struct mxl862xx_port {
|
||||
struct mxl862xx_priv *priv;
|
||||
u16 fid;
|
||||
DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS);
|
||||
unsigned long flood_block;
|
||||
+ bool isolated;
|
||||
bool learning;
|
||||
bool setup_done;
|
||||
/* VLAN state */
|
||||
u16 pvid;
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
From c2fb7f0df63ac994850f766e7f2eb50c6c5ef2cf Mon Sep 17 00:00:00 2001
|
||||
From 8856f7610167a3005ecef401c8528111d153b554 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Tue, 24 Mar 2026 18:17:49 +0000
|
||||
Subject: [PATCH 25/26] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
|
||||
Subject: [PATCH 18/19] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
|
||||
workaround for old firmware
|
||||
|
||||
Re-introduce the mxl862xx_disable_fw_global_rules() function that
|
||||
@@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
|
||||
@@ -477,6 +477,43 @@ static int mxl862xx_setup_drop_meter(str
|
||||
@@ -449,6 +449,43 @@ static int mxl862xx_setup_drop_meter(str
|
||||
return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg);
|
||||
}
|
||||
|
||||
@@ -62,9 +62,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
/* Per-CTP offset used for the link-local trap rule. Each port's CTP
|
||||
* flow-table block is pre-allocated by the firmware during init (44
|
||||
@@ -1109,9 +1146,11 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
/* Per-CTP offsets for protocol trap rules. Each port's CTP flow-table
|
||||
* block is pre-allocated by the firmware during init (44 entries per
|
||||
@@ -1114,9 +1151,11 @@ static int mxl862xx_setup(struct dsa_swi
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
From df0c747216063041ba0d786b01f9b1e2aba5316a Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Wed, 22 Apr 2026 15:15:52 +0100
|
||||
Subject: [PATCH] DO NOT SUBMIT: net: dsa: mxl862xx: increase CMD timeout
|
||||
|
||||
Lift the command timeout by 10x from 500ms to 5s.
|
||||
This is done because older firmware can be extremely slow to respond
|
||||
and cause -ETIMEOUT during setup, or crash the PHY state machine.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/dsa/mxl862xx/mxl862xx-host.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c
|
||||
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
|
||||
@@ -207,7 +207,7 @@ static int mxl862xx_busy_wait(struct mxl
|
||||
int val;
|
||||
|
||||
return readx_poll_timeout(mxl862xx_ctrl_read, priv, val,
|
||||
- !(val & CTRL_BUSY_MASK), 15, 500000);
|
||||
+ !(val & CTRL_BUSY_MASK), 50, 5000000);
|
||||
}
|
||||
|
||||
/* Issue a firmware command with CRC-6 protection on the ctrl and len_ret
|
||||
+1
-1
@@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -5298,6 +5298,8 @@ static int mtk_probe(struct platform_dev
|
||||
@@ -5295,6 +5295,8 @@ static int mtk_probe(struct platform_dev
|
||||
dev_err(eth->dev, "failed to allocated dummy device\n");
|
||||
goto err_unreg_netdev;
|
||||
}
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -2279,7 +2279,7 @@ int phylink_fwnode_phy_connect(struct ph
|
||||
@@ -2278,7 +2278,7 @@ int phylink_fwnode_phy_connect(struct ph
|
||||
{
|
||||
struct fwnode_handle *phy_fwnode;
|
||||
struct phy_device *phy_dev;
|
||||
@@ -29,7 +29,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
|
||||
/* Fixed links and 802.3z are handled without needing a PHY */
|
||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
|
||||
@@ -2309,6 +2309,25 @@ int phylink_fwnode_phy_connect(struct ph
|
||||
@@ -2308,6 +2308,25 @@ int phylink_fwnode_phy_connect(struct ph
|
||||
if (pl->config->mac_requires_rxc)
|
||||
flags |= PHY_F_RXC_ALWAYS_ON;
|
||||
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From 7289d42b8fd17713531c0f7e8ae7653d1f2c6f8f Mon Sep 17 00:00:00 2001
|
||||
From: Frank Wunderlich <frank-w@public-files.de>
|
||||
Date: Thu, 9 Apr 2026 15:33:42 +0200
|
||||
Subject: [PATCH] net: pcs: pcs-mtk-lynxi: fix bpi-r3 serdes configuration
|
||||
|
||||
Commit 8871389da151 introduces common pcs dts properties which writes
|
||||
rx=normal,tx=normal polarity to register SGMSYS_QPHY_WRAP_CTRL of switch.
|
||||
This is initialized with tx-bit set and so change inverts polarity
|
||||
compared to before.
|
||||
|
||||
It looks like mt7531 has tx polarity inverted in hardware and set tx-bit
|
||||
by default to restore the normal polarity.
|
||||
|
||||
Till this patch the register write was only called when mediatek,pnswap
|
||||
property was set which cannot be done for switch because the fw-node param
|
||||
was always NULL from switch driver in the mtk_pcs_lynxi_create call.
|
||||
|
||||
Do not configure switch side like it's done before.
|
||||
|
||||
Fixes: 8871389da151 ("net: pcs: pcs-mtk-lynxi: deprecate "mediatek,pnswap"")
|
||||
Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
|
||||
---
|
||||
drivers/net/pcs/pcs-mtk-lynxi.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
|
||||
@@ -129,6 +129,9 @@ static int mtk_pcs_config_polarity(struc
|
||||
unsigned int val = 0;
|
||||
int ret;
|
||||
|
||||
+ if (!fwnode)
|
||||
+ return 0;
|
||||
+
|
||||
if (fwnode_property_read_bool(fwnode, "mediatek,pnswap"))
|
||||
default_pol = PHY_POL_INVERT;
|
||||
|
||||
+10
-10
@@ -291,7 +291,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
eth->scratch_ring, eth->phy_scratch_ring,
|
||||
true);
|
||||
eth->scratch_ring = NULL;
|
||||
@@ -5386,6 +5390,9 @@ static void mtk_remove(struct platform_d
|
||||
@@ -5383,6 +5387,9 @@ static void mtk_remove(struct platform_d
|
||||
mtk_mdio_cleanup(eth);
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
static const struct mtk_soc_data mt2701_data = {
|
||||
.reg_map = &mtk_reg_map,
|
||||
.caps = MT7623_CAPS | MTK_HWLRO,
|
||||
@@ -5394,14 +5401,14 @@ static const struct mtk_soc_data mt2701_
|
||||
@@ -5391,14 +5398,14 @@ static const struct mtk_soc_data mt2701_
|
||||
.required_pctl = true,
|
||||
.version = 1,
|
||||
.tx = {
|
||||
@@ -318,7 +318,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_size = MTK_DMA_SIZE(2K),
|
||||
@@ -5422,14 +5429,14 @@ static const struct mtk_soc_data mt7621_
|
||||
@@ -5419,14 +5426,14 @@ static const struct mtk_soc_data mt7621_
|
||||
.hash_offset = 2,
|
||||
.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
|
||||
.tx = {
|
||||
@@ -335,7 +335,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_size = MTK_DMA_SIZE(2K),
|
||||
@@ -5452,14 +5459,14 @@ static const struct mtk_soc_data mt7622_
|
||||
@@ -5449,14 +5456,14 @@ static const struct mtk_soc_data mt7622_
|
||||
.has_accounting = true,
|
||||
.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
|
||||
.tx = {
|
||||
@@ -352,7 +352,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_size = MTK_DMA_SIZE(2K),
|
||||
@@ -5481,14 +5488,14 @@ static const struct mtk_soc_data mt7623_
|
||||
@@ -5478,14 +5485,14 @@ static const struct mtk_soc_data mt7623_
|
||||
.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
|
||||
.disable_pll_modes = true,
|
||||
.tx = {
|
||||
@@ -369,7 +369,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_size = MTK_DMA_SIZE(2K),
|
||||
@@ -5507,14 +5514,14 @@ static const struct mtk_soc_data mt7629_
|
||||
@@ -5504,14 +5511,14 @@ static const struct mtk_soc_data mt7629_
|
||||
.has_accounting = true,
|
||||
.version = 1,
|
||||
.tx = {
|
||||
@@ -386,7 +386,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_size = MTK_DMA_SIZE(2K),
|
||||
@@ -5537,14 +5544,14 @@ static const struct mtk_soc_data mt7981_
|
||||
@@ -5534,14 +5541,14 @@ static const struct mtk_soc_data mt7981_
|
||||
.has_accounting = true,
|
||||
.foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
|
||||
.tx = {
|
||||
@@ -403,7 +403,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
@@ -5567,14 +5574,14 @@ static const struct mtk_soc_data mt7986_
|
||||
@@ -5564,14 +5571,14 @@ static const struct mtk_soc_data mt7986_
|
||||
.has_accounting = true,
|
||||
.foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
|
||||
.tx = {
|
||||
@@ -420,7 +420,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
.irq_done_mask = MTK_RX_DONE_INT,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
@@ -5597,14 +5604,14 @@ static const struct mtk_soc_data mt7988_
|
||||
@@ -5594,14 +5601,14 @@ static const struct mtk_soc_data mt7988_
|
||||
.has_accounting = true,
|
||||
.foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
|
||||
.tx = {
|
||||
@@ -437,7 +437,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
.irq_done_mask = MTK_RX_DONE_INT_V2,
|
||||
.dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
|
||||
@@ -5621,13 +5628,13 @@ static const struct mtk_soc_data rt5350_
|
||||
@@ -5618,13 +5625,13 @@ static const struct mtk_soc_data rt5350_
|
||||
.required_pctl = false,
|
||||
.version = 1,
|
||||
.tx = {
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
From c6f6cb55c3c316f6169e07eacc5ccb214116719a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 31 Mar 2025 15:40:12 +0200
|
||||
Subject: [PATCH 1/7] net: phylink: keep and use MAC supported_interfaces in
|
||||
phylink struct
|
||||
|
||||
Add in phylink struct a copy of supported_interfaces from phylink_config
|
||||
and make use of that instead of relying on phylink_config value.
|
||||
|
||||
This in preparation for support of PCS handling internally to phylink
|
||||
where a PCS can be removed or added after the phylink is created and we
|
||||
need both a reference of the supported_interfaces value from
|
||||
phylink_config and an internal value that can be updated with the new
|
||||
PCS info.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 22 +++++++++++++++-------
|
||||
1 file changed, 15 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -59,6 +59,11 @@ struct phylink {
|
||||
/* The link configuration settings */
|
||||
struct phylink_link_state link_config;
|
||||
|
||||
+ /* What interface are supported by the current link.
|
||||
+ * Can change on removal or addition of new PCS.
|
||||
+ */
|
||||
+ DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
|
||||
+
|
||||
/* The current settings */
|
||||
phy_interface_t cur_interface;
|
||||
|
||||
@@ -623,7 +628,7 @@ static int phylink_validate_mask(struct
|
||||
static int phylink_validate(struct phylink *pl, unsigned long *supported,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
- const unsigned long *interfaces = pl->config->supported_interfaces;
|
||||
+ const unsigned long *interfaces = pl->supported_interfaces;
|
||||
|
||||
if (state->interface == PHY_INTERFACE_MODE_NA)
|
||||
return phylink_validate_mask(pl, NULL, supported, state,
|
||||
@@ -1853,6 +1858,9 @@ struct phylink *phylink_create(struct ph
|
||||
mutex_init(&pl->state_mutex);
|
||||
INIT_WORK(&pl->resolve, phylink_resolve);
|
||||
|
||||
+ phy_interface_copy(pl->supported_interfaces,
|
||||
+ config->supported_interfaces);
|
||||
+
|
||||
pl->config = config;
|
||||
if (config->type == PHYLINK_NETDEV) {
|
||||
pl->netdev = to_net_dev(config->dev);
|
||||
@@ -2011,7 +2019,7 @@ static int phylink_validate_phy(struct p
|
||||
* those which the host supports.
|
||||
*/
|
||||
phy_interface_and(interfaces, phy->possible_interfaces,
|
||||
- pl->config->supported_interfaces);
|
||||
+ pl->supported_interfaces);
|
||||
|
||||
if (phy_interface_empty(interfaces)) {
|
||||
phylink_err(pl, "PHY has no common interfaces\n");
|
||||
@@ -2753,12 +2761,12 @@ static phy_interface_t phylink_sfp_selec
|
||||
return interface;
|
||||
}
|
||||
|
||||
- if (!test_bit(interface, pl->config->supported_interfaces)) {
|
||||
+ if (!test_bit(interface, pl->supported_interfaces)) {
|
||||
phylink_err(pl,
|
||||
"selection of interface failed, SFP selected %s (%u) but MAC supports %*pbl\n",
|
||||
phy_modes(interface), interface,
|
||||
(int)PHY_INTERFACE_MODE_MAX,
|
||||
- pl->config->supported_interfaces);
|
||||
+ pl->supported_interfaces);
|
||||
return PHY_INTERFACE_MODE_NA;
|
||||
}
|
||||
|
||||
@@ -3686,14 +3694,14 @@ static int phylink_sfp_config_optical(st
|
||||
|
||||
phylink_dbg(pl, "optical SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n",
|
||||
(int)PHY_INTERFACE_MODE_MAX,
|
||||
- pl->config->supported_interfaces,
|
||||
+ pl->supported_interfaces,
|
||||
(int)PHY_INTERFACE_MODE_MAX,
|
||||
pl->sfp_interfaces);
|
||||
|
||||
/* Find the union of the supported interfaces by the PCS/MAC and
|
||||
* the SFP module.
|
||||
*/
|
||||
- phy_interface_and(pl->sfp_interfaces, pl->config->supported_interfaces,
|
||||
+ phy_interface_and(pl->sfp_interfaces, pl->supported_interfaces,
|
||||
pl->sfp_interfaces);
|
||||
if (phy_interface_empty(pl->sfp_interfaces)) {
|
||||
phylink_err(pl, "unsupported SFP module: no common interface modes\n");
|
||||
@@ -3864,7 +3872,7 @@ static int phylink_sfp_connect_phy(void
|
||||
|
||||
/* Set the PHY's host supported interfaces */
|
||||
phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,
|
||||
- pl->config->supported_interfaces);
|
||||
+ pl->supported_interfaces);
|
||||
|
||||
/* Do the initial configuration */
|
||||
return phylink_sfp_config_phy(pl, phy);
|
||||
+384
@@ -0,0 +1,384 @@
|
||||
From d134e22b540226a7404cabb88c86a54857486b4f Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 31 Mar 2025 16:03:26 +0200
|
||||
Subject: [PATCH 2/7] net: phylink: introduce internal phylink PCS handling
|
||||
|
||||
Introduce internal handling of PCS for phylink. This is an alternative
|
||||
to .mac_select_pcs that moves the selection logic of the PCS entirely to
|
||||
phylink with the usage of supported_interface value in the PCS struct.
|
||||
|
||||
MAC should now provide an array of available PCS in phylink_config in
|
||||
.available_pcs and fill the .num_available_pcs with the number of
|
||||
elements in the array. MAC should also define a new bitmap,
|
||||
pcs_interfaces, in phylink_config to define for what interface mode a
|
||||
dedicated PCS is required.
|
||||
|
||||
On phylink_create() this array is parsed and a linked list of PCS is
|
||||
created based on the PCS passed in phylink_config.
|
||||
Also the supported_interface value in phylink struct is updated with the
|
||||
new supported_interface from the provided PCS.
|
||||
|
||||
On phylink_start() every PCS in phylink PCS list gets attached to the
|
||||
phylink instance. This is done by setting the phylink value in
|
||||
phylink_pcs struct to the phylink instance.
|
||||
|
||||
On phylink_stop(), every PCS in phylink PCS list is detached from the
|
||||
phylink instance. This is done by setting the phylink value in
|
||||
phylink_pcs struct to NULL.
|
||||
|
||||
On phylink_stop(), every PCS in phylink PCS list is removed from the
|
||||
list.
|
||||
|
||||
phylink_validate_mac_and_pcs(), phylink_major_config() and
|
||||
phylink_inband_caps() are updated to support this new implementation
|
||||
with the PCS list stored in phylink.
|
||||
|
||||
They will make use of phylink_validate_pcs_interface() that will loop
|
||||
for every PCS in the phylink PCS available list and find one that supports
|
||||
the passed interface.
|
||||
|
||||
phylink_validate_pcs_interface() apply the same logic of .mac_select_pcs
|
||||
where if a supported_interface value is not set for the PCS struct, then
|
||||
it's assumed every interface is supported.
|
||||
|
||||
It's required for a MAC that implement either a .mac_select_pcs or make
|
||||
use of the PCS list implementation. Implementing both will result in a fail
|
||||
on MAC/PCS validation.
|
||||
|
||||
phylink value in phylink_pcs struct with this implementation is used to
|
||||
track from PCS side when it's attached to a phylink instance. PCS driver
|
||||
will make use of this information to correctly detach from a phylink
|
||||
instance if needed.
|
||||
|
||||
The .mac_select_pcs implementation is not changed but it's expected that
|
||||
every MAC driver migrates to the new implementation to later deprecate
|
||||
and remove .mac_select_pcs.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 149 +++++++++++++++++++++++++++++++++-----
|
||||
include/linux/phylink.h | 11 +++
|
||||
2 files changed, 141 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -59,6 +59,9 @@ struct phylink {
|
||||
/* The link configuration settings */
|
||||
struct phylink_link_state link_config;
|
||||
|
||||
+ /* List of available PCS */
|
||||
+ struct list_head pcs_list;
|
||||
+
|
||||
/* What interface are supported by the current link.
|
||||
* Can change on removal or addition of new PCS.
|
||||
*/
|
||||
@@ -149,6 +152,8 @@ static const phy_interface_t phylink_sfp
|
||||
|
||||
static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
|
||||
|
||||
+static void phylink_run_resolve(struct phylink *pl);
|
||||
+
|
||||
/**
|
||||
* phylink_set_port_modes() - set the port type modes in the ethtool mask
|
||||
* @mask: ethtool link mode mask
|
||||
@@ -512,22 +517,59 @@ static void phylink_validate_mask_caps(u
|
||||
linkmode_and(state->advertising, state->advertising, mask);
|
||||
}
|
||||
|
||||
+static int phylink_validate_pcs_interface(struct phylink_pcs *pcs,
|
||||
+ phy_interface_t interface)
|
||||
+{
|
||||
+ /* If PCS define an empty supported_interfaces value, assume
|
||||
+ * all interface are supported.
|
||||
+ */
|
||||
+ if (phy_interface_empty(pcs->supported_interfaces))
|
||||
+ return 0;
|
||||
+
|
||||
+ /* Ensure that this PCS supports the interface mode */
|
||||
+ if (!test_bit(interface, pcs->supported_interfaces))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int phylink_validate_mac_and_pcs(struct phylink *pl,
|
||||
unsigned long *supported,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
- struct phylink_pcs *pcs = NULL;
|
||||
unsigned long capabilities;
|
||||
+ struct phylink_pcs *pcs;
|
||||
+ bool pcs_found = false;
|
||||
int ret;
|
||||
|
||||
/* Get the PCS for this interface mode */
|
||||
if (pl->mac_ops->mac_select_pcs) {
|
||||
+ /* Make sure either PCS internal validation or .mac_select_pcs
|
||||
+ * is used. Return error if both are defined.
|
||||
+ */
|
||||
+ if (!list_empty(&pl->pcs_list)) {
|
||||
+ phylink_err(pl, "either phylink_pcs_add() or .mac_select_pcs must be used\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
|
||||
if (IS_ERR(pcs))
|
||||
return PTR_ERR(pcs);
|
||||
+
|
||||
+ pcs_found = !!pcs;
|
||||
+ } else {
|
||||
+ /* Check every assigned PCS and search for one that supports
|
||||
+ * the interface.
|
||||
+ */
|
||||
+ list_for_each_entry(pcs, &pl->pcs_list, list) {
|
||||
+ if (!phylink_validate_pcs_interface(pcs, state->interface)) {
|
||||
+ pcs_found = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
- if (pcs) {
|
||||
+ if (pcs_found) {
|
||||
/* The PCS, if present, must be setup before phylink_create()
|
||||
* has been called. If the ops is not initialised, print an
|
||||
* error and backtrace rather than oopsing the kernel.
|
||||
@@ -539,13 +581,10 @@ static int phylink_validate_mac_and_pcs(
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- /* Ensure that this PCS supports the interface which the MAC
|
||||
- * returned it for. It is an error for the MAC to return a PCS
|
||||
- * that does not support the interface mode.
|
||||
- */
|
||||
- if (!phy_interface_empty(pcs->supported_interfaces) &&
|
||||
- !test_bit(state->interface, pcs->supported_interfaces)) {
|
||||
- phylink_err(pl, "MAC returned PCS which does not support %s\n",
|
||||
+ /* Recheck PCS to handle legacy way for .mac_select_pcs */
|
||||
+ ret = phylink_validate_pcs_interface(pcs, state->interface);
|
||||
+ if (ret) {
|
||||
+ phylink_err(pl, "selected PCS does not support %s\n",
|
||||
phy_modes(state->interface));
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -959,12 +998,22 @@ static unsigned int phylink_inband_caps(
|
||||
phy_interface_t interface)
|
||||
{
|
||||
struct phylink_pcs *pcs;
|
||||
+ bool pcs_found = false;
|
||||
|
||||
- if (!pl->mac_ops->mac_select_pcs)
|
||||
- return 0;
|
||||
+ if (pl->mac_ops->mac_select_pcs) {
|
||||
+ pcs = pl->mac_ops->mac_select_pcs(pl->config,
|
||||
+ interface);
|
||||
+ pcs_found = !!pcs;
|
||||
+ } else {
|
||||
+ list_for_each_entry(pcs, &pl->pcs_list, list) {
|
||||
+ if (!phylink_validate_pcs_interface(pcs, interface)) {
|
||||
+ pcs_found = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- pcs = pl->mac_ops->mac_select_pcs(pl->config, interface);
|
||||
- if (!pcs)
|
||||
+ if (!pcs_found)
|
||||
return 0;
|
||||
|
||||
return phylink_pcs_inband_caps(pcs, interface);
|
||||
@@ -1259,10 +1308,36 @@ static void phylink_major_config(struct
|
||||
pl->major_config_failed = true;
|
||||
return;
|
||||
}
|
||||
+ /* Find a PCS in available PCS list for the requested interface.
|
||||
+ * This doesn't overwrite the previous .mac_select_pcs as either
|
||||
+ * .mac_select_pcs or PCS list implementation are permitted.
|
||||
+ *
|
||||
+ * Skip searching if the MAC doesn't require a dedicaed PCS for
|
||||
+ * the requested interface.
|
||||
+ */
|
||||
+ } else if (test_bit(state->interface, pl->config->pcs_interfaces)) {
|
||||
+ bool pcs_found = false;
|
||||
+
|
||||
+ list_for_each_entry(pcs, &pl->pcs_list, list) {
|
||||
+ if (!phylink_validate_pcs_interface(pcs,
|
||||
+ state->interface)) {
|
||||
+ pcs_found = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!pcs_found) {
|
||||
+ phylink_err(pl,
|
||||
+ "couldn't find a PCS for %s\n",
|
||||
+ phy_modes(state->interface));
|
||||
|
||||
- pcs_changed = pl->pcs != pcs;
|
||||
+ pl->major_config_failed = true;
|
||||
+ return;
|
||||
+ }
|
||||
}
|
||||
|
||||
+ pcs_changed = pl->pcs != pcs;
|
||||
+
|
||||
phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising);
|
||||
|
||||
phylink_dbg(pl, "major config, active %s/%s/%s\n",
|
||||
@@ -1289,11 +1364,13 @@ static void phylink_major_config(struct
|
||||
if (pcs_changed) {
|
||||
phylink_pcs_disable(pl->pcs);
|
||||
|
||||
- if (pl->pcs)
|
||||
- pl->pcs->phylink = NULL;
|
||||
+ if (pl->mac_ops->mac_select_pcs) {
|
||||
+ if (pl->pcs)
|
||||
+ pl->pcs->phylink = NULL;
|
||||
|
||||
- if (pcs)
|
||||
- pcs->phylink = pl;
|
||||
+ if (pcs)
|
||||
+ pcs->phylink = pl;
|
||||
+ }
|
||||
|
||||
pl->pcs = pcs;
|
||||
}
|
||||
@@ -1840,8 +1917,9 @@ struct phylink *phylink_create(struct ph
|
||||
phy_interface_t iface,
|
||||
const struct phylink_mac_ops *mac_ops)
|
||||
{
|
||||
+ struct phylink_pcs *pcs;
|
||||
struct phylink *pl;
|
||||
- int ret;
|
||||
+ int i, ret;
|
||||
|
||||
/* Validate the supplied configuration */
|
||||
if (phy_interface_empty(config->supported_interfaces)) {
|
||||
@@ -1857,9 +1935,21 @@ struct phylink *phylink_create(struct ph
|
||||
mutex_init(&pl->phydev_mutex);
|
||||
mutex_init(&pl->state_mutex);
|
||||
INIT_WORK(&pl->resolve, phylink_resolve);
|
||||
+ INIT_LIST_HEAD(&pl->pcs_list);
|
||||
+
|
||||
+ /* Fill the PCS list with available PCS from phylink config */
|
||||
+ for (i = 0; i < config->num_available_pcs; i++) {
|
||||
+ pcs = config->available_pcs[i];
|
||||
+
|
||||
+ list_add(&pcs->list, &pl->pcs_list);
|
||||
+ }
|
||||
|
||||
phy_interface_copy(pl->supported_interfaces,
|
||||
config->supported_interfaces);
|
||||
+ list_for_each_entry(pcs, &pl->pcs_list, list)
|
||||
+ phy_interface_or(pl->supported_interfaces,
|
||||
+ pl->supported_interfaces,
|
||||
+ pcs->supported_interfaces);
|
||||
|
||||
pl->config = config;
|
||||
if (config->type == PHYLINK_NETDEV) {
|
||||
@@ -1938,10 +2028,16 @@ EXPORT_SYMBOL_GPL(phylink_create);
|
||||
*/
|
||||
void phylink_destroy(struct phylink *pl)
|
||||
{
|
||||
+ struct phylink_pcs *pcs, *tmp;
|
||||
+
|
||||
sfp_bus_del_upstream(pl->sfp_bus);
|
||||
if (pl->link_gpio)
|
||||
gpiod_put(pl->link_gpio);
|
||||
|
||||
+ /* Remove every PCS from phylink PCS list */
|
||||
+ list_for_each_entry_safe(pcs, tmp, &pl->pcs_list, list)
|
||||
+ list_del(&pcs->list);
|
||||
+
|
||||
cancel_work_sync(&pl->resolve);
|
||||
kfree(pl);
|
||||
}
|
||||
@@ -2443,6 +2539,7 @@ static irqreturn_t phylink_link_handler(
|
||||
*/
|
||||
void phylink_start(struct phylink *pl)
|
||||
{
|
||||
+ struct phylink_pcs *pcs;
|
||||
bool poll = false;
|
||||
|
||||
ASSERT_RTNL();
|
||||
@@ -2469,6 +2566,10 @@ void phylink_start(struct phylink *pl)
|
||||
|
||||
pl->pcs_state = PCS_STATE_STARTED;
|
||||
|
||||
+ /* link available PCS to phylink struct */
|
||||
+ list_for_each_entry(pcs, &pl->pcs_list, list)
|
||||
+ pcs->phylink = pl;
|
||||
+
|
||||
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
|
||||
|
||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
|
||||
@@ -2513,6 +2614,8 @@ EXPORT_SYMBOL_GPL(phylink_start);
|
||||
*/
|
||||
void phylink_stop(struct phylink *pl)
|
||||
{
|
||||
+ struct phylink_pcs *pcs;
|
||||
+
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (pl->sfp_bus)
|
||||
@@ -2530,6 +2633,14 @@ void phylink_stop(struct phylink *pl)
|
||||
pl->pcs_state = PCS_STATE_DOWN;
|
||||
|
||||
phylink_pcs_disable(pl->pcs);
|
||||
+
|
||||
+ /* Drop link between phylink and PCS */
|
||||
+ list_for_each_entry(pcs, &pl->pcs_list, list)
|
||||
+ pcs->phylink = NULL;
|
||||
+
|
||||
+ /* Restore original supported interfaces */
|
||||
+ phy_interface_copy(pl->supported_interfaces,
|
||||
+ pl->config->supported_interfaces);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_stop);
|
||||
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -150,12 +150,16 @@ enum phylink_op_type {
|
||||
* if MAC link is at %MLO_AN_FIXED mode.
|
||||
* @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx
|
||||
* are supported by the MAC/PCS.
|
||||
+ * @pcs_interfaces: bitmap describing for which PHY_INTERFACE_MODE_xxx a
|
||||
+ * dedicated PCS is required.
|
||||
* @lpi_interfaces: bitmap describing which PHY interface modes can support
|
||||
* LPI signalling.
|
||||
* @mac_capabilities: MAC pause/speed/duplex capabilities.
|
||||
* @lpi_capabilities: MAC speeds which can support LPI signalling
|
||||
* @lpi_timer_default: Default EEE LPI timer setting.
|
||||
* @eee_enabled_default: If set, EEE will be enabled by phylink at creation time
|
||||
+ * @available_pcs: array of available phylink_pcs PCS
|
||||
+ * @num_available_pcs: num of available phylink_pcs PCS
|
||||
*/
|
||||
struct phylink_config {
|
||||
struct device *dev;
|
||||
@@ -168,11 +172,15 @@ struct phylink_config {
|
||||
void (*get_fixed_state)(struct phylink_config *config,
|
||||
struct phylink_link_state *state);
|
||||
DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
|
||||
+ DECLARE_PHY_INTERFACE_MASK(pcs_interfaces);
|
||||
DECLARE_PHY_INTERFACE_MASK(lpi_interfaces);
|
||||
unsigned long mac_capabilities;
|
||||
unsigned long lpi_capabilities;
|
||||
u32 lpi_timer_default;
|
||||
bool eee_enabled_default;
|
||||
+
|
||||
+ struct phylink_pcs **available_pcs;
|
||||
+ unsigned int num_available_pcs;
|
||||
};
|
||||
|
||||
void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed);
|
||||
@@ -468,6 +476,9 @@ struct phylink_pcs {
|
||||
struct phylink *phylink;
|
||||
bool poll;
|
||||
bool rxc_always_on;
|
||||
+
|
||||
+ /* private: */
|
||||
+ struct list_head list;
|
||||
};
|
||||
|
||||
/**
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
From 1cb4e56c3ba32ac1bce89dc9c34ef2dbc9b89ad4 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 31 Mar 2025 19:10:24 +0200
|
||||
Subject: [PATCH 3/7] net: phylink: add phylink_release_pcs() to externally
|
||||
release a PCS
|
||||
|
||||
Add phylink_release_pcs() to externally release a PCS from a phylink
|
||||
instance. This can be used to handle case when a single PCS needs to be
|
||||
removed and the phylink instance needs to be refreshed.
|
||||
|
||||
On calling phylink_release_pcs(), the PCS will be removed from the
|
||||
phylink internal PCS list and the phylink supported_interfaces value is
|
||||
reparsed with the remaining PCS interfaces.
|
||||
|
||||
Also a phylink resolve is triggered to handle the PCS removal.
|
||||
|
||||
It's also added to phylink a flag to make phylink resolve reconfigure
|
||||
the interface mode (even if it didn't change). This is needed to handle
|
||||
the special case when the current PCS used by phylink is removed and a
|
||||
major_config is needed to propagae the configuration change. With this
|
||||
option enabled we also force mac_config even if the PHY link is not up
|
||||
for the in-band case.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 57 ++++++++++++++++++++++++++++++++++++++-
|
||||
include/linux/phylink.h | 2 ++
|
||||
2 files changed, 58 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -86,6 +86,7 @@ struct phylink {
|
||||
bool link_failed;
|
||||
bool suspend_link_up;
|
||||
bool major_config_failed;
|
||||
+ bool reconfig_interface;
|
||||
bool mac_supports_eee_ops;
|
||||
bool mac_supports_eee;
|
||||
bool phy_enable_tx_lpi;
|
||||
@@ -917,6 +918,55 @@ static void phylink_resolve_an_pause(str
|
||||
}
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * phylink_release_pcs - Removes a PCS from the phylink PCS available list
|
||||
+ * @pcs: a pointer to the phylink_pcs struct to be released
|
||||
+ *
|
||||
+ * This function release a PCS from the phylink PCS available list if
|
||||
+ * actually in use. It also refreshes the supported interfaces of the
|
||||
+ * phylink instance by copying the supported interfaces from the phylink
|
||||
+ * conf and merging the supported interfaces of the remaining available PCS
|
||||
+ * in the list and trigger a resolve.
|
||||
+ */
|
||||
+void phylink_release_pcs(struct phylink_pcs *pcs)
|
||||
+{
|
||||
+ struct phylink *pl;
|
||||
+
|
||||
+ ASSERT_RTNL();
|
||||
+
|
||||
+ pl = pcs->phylink;
|
||||
+ if (!pl)
|
||||
+ return;
|
||||
+
|
||||
+ list_del(&pcs->list);
|
||||
+ pcs->phylink = NULL;
|
||||
+
|
||||
+ /* Check if we are removing the PCS currently
|
||||
+ * in use by phylink. If this is the case,
|
||||
+ * force phylink resolve to reconfigure the interface
|
||||
+ * mode and set the phylink PCS to NULL.
|
||||
+ */
|
||||
+ if (pl->pcs == pcs) {
|
||||
+ mutex_lock(&pl->state_mutex);
|
||||
+
|
||||
+ pl->reconfig_interface = true;
|
||||
+ pl->pcs = NULL;
|
||||
+
|
||||
+ mutex_unlock(&pl->state_mutex);
|
||||
+ }
|
||||
+
|
||||
+ /* Refresh supported interfaces */
|
||||
+ phy_interface_copy(pl->supported_interfaces,
|
||||
+ pl->config->supported_interfaces);
|
||||
+ list_for_each_entry(pcs, &pl->pcs_list, list)
|
||||
+ phy_interface_or(pl->supported_interfaces,
|
||||
+ pl->supported_interfaces,
|
||||
+ pcs->supported_interfaces);
|
||||
+
|
||||
+ phylink_run_resolve(pl);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(phylink_release_pcs);
|
||||
+
|
||||
static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
@@ -1730,6 +1780,10 @@ static void phylink_resolve(struct work_
|
||||
if (phy)
|
||||
link_state.link &= pl->phy_state.link;
|
||||
|
||||
+ /* Force mac_config if we need to reconfig the interface */
|
||||
+ if (pl->reconfig_interface)
|
||||
+ mac_config = true;
|
||||
+
|
||||
/* Only update if the PHY link is up */
|
||||
if (phy && pl->phy_state.link) {
|
||||
/* If the interface has changed, force a link down
|
||||
@@ -1763,7 +1817,8 @@ static void phylink_resolve(struct work_
|
||||
if (pl->act_link_an_mode != MLO_AN_FIXED)
|
||||
phylink_apply_manual_flow(pl, &link_state);
|
||||
|
||||
- if (mac_config && link_state.interface != pl->link_config.interface) {
|
||||
+ if ((mac_config && link_state.interface != pl->link_config.interface) ||
|
||||
+ pl->reconfig_interface) {
|
||||
/* The interface has changed, so force the link down and then
|
||||
* reconfigure.
|
||||
*/
|
||||
@@ -1773,6 +1828,7 @@ static void phylink_resolve(struct work_
|
||||
}
|
||||
phylink_major_config(pl, false, &link_state);
|
||||
pl->link_config.interface = link_state.interface;
|
||||
+ pl->reconfig_interface = false;
|
||||
}
|
||||
|
||||
/* If configuration of the interface failed, force the link down
|
||||
--- a/include/linux/phylink.h
|
||||
+++ b/include/linux/phylink.h
|
||||
@@ -717,6 +717,8 @@ void phylink_disconnect_phy(struct phyli
|
||||
int phylink_set_fixed_link(struct phylink *,
|
||||
const struct phylink_link_state *);
|
||||
|
||||
+void phylink_release_pcs(struct phylink_pcs *pcs);
|
||||
+
|
||||
void phylink_mac_change(struct phylink *, bool up);
|
||||
void phylink_pcs_change(struct phylink_pcs *, bool up);
|
||||
|
||||
+384
@@ -0,0 +1,384 @@
|
||||
From a90c644c73bbffd400cd3839fc17ffdfc69ea1e8 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 17 Mar 2025 01:46:53 +0100
|
||||
Subject: [PATCH 4/7] net: pcs: implement Firmware node support for PCS driver
|
||||
|
||||
Implement the foundation of Firmware node support for PCS driver.
|
||||
|
||||
To support this, implement a simple Provider API where a PCS driver can
|
||||
expose multiple PCS with an xlate .get function.
|
||||
|
||||
PCS driver will have to call fwnode_pcs_add_provider() and pass the
|
||||
firmware node pointer and a xlate function to return the correct PCS for
|
||||
the passed #pcs-cells.
|
||||
|
||||
This will register the PCS in a global list of providers so that
|
||||
consumer can access it.
|
||||
|
||||
Consumer will then use fwnode_pcs_get() to get the actual PCS by passing
|
||||
the firmware node pointer and the index for #pcs-cells.
|
||||
|
||||
For simple implementation where #pcs-cells is 0 and the PCS driver
|
||||
expose a single PCS, the xlate function fwnode_pcs_simple_get() is
|
||||
provided.
|
||||
|
||||
For advanced implementation a custom xlate function is required.
|
||||
|
||||
PCS driver on removal should first delete as a provider with
|
||||
the usage of fwnode_pcs_del_provider() and then call
|
||||
phylink_release_pcs() on every PCS the driver provides and
|
||||
|
||||
A generic function fwnode_phylink_pcs_parse() is provided for any MAC
|
||||
driver that will declare PCS in DT (or ACPI).
|
||||
This function will parse "pcs-handle" property and fill the passed array
|
||||
with the parsed PCS in availabel_pcs up to the passed num_pcs value.
|
||||
It's also possible to pass NULL as array to only parse the PCS and
|
||||
update the num_pcs value with the count of scanned PCS.
|
||||
|
||||
Co-developed-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/net/pcs/Kconfig | 7 ++
|
||||
drivers/net/pcs/Makefile | 1 +
|
||||
drivers/net/pcs/pcs.c | 201 +++++++++++++++++++++++++++++++
|
||||
include/linux/pcs/pcs-provider.h | 41 +++++++
|
||||
include/linux/pcs/pcs.h | 56 +++++++++
|
||||
5 files changed, 306 insertions(+)
|
||||
create mode 100644 drivers/net/pcs/pcs.c
|
||||
create mode 100644 include/linux/pcs/pcs-provider.h
|
||||
create mode 100644 include/linux/pcs/pcs.h
|
||||
|
||||
--- a/drivers/net/pcs/Kconfig
|
||||
+++ b/drivers/net/pcs/Kconfig
|
||||
@@ -5,6 +5,13 @@
|
||||
|
||||
menu "PCS device drivers"
|
||||
|
||||
+config FWNODE_PCS
|
||||
+ tristate
|
||||
+ depends on (ACPI || OF)
|
||||
+ depends on PHYLINK
|
||||
+ help
|
||||
+ Firmware node PCS accessors
|
||||
+
|
||||
config PCS_XPCS
|
||||
tristate "Synopsys DesignWare Ethernet XPCS"
|
||||
select PHYLINK
|
||||
--- a/drivers/net/pcs/Makefile
|
||||
+++ b/drivers/net/pcs/Makefile
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for Linux PCS drivers
|
||||
|
||||
+obj-$(CONFIG_FWNODE_PCS) += pcs.o
|
||||
pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-plat.o \
|
||||
pcs-xpcs-nxp.o pcs-xpcs-wx.o
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/pcs/pcs.c
|
||||
@@ -0,0 +1,201 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/property.h>
|
||||
+#include <linux/phylink.h>
|
||||
+#include <linux/pcs/pcs.h>
|
||||
+#include <linux/pcs/pcs-provider.h>
|
||||
+
|
||||
+MODULE_DESCRIPTION("PCS library");
|
||||
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
+struct fwnode_pcs_provider {
|
||||
+ struct list_head link;
|
||||
+
|
||||
+ struct fwnode_handle *fwnode;
|
||||
+ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec,
|
||||
+ void *data);
|
||||
+
|
||||
+ void *data;
|
||||
+};
|
||||
+
|
||||
+static LIST_HEAD(fwnode_pcs_providers);
|
||||
+static DEFINE_MUTEX(fwnode_pcs_mutex);
|
||||
+
|
||||
+struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec,
|
||||
+ void *data)
|
||||
+{
|
||||
+ return data;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(fwnode_pcs_simple_get);
|
||||
+
|
||||
+int fwnode_pcs_add_provider(struct fwnode_handle *fwnode,
|
||||
+ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec,
|
||||
+ void *data),
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct fwnode_pcs_provider *pp;
|
||||
+
|
||||
+ if (!fwnode)
|
||||
+ return 0;
|
||||
+
|
||||
+ pp = kzalloc(sizeof(*pp), GFP_KERNEL);
|
||||
+ if (!pp)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ pp->fwnode = fwnode_handle_get(fwnode);
|
||||
+ pp->data = data;
|
||||
+ pp->get = get;
|
||||
+
|
||||
+ mutex_lock(&fwnode_pcs_mutex);
|
||||
+ list_add(&pp->link, &fwnode_pcs_providers);
|
||||
+ mutex_unlock(&fwnode_pcs_mutex);
|
||||
+ pr_debug("Added pcs provider from %pfwf\n", fwnode);
|
||||
+
|
||||
+ fwnode_dev_initialized(fwnode, true);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(fwnode_pcs_add_provider);
|
||||
+
|
||||
+void fwnode_pcs_del_provider(struct fwnode_handle *fwnode)
|
||||
+{
|
||||
+ struct fwnode_pcs_provider *pp;
|
||||
+
|
||||
+ if (!fwnode)
|
||||
+ return;
|
||||
+
|
||||
+ mutex_lock(&fwnode_pcs_mutex);
|
||||
+ list_for_each_entry(pp, &fwnode_pcs_providers, link) {
|
||||
+ if (pp->fwnode == fwnode) {
|
||||
+ list_del(&pp->link);
|
||||
+ fwnode_dev_initialized(pp->fwnode, false);
|
||||
+ fwnode_handle_put(pp->fwnode);
|
||||
+ kfree(pp);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ mutex_unlock(&fwnode_pcs_mutex);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(fwnode_pcs_del_provider);
|
||||
+
|
||||
+static int fwnode_parse_pcsspec(const struct fwnode_handle *fwnode, int index,
|
||||
+ const char *name,
|
||||
+ struct fwnode_reference_args *out_args)
|
||||
+{
|
||||
+ int ret = -ENOENT;
|
||||
+
|
||||
+ if (!fwnode)
|
||||
+ return -ENOENT;
|
||||
+
|
||||
+ if (name)
|
||||
+ index = fwnode_property_match_string(fwnode, "pcs-names",
|
||||
+ name);
|
||||
+
|
||||
+ ret = fwnode_property_get_reference_args(fwnode, "pcs-handle",
|
||||
+ "#pcs-cells",
|
||||
+ -1, index, out_args);
|
||||
+ if (ret || (name && index < 0))
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct phylink_pcs *
|
||||
+fwnode_pcs_get_from_pcsspec(struct fwnode_reference_args *pcsspec)
|
||||
+{
|
||||
+ struct fwnode_pcs_provider *provider;
|
||||
+ struct phylink_pcs *pcs = ERR_PTR(-EPROBE_DEFER);
|
||||
+
|
||||
+ if (!pcsspec)
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+
|
||||
+ mutex_lock(&fwnode_pcs_mutex);
|
||||
+ list_for_each_entry(provider, &fwnode_pcs_providers, link) {
|
||||
+ if (provider->fwnode == pcsspec->fwnode) {
|
||||
+ pcs = provider->get(pcsspec, provider->data);
|
||||
+ if (!IS_ERR(pcs))
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ mutex_unlock(&fwnode_pcs_mutex);
|
||||
+
|
||||
+ return pcs;
|
||||
+}
|
||||
+
|
||||
+static struct phylink_pcs *__fwnode_pcs_get(struct fwnode_handle *fwnode,
|
||||
+ int index, const char *con_id)
|
||||
+{
|
||||
+ struct fwnode_reference_args pcsspec;
|
||||
+ struct phylink_pcs *pcs;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = fwnode_parse_pcsspec(fwnode, index, con_id, &pcsspec);
|
||||
+ if (ret)
|
||||
+ return ERR_PTR(ret);
|
||||
+
|
||||
+ pcs = fwnode_pcs_get_from_pcsspec(&pcsspec);
|
||||
+ fwnode_handle_put(pcsspec.fwnode);
|
||||
+
|
||||
+ return pcs;
|
||||
+}
|
||||
+
|
||||
+struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, int index)
|
||||
+{
|
||||
+ return __fwnode_pcs_get(fwnode, index, NULL);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(fwnode_pcs_get);
|
||||
+
|
||||
+static int fwnode_phylink_pcs_count(struct fwnode_handle *fwnode,
|
||||
+ unsigned int *num_pcs)
|
||||
+{
|
||||
+ struct fwnode_reference_args out_args;
|
||||
+ int index = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ while (true) {
|
||||
+ ret = fwnode_property_get_reference_args(fwnode, "pcs-handle",
|
||||
+ "#pcs-cells",
|
||||
+ -1, index, &out_args);
|
||||
+ /* We expect to reach an -ENOENT error while counting */
|
||||
+ if (ret)
|
||||
+ break;
|
||||
+
|
||||
+ fwnode_handle_put(out_args.fwnode);
|
||||
+ index++;
|
||||
+ }
|
||||
+
|
||||
+ /* Update num_pcs with parsed PCS */
|
||||
+ *num_pcs = index;
|
||||
+
|
||||
+ /* Return error if we didn't found any PCS */
|
||||
+ return index > 0 ? 0 : -ENOENT;
|
||||
+}
|
||||
+
|
||||
+int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode,
|
||||
+ struct phylink_pcs **available_pcs,
|
||||
+ unsigned int *num_pcs)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ if (!fwnode_property_present(fwnode, "pcs-handle"))
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ /* With available_pcs NULL, only count the PCS */
|
||||
+ if (!available_pcs)
|
||||
+ return fwnode_phylink_pcs_count(fwnode, num_pcs);
|
||||
+
|
||||
+ for (i = 0; i < *num_pcs; i++) {
|
||||
+ struct phylink_pcs *pcs;
|
||||
+
|
||||
+ pcs = fwnode_pcs_get(fwnode, i);
|
||||
+ if (IS_ERR(pcs))
|
||||
+ return PTR_ERR(pcs);
|
||||
+
|
||||
+ available_pcs[i] = pcs;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(fwnode_phylink_pcs_parse);
|
||||
--- /dev/null
|
||||
+++ b/include/linux/pcs/pcs-provider.h
|
||||
@@ -0,0 +1,41 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
+#ifndef __LINUX_PCS_PROVIDER_H
|
||||
+#define __LINUX_PCS_PROVIDER_H
|
||||
+
|
||||
+/**
|
||||
+ * fwnode_pcs_simple_get - Simple xlate function to retrieve PCS
|
||||
+ * @pcsspec: reference arguments
|
||||
+ * @data: Context data (assumed assigned to the single PCS)
|
||||
+ *
|
||||
+ * Returns the PCS. (pointed by data)
|
||||
+ */
|
||||
+struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec,
|
||||
+ void *data);
|
||||
+
|
||||
+/**
|
||||
+ * fwnode_pcs_add_provider - Registers a new PCS provider
|
||||
+ * @np: Firmware node
|
||||
+ * @get: xlate function to retrieve the PCS
|
||||
+ * @data: Context data
|
||||
+ *
|
||||
+ * Register and add a new PCS to the global providers list
|
||||
+ * for the firmware node. A function to get the PCS from
|
||||
+ * firmware node with the use fwnode reference arguments.
|
||||
+ * To the get function is also passed the interface type
|
||||
+ * requested for the PHY. PCS driver will use the passed
|
||||
+ * interface to understand if the PCS can support it or not.
|
||||
+ *
|
||||
+ * Returns 0 on success or -ENOMEM on allocation failure.
|
||||
+ */
|
||||
+int fwnode_pcs_add_provider(struct fwnode_handle *fwnode,
|
||||
+ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec,
|
||||
+ void *data),
|
||||
+ void *data);
|
||||
+
|
||||
+/**
|
||||
+ * fwnode_pcs_del_provider - Removes a PCS provider
|
||||
+ * @fwnode: Firmware node
|
||||
+ */
|
||||
+void fwnode_pcs_del_provider(struct fwnode_handle *fwnode);
|
||||
+
|
||||
+#endif /* __LINUX_PCS_PROVIDER_H */
|
||||
--- /dev/null
|
||||
+++ b/include/linux/pcs/pcs.h
|
||||
@@ -0,0 +1,56 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
+#ifndef __LINUX_PCS_H
|
||||
+#define __LINUX_PCS_H
|
||||
+
|
||||
+#include <linux/phylink.h>
|
||||
+
|
||||
+#if IS_ENABLED(CONFIG_FWNODE_PCS)
|
||||
+/**
|
||||
+ * fwnode_pcs_get - Retrieves a PCS from a firmware node
|
||||
+ * @fwnode: firmware node
|
||||
+ * @index: index fwnode PCS handle in firmware node
|
||||
+ *
|
||||
+ * Get a PCS from the firmware node at index.
|
||||
+ *
|
||||
+ * Returns a pointer to the phylink_pcs or a negative
|
||||
+ * error pointer. Can return -EPROBE_DEFER if the PCS is not
|
||||
+ * present in global providers list (either due to driver
|
||||
+ * still needs to be probed or it failed to probe/removed)
|
||||
+ */
|
||||
+struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode,
|
||||
+ int index);
|
||||
+
|
||||
+/**
|
||||
+ * fwnode_phylink_pcs_parse - generic PCS parse for fwnode PCS provider
|
||||
+ * @fwnode: firmware node
|
||||
+ * @available_pcs: pointer to preallocated array of PCS
|
||||
+ * @num_pcs: where to store count of parsed PCS
|
||||
+ *
|
||||
+ * Generic helper function to fill available_pcs array with PCS parsed
|
||||
+ * from a "pcs-handle" fwnode property defined in firmware node up to
|
||||
+ * passed num_pcs.
|
||||
+ *
|
||||
+ * If available_pcs is NULL, num_pcs is updated with the count of the
|
||||
+ * parsed PCS.
|
||||
+ *
|
||||
+ * Returns 0 or a negative error.
|
||||
+ */
|
||||
+int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode,
|
||||
+ struct phylink_pcs **available_pcs,
|
||||
+ unsigned int *num_pcs);
|
||||
+#else
|
||||
+static inline struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode,
|
||||
+ int index)
|
||||
+{
|
||||
+ return ERR_PTR(-ENOENT);
|
||||
+}
|
||||
+
|
||||
+static inline int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode,
|
||||
+ struct phylink_pcs **available_pcs,
|
||||
+ unsigned int *num_pcs)
|
||||
+{
|
||||
+ return -EOPNOTSUPP;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+#endif /* __LINUX_PCS_H */
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
From 684e49a015f2c5ae95ba968bb21ffc8fc36a2c7f Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Sun, 6 Apr 2025 03:28:22 +0200
|
||||
Subject: [PATCH 5/7] net: phylink: support late PCS provider attach
|
||||
|
||||
Add support in phylink for late PCS provider attach to a phylink
|
||||
instance. This works by creating a global notifier for the PCS provider
|
||||
and making each phylink instance that makes use of fwnode subscribe to
|
||||
this notifier.
|
||||
|
||||
The PCS notifier will emit the event FWNODE_PCS_PROVIDER_ADD every time
|
||||
a new PCS provider is added.
|
||||
|
||||
phylink will then react to this event and will call the new function
|
||||
fwnode_phylink_pcs_get_from_fwnode() that will check if the PCS fwnode
|
||||
provided by the event is present in the phy-handle property of the
|
||||
phylink instance.
|
||||
|
||||
If a related PCS is found, then such PCS is added to the phylink
|
||||
instance PCS list.
|
||||
|
||||
Then we link the PCS to the phylink instance if it's not disable and we
|
||||
refresh the supported interfaces of the phylink instance.
|
||||
|
||||
Finally we check if we are in a major_config_failed scenario and trigger
|
||||
an interface reconfiguration in the next phylink resolve.
|
||||
|
||||
If link was previously torn down due to removal of PCS, the link will be
|
||||
established again as the PCS came back and is not available to phylink.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/net/pcs/pcs.c | 34 +++++++++++++++++++++++++
|
||||
drivers/net/phy/phylink.c | 52 +++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/pcs/pcs.h | 48 ++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 134 insertions(+)
|
||||
|
||||
--- a/drivers/net/pcs/pcs.c
|
||||
+++ b/drivers/net/pcs/pcs.c
|
||||
@@ -22,6 +22,13 @@ struct fwnode_pcs_provider {
|
||||
|
||||
static LIST_HEAD(fwnode_pcs_providers);
|
||||
static DEFINE_MUTEX(fwnode_pcs_mutex);
|
||||
+static BLOCKING_NOTIFIER_HEAD(fwnode_pcs_notify_list);
|
||||
+
|
||||
+int register_fwnode_pcs_notifier(struct notifier_block *nb)
|
||||
+{
|
||||
+ return blocking_notifier_chain_register(&fwnode_pcs_notify_list, nb);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(register_fwnode_pcs_notifier);
|
||||
|
||||
struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec,
|
||||
void *data)
|
||||
@@ -55,6 +62,10 @@ int fwnode_pcs_add_provider(struct fwnod
|
||||
|
||||
fwnode_dev_initialized(fwnode, true);
|
||||
|
||||
+ blocking_notifier_call_chain(&fwnode_pcs_notify_list,
|
||||
+ FWNODE_PCS_PROVIDER_ADD,
|
||||
+ fwnode);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_pcs_add_provider);
|
||||
@@ -147,6 +158,29 @@ struct phylink_pcs *fwnode_pcs_get(struc
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_pcs_get);
|
||||
|
||||
+struct phylink_pcs *
|
||||
+fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode,
|
||||
+ struct fwnode_handle *pcs_fwnode)
|
||||
+{
|
||||
+ struct fwnode_reference_args pcsspec;
|
||||
+ int i = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ while (true) {
|
||||
+ ret = fwnode_parse_pcsspec(fwnode, i, NULL, &pcsspec);
|
||||
+ if (ret)
|
||||
+ break;
|
||||
+
|
||||
+ if (pcsspec.fwnode == pcs_fwnode)
|
||||
+ break;
|
||||
+
|
||||
+ i++;
|
||||
+ }
|
||||
+
|
||||
+ return fwnode_pcs_get(fwnode, i);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(fwnode_phylink_pcs_get_from_fwnode);
|
||||
+
|
||||
static int fwnode_phylink_pcs_count(struct fwnode_handle *fwnode,
|
||||
unsigned int *num_pcs)
|
||||
{
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_mdio.h>
|
||||
+#include <linux/pcs/pcs.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy_fixed.h>
|
||||
#include <linux/phylink.h>
|
||||
@@ -61,6 +62,7 @@ struct phylink {
|
||||
|
||||
/* List of available PCS */
|
||||
struct list_head pcs_list;
|
||||
+ struct notifier_block fwnode_pcs_nb;
|
||||
|
||||
/* What interface are supported by the current link.
|
||||
* Can change on removal or addition of new PCS.
|
||||
@@ -1952,6 +1954,51 @@ int phylink_set_fixed_link(struct phylin
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
|
||||
|
||||
+static int pcs_provider_notify(struct notifier_block *self,
|
||||
+ unsigned long val, void *data)
|
||||
+{
|
||||
+ struct phylink *pl = container_of(self, struct phylink, fwnode_pcs_nb);
|
||||
+ struct fwnode_handle *pcs_fwnode = data;
|
||||
+ struct phylink_pcs *pcs;
|
||||
+
|
||||
+ /* Check if the just added PCS provider is
|
||||
+ * in the phylink instance phy-handle property
|
||||
+ */
|
||||
+ pcs = fwnode_phylink_pcs_get_from_fwnode(dev_fwnode(pl->config->dev),
|
||||
+ pcs_fwnode);
|
||||
+ if (IS_ERR(pcs))
|
||||
+ return NOTIFY_DONE;
|
||||
+
|
||||
+ /* Add the PCS */
|
||||
+ rtnl_lock();
|
||||
+
|
||||
+ list_add(&pcs->list, &pl->pcs_list);
|
||||
+
|
||||
+ /* Link phylink if we are started */
|
||||
+ if (!pl->phylink_disable_state)
|
||||
+ pcs->phylink = pl;
|
||||
+
|
||||
+ /* Refresh supported interfaces */
|
||||
+ phy_interface_copy(pl->supported_interfaces,
|
||||
+ pl->config->supported_interfaces);
|
||||
+ list_for_each_entry(pcs, &pl->pcs_list, list)
|
||||
+ phy_interface_or(pl->supported_interfaces,
|
||||
+ pl->supported_interfaces,
|
||||
+ pcs->supported_interfaces);
|
||||
+
|
||||
+ mutex_lock(&pl->state_mutex);
|
||||
+ /* Force an interface reconfig if major config fail */
|
||||
+ if (pl->major_config_failed)
|
||||
+ pl->reconfig_interface = true;
|
||||
+ mutex_unlock(&pl->state_mutex);
|
||||
+
|
||||
+ rtnl_unlock();
|
||||
+
|
||||
+ phylink_run_resolve(pl);
|
||||
+
|
||||
+ return NOTIFY_OK;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* phylink_create() - create a phylink instance
|
||||
* @config: a pointer to the target &struct phylink_config
|
||||
@@ -2007,6 +2054,11 @@ struct phylink *phylink_create(struct ph
|
||||
pl->supported_interfaces,
|
||||
pcs->supported_interfaces);
|
||||
|
||||
+ if (!phy_interface_empty(config->pcs_interfaces)) {
|
||||
+ pl->fwnode_pcs_nb.notifier_call = pcs_provider_notify;
|
||||
+ register_fwnode_pcs_notifier(&pl->fwnode_pcs_nb);
|
||||
+ }
|
||||
+
|
||||
pl->config = config;
|
||||
if (config->type == PHYLINK_NETDEV) {
|
||||
pl->netdev = to_net_dev(config->dev);
|
||||
--- a/include/linux/pcs/pcs.h
|
||||
+++ b/include/linux/pcs/pcs.h
|
||||
@@ -4,8 +4,25 @@
|
||||
|
||||
#include <linux/phylink.h>
|
||||
|
||||
+enum fwnode_pcs_notify_event {
|
||||
+ FWNODE_PCS_PROVIDER_ADD,
|
||||
+};
|
||||
+
|
||||
#if IS_ENABLED(CONFIG_FWNODE_PCS)
|
||||
/**
|
||||
+ * register_fwnode_pcs_notifier - Register a notifier block for fwnode
|
||||
+ * PCS events
|
||||
+ * @nb: pointer to the notifier block
|
||||
+ *
|
||||
+ * Registers a notifier block to the fwnode_pcs_notify_list blocking
|
||||
+ * notifier chain. This allows phylink instance to subscribe for
|
||||
+ * PCS provider events.
|
||||
+ *
|
||||
+ * Returns 0 or a negative error.
|
||||
+ */
|
||||
+int register_fwnode_pcs_notifier(struct notifier_block *nb);
|
||||
+
|
||||
+/**
|
||||
* fwnode_pcs_get - Retrieves a PCS from a firmware node
|
||||
* @fwnode: firmware node
|
||||
* @index: index fwnode PCS handle in firmware node
|
||||
@@ -21,6 +38,25 @@ struct phylink_pcs *fwnode_pcs_get(struc
|
||||
int index);
|
||||
|
||||
/**
|
||||
+ * fwnode_phylink_pcs_get_from_fwnode - Retrieves the PCS provided
|
||||
+ * by the firmware node from a
|
||||
+ * firmware node
|
||||
+ * @fwnode: firmware node
|
||||
+ * @pcs_fwnode: PCS firmware node
|
||||
+ *
|
||||
+ * Parse 'pcs-handle' in 'fwnode' and get the PCS that match
|
||||
+ * 'pcs_fwnode' firmware node.
|
||||
+ *
|
||||
+ * Returns a pointer to the phylink_pcs or a negative
|
||||
+ * error pointer. Can return -EPROBE_DEFER if the PCS is not
|
||||
+ * present in global providers list (either due to driver
|
||||
+ * still needs to be probed or it failed to probe/removed)
|
||||
+ */
|
||||
+struct phylink_pcs *
|
||||
+fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode,
|
||||
+ struct fwnode_handle *pcs_fwnode);
|
||||
+
|
||||
+/**
|
||||
* fwnode_phylink_pcs_parse - generic PCS parse for fwnode PCS provider
|
||||
* @fwnode: firmware node
|
||||
* @available_pcs: pointer to preallocated array of PCS
|
||||
@@ -39,11 +75,23 @@ int fwnode_phylink_pcs_parse(struct fwno
|
||||
struct phylink_pcs **available_pcs,
|
||||
unsigned int *num_pcs);
|
||||
#else
|
||||
+static inline int register_fwnode_pcs_notifier(struct notifier_block *nb)
|
||||
+{
|
||||
+ return -EOPNOTSUPP;
|
||||
+}
|
||||
+
|
||||
static inline struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode,
|
||||
int index)
|
||||
{
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
+
|
||||
+static inline struct phylink_pcs *
|
||||
+fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode,
|
||||
+ struct fwnode_handle *pcs_fwnode)
|
||||
+{
|
||||
+ return ERR_PTR(-ENOENT);
|
||||
+}
|
||||
|
||||
static inline int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode,
|
||||
struct phylink_pcs **available_pcs,
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user