mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 17:42:41 +00:00
* update netifd * update hostapd * update mt76 * update maverick * add dynamic vlan * update ucentral-client * update ucentral-schema * update ucode * update 5.10 patches * fix realtek Signed-off-by: John Crispin <john@phrozen.org>
6269 lines
188 KiB
Diff
6269 lines
188 KiB
Diff
From cd5d073a6469beb8e7a3a267672e34baa73cbd83 Mon Sep 17 00:00:00 2001
|
|
From: John Crispin <john@phrozen.org>
|
|
Date: Tue, 16 Mar 2021 10:46:51 +0100
|
|
Subject: [PATCH 01/40] realtek: update to latest owrt HEAD
|
|
|
|
Signed-off-by: John Crispin <john@phrozen.org>
|
|
---
|
|
package/boot/uboot-envtools/files/realtek | 7 +
|
|
.../realtek/base-files/etc/board.d/01_leds | 1 -
|
|
.../realtek/base-files/etc/board.d/02_network | 3 +
|
|
target/linux/realtek/config-5.4 | 31 +-
|
|
.../realtek/dts/rtl8380_zyxel_gs1900-10hp.dts | 20 +-
|
|
.../dts/rtl8392_edgecore_ecs4100-12ph.dts | 297 +++++++++
|
|
target/linux/realtek/dts/rtl839x.dtsi | 197 ++++++
|
|
.../include/asm/mach-rtl838x/mach-rtl83xx.h | 23 +-
|
|
.../files-5.4/arch/mips/rtl838x/setup.c | 18 -
|
|
.../files-5.4/drivers/gpio/edgecore_reboot.c | 61 ++
|
|
.../files-5.4/drivers/gpio/gpio-rtl838x.c | 3 +
|
|
.../drivers/net/dsa/rtl83xx/common.c | 39 +-
|
|
.../files-5.4/drivers/net/dsa/rtl83xx/dsa.c | 609 +++++++++++++-----
|
|
.../drivers/net/dsa/rtl83xx/rtl838x.c | 581 +++++++++++++----
|
|
.../drivers/net/dsa/rtl83xx/rtl838x.h | 56 +-
|
|
.../drivers/net/dsa/rtl83xx/rtl839x.c | 540 ++++++++++++----
|
|
.../drivers/net/dsa/rtl83xx/rtl930x.c | 506 +++++++++++++--
|
|
.../drivers/net/dsa/rtl83xx/rtl931x.c | 69 +-
|
|
.../drivers/net/ethernet/rtl838x_eth.c | 155 +++--
|
|
.../drivers/net/ethernet/rtl838x_eth.h | 1 +
|
|
.../files-5.4/drivers/net/phy/rtl83xx-phy.c | 562 +++++++++-------
|
|
target/linux/realtek/image/Makefile | 9 +
|
|
.../301-gpio-add-rtl838x-driver.patch | 2 +-
|
|
...0-gpio-Add-Realtek-Otto-GPIO-support.patch | 412 ++++++++++++
|
|
.../realtek/patches-5.4/706-sysled.patch | 294 +++++++++
|
|
.../realtek/patches-5.4/707-reboot.patch | 9 +
|
|
.../realtek/patches-5.4/708-poor-stp.patch | 16 +
|
|
.../realtek/patches-5.4/710-adt7470.patch | 22 +
|
|
.../realtek/patches-5.4/711-ec4100.patch | 41 ++
|
|
29 files changed, 3785 insertions(+), 799 deletions(-)
|
|
create mode 100644 target/linux/realtek/dts/rtl8392_edgecore_ecs4100-12ph.dts
|
|
create mode 100644 target/linux/realtek/dts/rtl839x.dtsi
|
|
create mode 100644 target/linux/realtek/files-5.4/drivers/gpio/edgecore_reboot.c
|
|
create mode 100644 target/linux/realtek/patches-5.4/500-gpio-Add-Realtek-Otto-GPIO-support.patch
|
|
create mode 100644 target/linux/realtek/patches-5.4/706-sysled.patch
|
|
create mode 100644 target/linux/realtek/patches-5.4/707-reboot.patch
|
|
create mode 100644 target/linux/realtek/patches-5.4/708-poor-stp.patch
|
|
create mode 100644 target/linux/realtek/patches-5.4/710-adt7470.patch
|
|
create mode 100644 target/linux/realtek/patches-5.4/711-ec4100.patch
|
|
|
|
diff --git a/package/boot/uboot-envtools/files/realtek b/package/boot/uboot-envtools/files/realtek
|
|
index cce0628ffc..a4b7089d62 100644
|
|
--- a/package/boot/uboot-envtools/files/realtek
|
|
+++ b/package/boot/uboot-envtools/files/realtek
|
|
@@ -11,11 +11,18 @@ case "$board" in
|
|
d-link,dgs-1210-16|\
|
|
d-link,dgs-1210-28|\
|
|
d-link,dgs-1210-10p|\
|
|
+zyxel,gs1900-8hp-v1|\
|
|
+zyxel,gs1900-8hp-v2|\
|
|
zyxel,gs1900-10hp)
|
|
idx="$(find_mtd_index u-boot-env)"
|
|
[ -n "$idx" ] && \
|
|
ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x400" "0x10000"
|
|
;;
|
|
+edgecore,ecs4100-12ph)
|
|
+ idx="$(find_mtd_index u-boot-env)"
|
|
+ [ -n "$idx" ] && \
|
|
+ ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x20000" "0x10000"
|
|
+ ;;
|
|
*)
|
|
idx="$(find_mtd_index u-boot-env)"
|
|
[ -n "$idx" ] && \
|
|
diff --git a/target/linux/realtek/base-files/etc/board.d/01_leds b/target/linux/realtek/base-files/etc/board.d/01_leds
|
|
index 699ab817dd..36ca01a696 100755
|
|
--- a/target/linux/realtek/base-files/etc/board.d/01_leds
|
|
+++ b/target/linux/realtek/base-files/etc/board.d/01_leds
|
|
@@ -1,5 +1,4 @@
|
|
#!/bin/sh
|
|
-
|
|
. /lib/functions/uci-defaults.sh
|
|
|
|
board=$(board_name)
|
|
diff --git a/target/linux/realtek/base-files/etc/board.d/02_network b/target/linux/realtek/base-files/etc/board.d/02_network
|
|
index 2568fd2e0e..2b82ccb0aa 100755
|
|
--- a/target/linux/realtek/base-files/etc/board.d/02_network
|
|
+++ b/target/linux/realtek/base-files/etc/board.d/02_network
|
|
@@ -49,6 +49,9 @@ done
|
|
[ -n "$label_mac" ] && ucidef_set_label_macaddr $label_mac
|
|
|
|
case $board in
|
|
+edgecore,ecs4100-12ph)
|
|
+ ucidef_set_poe 130 "lan1 lan2 lan3 lan4 lan5 lan6 lan7 lan8"
|
|
+ ;;
|
|
netgear,gs110tpp-v1)
|
|
ucidef_set_poe 130 "$lan_list"
|
|
;;
|
|
diff --git a/target/linux/realtek/config-5.4 b/target/linux/realtek/config-5.4
|
|
index 2fbd904376..3f72b911cc 100644
|
|
--- a/target/linux/realtek/config-5.4
|
|
+++ b/target/linux/realtek/config-5.4
|
|
@@ -2,17 +2,18 @@ CONFIG_ARCH_32BIT_OFF_T=y
|
|
CONFIG_ARCH_CLOCKSOURCE_DATA=y
|
|
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
|
CONFIG_ARCH_MMAP_RND_BITS_MAX=15
|
|
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
|
|
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
|
CONFIG_BLK_DEV_RAM=y
|
|
CONFIG_BLK_DEV_RAM_COUNT=16
|
|
CONFIG_BLK_DEV_RAM_SIZE=4096
|
|
CONFIG_CEVT_R4K=y
|
|
-CONFIG_CLONE_BACKWARDS=y
|
|
-CONFIG_COMPAT_32BIT_TIME=y
|
|
-CONFIG_HAVE_CLK=y
|
|
CONFIG_CLKDEV_LOOKUP=y
|
|
+CONFIG_CLKSRC_MMIO=y
|
|
+CONFIG_CLONE_BACKWARDS=y
|
|
CONFIG_COMMON_CLK=y
|
|
CONFIG_COMMON_CLK_BOSTON=y
|
|
+CONFIG_COMPAT_32BIT_TIME=y
|
|
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15
|
|
CONFIG_CPU_BIG_ENDIAN=y
|
|
CONFIG_CPU_GENERIC_DUMP_TLB=y
|
|
@@ -40,14 +41,10 @@ CONFIG_DMA_NONCOHERENT_CACHE_SYNC=y
|
|
CONFIG_DTC=y
|
|
CONFIG_EARLY_PRINTK=y
|
|
CONFIG_EARLY_PRINTK_8250=y
|
|
-CONFIG_EFI_EARLYCON=y
|
|
CONFIG_ETHERNET_PACKET_MANGLE=y
|
|
CONFIG_EXTRA_FIRMWARE="rtl838x_phy/rtl838x_8214fc.fw rtl838x_phy/rtl838x_8218b.fw rtl838x_phy/rtl838x_8380.fw"
|
|
CONFIG_EXTRA_FIRMWARE_DIR="firmware"
|
|
CONFIG_FIXED_PHY=y
|
|
-CONFIG_FONT_8x16=y
|
|
-CONFIG_FONT_AUTOSELECT=y
|
|
-CONFIG_FONT_SUPPORT=y
|
|
CONFIG_FW_LOADER_PAGED_BUF=y
|
|
CONFIG_GENERIC_ATOMIC64=y
|
|
CONFIG_GENERIC_CLOCKEVENTS=y
|
|
@@ -74,7 +71,9 @@ CONFIG_GENERIC_TIME_VSYSCALL=y
|
|
CONFIG_GPIOLIB=y
|
|
CONFIG_GPIO_RTL8231=y
|
|
CONFIG_GPIO_RTL838X=y
|
|
-CONFIG_REALTEK_SOC_PHY=y
|
|
+CONFIG_GPIO_REALTEK_OTTO=y
|
|
+CONFIG_GPIO_WATCHDOG=y
|
|
+# CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set
|
|
CONFIG_GRO_CELLS=y
|
|
CONFIG_HANDLE_DOMAIN_IRQ=y
|
|
CONFIG_HARDWARE_WATCHPOINTS=y
|
|
@@ -82,12 +81,14 @@ CONFIG_HAS_DMA=y
|
|
CONFIG_HAS_IOMEM=y
|
|
CONFIG_HAS_IOPORT_MAP=y
|
|
# CONFIG_HIGH_RES_TIMERS is not set
|
|
+CONFIG_HWMON=y
|
|
CONFIG_HZ=250
|
|
CONFIG_HZ_250=y
|
|
CONFIG_HZ_PERIODIC=y
|
|
CONFIG_I2C=y
|
|
CONFIG_I2C_ALGOBIT=y
|
|
CONFIG_I2C_BOARDINFO=y
|
|
+CONFIG_I2C_CHARDEV=y
|
|
CONFIG_I2C_GPIO=y
|
|
CONFIG_INITRAMFS_SOURCE=""
|
|
CONFIG_IRQCHIP=y
|
|
@@ -101,10 +102,13 @@ CONFIG_LEGACY_PTYS=y
|
|
CONFIG_LEGACY_PTY_COUNT=256
|
|
CONFIG_LIBFDT=y
|
|
CONFIG_LOCK_DEBUGGING_SUPPORT=y
|
|
+CONFIG_MARVELL_PHY=y
|
|
CONFIG_MDIO_BUS=y
|
|
CONFIG_MDIO_DEVICE=y
|
|
CONFIG_MDIO_I2C=y
|
|
CONFIG_MEMFD_CREATE=y
|
|
+CONFIG_MFD_CORE=y
|
|
+CONFIG_MFD_REALTEK_EIO=y
|
|
CONFIG_MFD_SYSCON=y
|
|
CONFIG_MIGRATION=y
|
|
CONFIG_MIPS=y
|
|
@@ -159,11 +163,17 @@ CONFIG_PINCTRL=y
|
|
CONFIG_POWER_RESET=y
|
|
CONFIG_POWER_RESET_SYSCON=y
|
|
CONFIG_PSB6970_PHY=y
|
|
+CONFIG_RATIONAL=y
|
|
+CONFIG_REALTEK_PHY=y
|
|
+CONFIG_REALTEK_SOC_PHY=y
|
|
CONFIG_REGMAP=y
|
|
+CONFIG_REGMAP_I2C=y
|
|
CONFIG_REGMAP_MMIO=y
|
|
CONFIG_RESET_CONTROLLER=y
|
|
CONFIG_RTL838X=y
|
|
CONFIG_RTL9300_TIMER=y
|
|
+CONFIG_SENSORS_GPIO_FAN=y
|
|
+CONFIG_SENSORS_LM75=y
|
|
CONFIG_SERIAL_MCTRL_GPIO=y
|
|
CONFIG_SERIAL_OF_PLATFORM=y
|
|
CONFIG_SFP=y
|
|
@@ -172,7 +182,7 @@ CONFIG_SPI_MASTER=y
|
|
CONFIG_SPI_MEM=y
|
|
CONFIG_SPI_RTL838X=y
|
|
CONFIG_SRCU=y
|
|
-CONFIG_SWAP_IO_SPACE=y
|
|
+CONFIG_SWCONFIG=y
|
|
CONFIG_SWPHY=y
|
|
CONFIG_SYSCTL_EXCEPTION_TRACE=y
|
|
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
|
|
@@ -184,8 +194,11 @@ CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
|
|
CONFIG_SYS_SUPPORTS_MIPS16=y
|
|
CONFIG_TARGET_ISA_REV=2
|
|
CONFIG_TICK_CPU_ACCOUNTING=y
|
|
+CONFIG_TIMER_OF=y
|
|
+CONFIG_TIMER_PROBE=y
|
|
CONFIG_TINY_SRCU=y
|
|
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
|
|
CONFIG_USE_OF=y
|
|
+CONFIG_WATCHDOG_CORE=y
|
|
CONFIG_ZLIB_DEFLATE=y
|
|
CONFIG_ZLIB_INFLATE=y
|
|
diff --git a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp.dts b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp.dts
|
|
index 92d0e25fc4..a590450055 100644
|
|
--- a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp.dts
|
|
+++ b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp.dts
|
|
@@ -55,29 +55,17 @@
|
|
port@24 {
|
|
reg = <24>;
|
|
label = "lan9";
|
|
- phy-mode = "rgmii-id";
|
|
- phy-handle = <&phy24>;
|
|
+ phy-mode = "1000base-x";
|
|
+ managed = "in-band-status";
|
|
sfp = <&sfp0>;
|
|
-
|
|
- fixed-link {
|
|
- speed = <1000>;
|
|
- full-duplex;
|
|
- pause;
|
|
- };
|
|
};
|
|
|
|
port@26 {
|
|
reg = <26>;
|
|
label = "lan10";
|
|
- phy-mode = "rgmii-id";
|
|
- phy-handle = <&phy26>;
|
|
+ phy-mode = "1000base-x";
|
|
+ managed = "in-band-status";
|
|
sfp = <&sfp1>;
|
|
-
|
|
- fixed-link {
|
|
- speed = <1000>;
|
|
- full-duplex;
|
|
- pause;
|
|
- };
|
|
};
|
|
};
|
|
};
|
|
diff --git a/target/linux/realtek/dts/rtl8392_edgecore_ecs4100-12ph.dts b/target/linux/realtek/dts/rtl8392_edgecore_ecs4100-12ph.dts
|
|
new file mode 100644
|
|
index 0000000000..303b79ac83
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/dts/rtl8392_edgecore_ecs4100-12ph.dts
|
|
@@ -0,0 +1,297 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+
|
|
+#include "rtl839x.dtsi"
|
|
+
|
|
+#include <dt-bindings/input/input.h>
|
|
+#include <dt-bindings/gpio/gpio.h>
|
|
+
|
|
+/ {
|
|
+ compatible = "edgecore,ecs4100-12ph", "realtek,rtl838x-soc";
|
|
+ model = "Edgecore ECS4100-12PH Switch";
|
|
+
|
|
+ aliases {
|
|
+ led-boot = &led_sys;
|
|
+ led-failsafe = &led_sys;
|
|
+ led-running = &led_sys;
|
|
+ led-upgrade = &led_sys;
|
|
+ };
|
|
+
|
|
+ chosen {
|
|
+ bootargs = "console=ttyS0,115200";
|
|
+ };
|
|
+
|
|
+ memory@0 {
|
|
+ device_type = "memory";
|
|
+ reg = <0x0 0x10000000>;
|
|
+ };
|
|
+
|
|
+ /* i2c of the left SFP cage: port 9 */
|
|
+ i2c0: i2c-gpio-0 {
|
|
+ compatible = "i2c-gpio";
|
|
+ sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ scl-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ i2c-gpio,delay-us = <2>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ };
|
|
+
|
|
+ sfp0: sfp-p9 {
|
|
+ compatible = "sff,sfp";
|
|
+ i2c-bus = <&i2c0>;
|
|
+ los-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;
|
|
+ mod-def0-gpio = <&gpio1 8 GPIO_ACTIVE_LOW>;
|
|
+ };
|
|
+
|
|
+ i2c1: i2c-gpio-1 {
|
|
+ compatible = "i2c-gpio";
|
|
+ sda-gpios = <&gpio1 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ scl-gpios = <&gpio1 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ i2c-gpio,delay-us = <2>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ };
|
|
+
|
|
+ sfp1: sfp-p10 {
|
|
+ compatible = "sff,sfp";
|
|
+ i2c-bus = <&i2c1>;
|
|
+ los-gpio = <&gpio0 14 GPIO_ACTIVE_HIGH>;
|
|
+ mod-def0-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
|
|
+ };
|
|
+
|
|
+ i2c2: i2c-gpio-2 {
|
|
+ compatible = "i2c-gpio";
|
|
+ sda-gpios = <&gpio1 22 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ scl-gpios = <&gpio1 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ i2c-gpio,delay-us = <2>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ };
|
|
+
|
|
+ sfp2: sfp-p11 {
|
|
+ compatible = "sff,sfp";
|
|
+ i2c-bus = <&i2c2>;
|
|
+ los-gpio = <&gpio0 21 GPIO_ACTIVE_HIGH>;
|
|
+ mod-def0-gpio = <&gpio1 24 GPIO_ACTIVE_LOW>;
|
|
+ };
|
|
+
|
|
+ i2c3: i2c-gpio-3 {
|
|
+ compatible = "i2c-gpio";
|
|
+ sda-gpios = <&gpio1 11 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ scl-gpios = <&gpio1 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ i2c-gpio,delay-us = <2>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ };
|
|
+
|
|
+ sfp3: sfp-p12 {
|
|
+ compatible = "sff,sfp";
|
|
+ i2c-bus = <&i2c3>;
|
|
+ los-gpio = <&gpio0 22 GPIO_ACTIVE_HIGH>;
|
|
+ mod-def0-gpio = <&gpio1 13 GPIO_ACTIVE_LOW>;
|
|
+ };
|
|
+
|
|
+ i2c4: i2c-gpio-4 {
|
|
+ compatible = "i2c-gpio";
|
|
+ sda-gpios = <&gpio1 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ scl-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
|
|
+ i2c-gpio,delay-us = <2>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ adt7470@2f {
|
|
+ compatible = "adi,adt7470";
|
|
+ reg = <0x2f>;
|
|
+ };
|
|
+
|
|
+ lm75b@48 {
|
|
+ compatible = "nxp,lm75a";
|
|
+ reg = <0x48>;
|
|
+ };
|
|
+
|
|
+ eeprom@506 {
|
|
+ compatible = "atmel,24c32";
|
|
+ reg = <0x56>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ watchdog {
|
|
+ compatible = "linux,wdt-gpio";
|
|
+ gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
|
|
+ hw_algo = "toggle";
|
|
+ hw_margin_ms = <1200>;
|
|
+ };
|
|
+
|
|
+ reboot@0 {
|
|
+ compatible = "edgecore,reboot";
|
|
+ gpios = <&gpio1 26 GPIO_ACTIVE_HIGH>;
|
|
+ };
|
|
+
|
|
+ fan0: gpio-fan {
|
|
+ #cooling-cells = <2>;
|
|
+ compatible = "gpio-fan";
|
|
+ gpio-fan,speed-map = <0 0 3000 1>;
|
|
+ gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
|
|
+ status = "okay";
|
|
+ };
|
|
+};
|
|
+
|
|
+&gpio1 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&gpio0 {
|
|
+ poe_enable {
|
|
+ gpio-hog;
|
|
+ gpios = <16 GPIO_ACTIVE_HIGH>;
|
|
+ output-high;
|
|
+ };
|
|
+
|
|
+ poe_reset {
|
|
+ gpio-hog;
|
|
+ gpios = <18 GPIO_ACTIVE_HIGH>;
|
|
+ output-high;
|
|
+ };
|
|
+};
|
|
+
|
|
+&spi0 {
|
|
+ status = "okay";
|
|
+
|
|
+ flash@0 {
|
|
+ compatible = "jedec,spi-nor";
|
|
+ reg = <0>;
|
|
+ spi-max-frequency = <10000000>;
|
|
+
|
|
+ partitions {
|
|
+ compatible = "fixed-partitions";
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+
|
|
+ partition@0 {
|
|
+ label = "u-boot";
|
|
+ reg = <0x0 0x40000>;
|
|
+ read-only;
|
|
+ };
|
|
+ partition@100000 {
|
|
+ label = "u-boot-env";
|
|
+ reg = <0x100000 0x100000>;
|
|
+ read-only;
|
|
+ };
|
|
+ partition@b260000 {
|
|
+ label = "firmware";
|
|
+ reg = <0x200000 0xe00000>;
|
|
+ compatible = "openwrt,uimage", "denx,uimage";
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+ðernet0 {
|
|
+ mdio: mdio-bus {
|
|
+ compatible = "realtek,rtl838x-mdio";
|
|
+ regmap = <ðernet0>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ INTERNAL_PHY(0)
|
|
+ INTERNAL_PHY(1)
|
|
+ INTERNAL_PHY(2)
|
|
+ INTERNAL_PHY(3)
|
|
+ INTERNAL_PHY(4)
|
|
+ INTERNAL_PHY(5)
|
|
+ INTERNAL_PHY(6)
|
|
+ INTERNAL_PHY(7)
|
|
+
|
|
+ phy48: ethernet-phy@48 {
|
|
+ reg = <48>;
|
|
+ compatible = "ethernet-phy-ieee802.3-c22";
|
|
+ sfp = <&sfp0>;
|
|
+ };
|
|
+
|
|
+ phy49: ethernet-phy@49 {
|
|
+ reg = <49>;
|
|
+ compatible = "ethernet-phy-ieee802.3-c22";
|
|
+ sfp = <&sfp1>;
|
|
+ };
|
|
+
|
|
+ phy50: ethernet-phy@50 {
|
|
+ reg = <50>;
|
|
+ compatible = "ethernet-phy-ieee802.3-c22";
|
|
+ sfp = <&sfp2>;
|
|
+ };
|
|
+
|
|
+ phy51: ethernet-phy@51 {
|
|
+ reg = <51>;
|
|
+ compatible = "ethernet-phy-ieee802.3-c22";
|
|
+ sfp = <&sfp3>;
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+&switch0 {
|
|
+ ext_io: ext-io@e4 {
|
|
+ compatible = "realtek,rtl8390-eio", "syscon";
|
|
+ reg = <0xe4 0x17c>;
|
|
+
|
|
+ led_sys: sys-led {
|
|
+ active-low;
|
|
+ label = "green:status";
|
|
+ linux,default-trigger = "default-on";
|
|
+ };
|
|
+ };
|
|
+ ports {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ SWITCH_PORT(0, 1, internal)
|
|
+ SWITCH_PORT(1, 2, internal)
|
|
+ SWITCH_PORT(2, 3, internal)
|
|
+ SWITCH_PORT(3, 4, internal)
|
|
+ SWITCH_PORT(4, 5, internal)
|
|
+ SWITCH_PORT(5, 6, internal)
|
|
+ SWITCH_PORT(6, 7, internal)
|
|
+ SWITCH_PORT(7, 8, internal)
|
|
+
|
|
+ port@48 {
|
|
+ reg = <48>;
|
|
+ label = "lan9";
|
|
+ phy-mode = "sgmii";
|
|
+ phy-handle = <&phy48>;
|
|
+ managed = "in-band-status";
|
|
+ };
|
|
+
|
|
+ port@49 {
|
|
+ reg = <49>;
|
|
+ label = "lan10";
|
|
+ phy-mode = "sgmii";
|
|
+ phy-handle = <&phy49>;
|
|
+ managed = "in-band-status";
|
|
+ };
|
|
+
|
|
+ port@50 {
|
|
+ reg = <50>;
|
|
+ label = "lan11";
|
|
+ phy-mode = "sgmii";
|
|
+ phy-handle = <&phy50>;
|
|
+ managed = "in-band-status";
|
|
+ };
|
|
+
|
|
+ port@51 {
|
|
+ reg = <51>;
|
|
+ label = "lan12";
|
|
+ phy-mode = "sgmii";
|
|
+ phy-handle = <&phy51>;
|
|
+ managed = "in-band-status";
|
|
+ };
|
|
+
|
|
+ port@28 {
|
|
+ ethernet = <ðernet0>;
|
|
+ reg = <28>;
|
|
+ phy-mode = "internal";
|
|
+
|
|
+ fixed-link {
|
|
+ speed = <1000>;
|
|
+ full-duplex;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
diff --git a/target/linux/realtek/dts/rtl839x.dtsi b/target/linux/realtek/dts/rtl839x.dtsi
|
|
new file mode 100644
|
|
index 0000000000..9816b2a09b
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/dts/rtl839x.dtsi
|
|
@@ -0,0 +1,197 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
|
|
+
|
|
+/dts-v1/;
|
|
+
|
|
+#define STRINGIZE(s) #s
|
|
+#define LAN_LABEL(p, s) STRINGIZE(p ## s)
|
|
+#define SWITCH_PORT_LABEL(n) LAN_LABEL(lan, n)
|
|
+
|
|
+#define INTERNAL_PHY(n) \
|
|
+ phy##n: ethernet-phy@##n { \
|
|
+ reg = <##n>; \
|
|
+ compatible = "ethernet-phy-ieee802.3-c22"; \
|
|
+ phy-is-integrated; \
|
|
+ };
|
|
+
|
|
+#define EXTERNAL_PHY(n) \
|
|
+ phy##n: ethernet-phy@##n { \
|
|
+ reg = <##n>; \
|
|
+ compatible = "ethernet-phy-ieee802.3-c22"; \
|
|
+ };
|
|
+
|
|
+#define EXTERNAL_SFP_PHY(n) \
|
|
+ phy##n: ethernet-phy@##n { \
|
|
+ compatible = "ethernet-phy-ieee802.3-c22"; \
|
|
+ sfp; \
|
|
+ media = "fibre"; \
|
|
+ reg = <##n>; \
|
|
+ };
|
|
+
|
|
+#define SWITCH_PORT(n, s, m) \
|
|
+ port@##n { \
|
|
+ reg = <##n>; \
|
|
+ label = SWITCH_PORT_LABEL(s) ; \
|
|
+ phy-handle = <&phy##n>; \
|
|
+ phy-mode = #m ; \
|
|
+ };
|
|
+
|
|
+#define SWITCH_SFP_PORT(n, s, m) \
|
|
+ port@##n { \
|
|
+ reg = <##n>; \
|
|
+ label = SWITCH_PORT_LABEL(s) ; \
|
|
+ phy-handle = <&phy##n>; \
|
|
+ phy-mode = #m ; \
|
|
+ fixed-link { \
|
|
+ speed = <1000>; \
|
|
+ full-duplex; \
|
|
+ }; \
|
|
+ };
|
|
+
|
|
+/ {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+
|
|
+ compatible = "realtek,rtl838x-soc";
|
|
+
|
|
+ cpus {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ frequency = <700000000>;
|
|
+
|
|
+ cpu@0 {
|
|
+ compatible = "mips,mips34Kc";
|
|
+ reg = <0>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ memory@0 {
|
|
+ device_type = "memory";
|
|
+ reg = <0x0 0x8000000>;
|
|
+ };
|
|
+
|
|
+ chosen {
|
|
+ bootargs = "console=ttyS0,38400";
|
|
+ };
|
|
+
|
|
+
|
|
+ cpuintc: cpuintc {
|
|
+ #address-cells = <0>;
|
|
+ #interrupt-cells = <1>;
|
|
+ interrupt-controller;
|
|
+ compatible = "mti,cpu-interrupt-controller";
|
|
+ };
|
|
+
|
|
+ intc: rtlintc {
|
|
+ #address-cells = <0>;
|
|
+ #interrupt-cells = <1>;
|
|
+ interrupt-controller;
|
|
+ compatible = "realtek,rt8380-intc";
|
|
+ reg = <0xb8003000 0x20>;
|
|
+ };
|
|
+
|
|
+ spi0: spi@b8001200 {
|
|
+ status = "okay";
|
|
+
|
|
+ compatible = "realtek,rtl838x-nor";
|
|
+ reg = <0xb8001200 0x100>;
|
|
+
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ };
|
|
+
|
|
+ uart0: uart@b8002000 {
|
|
+ status = "okay";
|
|
+
|
|
+ compatible = "ns16550a";
|
|
+ reg = <0xb8002000 0x100>;
|
|
+
|
|
+ clock-frequency = <200000000>;
|
|
+
|
|
+ interrupt-parent = <&intc>;
|
|
+ interrupts = <31>;
|
|
+
|
|
+ reg-io-width = <1>;
|
|
+ reg-shift = <2>;
|
|
+ fifo-size = <1>;
|
|
+ no-loopback-test;
|
|
+ };
|
|
+
|
|
+ uart1: uart@b8002100 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&enable_uart1>;
|
|
+
|
|
+ status = "okay";
|
|
+
|
|
+ compatible = "ns16550a";
|
|
+ reg = <0xb8002100 0x100>;
|
|
+
|
|
+ clock-frequency = <200000000>;
|
|
+
|
|
+ interrupt-parent = <&intc>;
|
|
+ interrupts = <30>;
|
|
+
|
|
+ reg-io-width = <1>;
|
|
+ reg-shift = <2>;
|
|
+ fifo-size = <1>;
|
|
+ no-loopback-test;
|
|
+ };
|
|
+
|
|
+ gpio0: gpio-controller@b8003500 {
|
|
+ compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio";
|
|
+ reg = <0xb8003500 0x20>;
|
|
+ gpio-controller;
|
|
+ #gpio-cells = <2>;
|
|
+ ngpios = <24>;
|
|
+ };
|
|
+
|
|
+ gpio1: rtl8231-gpio {
|
|
+ status = "disabled";
|
|
+ compatible = "realtek,rtl8231-gpio";
|
|
+ #gpio-cells = <2>;
|
|
+ indirect-access-bus-id = <3>;
|
|
+ gpio-controller;
|
|
+ };
|
|
+
|
|
+ pinmux: pinmux@bb001000 {
|
|
+ compatible = "pinctrl-single";
|
|
+ reg = <0xbb000004 0x4>;
|
|
+
|
|
+ pinctrl-single,bit-per-mux;
|
|
+ pinctrl-single,register-width = <32>;
|
|
+ pinctrl-single,function-mask = <0x1>;
|
|
+ #pinctrl-cells = <2>;
|
|
+
|
|
+ enable_uart1: pinmux_enable_uart1 {
|
|
+ pinctrl-single,bits = <0x0 0x01 0x03>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ ethernet0: ethernet@bb00a300 {
|
|
+ status = "okay";
|
|
+
|
|
+ compatible = "realtek,rtl838x-eth";
|
|
+ reg = <0xbb00a300 0x100>;
|
|
+ interrupt-parent = <&intc>;
|
|
+ interrupts = <24>;
|
|
+ #interrupt-cells = <1>;
|
|
+ phy-mode = "internal";
|
|
+
|
|
+ fixed-link {
|
|
+ speed = <1000>;
|
|
+ full-duplex;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ switch0: switch@bb000000 {
|
|
+ status = "okay";
|
|
+
|
|
+ interrupt-parent = <&intc>;
|
|
+ interrupts = <20>;
|
|
+
|
|
+ compatible = "realtek,rtl83xx-switch", "simple-bus";
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ ranges = <0x0 0xbb000000 0x10000>;
|
|
+
|
|
+ };
|
|
+};
|
|
diff --git a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
|
|
index fc401e5481..0abfc6f4d2 100644
|
|
--- a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
|
|
+++ b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
|
|
@@ -313,8 +313,29 @@
|
|
#define RTL839X_SMI_PORT_POLLING_CTRL (0x03fc)
|
|
#define RTL839X_PHYREG_ACCESS_CTRL (0x03DC)
|
|
#define RTL839X_PHYREG_CTRL (0x03E0)
|
|
-#define RTL839X_PHYREG_PORT_CTRL(p) (0x03E4 + ((p >> 5) << 2))
|
|
+#define RTL839X_PHYREG_PORT_CTRL (0x03E4)
|
|
#define RTL839X_PHYREG_DATA_CTRL (0x03F0)
|
|
+#define RTL839X_PHYREG_MMD_CTRL (0x3F4)
|
|
+
|
|
+#define RTL930X_SMI_GLB_CTRL (0xCA00)
|
|
+#define RTL930X_SMI_POLL_CTRL (0xca90)
|
|
+#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08)
|
|
+#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C)
|
|
+#define RTL930X_SMI_PORT0_5_ADDR (0xCB80)
|
|
+#define RTL930X_SMI_ACCESS_PHY_CTRL_0 (0xCB70)
|
|
+#define RTL930X_SMI_ACCESS_PHY_CTRL_1 (0xCB74)
|
|
+#define RTL930X_SMI_ACCESS_PHY_CTRL_2 (0xCB78)
|
|
+#define RTL930X_SMI_ACCESS_PHY_CTRL_3 (0xCB7C)
|
|
+
|
|
+#define RTL931X_SMI_GLB_CTRL1 (0x0CBC)
|
|
+#define RTL931X_SMI_GLB_CTRL0 (0x0CC0)
|
|
+#define RTL931X_SMI_PORT_POLLING_CTRL (0x0CCC)
|
|
+#define RTL931X_SMI_INDRT_ACCESS_CTRL_0 (0x0C00)
|
|
+#define RTL931X_SMI_INDRT_ACCESS_CTRL_1 (0x0C04)
|
|
+#define RTL931X_SMI_INDRT_ACCESS_CTRL_2 (0x0C08)
|
|
+#define RTL931X_SMI_INDRT_ACCESS_CTRL_3 (0x0C10)
|
|
+#define RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL (0x0C14)
|
|
+#define RTL931X_SMI_INDRT_ACCESS_MMD_CTRL (0xC18)
|
|
|
|
#define RTL930X_SMI_GLB_CTRL (0xCA00)
|
|
#define RTL930X_SMI_POLL_CTRL (0xca90)
|
|
diff --git a/target/linux/realtek/files-5.4/arch/mips/rtl838x/setup.c b/target/linux/realtek/files-5.4/arch/mips/rtl838x/setup.c
|
|
index ef97d485e1..ea2dd0c866 100644
|
|
--- a/target/linux/realtek/files-5.4/arch/mips/rtl838x/setup.c
|
|
+++ b/target/linux/realtek/files-5.4/arch/mips/rtl838x/setup.c
|
|
@@ -46,21 +46,6 @@ static void rtl838x_restart(char *command)
|
|
sw_w32(1, RTL838X_RST_GLB_CTRL_1);
|
|
}
|
|
|
|
-static void rtl839x_restart(char *command)
|
|
-{
|
|
- /* SoC reset vector (in flash memory): on RTL839x platform preferred way to reset */
|
|
- void (*f)(void) = (void *) 0xbfc00000;
|
|
-
|
|
- pr_info("System restart.\n");
|
|
- /* Reset SoC */
|
|
- sw_w32(0xFFFFFFFF, RTL839X_RST_GLB_CTRL);
|
|
- /* and call reset vector */
|
|
- f();
|
|
- /* If this fails, halt the CPU */
|
|
- while
|
|
- (1);
|
|
-}
|
|
-
|
|
static void rtl930x_restart(char *command)
|
|
{
|
|
pr_info("System restart.\n");
|
|
@@ -109,8 +94,6 @@ static void __init rtl838x_setup(void)
|
|
static void __init rtl839x_setup(void)
|
|
{
|
|
pr_info("Registering _machine_restart\n");
|
|
- _machine_restart = rtl839x_restart;
|
|
- _machine_halt = rtl838x_halt;
|
|
|
|
/* Setup System LED. Bit 14 of RTL839X_LED_GLB_CTRL then allows to toggle it */
|
|
sw_w32_mask(0, 3 << 15, RTL839X_LED_GLB_CTRL);
|
|
@@ -141,7 +124,6 @@ void __init plat_mem_setup(void)
|
|
void *dtb;
|
|
|
|
set_io_port_base(KSEG1);
|
|
- _machine_restart = rtl838x_restart;
|
|
|
|
if (fw_passed_dtb) /* UHI interface */
|
|
dtb = (void *)fw_passed_dtb;
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/gpio/edgecore_reboot.c b/target/linux/realtek/files-5.4/drivers/gpio/edgecore_reboot.c
|
|
new file mode 100644
|
|
index 0000000000..2cafab4279
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/files-5.4/drivers/gpio/edgecore_reboot.c
|
|
@@ -0,0 +1,61 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/* Copyright (C) 2021 John Crispin <john@phrozen.org> */
|
|
+#include <linux/delay.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/notifier.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/reboot.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+
|
|
+static struct notifier_block edgecore_reboot_handler;
|
|
+static struct gpio_desc *gpiod;
|
|
+static int edgecore_reboot_handle(struct notifier_block *this,
|
|
+ unsigned long mode, void *cmd)
|
|
+{
|
|
+ gpiod_direction_output(gpiod, 0);
|
|
+ mdelay(1000);
|
|
+
|
|
+ pr_emerg("Unable to restart system\n");
|
|
+ return NOTIFY_DONE;
|
|
+}
|
|
+
|
|
+static int __init edgecore_reboot_probe(struct platform_device *pdev)
|
|
+{
|
|
+ int err;
|
|
+ unsigned long flags = GPIOF_IN;
|
|
+
|
|
+ gpiod = devm_gpiod_get_index(&pdev->dev, NULL, 0, flags);
|
|
+ if (!IS_ERR(gpiod))
|
|
+ gpiod_set_consumer_name(gpiod, "reboot");
|
|
+ else
|
|
+ return -EPROBE_DEFER;
|
|
+
|
|
+ edgecore_reboot_handler.notifier_call = edgecore_reboot_handle;
|
|
+ edgecore_reboot_handler.priority = 255;
|
|
+ err = register_restart_handler(&edgecore_reboot_handler);
|
|
+ if (err)
|
|
+ printk("can't register restart notifier (err=%d)\n", err);
|
|
+
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id edgecore_reboot_of_ids[] = {
|
|
+ { .compatible = "edgecore,reboot"},
|
|
+ { /* sentinel */ }
|
|
+};
|
|
+
|
|
+
|
|
+static struct platform_driver edgecore_reboot_driver = {
|
|
+ .probe = edgecore_reboot_probe,
|
|
+ .driver = {
|
|
+ .name = "edgecore_reboot",
|
|
+ .of_match_table = edgecore_reboot_of_ids,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(edgecore_reboot_driver);
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl838x.c b/target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl838x.c
|
|
index 8207e4bb73..60b6f08834 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl838x.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl838x.c
|
|
@@ -348,6 +348,9 @@ static int rtl838x_gpio_probe(struct platform_device *pdev)
|
|
case 0x8391:
|
|
pr_debug("Found RTL8391 GPIO\n");
|
|
break;
|
|
+ case 0x8392:
|
|
+ pr_debug("Found RTL8392 GPIO\n");
|
|
+ break;
|
|
case 0x8393:
|
|
pr_debug("Found RTL8393 GPIO\n");
|
|
break;
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c
|
|
index 698f2892ea..a380906b92 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c
|
|
@@ -18,27 +18,6 @@ extern const struct dsa_switch_ops rtl930x_switch_ops;
|
|
|
|
DEFINE_MUTEX(smi_lock);
|
|
|
|
-// TODO: unused
|
|
-static void dump_fdb(struct rtl838x_switch_priv *priv)
|
|
-{
|
|
- struct rtl838x_l2_entry e;
|
|
- int i;
|
|
-
|
|
- mutex_lock(&priv->reg_mutex);
|
|
-
|
|
- for (i = 0; i < priv->fib_entries; i++) {
|
|
- priv->r->read_l2_entry_using_hash(i >> 2, i & 0x3, &e);
|
|
-
|
|
- if (!e.valid) /* Check for invalid entry */
|
|
- continue;
|
|
-
|
|
- pr_debug("-> port %02d: mac %pM, vid: %d, rvid: %d, MC: %d, %d\n",
|
|
- e.port, &e.mac[0], e.vid, e.rvid, e.is_ip_mc, e.is_ipv6_mc);
|
|
- }
|
|
-
|
|
- mutex_unlock(&priv->reg_mutex);
|
|
-}
|
|
-
|
|
int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port)
|
|
{
|
|
u32 msti = 0;
|
|
@@ -144,7 +123,6 @@ void rtl_table_read(struct table_reg *r, int idx)
|
|
|
|
cmd |= BIT(r->c_bit + 1) | (r->tbl << r->t_bit) | (idx & (BIT(r->t_bit) - 1));
|
|
sw_w32(cmd, r->addr);
|
|
- pr_debug("Writing %08x to %x for read\n", cmd, r->addr);
|
|
do { } while (sw_r32(r->addr) & BIT(r->c_bit + 1));
|
|
}
|
|
|
|
@@ -156,8 +134,6 @@ void rtl_table_write(struct table_reg *r, int idx)
|
|
u32 cmd = r->rmode ? 0 : BIT(r->c_bit);
|
|
|
|
cmd |= BIT(r->c_bit + 1) | (r->tbl << r->t_bit) | (idx & (BIT(r->t_bit) - 1));
|
|
- pr_debug("Writing %08x to %x for write, value %08x\n",
|
|
- cmd, r->addr, sw_r32(0xb344));
|
|
sw_w32(cmd, r->addr);
|
|
do { } while (sw_r32(r->addr) & BIT(r->c_bit + 1));
|
|
}
|
|
@@ -368,8 +344,8 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv)
|
|
|
|
/* Enable PHY control via SoC */
|
|
if (priv->family_id == RTL8380_FAMILY_ID) {
|
|
- /* Enable PHY control via SoC */
|
|
- sw_w32_mask(0, BIT(15), RTL838X_SMI_GLB_CTRL);
|
|
+ /* Enable SerDes NWAY and PHY control via SoC */
|
|
+ sw_w32_mask(BIT(7), BIT(15), RTL838X_SMI_GLB_CTRL);
|
|
} else {
|
|
/* Disable PHY polling via SoC */
|
|
sw_w32_mask(BIT(7), 0, RTL839X_SMI_GLB_CTRL);
|
|
@@ -555,7 +531,6 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev)
|
|
int err = 0, i;
|
|
struct rtl838x_switch_priv *priv;
|
|
struct device *dev = &pdev->dev;
|
|
- u64 irq_mask;
|
|
u64 bpdu_mask;
|
|
|
|
pr_debug("Probing RTL838X switch device\n");
|
|
@@ -594,6 +569,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev)
|
|
priv->fib_entries = 8192;
|
|
rtl8380_get_version(priv);
|
|
priv->n_lags = 8;
|
|
+ priv->l2_bucket_size = 4;
|
|
break;
|
|
case RTL8390_FAMILY_ID:
|
|
priv->ds->ops = &rtl83xx_switch_ops;
|
|
@@ -606,6 +582,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev)
|
|
priv->fib_entries = 16384;
|
|
rtl8390_get_version(priv);
|
|
priv->n_lags = 16;
|
|
+ priv->l2_bucket_size = 4;
|
|
break;
|
|
case RTL9300_FAMILY_ID:
|
|
priv->ds->ops = &rtl930x_switch_ops;
|
|
@@ -619,6 +596,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev)
|
|
priv->version = RTL8390_VERSION_A;
|
|
priv->n_lags = 16;
|
|
sw_w32(1, RTL930X_ST_CTRL);
|
|
+ priv->l2_bucket_size = 8;
|
|
break;
|
|
case RTL9310_FAMILY_ID:
|
|
priv->ds->ops = &rtl930x_switch_ops;
|
|
@@ -631,6 +609,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev)
|
|
priv->fib_entries = 16384;
|
|
priv->version = RTL8390_VERSION_A;
|
|
priv->n_lags = 16;
|
|
+ priv->l2_bucket_size = 8;
|
|
break;
|
|
}
|
|
pr_debug("Chip version %c\n", priv->version);
|
|
@@ -650,9 +629,9 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev)
|
|
|
|
/* Enable link and media change interrupts. Are the SERDES masks needed? */
|
|
sw_w32_mask(0, 3, priv->r->isr_glb_src);
|
|
-
|
|
- priv->r->set_port_reg_le(irq_mask, priv->r->isr_port_link_sts_chg);
|
|
- priv->r->set_port_reg_le(irq_mask, priv->r->imr_port_link_sts_chg);
|
|
+
|
|
+ priv->r->set_port_reg_le(priv->irq_mask, priv->r->isr_port_link_sts_chg);
|
|
+ priv->r->set_port_reg_le(priv->irq_mask, priv->r->imr_port_link_sts_chg);
|
|
|
|
priv->link_state_irq = platform_get_irq(pdev, 0);
|
|
pr_info("LINK state irq: %d\n", priv->link_state_irq);
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c
|
|
index e0832c42b8..c5f243c55a 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c
|
|
@@ -26,50 +26,6 @@ static void rtl83xx_init_stats(struct rtl838x_switch_priv *priv)
|
|
mutex_unlock(&priv->reg_mutex);
|
|
}
|
|
|
|
-static void rtl83xx_write_cam(int idx, u32 *r)
|
|
-{
|
|
- u32 cmd = BIT(16) /* Execute cmd */
|
|
- | BIT(15) /* Read */
|
|
- | BIT(13) /* Table type 0b01 */
|
|
- | (idx & 0x3f);
|
|
-
|
|
- sw_w32(r[0], RTL838X_TBL_ACCESS_L2_DATA(0));
|
|
- sw_w32(r[1], RTL838X_TBL_ACCESS_L2_DATA(1));
|
|
- sw_w32(r[2], RTL838X_TBL_ACCESS_L2_DATA(2));
|
|
-
|
|
- sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL);
|
|
- do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16));
|
|
-}
|
|
-
|
|
-static u64 rtl83xx_hash_key(struct rtl838x_switch_priv *priv, u64 mac, u32 vid)
|
|
-{
|
|
- switch (priv->family_id) {
|
|
- case RTL8380_FAMILY_ID:
|
|
- return rtl838x_hash(priv, mac << 12 | vid);
|
|
- case RTL8390_FAMILY_ID:
|
|
- return rtl839x_hash(priv, mac << 12 | vid);
|
|
- case RTL9300_FAMILY_ID:
|
|
- return rtl930x_hash(priv, ((u64)vid) << 48 | mac);
|
|
- default:
|
|
- pr_err("Hash not implemented\n");
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void rtl83xx_write_hash(int idx, u32 *r)
|
|
-{
|
|
- u32 cmd = BIT(16) /* Execute cmd */
|
|
- | 0 << 15 /* Write */
|
|
- | 0 << 13 /* Table type 0b00 */
|
|
- | (idx & 0x1fff);
|
|
-
|
|
- sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(0));
|
|
- sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(1));
|
|
- sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(2));
|
|
- sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL);
|
|
- do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16));
|
|
-}
|
|
-
|
|
static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv)
|
|
{
|
|
int i;
|
|
@@ -79,7 +35,7 @@ static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv)
|
|
/* Enable all ports with a PHY, including the SFP-ports */
|
|
for (i = 0; i < priv->cpu_port; i++) {
|
|
if (priv->ports[i].phy)
|
|
- v |= BIT(i);
|
|
+ v |= BIT_ULL(i);
|
|
}
|
|
|
|
pr_debug("%s: %16llx\n", __func__, v);
|
|
@@ -153,6 +109,36 @@ static enum dsa_tag_protocol rtl83xx_get_tag_protocol(struct dsa_switch *ds, int
|
|
return DSA_TAG_PROTO_TRAILER;
|
|
}
|
|
|
|
+/*
|
|
+ * Initialize all VLANS
|
|
+ */
|
|
+static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv)
|
|
+{
|
|
+ struct rtl838x_vlan_info info;
|
|
+ int i;
|
|
+
|
|
+ pr_info("In %s\n", __func__);
|
|
+
|
|
+ priv->r->vlan_profile_setup(0);
|
|
+ priv->r->vlan_profile_setup(1);
|
|
+ pr_info("UNKNOWN_MC_PMASK: %016llx\n", priv->r->read_mcast_pmask(UNKNOWN_MC_PMASK));
|
|
+ priv->r->vlan_profile_dump(0);
|
|
+
|
|
+ info.fid = 0; // Default Forwarding ID / MSTI
|
|
+ info.hash_uc_fid = false; // Do not build the L2 lookup hash with FID, but VID
|
|
+ info.hash_mc_fid = false; // Do the same for Multicast packets
|
|
+ info.profile_id = 0; // Use default Vlan Profile 0
|
|
+ info.tagged_ports = 0; // Initially no port members
|
|
+
|
|
+ // Initialize all vlans 0-4095
|
|
+ for (i = 0; i < MAX_VLANS; i ++)
|
|
+ priv->r->vlan_set_tagged(i, &info);
|
|
+
|
|
+ // Set forwarding action based on inner VLAN tag
|
|
+ for (i = 0; i < priv->cpu_port; i++)
|
|
+ priv->r->vlan_fwd_on_inner(i, true);
|
|
+}
|
|
+
|
|
static int rtl83xx_setup(struct dsa_switch *ds)
|
|
{
|
|
int i;
|
|
@@ -174,7 +160,7 @@ static int rtl83xx_setup(struct dsa_switch *ds)
|
|
*/
|
|
for (i = 0; i < priv->cpu_port; i++) {
|
|
if (priv->ports[i].phy) {
|
|
- priv->r->set_port_reg_be(BIT_ULL(priv->cpu_port) | BIT(i),
|
|
+ priv->r->set_port_reg_be(BIT_ULL(priv->cpu_port) | BIT_ULL(i),
|
|
priv->r->port_iso_ctrl(i));
|
|
port_bitmap |= BIT_ULL(i);
|
|
}
|
|
@@ -188,6 +174,8 @@ static int rtl83xx_setup(struct dsa_switch *ds)
|
|
|
|
rtl83xx_init_stats(priv);
|
|
|
|
+ rtl83xx_vlan_setup(priv);
|
|
+
|
|
ds->configure_vlan_while_not_filtering = true;
|
|
|
|
/* Enable MAC Polling PHY again */
|
|
@@ -218,8 +206,8 @@ static int rtl930x_setup(struct dsa_switch *ds)
|
|
|
|
for (i = 0; i < priv->cpu_port; i++) {
|
|
if (priv->ports[i].phy) {
|
|
- priv->r->traffic_set(i, BIT(priv->cpu_port) | BIT(i));
|
|
- port_bitmap |= 1ULL << i;
|
|
+ priv->r->traffic_set(i, BIT_ULL(priv->cpu_port) | BIT_ULL(i));
|
|
+ port_bitmap |= BIT_ULL(i);
|
|
}
|
|
}
|
|
priv->r->traffic_set(priv->cpu_port, port_bitmap);
|
|
@@ -228,6 +216,8 @@ static int rtl930x_setup(struct dsa_switch *ds)
|
|
|
|
// TODO: Initialize statistics
|
|
|
|
+ rtl83xx_vlan_setup(priv);
|
|
+
|
|
ds->configure_vlan_while_not_filtering = true;
|
|
|
|
rtl83xx_enable_phy_polling(priv);
|
|
@@ -242,9 +232,10 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
|
|
|
- pr_debug("In %s port %d", __func__, port);
|
|
+ pr_debug("In %s port %d, state is %d", __func__, port, state->interface);
|
|
|
|
if (!phy_interface_mode_is_rgmii(state->interface) &&
|
|
+ state->interface != PHY_INTERFACE_MODE_NA &&
|
|
state->interface != PHY_INTERFACE_MODE_1000BASEX &&
|
|
state->interface != PHY_INTERFACE_MODE_MII &&
|
|
state->interface != PHY_INTERFACE_MODE_REVMII &&
|
|
@@ -278,6 +269,10 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
|
|
if (port >= 24 && port <= 27 && priv->family_id == RTL8380_FAMILY_ID)
|
|
phylink_set(mask, 1000baseX_Full);
|
|
|
|
+ /* On the RTL839x family of SoCs, ports 48 to 51 are SFP ports */
|
|
+ if (port >=48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID)
|
|
+ phylink_set(mask, 1000baseX_Full);
|
|
+
|
|
phylink_set(mask, 10baseT_Half);
|
|
phylink_set(mask, 10baseT_Full);
|
|
phylink_set(mask, 100baseT_Half);
|
|
@@ -310,7 +305,7 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
|
|
link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
|
|
if (link & BIT_ULL(port))
|
|
state->link = 1;
|
|
- pr_info("%s: link state: %llx\n", __func__, link & BIT_ULL(port));
|
|
+ pr_debug("%s: link state port %d: %llx\n", __func__, port, link & BIT_ULL(port));
|
|
|
|
state->duplex = 0;
|
|
if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port))
|
|
@@ -329,7 +324,8 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
|
|
state->speed = SPEED_1000;
|
|
break;
|
|
case 3:
|
|
- if (port == 24 || port == 26) /* Internal serdes */
|
|
+ if (priv->family_id == RTL9300_FAMILY_ID
|
|
+ && (port == 24 || port == 26)) /* Internal serdes */
|
|
state->speed = SPEED_2500;
|
|
else
|
|
state->speed = SPEED_100; /* Is in fact 500Mbit */
|
|
@@ -343,6 +339,43 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
|
|
return 1;
|
|
}
|
|
|
|
+static void rtl83xx_config_interface(int port, phy_interface_t interface)
|
|
+{
|
|
+ u32 old, int_shift, sds_shift;
|
|
+
|
|
+ switch (port) {
|
|
+ case 24:
|
|
+ int_shift = 0;
|
|
+ sds_shift = 5;
|
|
+ break;
|
|
+ case 26:
|
|
+ int_shift = 3;
|
|
+ sds_shift = 0;
|
|
+ break;
|
|
+ default:
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ old = sw_r32(RTL838X_SDS_MODE_SEL);
|
|
+ switch (interface) {
|
|
+ case PHY_INTERFACE_MODE_1000BASEX:
|
|
+ if ((old >> sds_shift & 0x1f) == 4)
|
|
+ return;
|
|
+ sw_w32_mask(0x7 << int_shift, 1 << int_shift, RTL838X_INT_MODE_CTRL);
|
|
+ sw_w32_mask(0x1f << sds_shift, 4 << sds_shift, RTL838X_SDS_MODE_SEL);
|
|
+ break;
|
|
+ case PHY_INTERFACE_MODE_SGMII:
|
|
+ if ((old >> sds_shift & 0x1f) == 2)
|
|
+ return;
|
|
+ sw_w32_mask(0x7 << int_shift, 2 << int_shift, RTL838X_INT_MODE_CTRL);
|
|
+ sw_w32_mask(0x1f << sds_shift, 2 << sds_shift, RTL838X_SDS_MODE_SEL);
|
|
+ break;
|
|
+ default:
|
|
+ return;
|
|
+ }
|
|
+ pr_debug("configured port %d for interface %s\n", port, phy_modes(interface));
|
|
+}
|
|
+
|
|
static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
|
|
unsigned int mode,
|
|
const struct phylink_link_state *state)
|
|
@@ -376,10 +409,11 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
|
|
reg = sw_r32(priv->r->mac_force_mode_ctrl(port));
|
|
/* Auto-Negotiation does not work for MAC in RTL8390 */
|
|
if (priv->family_id == RTL8380_FAMILY_ID) {
|
|
- if (mode == MLO_AN_PHY) {
|
|
+ if (mode == MLO_AN_PHY || phylink_autoneg_inband(mode)) {
|
|
pr_debug("PHY autonegotiates\n");
|
|
reg |= BIT(2);
|
|
sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
|
|
+ rtl83xx_config_interface(port, state->interface);
|
|
return;
|
|
}
|
|
}
|
|
@@ -509,8 +543,11 @@ static int rtl83xx_port_enable(struct dsa_switch *ds, int port,
|
|
v |= priv->ports[port].pm;
|
|
priv->r->traffic_set(port, v);
|
|
|
|
- sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_SABLK_CTRL);
|
|
- sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_DABLK_CTRL);
|
|
+ // TODO: Figure out if this is necessary
|
|
+ if (priv->family_id == RTL9300_FAMILY_ID) {
|
|
+ sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_SABLK_CTRL);
|
|
+ sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_DABLK_CTRL);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
@@ -537,58 +574,55 @@ static void rtl83xx_port_disable(struct dsa_switch *ds, int port)
|
|
priv->ports[port].enable = false;
|
|
}
|
|
|
|
+static int rtl83xx_set_mac_eee(struct dsa_switch *ds, int port,
|
|
+ struct ethtool_eee *e)
|
|
+{
|
|
+ struct rtl838x_switch_priv *priv = ds->priv;
|
|
+
|
|
+ if (e->eee_enabled && !priv->eee_enabled) {
|
|
+ pr_info("Globally enabling EEE\n");
|
|
+ priv->r->init_eee(priv, true);
|
|
+ }
|
|
+
|
|
+ priv->r->port_eee_set(priv, port, e->eee_enabled);
|
|
+
|
|
+ if (e->eee_enabled)
|
|
+ pr_info("Enabled EEE for port %d\n", port);
|
|
+ else
|
|
+ pr_info("Disabled EEE for port %d\n", port);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int rtl83xx_get_mac_eee(struct dsa_switch *ds, int port,
|
|
struct ethtool_eee *e)
|
|
{
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
|
|
- pr_debug("%s: port %d", __func__, port);
|
|
e->supported = SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full;
|
|
- if (sw_r32(priv->r->mac_force_mode_ctrl(port)) & BIT(9))
|
|
- e->advertised |= ADVERTISED_100baseT_Full;
|
|
|
|
- if (sw_r32(priv->r->mac_force_mode_ctrl(port)) & BIT(10))
|
|
- e->advertised |= ADVERTISED_1000baseT_Full;
|
|
+ priv->r->eee_port_ability(priv, e, port);
|
|
|
|
e->eee_enabled = priv->ports[port].eee_enabled;
|
|
- pr_debug("enabled: %d, active %x\n", e->eee_enabled, e->advertised);
|
|
-
|
|
- if (sw_r32(RTL838X_MAC_EEE_ABLTY) & BIT(port)) {
|
|
- e->lp_advertised = ADVERTISED_100baseT_Full;
|
|
- e->lp_advertised |= ADVERTISED_1000baseT_Full;
|
|
- }
|
|
|
|
e->eee_active = !!(e->advertised & e->lp_advertised);
|
|
- pr_debug("active: %d, lp %x\n", e->eee_active, e->lp_advertised);
|
|
|
|
return 0;
|
|
}
|
|
|
|
-static int rtl83xx_set_mac_eee(struct dsa_switch *ds, int port,
|
|
+static int rtl93xx_get_mac_eee(struct dsa_switch *ds, int port,
|
|
struct ethtool_eee *e)
|
|
{
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
|
|
- pr_debug("%s: port %d", __func__, port);
|
|
- if (e->eee_enabled) {
|
|
- pr_debug("Globally enabling EEE\n");
|
|
- sw_w32_mask(0x4, 0, RTL838X_SMI_GLB_CTRL);
|
|
- }
|
|
- if (e->eee_enabled) {
|
|
- pr_debug("Enabling EEE for MAC %d\n", port);
|
|
- sw_w32_mask(0, 3 << 9, priv->r->mac_force_mode_ctrl(port));
|
|
- sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_TX_EN);
|
|
- sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_RX_EN);
|
|
- priv->ports[port].eee_enabled = true;
|
|
- e->eee_enabled = true;
|
|
- } else {
|
|
- pr_debug("Disabling EEE for MAC %d\n", port);
|
|
- sw_w32_mask(3 << 9, 0, priv->r->mac_force_mode_ctrl(port));
|
|
- sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_TX_EN);
|
|
- sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_RX_EN);
|
|
- priv->ports[port].eee_enabled = false;
|
|
- e->eee_enabled = false;
|
|
- }
|
|
+ e->supported = SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full
|
|
+ | SUPPORTED_2500baseX_Full;
|
|
+
|
|
+ priv->r->eee_port_ability(priv, e, port);
|
|
+
|
|
+ e->eee_enabled = priv->ports[port].eee_enabled;
|
|
+
|
|
+ e->eee_active = !!(e->advertised & e->lp_advertised);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -618,7 +652,7 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
|
|
struct net_device *bridge)
|
|
{
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
- u64 port_bitmap = 1ULL << priv->cpu_port, v;
|
|
+ u64 port_bitmap = BIT_ULL(priv->cpu_port), v;
|
|
int i;
|
|
|
|
pr_debug("%s %x: %d %llx", __func__, (u32)priv, port, port_bitmap);
|
|
@@ -634,8 +668,8 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
|
|
if (priv->ports[i].enable)
|
|
priv->r->traffic_enable(i, port);
|
|
|
|
- priv->ports[i].pm |= 1ULL << port;
|
|
- port_bitmap |= 1ULL << i;
|
|
+ priv->ports[i].pm |= BIT_ULL(port);
|
|
+ port_bitmap |= BIT_ULL(i);
|
|
}
|
|
}
|
|
|
|
@@ -656,7 +690,7 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
|
struct net_device *bridge)
|
|
{
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
- u64 port_bitmap = 1ULL << priv->cpu_port, v;
|
|
+ u64 port_bitmap = BIT_ULL(priv->cpu_port), v;
|
|
int i;
|
|
|
|
pr_debug("%s %x: %d", __func__, (u32)priv, port);
|
|
@@ -674,7 +708,7 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port,
|
|
if (priv->ports[i].enable)
|
|
priv->r->traffic_disable(i, port);
|
|
|
|
- priv->ports[i].pm |= 1ULL << port;
|
|
+ priv->ports[i].pm |= BIT_ULL(port);
|
|
port_bitmap &= ~BIT_ULL(i);
|
|
}
|
|
}
|
|
@@ -826,17 +860,16 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port,
|
|
struct rtl838x_vlan_info info;
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
|
|
- pr_info("%s: port %d\n", __func__, port);
|
|
+ priv->r->vlan_tables_read(0, &info);
|
|
|
|
- mutex_lock(&priv->reg_mutex);
|
|
+ pr_debug("VLAN 0: Tagged ports %llx, untag %llx, profile %d, MC# %d, UC# %d, FID %x\n",
|
|
+ info.tagged_ports, info.untagged_ports, info.profile_id,
|
|
+ info.hash_mc_fid, info.hash_uc_fid, info.fid);
|
|
|
|
- priv->r->vlan_profile_dump(1);
|
|
priv->r->vlan_tables_read(1, &info);
|
|
-
|
|
- pr_info("Tagged ports %llx, untag %llx, prof %x, MC# %d, UC# %d, FID %x\n",
|
|
+ pr_debug("VLAN 1: Tagged ports %llx, untag %llx, profile %d, MC# %d, UC# %d, FID %x\n",
|
|
info.tagged_ports, info.untagged_ports, info.profile_id,
|
|
info.hash_mc_fid, info.hash_uc_fid, info.fid);
|
|
-
|
|
priv->r->vlan_set_untagged(1, info.untagged_ports);
|
|
pr_debug("SET: Untagged ports, VLAN %d: %llx\n", 1, info.untagged_ports);
|
|
|
|
@@ -854,7 +887,7 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port,
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
int v;
|
|
|
|
- pr_info("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__,
|
|
+ pr_debug("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__,
|
|
port, vlan->vid_begin, vlan->vid_end, vlan->flags);
|
|
|
|
if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) {
|
|
@@ -899,10 +932,10 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port,
|
|
info.untagged_ports |= BIT_ULL(port);
|
|
|
|
priv->r->vlan_set_untagged(v, info.untagged_ports);
|
|
- pr_info("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports);
|
|
+ pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports);
|
|
|
|
priv->r->vlan_set_tagged(v, &info);
|
|
- pr_info("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports);
|
|
+ pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports);
|
|
}
|
|
|
|
mutex_unlock(&priv->reg_mutex);
|
|
@@ -953,59 +986,136 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port,
|
|
return 0;
|
|
}
|
|
|
|
-static int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port,
|
|
- const unsigned char *addr, u16 vid)
|
|
+static void dump_l2_entry(struct rtl838x_l2_entry *e)
|
|
{
|
|
- struct rtl838x_switch_priv *priv = ds->priv;
|
|
- u64 mac = ether_addr_to_u64(addr);
|
|
- u32 key = rtl83xx_hash_key(priv, mac, vid);
|
|
- struct rtl838x_l2_entry e;
|
|
- u32 r[3];
|
|
+ pr_info("MAC: %02x:%02x:%02x:%02x:%02x:%02x vid: %d, rvid: %d, port: %d, valid: %d\n",
|
|
+ e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5],
|
|
+ e->vid, e->rvid, e->port, e->valid);
|
|
+
|
|
+ if (e->type != L2_MULTICAST) {
|
|
+ pr_info("Type: %d, is_static: %d, is_ip_mc: %d, is_ipv6_mc: %d, block_da: %d\n",
|
|
+ e->type, e->is_static, e->is_ip_mc, e->is_ipv6_mc, e->block_da);
|
|
+ pr_info(" block_sa: %d, susp: %d, nh: %d, age: %d, is_trunk: %d, trunk: %d\n",
|
|
+ e->block_sa, e->suspended, e->next_hop, e->age, e->is_trunk, e->trunk);
|
|
+ }
|
|
+ if (e->type == L2_MULTICAST)
|
|
+ pr_info(" L2_MULTICAST mc_portmask_index: %d\n", e->mc_portmask_index);
|
|
+ if (e->is_ip_mc || e->is_ipv6_mc)
|
|
+ pr_info(" mc_portmask_index: %d, mc_gip: %d, mc_sip: %d\n",
|
|
+ e->mc_portmask_index, e->mc_gip, e->mc_sip);
|
|
+ pr_info(" stack_dev: %d\n", e->stack_dev);
|
|
+ if (e->next_hop)
|
|
+ pr_info(" nh_route_id: %d\n", e->nh_route_id);
|
|
+}
|
|
+
|
|
+static void rtl83xx_setup_l2_uc_entry(struct rtl838x_l2_entry *e, int port, int vid, u64 mac)
|
|
+{
|
|
+ e->is_ip_mc = e->is_ipv6_mc = false;
|
|
+ e->valid = true;
|
|
+ e->age = 3;
|
|
+ e->port = port,
|
|
+ e->vid = vid;
|
|
+ u64_to_ether_addr(mac, e->mac);
|
|
+}
|
|
+
|
|
+static void rtl83xx_setup_l2_mc_entry(struct rtl838x_switch_priv *priv,
|
|
+ struct rtl838x_l2_entry *e, int vid, u64 mac, int mc_group)
|
|
+{
|
|
+ e->is_ip_mc = e->is_ipv6_mc = false;
|
|
+ e->valid = true;
|
|
+ e->mc_portmask_index = mc_group;
|
|
+ e->type = L2_MULTICAST;
|
|
+ e->rvid = e->vid = vid;
|
|
+ pr_debug("%s: vid: %d, rvid: %d\n", __func__, e->vid, e->rvid);
|
|
+ u64_to_ether_addr(mac, e->mac);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Uses the seed to identify a hash bucket in the L2 using the derived hash key and then loops
|
|
+ * over the entries in the bucket until either a matching entry is found or an empty slot
|
|
+ * Returns the filled in rtl838x_l2_entry and the index in the bucket when an entry was found
|
|
+ * when an empty slot was found and must exist is false, the index of the slot is returned
|
|
+ * when no slots are available returns -1
|
|
+ */
|
|
+static int rtl83xx_find_l2_hash_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
|
+ bool must_exist, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ int i, idx = -1;
|
|
+ u32 key = priv->r->l2_hash_key(priv, seed);
|
|
u64 entry;
|
|
- int idx = -1, err = 0, i;
|
|
|
|
- mutex_lock(&priv->reg_mutex);
|
|
- for (i = 0; i < 4; i++) {
|
|
- entry = priv->r->read_l2_entry_using_hash(key, i, &e);
|
|
- if (!e.valid) {
|
|
- idx = (key << 2) | i;
|
|
- break;
|
|
- }
|
|
- if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) {
|
|
- idx = (key << 2) | i;
|
|
+ pr_debug("%s: using key %x, for seed %016llx\n", __func__, key, seed);
|
|
+ // Loop over all entries in the hash-bucket and over the second block on 93xx SoCs
|
|
+ for (i = 0; i < priv->l2_bucket_size; i++) {
|
|
+ entry = priv->r->read_l2_entry_using_hash(key, i, e);
|
|
+ pr_debug("valid %d, mac %016llx\n", e->valid, ether_addr_to_u64(&e->mac[0]));
|
|
+ if (must_exist && !e->valid)
|
|
+ continue;
|
|
+ if (!e->valid || ((entry & 0x0fffffffffffffffULL) == seed)) {
|
|
+ idx = i > 3 ? ((key >> 14) & 0xffff) | i >> 1 : ((key << 2) | i) & 0xffff;
|
|
break;
|
|
}
|
|
}
|
|
- if (idx >= 0) {
|
|
- r[0] = 3 << 17 | port << 12; // Aging and port
|
|
- r[0] |= vid;
|
|
- r[1] = mac >> 16;
|
|
- r[2] = (mac & 0xffff) << 12; /* rvid = 0 */
|
|
- rtl83xx_write_hash(idx, r);
|
|
- goto out;
|
|
- }
|
|
|
|
- /* Hash buckets full, try CAM */
|
|
+ return idx;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Uses the seed to identify an entry in the CAM by looping over all its entries
|
|
+ * Returns the filled in rtl838x_l2_entry and the index in the CAM when an entry was found
|
|
+ * when an empty slot was found the index of the slot is returned
|
|
+ * when no slots are available returns -1
|
|
+ */
|
|
+static int rtl83xx_find_l2_cam_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
|
+ bool must_exist, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ int i, idx = -1;
|
|
+ u64 entry;
|
|
+
|
|
for (i = 0; i < 64; i++) {
|
|
- entry = priv->r->read_cam(i, &e);
|
|
- if (!e.valid) {
|
|
+ entry = priv->r->read_cam(i, e);
|
|
+ if (!must_exist && !e->valid) {
|
|
if (idx < 0) /* First empty entry? */
|
|
idx = i;
|
|
break;
|
|
- } else if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) {
|
|
+ } else if ((entry & 0x0fffffffffffffffULL) == seed) {
|
|
pr_debug("Found entry in CAM\n");
|
|
idx = i;
|
|
break;
|
|
}
|
|
}
|
|
+ return idx;
|
|
+}
|
|
+
|
|
+static int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port,
|
|
+ const unsigned char *addr, u16 vid)
|
|
+{
|
|
+ struct rtl838x_switch_priv *priv = ds->priv;
|
|
+ u64 mac = ether_addr_to_u64(addr);
|
|
+ struct rtl838x_l2_entry e;
|
|
+ int err = 0, idx;
|
|
+ u64 seed = priv->r->l2_hash_seed(mac, vid);
|
|
+
|
|
+ mutex_lock(&priv->reg_mutex);
|
|
+
|
|
+ idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e);
|
|
+
|
|
+ // Found an existing or empty entry
|
|
+ if (idx >= 0) {
|
|
+ rtl83xx_setup_l2_uc_entry(&e, port, vid, mac);
|
|
+ priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ // Hash buckets full, try CAM
|
|
+ rtl83xx_find_l2_cam_entry(priv, seed, false, &e);
|
|
+
|
|
if (idx >= 0) {
|
|
- r[0] = 3 << 17 | port << 12; // Aging
|
|
- r[0] |= vid;
|
|
- r[1] = mac >> 16;
|
|
- r[2] = (mac & 0xffff) << 12; /* rvid = 0 */
|
|
- rtl83xx_write_cam(idx, r);
|
|
+ rtl83xx_setup_l2_uc_entry(&e, port, vid, mac);
|
|
+ priv->r->write_cam(idx, &e);
|
|
goto out;
|
|
}
|
|
+
|
|
err = -ENOTSUPP;
|
|
out:
|
|
mutex_unlock(&priv->reg_mutex);
|
|
@@ -1017,41 +1127,29 @@ static int rtl83xx_port_fdb_del(struct dsa_switch *ds, int port,
|
|
{
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
u64 mac = ether_addr_to_u64(addr);
|
|
- u32 key = rtl83xx_hash_key(priv, mac, vid);
|
|
struct rtl838x_l2_entry e;
|
|
- u32 r[3];
|
|
- u64 entry;
|
|
- int idx = -1, err = 0, i;
|
|
+ int err = 0, idx;
|
|
+ u64 seed = priv->r->l2_hash_seed(mac, vid);
|
|
|
|
- pr_debug("In %s, mac %llx, vid: %d, key: %x08x\n", __func__, mac, vid, key);
|
|
+ pr_info("In %s, mac %llx, vid: %d\n", __func__, mac, vid);
|
|
mutex_lock(&priv->reg_mutex);
|
|
- for (i = 0; i < 4; i++) {
|
|
- entry = priv->r->read_l2_entry_using_hash(key, i, &e);
|
|
- if (!e.valid)
|
|
- continue;
|
|
- if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) {
|
|
- idx = (key << 2) | i;
|
|
- break;
|
|
- }
|
|
- }
|
|
|
|
+ idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e);
|
|
+
|
|
+ pr_info("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3);
|
|
if (idx >= 0) {
|
|
- r[0] = r[1] = r[2] = 0;
|
|
- rtl83xx_write_hash(idx, r);
|
|
+ e.valid = false;
|
|
+ dump_l2_entry(&e);
|
|
+ priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
|
|
goto out;
|
|
}
|
|
|
|
/* Check CAM for spillover from hash buckets */
|
|
- for (i = 0; i < 64; i++) {
|
|
- entry = priv->r->read_cam(i, &e);
|
|
- if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) {
|
|
- idx = i;
|
|
- break;
|
|
- }
|
|
- }
|
|
+ rtl83xx_find_l2_cam_entry(priv, seed, true, &e);
|
|
+
|
|
if (idx >= 0) {
|
|
- r[0] = r[1] = r[2] = 0;
|
|
- rtl83xx_write_cam(idx, r);
|
|
+ e.valid = false;
|
|
+ priv->r->write_cam(idx, &e);
|
|
goto out;
|
|
}
|
|
err = -ENOENT;
|
|
@@ -1066,8 +1164,7 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
|
|
struct rtl838x_l2_entry e;
|
|
struct rtl838x_switch_priv *priv = ds->priv;
|
|
int i;
|
|
- u32 fid;
|
|
- u32 pkey;
|
|
+ u32 fid, pkey;
|
|
u64 mac;
|
|
|
|
mutex_lock(&priv->reg_mutex);
|
|
@@ -1079,13 +1176,25 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
|
|
continue;
|
|
|
|
if (e.port == port) {
|
|
- fid = (i & 0x3ff) | (e.rvid & ~0x3ff);
|
|
+ fid = ((i >> 2) & 0x3ff) | (e.rvid & ~0x3ff);
|
|
mac = ether_addr_to_u64(&e.mac[0]);
|
|
- pkey = rtl838x_hash(priv, mac << 12 | fid);
|
|
+ pkey = priv->r->l2_hash_key(priv, priv->r->l2_hash_seed(mac, fid));
|
|
fid = (pkey & 0x3ff) | (fid & ~0x3ff);
|
|
- pr_debug("-> mac %016llx, fid: %d\n", mac, fid);
|
|
+ pr_info("-> index %d, key %x, bucket %d, dmac %016llx, fid: %x rvid: %x\n",
|
|
+ i, i >> 2, i & 0x3, mac, fid, e.rvid);
|
|
+ dump_l2_entry(&e);
|
|
+ u64 seed = priv->r->l2_hash_seed(mac, e.rvid);
|
|
+ u32 key = priv->r->l2_hash_key(priv, seed);
|
|
+ pr_info("seed: %016llx, key based on rvid: %08x\n", seed, key);
|
|
cb(e.mac, e.vid, e.is_static, data);
|
|
}
|
|
+ if (e.type == L2_MULTICAST) {
|
|
+ u64 portmask = priv->r->read_mcast_pmask(e.mc_portmask_index);
|
|
+ if (portmask & BIT_ULL(port)) {
|
|
+ dump_l2_entry(&e);
|
|
+ pr_info(" PM: %016llx\n", portmask);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
@@ -1102,6 +1211,164 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
|
|
return 0;
|
|
}
|
|
|
|
+static int rtl83xx_port_mdb_prepare(struct dsa_switch *ds, int port,
|
|
+ const struct switchdev_obj_port_mdb *mdb)
|
|
+{
|
|
+ struct rtl838x_switch_priv *priv = ds->priv;
|
|
+
|
|
+ if (priv->id >= 0x9300)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rtl83xx_mc_group_alloc(struct rtl838x_switch_priv *priv, int port)
|
|
+{
|
|
+ int mc_group = find_first_zero_bit(priv->mc_group_bm, MAX_MC_GROUPS - 1);
|
|
+ u64 portmask;
|
|
+
|
|
+ if (mc_group >= MAX_MC_GROUPS - 1)
|
|
+ return -1;
|
|
+
|
|
+ pr_debug("Using MC group %d\n", mc_group);
|
|
+ set_bit(mc_group, priv->mc_group_bm);
|
|
+ mc_group++; // We cannot use group 0, as this is used for lookup miss flooding
|
|
+ portmask = BIT_ULL(port);
|
|
+ priv->r->write_mcast_pmask(mc_group, portmask);
|
|
+
|
|
+ return mc_group;
|
|
+}
|
|
+
|
|
+static u64 rtl83xx_mc_group_add_port(struct rtl838x_switch_priv *priv, int mc_group, int port)
|
|
+{
|
|
+ u64 portmask = priv->r->read_mcast_pmask(mc_group);
|
|
+
|
|
+ portmask |= BIT_ULL(port);
|
|
+ priv->r->write_mcast_pmask(mc_group, portmask);
|
|
+
|
|
+ return portmask;
|
|
+}
|
|
+
|
|
+static u64 rtl83xx_mc_group_del_port(struct rtl838x_switch_priv *priv, int mc_group, int port)
|
|
+{
|
|
+ u64 portmask = priv->r->read_mcast_pmask(mc_group);
|
|
+
|
|
+ portmask &= ~BIT_ULL(port);
|
|
+ priv->r->write_mcast_pmask(mc_group, portmask);
|
|
+ if (!portmask)
|
|
+ clear_bit(mc_group, priv->mc_group_bm);
|
|
+
|
|
+ return portmask;
|
|
+}
|
|
+
|
|
+static void rtl83xx_port_mdb_add(struct dsa_switch *ds, int port,
|
|
+ const struct switchdev_obj_port_mdb *mdb)
|
|
+{
|
|
+ struct rtl838x_switch_priv *priv = ds->priv;
|
|
+ u64 mac = ether_addr_to_u64(mdb->addr);
|
|
+ struct rtl838x_l2_entry e;
|
|
+ int err = 0, idx;
|
|
+ int vid = mdb->vid;
|
|
+ u64 seed = priv->r->l2_hash_seed(mac, vid);
|
|
+ int mc_group;
|
|
+
|
|
+ pr_debug("In %s port %d, mac %llx, vid: %d\n", __func__, port, mac, vid);
|
|
+ mutex_lock(&priv->reg_mutex);
|
|
+
|
|
+ idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e);
|
|
+
|
|
+ // Found an existing or empty entry
|
|
+ if (idx >= 0) {
|
|
+ if (e.valid) {
|
|
+ pr_debug("Found an existing entry %016llx, mc_group %d\n",
|
|
+ ether_addr_to_u64(e.mac), e.mc_portmask_index);
|
|
+ rtl83xx_mc_group_add_port(priv, e.mc_portmask_index, port);
|
|
+ } else {
|
|
+ pr_debug("New entry for seed %016llx\n", seed);
|
|
+ mc_group = rtl83xx_mc_group_alloc(priv, port);
|
|
+ if (mc_group < 0) {
|
|
+ err = -ENOTSUPP;
|
|
+ goto out;
|
|
+ }
|
|
+ rtl83xx_setup_l2_mc_entry(priv, &e, vid, mac, mc_group);
|
|
+ priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
|
|
+ }
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ // Hash buckets full, try CAM
|
|
+ rtl83xx_find_l2_cam_entry(priv, seed, false, &e);
|
|
+
|
|
+ if (idx >= 0) {
|
|
+ if (e.valid) {
|
|
+ pr_debug("Found existing CAM entry %016llx, mc_group %d\n",
|
|
+ ether_addr_to_u64(e.mac), e.mc_portmask_index);
|
|
+ rtl83xx_mc_group_add_port(priv, e.mc_portmask_index, port);
|
|
+ } else {
|
|
+ pr_debug("New entry\n");
|
|
+ mc_group = rtl83xx_mc_group_alloc(priv, port);
|
|
+ if (mc_group < 0) {
|
|
+ err = -ENOTSUPP;
|
|
+ goto out;
|
|
+ }
|
|
+ rtl83xx_setup_l2_mc_entry(priv, &e, vid, mac, mc_group);
|
|
+ priv->r->write_cam(idx, &e);
|
|
+ }
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ err = -ENOTSUPP;
|
|
+out:
|
|
+ mutex_unlock(&priv->reg_mutex);
|
|
+ if (err)
|
|
+ dev_err(ds->dev, "failed to add MDB entry\n");
|
|
+}
|
|
+
|
|
+int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port,
|
|
+ const struct switchdev_obj_port_mdb *mdb)
|
|
+{
|
|
+ struct rtl838x_switch_priv *priv = ds->priv;
|
|
+ u64 mac = ether_addr_to_u64(mdb->addr);
|
|
+ struct rtl838x_l2_entry e;
|
|
+ int err = 0, idx;
|
|
+ int vid = mdb->vid;
|
|
+ u64 seed = priv->r->l2_hash_seed(mac, vid);
|
|
+ u64 portmask;
|
|
+
|
|
+ pr_debug("In %s, port %d, mac %llx, vid: %d\n", __func__, port, mac, vid);
|
|
+ mutex_lock(&priv->reg_mutex);
|
|
+
|
|
+ idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e);
|
|
+
|
|
+ pr_debug("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3);
|
|
+ if (idx >= 0) {
|
|
+ portmask = rtl83xx_mc_group_del_port(priv, e.mc_portmask_index, port);
|
|
+ if (!portmask) {
|
|
+ e.valid = false;
|
|
+ // dump_l2_entry(&e);
|
|
+ priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
|
|
+ }
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Check CAM for spillover from hash buckets */
|
|
+ rtl83xx_find_l2_cam_entry(priv, seed, true, &e);
|
|
+
|
|
+ if (idx >= 0) {
|
|
+ portmask = rtl83xx_mc_group_del_port(priv, e.mc_portmask_index, port);
|
|
+ if (!portmask) {
|
|
+ e.valid = false;
|
|
+ // dump_l2_entry(&e);
|
|
+ priv->r->write_cam(idx, &e);
|
|
+ }
|
|
+ goto out;
|
|
+ }
|
|
+ // TODO: Re-enable with a newer kernel: err = -ENOENT;
|
|
+out:
|
|
+ mutex_unlock(&priv->reg_mutex);
|
|
+ return err;
|
|
+}
|
|
+
|
|
static int rtl83xx_port_mirror_add(struct dsa_switch *ds, int port,
|
|
struct dsa_mall_mirror_tc_entry *mirror,
|
|
bool ingress)
|
|
@@ -1268,6 +1535,10 @@ const struct dsa_switch_ops rtl83xx_switch_ops = {
|
|
.port_fdb_del = rtl83xx_port_fdb_del,
|
|
.port_fdb_dump = rtl83xx_port_fdb_dump,
|
|
|
|
+ .port_mdb_prepare = rtl83xx_port_mdb_prepare,
|
|
+ .port_mdb_add = rtl83xx_port_mdb_add,
|
|
+ .port_mdb_del = rtl83xx_port_mdb_del,
|
|
+
|
|
.port_mirror_add = rtl83xx_port_mirror_add,
|
|
.port_mirror_del = rtl83xx_port_mirror_del,
|
|
};
|
|
@@ -1292,6 +1563,9 @@ const struct dsa_switch_ops rtl930x_switch_ops = {
|
|
.port_enable = rtl83xx_port_enable,
|
|
.port_disable = rtl83xx_port_disable,
|
|
|
|
+ .get_mac_eee = rtl93xx_get_mac_eee,
|
|
+ .set_mac_eee = rtl83xx_set_mac_eee,
|
|
+
|
|
.set_ageing_time = rtl83xx_set_l2aging,
|
|
.port_bridge_join = rtl83xx_port_bridge_join,
|
|
.port_bridge_leave = rtl83xx_port_bridge_leave,
|
|
@@ -1306,4 +1580,9 @@ const struct dsa_switch_ops rtl930x_switch_ops = {
|
|
.port_fdb_add = rtl83xx_port_fdb_add,
|
|
.port_fdb_del = rtl83xx_port_fdb_del,
|
|
.port_fdb_dump = rtl83xx_port_fdb_dump,
|
|
+
|
|
+ .port_mdb_prepare = rtl83xx_port_mdb_prepare,
|
|
+ .port_mdb_add = rtl83xx_port_mdb_add,
|
|
+ .port_mdb_del = rtl83xx_port_mdb_del,
|
|
+
|
|
};
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
|
|
index de9e83bb8d..5d764b6a32 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
|
|
@@ -12,10 +12,10 @@ void rtl838x_print_matrix(void)
|
|
|
|
ptr8 = RTL838X_SW_BASE + RTL838X_PORT_ISO_CTRL(0);
|
|
for (i = 0; i < 28; i += 8)
|
|
- pr_info("> %8x %8x %8x %8x %8x %8x %8x %8x\n",
|
|
+ pr_debug("> %8x %8x %8x %8x %8x %8x %8x %8x\n",
|
|
ptr8[i + 0], ptr8[i + 1], ptr8[i + 2], ptr8[i + 3],
|
|
ptr8[i + 4], ptr8[i + 5], ptr8[i + 6], ptr8[i + 7]);
|
|
- pr_info("CPU_PORT> %8x\n", ptr8[28]);
|
|
+ pr_debug("CPU_PORT> %8x\n", ptr8[28]);
|
|
}
|
|
|
|
static inline int rtl838x_port_iso_ctrl(int p)
|
|
@@ -42,56 +42,98 @@ static inline int rtl838x_tbl_access_data_0(int i)
|
|
|
|
static void rtl838x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info)
|
|
{
|
|
- u32 cmd, v;
|
|
+ u32 v;
|
|
+ // Read VLAN table (0) via register 0
|
|
+ struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 0);
|
|
+
|
|
+ rtl_table_read(r, vlan);
|
|
+ info->tagged_ports = sw_r32(rtl_table_data(r, 0));
|
|
+ v = sw_r32(rtl_table_data(r, 1));
|
|
+ pr_debug("VLAN_READ %d: %016llx %08x\n", vlan, info->tagged_ports, v);
|
|
+ rtl_table_release(r);
|
|
|
|
- cmd = BIT(15) /* Execute cmd */
|
|
- | BIT(14) /* Read */
|
|
- | 0 << 12 /* Table type 0b00 */
|
|
- | (vlan & 0xfff);
|
|
- rtl838x_exec_tbl0_cmd(cmd);
|
|
- info->tagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0));
|
|
- v = sw_r32(RTL838X_TBL_ACCESS_DATA_0(1));
|
|
info->profile_id = v & 0x7;
|
|
info->hash_mc_fid = !!(v & 0x8);
|
|
info->hash_uc_fid = !!(v & 0x10);
|
|
info->fid = (v >> 5) & 0x3f;
|
|
|
|
-
|
|
- cmd = BIT(15) /* Execute cmd */
|
|
- | BIT(14) /* Read */
|
|
- | 0 << 12 /* Table type 0b00 */
|
|
- | (vlan & 0xfff);
|
|
- rtl838x_exec_tbl1_cmd(cmd);
|
|
- info->untagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_1(0));
|
|
+ // Read UNTAG table (0) via table register 1
|
|
+ r = rtl_table_get(RTL8380_TBL_1, 0);
|
|
+ rtl_table_read(r, vlan);
|
|
+ info->untagged_ports = sw_r32(rtl_table_data(r, 0));
|
|
+ rtl_table_release(r);
|
|
}
|
|
|
|
static void rtl838x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info)
|
|
{
|
|
- u32 cmd = BIT(15) /* Execute cmd */
|
|
- | 0 << 14 /* Write */
|
|
- | 0 << 12 /* Table type 0b00 */
|
|
- | (vlan & 0xfff);
|
|
u32 v;
|
|
+ // Access VLAN table (0) via register 0
|
|
+ struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 0);
|
|
|
|
- sw_w32(info->tagged_ports, RTL838X_TBL_ACCESS_DATA_0(0));
|
|
+ sw_w32(info->tagged_ports, rtl_table_data(r, 0));
|
|
|
|
v = info->profile_id;
|
|
v |= info->hash_mc_fid ? 0x8 : 0;
|
|
v |= info->hash_uc_fid ? 0x10 : 0;
|
|
v |= ((u32)info->fid) << 5;
|
|
+ sw_w32(v, rtl_table_data(r, 1));
|
|
|
|
- sw_w32(v, RTL838X_TBL_ACCESS_DATA_0(1));
|
|
- rtl838x_exec_tbl0_cmd(cmd);
|
|
+ rtl_table_write(r, vlan);
|
|
+ rtl_table_release(r);
|
|
}
|
|
|
|
static void rtl838x_vlan_set_untagged(u32 vlan, u64 portmask)
|
|
{
|
|
- u32 cmd = BIT(15) /* Execute cmd */
|
|
- | 0 << 14 /* Write */
|
|
- | 0 << 12 /* Table type 0b00 */
|
|
- | (vlan & 0xfff);
|
|
- sw_w32(portmask & 0x1fffffff, RTL838X_TBL_ACCESS_DATA_1(0));
|
|
- rtl838x_exec_tbl1_cmd(cmd);
|
|
+ // Access UNTAG table (0) via register 1
|
|
+ struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 0);
|
|
+
|
|
+ sw_w32(portmask & 0x1fffffff, rtl_table_data(r, 0));
|
|
+ rtl_table_write(r, vlan);
|
|
+ rtl_table_release(r);
|
|
+}
|
|
+
|
|
+/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer
|
|
+ */
|
|
+static void rtl838x_vlan_fwd_on_inner(int port, bool is_set)
|
|
+{
|
|
+ if (is_set)
|
|
+ sw_w32_mask(BIT(port), 0, RTL838X_VLAN_PORT_FWD);
|
|
+ else
|
|
+ sw_w32_mask(0, BIT(port), RTL838X_VLAN_PORT_FWD);
|
|
+}
|
|
+
|
|
+static u64 rtl838x_l2_hash_seed(u64 mac, u32 vid)
|
|
+{
|
|
+ return mac << 12 | vid;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Applies the same hash algorithm as the one used currently by the ASIC to the seed
|
|
+ * and returns a key into the L2 hash table
|
|
+ */
|
|
+static u32 rtl838x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed)
|
|
+{
|
|
+ u32 h1, h2, h3, h;
|
|
+
|
|
+ if (sw_r32(priv->r->l2_ctrl_0) & 1) {
|
|
+ h1 = (seed >> 11) & 0x7ff;
|
|
+ h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f);
|
|
+
|
|
+ h2 = (seed >> 33) & 0x7ff;
|
|
+ h2 = ((h2 & 0x3f) << 5) | ((h2 >> 6) & 0x1f);
|
|
+
|
|
+ h3 = (seed >> 44) & 0x7ff;
|
|
+ h3 = ((h3 & 0x7f) << 4) | ((h3 >> 7) & 0xf);
|
|
+
|
|
+ h = h1 ^ h2 ^ h3 ^ ((seed >> 55) & 0x1ff);
|
|
+ h ^= ((seed >> 22) & 0x7ff) ^ (seed & 0x7ff);
|
|
+ } else {
|
|
+ h = ((seed >> 55) & 0x1ff) ^ ((seed >> 44) & 0x7ff)
|
|
+ ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff)
|
|
+ ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff);
|
|
+ }
|
|
+
|
|
+ return h;
|
|
}
|
|
|
|
static inline int rtl838x_mac_force_mode_ctrl(int p)
|
|
@@ -124,87 +166,239 @@ inline static int rtl838x_trk_mbr_ctr(int group)
|
|
return RTL838X_TRK_MBR_CTR + (group << 2);
|
|
}
|
|
|
|
-static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
|
|
+/*
|
|
+ * Fills an L2 entry structure from the SoC registers
|
|
+ */
|
|
+static void rtl838x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ /* Table contains different entry types, we need to identify the right one:
|
|
+ * Check for MC entries, first
|
|
+ * In contrast to the RTL93xx SoCs, there is no valid bit, use heuristics to
|
|
+ * identify valid entries
|
|
+ */
|
|
+ e->is_ip_mc = !!(r[0] & BIT(22));
|
|
+ e->is_ipv6_mc = !!(r[0] & BIT(21));
|
|
+ e->type = L2_INVALID;
|
|
+
|
|
+ if (!e->is_ip_mc && !e->is_ipv6_mc) {
|
|
+ e->mac[0] = (r[1] >> 20);
|
|
+ e->mac[1] = (r[1] >> 12);
|
|
+ e->mac[2] = (r[1] >> 4);
|
|
+ e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28);
|
|
+ e->mac[4] = (r[2] >> 20);
|
|
+ e->mac[5] = (r[2] >> 12);
|
|
+
|
|
+ e->rvid = r[2] & 0xfff;
|
|
+ e->vid = r[0] & 0xfff;
|
|
+
|
|
+ /* Is it a unicast entry? check multicast bit */
|
|
+ if (!(e->mac[0] & 1)) {
|
|
+ e->is_static = !!((r[0] >> 19) & 1);
|
|
+ e->port = (r[0] >> 12) & 0x1f;
|
|
+ e->block_da = !!(r[1] & BIT(30));
|
|
+ e->block_sa = !!(r[1] & BIT(31));
|
|
+ e->suspended = !!(r[1] & BIT(29));
|
|
+ e->next_hop = !!(r[1] & BIT(28));
|
|
+ if (e->next_hop) {
|
|
+ pr_info("Found next hop entry, need to read extra data\n");
|
|
+ e->nh_vlan_target = !!(r[0] & BIT(9));
|
|
+ e->nh_route_id = r[0] & 0x1ff;
|
|
+ }
|
|
+ e->age = (r[0] >> 17) & 0x3;
|
|
+ e->valid = true;
|
|
+
|
|
+ /* A valid entry has one of mutli-cast, aging, sa/da-blocking,
|
|
+ * next-hop or static entry bit set */
|
|
+ if (!(r[0] & 0x007c0000) && !(r[1] & 0xd0000000))
|
|
+ e->valid = false;
|
|
+ else
|
|
+ e->type = L2_UNICAST;
|
|
+ } else { // L2 multicast
|
|
+ pr_info("Got L2 MC entry: %08x %08x %08x\n", r[0], r[1], r[2]);
|
|
+ e->valid = true;
|
|
+ e->type = L2_MULTICAST;
|
|
+ e->mc_portmask_index = (r[0] >> 12) & 0x1ff;
|
|
+ }
|
|
+ } else { // IPv4 and IPv6 multicast
|
|
+ e->valid = true;
|
|
+ e->mc_portmask_index = (r[0] >> 12) & 0x1ff;
|
|
+ e->mc_gip = r[1];
|
|
+ e->mc_sip = r[2];
|
|
+ e->rvid = r[0] & 0xfff;
|
|
+ }
|
|
+ if (e->is_ip_mc)
|
|
+ e->type = IP4_MULTICAST;
|
|
+ if (e->is_ipv6_mc)
|
|
+ e->type = IP6_MULTICAST;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry
|
|
+ */
|
|
+static void rtl838x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u64 mac = ether_addr_to_u64(e->mac);
|
|
+
|
|
+ if (!e->valid) {
|
|
+ r[0] = r[1] = r[2] = 0;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ r[0] = e->is_ip_mc ? BIT(22) : 0;
|
|
+ r[0] |= e->is_ipv6_mc ? BIT(21) : 0;
|
|
+
|
|
+ if (!e->is_ip_mc && !e->is_ipv6_mc) {
|
|
+ r[1] = mac >> 20;
|
|
+ r[2] = (mac & 0xfffff) << 12;
|
|
+
|
|
+ /* Is it a unicast entry? check multicast bit */
|
|
+ if (!(e->mac[0] & 1)) {
|
|
+ r[0] |= e->is_static ? BIT(19) : 0;
|
|
+ r[0] |= (e->port & 0x3f) << 12;
|
|
+ r[0] |= e->vid;
|
|
+ r[1] |= e->block_da ? BIT(30) : 0;
|
|
+ r[1] |= e->block_sa ? BIT(31) : 0;
|
|
+ r[1] |= e->suspended ? BIT(29) : 0;
|
|
+ r[2] |= e->rvid & 0xfff;
|
|
+ if (e->next_hop) {
|
|
+ r[1] |= BIT(28);
|
|
+ r[0] |= e->nh_vlan_target ? BIT(9) : 0;
|
|
+ r[0] |= e->nh_route_id &0x1ff;
|
|
+ }
|
|
+ r[0] |= (e->age & 0x3) << 17;
|
|
+ } else { // L2 Multicast
|
|
+ r[0] |= (e->mc_portmask_index & 0x1ff) << 12;
|
|
+ r[2] |= e->rvid & 0xfff;
|
|
+ r[0] |= e->vid & 0xfff;
|
|
+ pr_info("FILL MC: %08x %08x %08x\n", r[0], r[1], r[2]);
|
|
+ }
|
|
+ } else { // IPv4 and IPv6 multicast
|
|
+ r[1] = e->mc_gip;
|
|
+ r[2] = e->mc_sip;
|
|
+ r[0] |= e->rvid;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table
|
|
+ * hash is the id of the bucket and pos is the position of the entry in that bucket
|
|
+ * The data read from the SoC is filled into rtl838x_l2_entry
|
|
+ */
|
|
+static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
|
|
{
|
|
u64 entry;
|
|
u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0); // Access L2 Table 0
|
|
+ u32 idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket
|
|
+ int i;
|
|
+
|
|
+ rtl_table_read(q, idx);
|
|
+ for (i= 0; i < 3; i++)
|
|
+ r[i] = sw_r32(rtl_table_data(q, i));
|
|
|
|
- /* Search in SRAM, with hash and at position in hash bucket (0-3) */
|
|
- u32 idx = (0 << 14) | (hash << 2) | position;
|
|
-
|
|
- u32 cmd = BIT(16) /* Execute cmd */
|
|
- | BIT(15) /* Read */
|
|
- | 0 << 13 /* Table type 0b00 */
|
|
- | (idx & 0x1fff);
|
|
-
|
|
- sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL);
|
|
- do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16));
|
|
- r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0));
|
|
- r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1));
|
|
- r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2));
|
|
-
|
|
- e->mac[0] = (r[1] >> 20);
|
|
- e->mac[1] = (r[1] >> 12);
|
|
- e->mac[2] = (r[1] >> 4);
|
|
- e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28);
|
|
- e->mac[4] = (r[2] >> 20);
|
|
- e->mac[5] = (r[2] >> 12);
|
|
- e->is_static = !!((r[0] >> 19) & 1);
|
|
- e->vid = r[0] & 0xfff;
|
|
- e->rvid = r[2] & 0xfff;
|
|
- e->port = (r[0] >> 12) & 0x1f;
|
|
-
|
|
- e->valid = true;
|
|
- if (!(r[0] >> 17)) /* Check for invalid entry */
|
|
- e->valid = false;
|
|
-
|
|
- if (e->valid)
|
|
- pr_debug("Found in Hash: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
|
|
+ rtl_table_release(q);
|
|
+
|
|
+ rtl838x_fill_l2_entry(r, e);
|
|
+ if (!e->valid)
|
|
+ return 0;
|
|
|
|
entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff);
|
|
return entry;
|
|
}
|
|
|
|
+static void rtl838x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0);
|
|
+ int i;
|
|
+
|
|
+ u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket
|
|
+
|
|
+ rtl838x_fill_l2_row(r, e);
|
|
+
|
|
+ for (i= 0; i < 3; i++)
|
|
+ sw_w32(r[i], rtl_table_data(q, i));
|
|
+
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
+}
|
|
+
|
|
static u64 rtl838x_read_cam(int idx, struct rtl838x_l2_entry *e)
|
|
{
|
|
u64 entry;
|
|
u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1
|
|
+ int i;
|
|
+
|
|
+ rtl_table_read(q, idx);
|
|
+ for (i= 0; i < 3; i++)
|
|
+ r[i] = sw_r32(rtl_table_data(q, i));
|
|
|
|
- u32 cmd = BIT(16) /* Execute cmd */
|
|
- | BIT(15) /* Read */
|
|
- | BIT(13) /* Table type 0b01 */
|
|
- | (idx & 0x3f);
|
|
- sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL);
|
|
- do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16));
|
|
- r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0));
|
|
- r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1));
|
|
- r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2));
|
|
-
|
|
- e->mac[0] = (r[1] >> 20);
|
|
- e->mac[1] = (r[1] >> 12);
|
|
- e->mac[2] = (r[1] >> 4);
|
|
- e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28);
|
|
- e->mac[4] = (r[2] >> 20);
|
|
- e->mac[5] = (r[2] >> 12);
|
|
- e->is_static = !!((r[0] >> 19) & 1);
|
|
- e->vid = r[0] & 0xfff;
|
|
- e->rvid = r[2] & 0xfff;
|
|
- e->port = (r[0] >> 12) & 0x1f;
|
|
-
|
|
- e->valid = true;
|
|
- if (!(r[0] >> 17)) /* Check for invalid entry */
|
|
- e->valid = false;
|
|
-
|
|
- if (e->valid)
|
|
- pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
|
|
+ rtl_table_release(q);
|
|
|
|
+ rtl838x_fill_l2_entry(r, e);
|
|
+ if (!e->valid)
|
|
+ return 0;
|
|
+
|
|
+ pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
|
|
+
|
|
+ // Return MAC with concatenated VID ac concatenated ID
|
|
entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff);
|
|
return entry;
|
|
}
|
|
|
|
-static inline int rtl838x_vlan_profile(int profile)
|
|
+static void rtl838x_write_cam(int idx, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1
|
|
+ int i;
|
|
+
|
|
+ rtl838x_fill_l2_row(r, e);
|
|
+
|
|
+ for (i= 0; i < 3; i++)
|
|
+ sw_w32(r[i], rtl_table_data(q, i));
|
|
+
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
+}
|
|
+
|
|
+static u64 rtl838x_read_mcast_pmask(int idx)
|
|
+{
|
|
+ u32 portmask;
|
|
+ // Read MC_PMSK (2) via register RTL8380_TBL_L2
|
|
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 2);
|
|
+
|
|
+ rtl_table_read(q, idx);
|
|
+ portmask = sw_r32(rtl_table_data(q, 0));
|
|
+ rtl_table_release(q);
|
|
+
|
|
+ return portmask;
|
|
+}
|
|
+
|
|
+static void rtl838x_write_mcast_pmask(int idx, u64 portmask)
|
|
{
|
|
- return RTL838X_VLAN_PROFILE(profile);
|
|
+ // Access MC_PMSK (2) via register RTL8380_TBL_L2
|
|
+ struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 2);
|
|
+
|
|
+ sw_w32(((u32)portmask) & 0x1fffffff, rtl_table_data(q, 0));
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
+}
|
|
+
|
|
+static void rtl838x_vlan_profile_setup(int profile)
|
|
+{
|
|
+ u32 pmask_id = UNKNOWN_MC_PMASK;
|
|
+ // Enable L2 Learning BIT 0, portmask UNKNOWN_MC_PMASK for unknown MC traffic flooding
|
|
+ u32 p = 1 | pmask_id << 1 | pmask_id << 10 | pmask_id << 19;
|
|
+
|
|
+ sw_w32(p, RTL838X_VLAN_PROFILE(profile));
|
|
+
|
|
+ /* RTL8380 and RTL8390 use an index into the portmask table to set the
|
|
+ * unknown multicast portmask, setup a default at a safe location
|
|
+ * On RTL93XX, the portmask is directly set in the profile,
|
|
+ * see e.g. rtl9300_vlan_profile_setup
|
|
+ */
|
|
+ rtl838x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0x1fffffff);
|
|
}
|
|
|
|
static inline int rtl838x_vlan_port_egr_filter(int port)
|
|
@@ -263,6 +457,84 @@ void rtl838x_traffic_disable(int source, int dest)
|
|
rtl838x_mask_port_reg(BIT(dest), 0, rtl838x_port_iso_ctrl(source));
|
|
}
|
|
|
|
+/*
|
|
+ * Enables or disables the EEE/EEEP capability of a port
|
|
+ */
|
|
+static void rtl838x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ // This works only for Ethernet ports, and on the RTL838X, ports from 24 are SFP
|
|
+ if (port >= 24)
|
|
+ return;
|
|
+
|
|
+ pr_debug("In %s: setting port %d to %d\n", __func__, port, enable);
|
|
+ v = enable ? 0x3 : 0x0;
|
|
+
|
|
+ // Set EEE state for 100 (bit 9) & 1000MBit (bit 10)
|
|
+ sw_w32_mask(0x3 << 9, v << 9, priv->r->mac_force_mode_ctrl(port));
|
|
+
|
|
+ // Set TX/RX EEE state
|
|
+ if (enable) {
|
|
+ sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_TX_EN);
|
|
+ sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_RX_EN);
|
|
+ } else {
|
|
+ sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_TX_EN);
|
|
+ sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_RX_EN);
|
|
+ }
|
|
+ priv->ports[port].eee_enabled = enable;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Get EEE own capabilities and negotiation result
|
|
+ */
|
|
+static int rtl838x_eee_port_ability(struct rtl838x_switch_priv *priv,
|
|
+ struct ethtool_eee *e, int port)
|
|
+{
|
|
+ u64 link;
|
|
+
|
|
+ if (port >= 24)
|
|
+ return 0;
|
|
+
|
|
+ link = rtl839x_get_port_reg_le(RTL838X_MAC_LINK_STS);
|
|
+ if (!(link & BIT(port)))
|
|
+ return 0;
|
|
+
|
|
+ if (sw_r32(rtl838x_mac_force_mode_ctrl(port)) & BIT(9))
|
|
+ e->advertised |= ADVERTISED_100baseT_Full;
|
|
+
|
|
+ if (sw_r32(rtl838x_mac_force_mode_ctrl(port)) & BIT(10))
|
|
+ e->advertised |= ADVERTISED_1000baseT_Full;
|
|
+
|
|
+ if (sw_r32(RTL838X_MAC_EEE_ABLTY) & BIT(port)) {
|
|
+ e->lp_advertised = ADVERTISED_100baseT_Full;
|
|
+ e->lp_advertised |= ADVERTISED_1000baseT_Full;
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rtl838x_init_eee(struct rtl838x_switch_priv *priv, bool enable)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ pr_info("Setting up EEE, state: %d\n", enable);
|
|
+ sw_w32_mask(0x4, 0, RTL838X_SMI_GLB_CTRL);
|
|
+
|
|
+ /* Set timers for EEE */
|
|
+ sw_w32(0x5001411, RTL838X_EEE_TX_TIMER_GIGA_CTRL);
|
|
+ sw_w32(0x5001417, RTL838X_EEE_TX_TIMER_GELITE_CTRL);
|
|
+
|
|
+ // Enable EEE MAC support on ports
|
|
+ for (i = 0; i < priv->cpu_port; i++) {
|
|
+ if (priv->ports[i].phy)
|
|
+ rtl838x_port_eee_set(priv, i, enable);
|
|
+ }
|
|
+ priv->eee_enabled = enable;
|
|
+}
|
|
+
|
|
const struct rtl838x_reg rtl838x_reg = {
|
|
.mask_port_reg_be = rtl838x_mask_port_reg,
|
|
.set_port_reg_be = rtl838x_set_port_reg,
|
|
@@ -295,6 +567,8 @@ const struct rtl838x_reg rtl838x_reg = {
|
|
.vlan_set_untagged = rtl838x_vlan_set_untagged,
|
|
.mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl,
|
|
.vlan_profile_dump = rtl838x_vlan_profile_dump,
|
|
+ .vlan_profile_setup = rtl838x_vlan_profile_setup,
|
|
+ .vlan_fwd_on_inner = rtl838x_vlan_fwd_on_inner,
|
|
.stp_get = rtl838x_stp_get,
|
|
.stp_set = rtl838x_stp_set,
|
|
.mac_port_ctrl = rtl838x_mac_port_ctrl,
|
|
@@ -309,7 +583,9 @@ const struct rtl838x_reg rtl838x_reg = {
|
|
.mac_rx_pause_sts = RTL838X_MAC_RX_PAUSE_STS,
|
|
.mac_tx_pause_sts = RTL838X_MAC_TX_PAUSE_STS,
|
|
.read_l2_entry_using_hash = rtl838x_read_l2_entry_using_hash,
|
|
+ .write_l2_entry_using_hash = rtl838x_write_l2_entry_using_hash,
|
|
.read_cam = rtl838x_read_cam,
|
|
+ .write_cam = rtl838x_write_cam,
|
|
.vlan_port_egr_filter = RTL838X_VLAN_PORT_EGR_FLTR,
|
|
.vlan_port_igr_filter = RTL838X_VLAN_PORT_IGR_FLTR(0),
|
|
.vlan_port_pb = RTL838X_VLAN_PORT_PB_VLAN,
|
|
@@ -317,6 +593,13 @@ const struct rtl838x_reg rtl838x_reg = {
|
|
.trk_mbr_ctr = rtl838x_trk_mbr_ctr,
|
|
.rma_bpdu_fld_pmask = RTL838X_RMA_BPDU_FLD_PMSK,
|
|
.spcl_trap_eapol_ctrl = RTL838X_SPCL_TRAP_EAPOL_CTRL,
|
|
+ .init_eee = rtl838x_init_eee,
|
|
+ .port_eee_set = rtl838x_port_eee_set,
|
|
+ .eee_port_ability = rtl838x_eee_port_ability,
|
|
+ .l2_hash_seed = rtl838x_l2_hash_seed,
|
|
+ .l2_hash_key = rtl838x_l2_hash_key,
|
|
+ .read_mcast_pmask = rtl838x_read_mcast_pmask,
|
|
+ .write_mcast_pmask = rtl838x_write_mcast_pmask,
|
|
};
|
|
|
|
irqreturn_t rtl838x_switch_irq(int irq, void *dev_id)
|
|
@@ -432,6 +715,79 @@ timeout:
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
+/*
|
|
+ * Read an mmd register of a PHY
|
|
+ */
|
|
+int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ mutex_lock(&smi_lock);
|
|
+
|
|
+ if (rtl838x_smi_wait_op(10000))
|
|
+ goto timeout;
|
|
+
|
|
+ sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
|
|
+ mdelay(10);
|
|
+
|
|
+ sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
|
|
+
|
|
+ v = addr << 16 | reg;
|
|
+ sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3);
|
|
+
|
|
+ /* mmd-access | read | cmd-start */
|
|
+ v = 1 << 1 | 0 << 2 | 1;
|
|
+ sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
|
|
+
|
|
+ if (rtl838x_smi_wait_op(10000))
|
|
+ goto timeout;
|
|
+
|
|
+ *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff;
|
|
+
|
|
+ mutex_unlock(&smi_lock);
|
|
+ return 0;
|
|
+
|
|
+timeout:
|
|
+ mutex_unlock(&smi_lock);
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Write to an mmd register of a PHY
|
|
+ */
|
|
+int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val);
|
|
+ val &= 0xffff;
|
|
+ mutex_lock(&smi_lock);
|
|
+
|
|
+ if (rtl838x_smi_wait_op(10000))
|
|
+ goto timeout;
|
|
+
|
|
+ sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
|
|
+ mdelay(10);
|
|
+
|
|
+ sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
|
|
+
|
|
+ sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3);
|
|
+ sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3);
|
|
+ /* mmd-access | write | cmd-start */
|
|
+ v = 1 << 1 | 1 << 2 | 1;
|
|
+ sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
|
|
+
|
|
+ if (rtl838x_smi_wait_op(10000))
|
|
+ goto timeout;
|
|
+
|
|
+ mutex_unlock(&smi_lock);
|
|
+ return 0;
|
|
+
|
|
+timeout:
|
|
+ mutex_unlock(&smi_lock);
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+
|
|
void rtl8380_get_version(struct rtl838x_switch_priv *priv)
|
|
{
|
|
u32 rw_save, info_save;
|
|
@@ -459,47 +815,18 @@ void rtl8380_get_version(struct rtl838x_switch_priv *priv)
|
|
}
|
|
}
|
|
|
|
-/*
|
|
- * Applies the same hash algorithm as the one used currently by the ASIC
|
|
- */
|
|
-u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed)
|
|
-{
|
|
- u32 h1, h2, h3, h;
|
|
-
|
|
- if (sw_r32(priv->r->l2_ctrl_0) & 1) {
|
|
- h1 = (seed >> 11) & 0x7ff;
|
|
- h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f);
|
|
-
|
|
- h2 = (seed >> 33) & 0x7ff;
|
|
- h2 = ((h2 & 0x3f) << 5) | ((h2 >> 6) & 0x1f);
|
|
-
|
|
- h3 = (seed >> 44) & 0x7ff;
|
|
- h3 = ((h3 & 0x7f) << 4) | ((h3 >> 7) & 0xf);
|
|
-
|
|
- h = h1 ^ h2 ^ h3 ^ ((seed >> 55) & 0x1ff);
|
|
- h ^= ((seed >> 22) & 0x7ff) ^ (seed & 0x7ff);
|
|
- } else {
|
|
- h = ((seed >> 55) & 0x1ff) ^ ((seed >> 44) & 0x7ff)
|
|
- ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff)
|
|
- ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff);
|
|
- }
|
|
-
|
|
- return h;
|
|
-}
|
|
-
|
|
-void rtl838x_vlan_profile_dump(int index)
|
|
+void rtl838x_vlan_profile_dump(int profile)
|
|
{
|
|
- u32 profile;
|
|
+ u32 p;
|
|
|
|
- if (index < 0 || index > 7)
|
|
+ if (profile < 0 || profile > 7)
|
|
return;
|
|
|
|
- profile = sw_r32(RTL838X_VLAN_PROFILE(index));
|
|
+ p = sw_r32(RTL838X_VLAN_PROFILE(profile));
|
|
|
|
- pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \
|
|
- IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x",
|
|
- index, profile & 1, (profile >> 1) & 0x1ff, (profile >> 10) & 0x1ff,
|
|
- (profile >> 19) & 0x1ff);
|
|
+ pr_info("VLAN profile %d: L2 learning: %d, UNKN L2MC FLD PMSK %d, \
|
|
+ UNKN IPMC FLD PMSK %d, UNKN IPv6MC FLD PMSK: %d",
|
|
+ profile, p & 1, (p >> 1) & 0x1ff, (p >> 10) & 0x1ff, (p >> 19) & 0x1ff);
|
|
}
|
|
|
|
void rtl8380_sds_rst(int mac)
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h
|
|
index d5ca153a10..b2097363b9 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h
|
|
@@ -162,6 +162,16 @@
|
|
#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04)
|
|
#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08)
|
|
|
|
+#define RTL839X_EEE_TX_TIMER_GELITE_CTRL (0x042C)
|
|
+#define RTL839X_EEE_TX_TIMER_GIGA_CTRL (0x0430)
|
|
+#define RTL839X_EEE_TX_TIMER_10G_CTRL (0x0434)
|
|
+#define RTL839X_EEE_CTRL(p) (0x8008 + ((p) << 7))
|
|
+#define RTL839X_MAC_EEE_ABLTY (0x03C8)
|
|
+
|
|
+#define RTL930X_MAC_EEE_ABLTY (0xCB34)
|
|
+#define RTL930X_EEE_CTRL(p) (0x3274 + ((p) << 6))
|
|
+#define RTL930X_EEEP_PORT_CTRL(p) (0x3278 + ((p) << 6))
|
|
+
|
|
/* L2 functionality */
|
|
#define RTL838X_L2_CTRL_0 (0x3200)
|
|
#define RTL839X_L2_CTRL_0 (0x3800)
|
|
@@ -206,6 +216,12 @@
|
|
#define RTL931X_RMA_BPDU_FLD_PMSK (0x8950)
|
|
#define RTL839X_RMA_BPDU_FLD_PMSK (0x125C)
|
|
|
|
+#define RTL838X_L2_PORT_LM_ACT(p) (0x3208 + ((p) << 2))
|
|
+#define RTL838X_VLAN_PORT_FWD (0x3A78)
|
|
+#define RTL839X_VLAN_PORT_FWD (0x27AC)
|
|
+#define RTL930X_VLAN_PORT_FWD (0x834C)
|
|
+#define RTL838X_VLAN_FID_CTRL (0x3aa8)
|
|
+
|
|
/* Port Mirroring */
|
|
#define RTL838X_MIR_CTRL (0x5D00)
|
|
#define RTL838X_MIR_DPM_CTRL (0x5D20)
|
|
@@ -322,8 +338,12 @@
|
|
/* Debug features */
|
|
#define RTL930X_STAT_PRVTE_DROP_COUNTER0 (0xB5B8)
|
|
|
|
+#define MAX_VLANS 4096
|
|
#define MAX_LAGS 16
|
|
#define MAX_PRIOS 8
|
|
+#define RTL930X_PORT_IGNORE 0x3f
|
|
+#define MAX_MC_GROUPS 512
|
|
+#define UNKNOWN_MC_PMASK (MAX_MC_GROUPS - 1)
|
|
|
|
enum phy_type {
|
|
PHY_NONE = 0,
|
|
@@ -379,8 +399,27 @@ struct rtl838x_l2_entry {
|
|
bool next_hop;
|
|
int age;
|
|
u8 trunk;
|
|
- u8 stackDev;
|
|
+ bool is_trunk;
|
|
+ u8 stack_dev;
|
|
u16 mc_portmask_index;
|
|
+ u32 mc_gip;
|
|
+ u32 mc_sip;
|
|
+ u16 mc_mac_index;
|
|
+ u16 nh_route_id;
|
|
+ bool nh_vlan_target; // Only RTL83xx: VLAN used for next hop
|
|
+};
|
|
+
|
|
+struct rtl838x_nexthop {
|
|
+ u16 id; // ID in HW Nexthop table
|
|
+ u32 ip; // IP Addres of nexthop
|
|
+ u32 dev_id;
|
|
+ u16 port;
|
|
+ u16 vid;
|
|
+ u16 fid;
|
|
+ u64 mac;
|
|
+ u16 mac_id;
|
|
+ u16 l2_id; // Index of this next hop forwarding entry in L2 FIB table
|
|
+ u16 if_id;
|
|
};
|
|
|
|
struct rtl838x_switch_priv;
|
|
@@ -416,6 +455,7 @@ struct rtl838x_reg {
|
|
void (*vlan_set_tagged)(u32 vlan, struct rtl838x_vlan_info *info);
|
|
void (*vlan_set_untagged)(u32 vlan, u64 portmask);
|
|
void (*vlan_profile_dump)(int index);
|
|
+ void (*vlan_profile_setup)(int profile);
|
|
void (*stp_get)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]);
|
|
void (*stp_set)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]);
|
|
int (*mac_force_mode_ctrl)(int port);
|
|
@@ -431,7 +471,9 @@ struct rtl838x_reg {
|
|
int mac_rx_pause_sts;
|
|
int mac_tx_pause_sts;
|
|
u64 (*read_l2_entry_using_hash)(u32 hash, u32 position, struct rtl838x_l2_entry *e);
|
|
+ void (*write_l2_entry_using_hash)(u32 hash, u32 pos, struct rtl838x_l2_entry *e);
|
|
u64 (*read_cam)(int idx, struct rtl838x_l2_entry *e);
|
|
+ void (*write_cam)(int idx, struct rtl838x_l2_entry *e);
|
|
int vlan_port_egr_filter;
|
|
int vlan_port_igr_filter;
|
|
int vlan_port_pb;
|
|
@@ -440,6 +482,15 @@ struct rtl838x_reg {
|
|
int (*trk_mbr_ctr)(int group);
|
|
int rma_bpdu_fld_pmask;
|
|
int spcl_trap_eapol_ctrl;
|
|
+ void (*init_eee)(struct rtl838x_switch_priv *priv, bool enable);
|
|
+ void (*port_eee_set)(struct rtl838x_switch_priv *priv, int port, bool enable);
|
|
+ int (*eee_port_ability)(struct rtl838x_switch_priv *priv,
|
|
+ struct ethtool_eee *e, int port);
|
|
+ u64 (*l2_hash_seed)(u64 mac, u32 vid);
|
|
+ u32 (*l2_hash_key)(struct rtl838x_switch_priv *priv, u64 seed);
|
|
+ u64 (*read_mcast_pmask)(int idx);
|
|
+ void (*write_mcast_pmask)(int idx, u64 portmask);
|
|
+ void (*vlan_fwd_on_inner)(int port, bool is_set);
|
|
};
|
|
|
|
struct rtl838x_switch_priv {
|
|
@@ -460,11 +511,14 @@ struct rtl838x_switch_priv {
|
|
u8 port_width;
|
|
u64 irq_mask;
|
|
u32 fib_entries;
|
|
+ int l2_bucket_size;
|
|
struct dentry *dbgfs_dir;
|
|
int n_lags;
|
|
u64 lags_port_members[MAX_LAGS];
|
|
struct net_device *lag_devs[MAX_LAGS];
|
|
struct notifier_block nb;
|
|
+ bool eee_enabled;
|
|
+ unsigned long int mc_group_bm[MAX_MC_GROUPS >> 5];
|
|
};
|
|
|
|
void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv);
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
|
|
index 5106bd2e9d..c62dc441c1 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
|
|
@@ -48,68 +48,120 @@ static inline int rtl839x_tbl_access_data_0(int i)
|
|
|
|
static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info)
|
|
{
|
|
- u32 cmd;
|
|
- u64 v;
|
|
- u32 u, w;
|
|
-
|
|
- cmd = 1 << 16 /* Execute cmd */
|
|
- | 0 << 15 /* Read */
|
|
- | 0 << 12 /* Table type 0b000 */
|
|
- | (vlan & 0xfff);
|
|
- rtl839x_exec_tbl0_cmd(cmd);
|
|
-
|
|
- v = sw_r32(RTL839X_TBL_ACCESS_DATA_0(0));
|
|
- v <<= 32;
|
|
- u = sw_r32(RTL839X_TBL_ACCESS_DATA_0(1));
|
|
- v |= u;
|
|
- info->tagged_ports = v >> 11;
|
|
-
|
|
- w = sw_r32(RTL839X_TBL_ACCESS_DATA_0(2));
|
|
-
|
|
- info->profile_id = w >> 30 | ((u & 1) << 2);
|
|
- info->hash_mc_fid = !!(u & 2);
|
|
- info->hash_uc_fid = !!(u & 4);
|
|
- info->fid = (u >> 3) & 0xff;
|
|
-
|
|
- cmd = 1 << 15 /* Execute cmd */
|
|
- | 0 << 14 /* Read */
|
|
- | 0 << 12 /* Table type 0b00 */
|
|
- | (vlan & 0xfff);
|
|
- rtl839x_exec_tbl1_cmd(cmd);
|
|
- v = sw_r32(RTL839X_TBL_ACCESS_DATA_1(0));
|
|
- v <<= 32;
|
|
- v |= sw_r32(RTL839X_TBL_ACCESS_DATA_1(1));
|
|
- info->untagged_ports = v >> 11;
|
|
+ u32 u, v, w;
|
|
+ // Read VLAN table (0) via register 0
|
|
+ struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 0);
|
|
+
|
|
+ rtl_table_read(r, vlan);
|
|
+ u = sw_r32(rtl_table_data(r, 0));
|
|
+ v = sw_r32(rtl_table_data(r, 1));
|
|
+ w = sw_r32(rtl_table_data(r, 2));
|
|
+ rtl_table_release(r);
|
|
+
|
|
+ info->tagged_ports = u;
|
|
+ info->tagged_ports = (info->tagged_ports << 21) | ((v >> 11) & 0x1fffff);
|
|
+ info->profile_id = w >> 30 | ((v & 1) << 2);
|
|
+ info->hash_mc_fid = !!(w & BIT(2));
|
|
+ info->hash_uc_fid = !!(w & BIT(3));
|
|
+ info->fid = (v >> 3) & 0xff;
|
|
+
|
|
+ // Read UNTAG table (0) via table register 1
|
|
+ r = rtl_table_get(RTL8390_TBL_1, 0);
|
|
+ rtl_table_read(r, vlan);
|
|
+ u = sw_r32(rtl_table_data(r, 0));
|
|
+ v = sw_r32(rtl_table_data(r, 1));
|
|
+ rtl_table_release(r);
|
|
+
|
|
+ info->untagged_ports = u;
|
|
+ info->untagged_ports = (info->untagged_ports << 21) | ((v >> 11) & 0x1fffff);
|
|
}
|
|
|
|
static void rtl839x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info)
|
|
{
|
|
- u32 cmd = BIT(16) /* Execute cmd */
|
|
- | BIT(15) /* Write */
|
|
- | 0 << 12 /* Table type 0b00 */
|
|
- | (vlan & 0xfff);
|
|
- u32 w;
|
|
- u64 v = info->tagged_ports << 11;
|
|
+ u32 u, v, w;
|
|
+ // Access VLAN table (0) via register 0
|
|
+ struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 0);
|
|
|
|
- v |= info->profile_id >> 2;
|
|
- v |= info->hash_mc_fid ? 2 : 0;
|
|
- v |= info->hash_uc_fid ? 4 : 0;
|
|
+ u = info->tagged_ports >> 21;
|
|
+ v = info->tagged_ports << 11;
|
|
v |= ((u32)info->fid) << 3;
|
|
- rtl839x_set_port_reg_be(v, RTL838X_TBL_ACCESS_DATA_0(0));
|
|
+ v |= info->hash_uc_fid ? BIT(2) : 0;
|
|
+ v |= info->hash_mc_fid ? BIT(1) : 0;
|
|
+ v |= (info->profile_id & 0x4) ? 1 : 0;
|
|
+ w = ((u32)(info->profile_id & 3)) << 30;
|
|
+
|
|
+ sw_w32(u, rtl_table_data(r, 0));
|
|
+ sw_w32(v, rtl_table_data(r, 1));
|
|
+ sw_w32(w, rtl_table_data(r, 2));
|
|
|
|
- w = info->profile_id;
|
|
- sw_w32(w << 30, RTL838X_TBL_ACCESS_DATA_0(2));
|
|
- rtl839x_exec_tbl0_cmd(cmd);
|
|
+ rtl_table_write(r, vlan);
|
|
+ rtl_table_release(r);
|
|
}
|
|
|
|
static void rtl839x_vlan_set_untagged(u32 vlan, u64 portmask)
|
|
{
|
|
- u32 cmd = BIT(16) /* Execute cmd */
|
|
- | BIT(15) /* Write */
|
|
- | 0 << 12 /* Table type 0b00 */
|
|
- | (vlan & 0xfff);
|
|
- rtl839x_set_port_reg_be(portmask << 11, RTL838X_TBL_ACCESS_DATA_1(0));
|
|
- rtl839x_exec_tbl1_cmd(cmd);
|
|
+ u32 u, v;
|
|
+
|
|
+ // Access UNTAG table (0) via table register 1
|
|
+ struct table_reg *r = rtl_table_get(RTL8390_TBL_1, 0);
|
|
+
|
|
+ u = portmask >> 21;
|
|
+ v = portmask << 11;
|
|
+
|
|
+ sw_w32(u, rtl_table_data(r, 0));
|
|
+ sw_w32(v, rtl_table_data(r, 1));
|
|
+ rtl_table_write(r, vlan);
|
|
+
|
|
+ rtl_table_release(r);
|
|
+}
|
|
+
|
|
+/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer
|
|
+ */
|
|
+static void rtl839x_vlan_fwd_on_inner(int port, bool is_set)
|
|
+{
|
|
+ if (is_set)
|
|
+ rtl839x_mask_port_reg_be(BIT_ULL(port), 0ULL, RTL839X_VLAN_PORT_FWD);
|
|
+ else
|
|
+ rtl839x_mask_port_reg_be(0ULL, BIT_ULL(port), RTL839X_VLAN_PORT_FWD);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Hash seed is vid (actually rvid) concatenated with the MAC address
|
|
+ */
|
|
+static u64 rtl839x_l2_hash_seed(u64 mac, u32 vid)
|
|
+{
|
|
+ u64 v = vid;
|
|
+
|
|
+ v <<= 48;
|
|
+ v |= mac;
|
|
+
|
|
+ return v;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Applies the same hash algorithm as the one used currently by the ASIC to the seed
|
|
+ * and returns a key into the L2 hash table
|
|
+ */
|
|
+static u32 rtl839x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed)
|
|
+{
|
|
+ u32 h1, h2, h;
|
|
+
|
|
+ if (sw_r32(priv->r->l2_ctrl_0) & 1) {
|
|
+ h1 = (u32) (((seed >> 60) & 0x3f) ^ ((seed >> 54) & 0x3f)
|
|
+ ^ ((seed >> 36) & 0x3f) ^ ((seed >> 30) & 0x3f)
|
|
+ ^ ((seed >> 12) & 0x3f) ^ ((seed >> 6) & 0x3f));
|
|
+ h2 = (u32) (((seed >> 48) & 0x3f) ^ ((seed >> 42) & 0x3f)
|
|
+ ^ ((seed >> 24) & 0x3f) ^ ((seed >> 18) & 0x3f)
|
|
+ ^ (seed & 0x3f));
|
|
+ h = (h1 << 6) | h2;
|
|
+ } else {
|
|
+ h = (seed >> 60)
|
|
+ ^ ((((seed >> 48) & 0x3f) << 6) | ((seed >> 54) & 0x3f))
|
|
+ ^ ((seed >> 36) & 0xfff) ^ ((seed >> 24) & 0xfff)
|
|
+ ^ ((seed >> 12) & 0xfff) ^ (seed & 0xfff);
|
|
+ }
|
|
+
|
|
+ return h;
|
|
}
|
|
|
|
static inline int rtl839x_mac_force_mode_ctrl(int p)
|
|
@@ -192,60 +244,174 @@ static void rtl839x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
|
|
}
|
|
}
|
|
|
|
-static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
|
|
+/*
|
|
+ * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry
|
|
+ */
|
|
+static void rtl839x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e)
|
|
{
|
|
- u64 entry;
|
|
- u32 r[3];
|
|
+ if (!e->valid) {
|
|
+ r[0] = r[1] = r[2] = 0;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ r[2] = e->is_ip_mc ? BIT(31) : 0;
|
|
+ r[2] |= e->is_ipv6_mc ? BIT(30) : 0;
|
|
+
|
|
+ if (!e->is_ip_mc && !e->is_ipv6_mc) {
|
|
+ r[0] = ((u32)e->mac[0]) << 12;
|
|
+ r[0] |= ((u32)e->mac[1]) << 4;
|
|
+ r[0] |= ((u32)e->mac[2]) >> 4;
|
|
+ r[1] = ((u32)e->mac[2]) << 28;
|
|
+ r[1] |= ((u32)e->mac[3]) << 20;
|
|
+ r[1] |= ((u32)e->mac[4]) << 12;
|
|
+ r[1] |= ((u32)e->mac[5]) << 4;
|
|
+
|
|
+ if (!(e->mac[0] & 1)) { // Not multicast
|
|
+ r[2] |= e->is_static ? BIT(18) : 0;
|
|
+ r[2] |= e->vid << 4;
|
|
+ r[0] |= ((u32)e->rvid) << 20;
|
|
+ r[2] |= e->port << 24;
|
|
+ r[2] |= e->block_da ? BIT(19) : 0;
|
|
+ r[2] |= e->block_sa ? BIT(20) : 0;
|
|
+ r[2] |= e->suspended ? BIT(17) : 0;
|
|
+ if (e->next_hop) {
|
|
+ r[2] |= BIT(16);
|
|
+ r[2] |= e->nh_vlan_target ? BIT(15) : 0;
|
|
+ r[2] |= (e->nh_route_id & 0x7ff) << 4;
|
|
+ }
|
|
+ r[2] |= ((u32)e->age) << 21;
|
|
+ } else { // L2 Multicast
|
|
+ r[0] |= ((u32)e->rvid) << 20;
|
|
+ r[2] |= ((u32)e->mc_portmask_index) << 6;
|
|
+ pr_debug("Write L2 MC entry: %08x %08x %08x\n", r[0], r[1], r[2]);
|
|
+ }
|
|
+ } else { // IPv4 or IPv6 MC entry
|
|
+ r[0] = ((u32)e->rvid) << 20;
|
|
+ r[2] |= ((u32)e->mc_portmask_index) << 6;
|
|
+ r[1] = e->mc_gip;
|
|
+ }
|
|
+}
|
|
|
|
- /* Search in SRAM, with hash and at position in hash bucket (0-3) */
|
|
- u32 idx = (0 << 14) | (hash << 2) | position;
|
|
+/*
|
|
+ * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table
|
|
+ * hash is the id of the bucket and pos is the position of the entry in that bucket
|
|
+ * The data read from the SoC is filled into rtl838x_l2_entry
|
|
+ */
|
|
+static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 0);
|
|
+ u32 idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket
|
|
+ int i;
|
|
|
|
- u32 cmd = 1 << 17 /* Execute cmd */
|
|
- | 0 << 16 /* Read */
|
|
- | 0 << 14 /* Table type 0b00 */
|
|
- | (idx & 0x3fff);
|
|
+ rtl_table_read(q, idx);
|
|
+ for (i= 0; i < 3; i++)
|
|
+ r[i] = sw_r32(rtl_table_data(q, i));
|
|
|
|
- sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL);
|
|
- do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17));
|
|
- r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0));
|
|
- r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1));
|
|
- r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2));
|
|
+ rtl_table_release(q);
|
|
|
|
rtl839x_fill_l2_entry(r, e);
|
|
+ if (!e->valid)
|
|
+ return 0;
|
|
|
|
- entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff);
|
|
- return entry;
|
|
+ return rtl839x_l2_hash_seed(ether_addr_to_u64(&e->mac[0]), e->rvid);
|
|
+}
|
|
+
|
|
+static void rtl839x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 0);
|
|
+ int i;
|
|
+
|
|
+ u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket
|
|
+
|
|
+ rtl839x_fill_l2_row(r, e);
|
|
+
|
|
+ for (i= 0; i < 3; i++)
|
|
+ sw_w32(r[i], rtl_table_data(q, i));
|
|
+
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
}
|
|
|
|
static u64 rtl839x_read_cam(int idx, struct rtl838x_l2_entry *e)
|
|
{
|
|
- u64 entry;
|
|
u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 1); // Access L2 Table 1
|
|
+ int i;
|
|
|
|
- u32 cmd = 1 << 17 /* Execute cmd */
|
|
- | 0 << 16 /* Read */
|
|
- | 1 << 14 /* Table type 0b01 */
|
|
- | (idx & 0x3f);
|
|
- sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL);
|
|
- do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17));
|
|
- r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0));
|
|
- r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1));
|
|
- r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2));
|
|
+ rtl_table_read(q, idx);
|
|
+ for (i= 0; i < 3; i++)
|
|
+ r[i] = sw_r32(rtl_table_data(q, i));
|
|
|
|
+ rtl_table_release(q);
|
|
|
|
rtl839x_fill_l2_entry(r, e);
|
|
- if (e->valid)
|
|
- pr_info("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
|
|
- else
|
|
+ if (!e->valid)
|
|
return 0;
|
|
|
|
- entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff);
|
|
- return entry;
|
|
+ pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
|
|
+
|
|
+ // Return MAC with concatenated VID ac concatenated ID
|
|
+ return rtl839x_l2_hash_seed(ether_addr_to_u64(&e->mac[0]), e->rvid);
|
|
+}
|
|
+
|
|
+static void rtl839x_write_cam(int idx, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 1); // Access L2 Table 1
|
|
+ int i;
|
|
+
|
|
+ rtl839x_fill_l2_row(r, e);
|
|
+
|
|
+ for (i= 0; i < 3; i++)
|
|
+ sw_w32(r[i], rtl_table_data(q, i));
|
|
+
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
}
|
|
|
|
-static inline int rtl839x_vlan_profile(int profile)
|
|
+static u64 rtl839x_read_mcast_pmask(int idx)
|
|
{
|
|
- return RTL839X_VLAN_PROFILE(profile);
|
|
+ u64 portmask;
|
|
+ // Read MC_PMSK (2) via register RTL8390_TBL_L2
|
|
+ struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 2);
|
|
+
|
|
+ rtl_table_read(q, idx);
|
|
+ portmask = sw_r32(rtl_table_data(q, 0));
|
|
+ portmask <<= 32;
|
|
+ portmask |= sw_r32(rtl_table_data(q, 1));
|
|
+ portmask >>= 11; // LSB is bit 11 in data registers
|
|
+ rtl_table_release(q);
|
|
+
|
|
+ return portmask;
|
|
+}
|
|
+
|
|
+static void rtl839x_write_mcast_pmask(int idx, u64 portmask)
|
|
+{
|
|
+ // Access MC_PMSK (2) via register RTL8380_TBL_L2
|
|
+ struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 2);
|
|
+
|
|
+ portmask <<= 11; // LSB is bit 11 in data registers
|
|
+ sw_w32((u32)(portmask >> 32), rtl_table_data(q, 0));
|
|
+ sw_w32((u32)((portmask & 0xfffff800)), rtl_table_data(q, 1));
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
+}
|
|
+
|
|
+static void rtl839x_vlan_profile_setup(int profile)
|
|
+{
|
|
+ u32 p[2];
|
|
+ u32 pmask_id = UNKNOWN_MC_PMASK;
|
|
+
|
|
+ p[0] = pmask_id; // Use portmaks 0xfff for unknown IPv6 MC flooding
|
|
+ // Enable L2 Learning BIT 0, portmask UNKNOWN_MC_PMASK for IP/L2-MC traffic flooding
|
|
+ p[1] = 1 | pmask_id << 1 | pmask_id << 13;
|
|
+
|
|
+ sw_w32(p[0], RTL839X_VLAN_PROFILE(profile));
|
|
+ sw_w32(p[1], RTL839X_VLAN_PROFILE(profile) + 4);
|
|
+
|
|
+ rtl839x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0x001fffffffffffff);
|
|
}
|
|
|
|
static inline int rtl839x_vlan_port_egr_filter(int port)
|
|
@@ -275,7 +441,7 @@ void rtl839x_traffic_enable(int source, int dest)
|
|
|
|
void rtl839x_traffic_disable(int source, int dest)
|
|
{
|
|
- rtl839x_mask_port_reg_be(BIT(dest), 0, rtl839x_port_iso_ctrl(source));
|
|
+ rtl839x_mask_port_reg_be(BIT_ULL(dest), 0, rtl839x_port_iso_ctrl(source));
|
|
}
|
|
|
|
irqreturn_t rtl839x_switch_irq(int irq, void *dev_id)
|
|
@@ -290,10 +456,10 @@ irqreturn_t rtl839x_switch_irq(int irq, void *dev_id)
|
|
rtl839x_set_port_reg_le(ports, RTL839X_ISR_PORT_LINK_STS_CHG);
|
|
pr_debug("RTL8390 Link change: status: %x, ports %llx\n", status, ports);
|
|
|
|
- for (i = 0; i < 52; i++) {
|
|
- if (ports & (1ULL << i)) {
|
|
+ for (i = 0; i < RTL839X_CPU_PORT; i++) {
|
|
+ if (ports & BIT_ULL(i)) {
|
|
link = rtl839x_get_port_reg_le(RTL839X_MAC_LINK_STS);
|
|
- if (link & (1ULL << i))
|
|
+ if (link & BIT_ULL(i))
|
|
dsa_port_phylink_mac_change(ds, i, true);
|
|
else
|
|
dsa_port_phylink_mac_change(ds, i, false);
|
|
@@ -358,10 +524,9 @@ int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val)
|
|
return -ENOTSUPP;
|
|
|
|
mutex_lock(&smi_lock);
|
|
- /* Clear both port registers */
|
|
- sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0));
|
|
- sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0) + 4);
|
|
- sw_w32_mask(0, BIT(port), RTL839X_PHYREG_PORT_CTRL(port));
|
|
+
|
|
+ // Set PHY to access
|
|
+ rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL);
|
|
|
|
sw_w32_mask(0xffff0000, val << 16, RTL839X_PHYREG_DATA_CTRL);
|
|
|
|
@@ -383,6 +548,68 @@ int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val)
|
|
return err;
|
|
}
|
|
|
|
+/*
|
|
+ * Read an mmd register of the PHY
|
|
+ */
|
|
+int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
|
|
+{
|
|
+ int err = 0;
|
|
+ u32 v;
|
|
+
|
|
+ mutex_lock(&smi_lock);
|
|
+
|
|
+ // Set PHY to access
|
|
+ sw_w32_mask(0xffff << 16, port << 16, RTL839X_PHYREG_DATA_CTRL);
|
|
+
|
|
+ // Set MMD device number and register to write to
|
|
+ sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL);
|
|
+
|
|
+ v = BIT(2) | BIT(0); // MMD-access | EXEC
|
|
+ sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL);
|
|
+
|
|
+ do {
|
|
+ v = sw_r32(RTL839X_PHYREG_ACCESS_CTRL);
|
|
+ } while (v & BIT(0));
|
|
+ // There is no error-checking via BIT 1 of v, as it does not seem to be set correctly
|
|
+ *val = (sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff);
|
|
+ pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err);
|
|
+
|
|
+ mutex_unlock(&smi_lock);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Write to an mmd register of the PHY
|
|
+ */
|
|
+int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val)
|
|
+{
|
|
+ int err = 0;
|
|
+ u32 v;
|
|
+
|
|
+ mutex_lock(&smi_lock);
|
|
+
|
|
+ // Set PHY to access
|
|
+ rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL);
|
|
+
|
|
+ // Set data to write
|
|
+ sw_w32_mask(0xffff << 16, val << 16, RTL839X_PHYREG_DATA_CTRL);
|
|
+
|
|
+ // Set MMD device number and register to write to
|
|
+ sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL);
|
|
+
|
|
+ v = BIT(3) | BIT(2) | BIT(0); // WRITE | MMD-access | EXEC
|
|
+ sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL);
|
|
+
|
|
+ do {
|
|
+ v = sw_r32(RTL839X_PHYREG_ACCESS_CTRL);
|
|
+ } while (v & BIT(0));
|
|
+
|
|
+ pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err);
|
|
+ mutex_unlock(&smi_lock);
|
|
+ return err;
|
|
+}
|
|
+
|
|
void rtl8390_get_version(struct rtl838x_switch_priv *priv)
|
|
{
|
|
u32 info;
|
|
@@ -393,42 +620,21 @@ void rtl8390_get_version(struct rtl838x_switch_priv *priv)
|
|
priv->version = RTL8390_VERSION_A;
|
|
}
|
|
|
|
-u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed)
|
|
+void rtl839x_vlan_profile_dump(int profile)
|
|
{
|
|
- u32 h1, h2, h;
|
|
+ u32 p[2];
|
|
|
|
- if (sw_r32(priv->r->l2_ctrl_0) & 1) {
|
|
- h1 = (u32) (((seed >> 60) & 0x3f) ^ ((seed >> 54) & 0x3f)
|
|
- ^ ((seed >> 36) & 0x3f) ^ ((seed >> 30) & 0x3f)
|
|
- ^ ((seed >> 12) & 0x3f) ^ ((seed >> 6) & 0x3f));
|
|
- h2 = (u32) (((seed >> 48) & 0x3f) ^ ((seed >> 42) & 0x3f)
|
|
- ^ ((seed >> 24) & 0x3f) ^ ((seed >> 18) & 0x3f)
|
|
- ^ (seed & 0x3f));
|
|
- h = (h1 << 6) | h2;
|
|
- } else {
|
|
- h = (seed >> 60)
|
|
- ^ ((((seed >> 48) & 0x3f) << 6) | ((seed >> 54) & 0x3f))
|
|
- ^ ((seed >> 36) & 0xfff) ^ ((seed >> 24) & 0xfff)
|
|
- ^ ((seed >> 12) & 0xfff) ^ (seed & 0xfff);
|
|
- }
|
|
-
|
|
- return h;
|
|
-}
|
|
-
|
|
-void rtl839x_vlan_profile_dump(int index)
|
|
-{
|
|
- u32 profile, profile1;
|
|
-
|
|
- if (index < 0 || index > 7)
|
|
+ if (profile < 0 || profile > 7)
|
|
return;
|
|
|
|
- profile1 = sw_r32(RTL839X_VLAN_PROFILE(index) + 4);
|
|
- profile = sw_r32(RTL839X_VLAN_PROFILE(index));
|
|
+ p[0] = sw_r32(RTL839X_VLAN_PROFILE(profile));
|
|
+ p[1] = sw_r32(RTL839X_VLAN_PROFILE(profile) + 4);
|
|
|
|
- pr_debug("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \
|
|
- IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x",
|
|
- index, profile & 1, (profile >> 1) & 0xfff, (profile >> 13) & 0xfff,
|
|
- (profile1) & 0xfff);
|
|
+ pr_info("VLAN profile %d: L2 learning: %d, UNKN L2MC FLD PMSK %d, \
|
|
+ UNKN IPMC FLD PMSK %d, UNKN IPv6MC FLD PMSK: %d",
|
|
+ profile, p[1] & 1, (p[1] >> 1) & 0xfff, (p[1] >> 13) & 0xfff,
|
|
+ (p[0]) & 0xfff);
|
|
+ pr_info("VLAN profile %d: raw %08x, %08x\n", profile, p[0], p[1]);
|
|
}
|
|
|
|
static void rtl839x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
|
|
@@ -456,6 +662,83 @@ static void rtl839x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port
|
|
priv->r->exec_tbl0_cmd(cmd);
|
|
}
|
|
|
|
+/*
|
|
+ * Enables or disables the EEE/EEEP capability of a port
|
|
+ */
|
|
+void rtl839x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ // This works only for Ethernet ports, and on the RTL839X, ports above 47 are SFP
|
|
+ if (port >= 48)
|
|
+ return;
|
|
+
|
|
+ enable = true;
|
|
+ pr_debug("In %s: setting port %d to %d\n", __func__, port, enable);
|
|
+ v = enable ? 0xf : 0x0;
|
|
+
|
|
+ // Set EEE for 100, 500, 1000MBit and 10GBit
|
|
+ sw_w32_mask(0xf << 8, v << 8, rtl839x_mac_force_mode_ctrl(port));
|
|
+
|
|
+ // Set TX/RX EEE state
|
|
+ v = enable ? 0x3 : 0x0;
|
|
+ sw_w32(v, RTL839X_EEE_CTRL(port));
|
|
+
|
|
+ priv->ports[port].eee_enabled = enable;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get EEE own capabilities and negotiation result
|
|
+ */
|
|
+int rtl839x_eee_port_ability(struct rtl838x_switch_priv *priv, struct ethtool_eee *e, int port)
|
|
+{
|
|
+ u64 link, a;
|
|
+
|
|
+ if (port >= 48)
|
|
+ return 0;
|
|
+
|
|
+ link = rtl839x_get_port_reg_le(RTL839X_MAC_LINK_STS);
|
|
+ if (!(link & BIT_ULL(port)))
|
|
+ return 0;
|
|
+
|
|
+ if (sw_r32(rtl839x_mac_force_mode_ctrl(port)) & BIT(8))
|
|
+ e->advertised |= ADVERTISED_100baseT_Full;
|
|
+
|
|
+ if (sw_r32(rtl839x_mac_force_mode_ctrl(port)) & BIT(10))
|
|
+ e->advertised |= ADVERTISED_1000baseT_Full;
|
|
+
|
|
+ a = rtl839x_get_port_reg_le(RTL839X_MAC_EEE_ABLTY);
|
|
+ pr_info("Link partner: %016llx\n", a);
|
|
+ if (rtl839x_get_port_reg_le(RTL839X_MAC_EEE_ABLTY) & BIT_ULL(port)) {
|
|
+ e->lp_advertised = ADVERTISED_100baseT_Full;
|
|
+ e->lp_advertised |= ADVERTISED_1000baseT_Full;
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rtl839x_init_eee(struct rtl838x_switch_priv *priv, bool enable)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ pr_info("Setting up EEE, state: %d\n", enable);
|
|
+
|
|
+ // Set wake timer for TX and pause timer both to 0x21
|
|
+ sw_w32_mask(0xff << 20| 0xff, 0x21 << 20| 0x21, RTL839X_EEE_TX_TIMER_GELITE_CTRL);
|
|
+ // Set pause wake timer for GIGA-EEE to 0x11
|
|
+ sw_w32_mask(0xff << 20, 0x11 << 20, RTL839X_EEE_TX_TIMER_GIGA_CTRL);
|
|
+ // Set pause wake timer for 10GBit ports to 0x11
|
|
+ sw_w32_mask(0xff << 20, 0x11 << 20, RTL839X_EEE_TX_TIMER_10G_CTRL);
|
|
+
|
|
+ // Setup EEE on all ports
|
|
+ for (i = 0; i < priv->cpu_port; i++) {
|
|
+ if (priv->ports[i].phy)
|
|
+ rtl839x_port_eee_set(priv, i, enable);
|
|
+ }
|
|
+ priv->eee_enabled = enable;
|
|
+}
|
|
+
|
|
const struct rtl838x_reg rtl839x_reg = {
|
|
.mask_port_reg_be = rtl839x_mask_port_reg_be,
|
|
.set_port_reg_be = rtl839x_set_port_reg_be,
|
|
@@ -487,6 +770,8 @@ const struct rtl838x_reg rtl839x_reg = {
|
|
.vlan_set_tagged = rtl839x_vlan_set_tagged,
|
|
.vlan_set_untagged = rtl839x_vlan_set_untagged,
|
|
.vlan_profile_dump = rtl839x_vlan_profile_dump,
|
|
+ .vlan_profile_setup = rtl839x_vlan_profile_setup,
|
|
+ .vlan_fwd_on_inner = rtl839x_vlan_fwd_on_inner,
|
|
.stp_get = rtl839x_stp_get,
|
|
.stp_set = rtl839x_stp_set,
|
|
.mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl,
|
|
@@ -502,7 +787,9 @@ const struct rtl838x_reg rtl839x_reg = {
|
|
.mac_rx_pause_sts = RTL839X_MAC_RX_PAUSE_STS,
|
|
.mac_tx_pause_sts = RTL839X_MAC_TX_PAUSE_STS,
|
|
.read_l2_entry_using_hash = rtl839x_read_l2_entry_using_hash,
|
|
+ .write_l2_entry_using_hash = rtl839x_write_l2_entry_using_hash,
|
|
.read_cam = rtl839x_read_cam,
|
|
+ .write_cam = rtl839x_write_cam,
|
|
.vlan_port_egr_filter = RTL839X_VLAN_PORT_EGR_FLTR(0),
|
|
.vlan_port_igr_filter = RTL839X_VLAN_PORT_IGR_FLTR(0),
|
|
.vlan_port_pb = RTL839X_VLAN_PORT_PB_VLAN,
|
|
@@ -510,4 +797,11 @@ const struct rtl838x_reg rtl839x_reg = {
|
|
.trk_mbr_ctr = rtl839x_trk_mbr_ctr,
|
|
.rma_bpdu_fld_pmask = RTL839X_RMA_BPDU_FLD_PMSK,
|
|
.spcl_trap_eapol_ctrl = RTL839X_SPCL_TRAP_EAPOL_CTRL,
|
|
+ .init_eee = rtl839x_init_eee,
|
|
+ .port_eee_set = rtl839x_port_eee_set,
|
|
+ .eee_port_ability = rtl839x_eee_port_ability,
|
|
+ .l2_hash_seed = rtl839x_l2_hash_seed,
|
|
+ .l2_hash_key = rtl839x_l2_hash_key,
|
|
+ .read_mcast_pmask = rtl839x_read_mcast_pmask,
|
|
+ .write_mcast_pmask = rtl839x_write_mcast_pmask,
|
|
};
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
|
|
index 59c283903b..f1de39f0bc 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
|
|
@@ -54,7 +54,7 @@ inline static int rtl930x_trk_mbr_ctr(int group)
|
|
static void rtl930x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info)
|
|
{
|
|
u32 v, w;
|
|
- // Read VLAN table (0) via register 0
|
|
+ // Read VLAN table (1) via register 0
|
|
struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 1);
|
|
|
|
rtl_table_read(r, vlan);
|
|
@@ -99,22 +99,28 @@ static void rtl930x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info)
|
|
rtl_table_release(r);
|
|
}
|
|
|
|
-void rtl930x_vlan_profile_dump(int index)
|
|
+void rtl930x_vlan_profile_dump(int profile)
|
|
{
|
|
- u32 profile[5];
|
|
+ u32 p[5];
|
|
|
|
- if (index < 0 || index > 7)
|
|
+ if (profile < 0 || profile > 7)
|
|
return;
|
|
|
|
- profile[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(index));
|
|
- profile[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 4);
|
|
- profile[2] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 8) & 0x1FFFFFFF;
|
|
- profile[3] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 12) & 0x1FFFFFFF;
|
|
- profile[4] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 16) & 0x1FFFFFFF;
|
|
-
|
|
- pr_debug("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \
|
|
- IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x",
|
|
- index, profile[0] & (3 << 21), profile[2], profile[3], profile[4]);
|
|
+ p[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile));
|
|
+ p[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 4);
|
|
+ p[2] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 8) & 0x1FFFFFFF;
|
|
+ p[3] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 12) & 0x1FFFFFFF;
|
|
+ p[4] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 16) & 0x1FFFFFFF;
|
|
+
|
|
+ pr_info("VLAN %d: L2 learn: %d; Unknown MC PMasks: L2 %0x, IPv4 %0x, IPv6: %0x",
|
|
+ profile, p[0] & (3 << 21), p[2], p[3], p[4]);
|
|
+ pr_info(" Routing enabled: IPv4 UC %c, IPv6 UC %c, IPv4 MC %c, IPv6 MC %c\n",
|
|
+ p[0] & BIT(17) ? 'y' : 'n', p[0] & BIT(16) ? 'y' : 'n',
|
|
+ p[0] & BIT(13) ? 'y' : 'n', p[0] & BIT(12) ? 'y' : 'n');
|
|
+ pr_info(" Bridge enabled: IPv4 MC %c, IPv6 MC %c,\n",
|
|
+ p[0] & BIT(15) ? 'y' : 'n', p[0] & BIT(14) ? 'y' : 'n');
|
|
+ pr_info("VLAN profile %d: raw %08x %08x %08x %08x %08x\n",
|
|
+ profile, p[0], p[1], p[2], p[3], p[4]);
|
|
}
|
|
|
|
static void rtl930x_vlan_set_untagged(u32 vlan, u64 portmask)
|
|
@@ -126,6 +132,39 @@ static void rtl930x_vlan_set_untagged(u32 vlan, u64 portmask)
|
|
rtl_table_release(r);
|
|
}
|
|
|
|
+/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer
|
|
+ */
|
|
+static void rtl930x_vlan_fwd_on_inner(int port, bool is_set)
|
|
+{
|
|
+ // Always set all tag modes to fwd based on either inner or outer tag
|
|
+ if (is_set)
|
|
+ sw_w32_mask(0, 0xf, RTL930X_VLAN_PORT_FWD + (port << 2));
|
|
+ else
|
|
+ sw_w32_mask(0xf, 0, RTL930X_VLAN_PORT_FWD + (port << 2));
|
|
+}
|
|
+
|
|
+static void rtl930x_vlan_profile_setup(int profile)
|
|
+{
|
|
+ u32 p[5];
|
|
+
|
|
+ pr_info("In %s\n", __func__);
|
|
+ p[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile));
|
|
+ p[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 4);
|
|
+
|
|
+ // Enable routing of Ipv4/6 Unicast and IPv4/6 Multicast traffic
|
|
+ p[0] |= BIT(17) | BIT(16) | BIT(13) | BIT(12);
|
|
+ p[2] = 0x1fffffff; // L2 unknown MC flooding portmask all ports, including the CPU-port
|
|
+ p[3] = 0x1fffffff; // IPv4 unknown MC flooding portmask
|
|
+ p[4] = 0x1fffffff; // IPv6 unknown MC flooding portmask
|
|
+
|
|
+ sw_w32(p[0], RTL930X_VLAN_PROFILE_SET(profile));
|
|
+ sw_w32(p[1], RTL930X_VLAN_PROFILE_SET(profile) + 4);
|
|
+ sw_w32(p[2], RTL930X_VLAN_PROFILE_SET(profile) + 8);
|
|
+ sw_w32(p[3], RTL930X_VLAN_PROFILE_SET(profile) + 12);
|
|
+ sw_w32(p[4], RTL930X_VLAN_PROFILE_SET(profile) + 16);
|
|
+ pr_info("Leaving %s\n", __func__);
|
|
+}
|
|
+
|
|
static void rtl930x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
|
|
{
|
|
int i;
|
|
@@ -168,12 +207,74 @@ static inline int rtl930x_mac_link_spd_sts(int p)
|
|
return RTL930X_MAC_LINK_SPD_STS(p);
|
|
}
|
|
|
|
+static u64 rtl930x_l2_hash_seed(u64 mac, u32 vid)
|
|
+{
|
|
+ u64 v = vid;
|
|
+
|
|
+ v <<= 48;
|
|
+ v |= mac;
|
|
+
|
|
+ return v;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Calculate both the block 0 and the block 1 hash by applyingthe same hash
|
|
+ * algorithm as the one used currently by the ASIC to the seed, and return
|
|
+ * both hashes in the lower and higher word of the return value since only 12 bit of
|
|
+ * the hash are significant
|
|
+ */
|
|
+static u32 rtl930x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed)
|
|
+{
|
|
+ u32 k0, k1, h1, h2, h;
|
|
+
|
|
+ k0 = (u32) (((seed >> 55) & 0x1f) ^ ((seed >> 44) & 0x7ff)
|
|
+ ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff)
|
|
+ ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff));
|
|
+
|
|
+ h1 = (seed >> 11) & 0x7ff;
|
|
+ h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f);
|
|
+
|
|
+ h2 = (seed >> 33) & 0x7ff;
|
|
+ h2 = ((h2 & 0x3f) << 5)| ((h2 >> 6) & 0x3f);
|
|
+
|
|
+ k1 = (u32) (((seed << 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) ^ h2
|
|
+ ^ ((seed >> 22) & 0x7ff) ^ h1
|
|
+ ^ (seed & 0x7ff));
|
|
+
|
|
+ // Algorithm choice for block 0
|
|
+ if (sw_r32(RTL930X_L2_CTRL) & BIT(0))
|
|
+ h = k1;
|
|
+ else
|
|
+ h = k0;
|
|
+
|
|
+ /* Algorithm choice for block 1
|
|
+ * Since k0 and k1 are < 2048, adding 2048 will offset the hash into the second
|
|
+ * half of hash-space
|
|
+ * 2048 is in fact the hash-table size 16384 divided by 4 hashes per bucket
|
|
+ * divided by 2 to divide the hash space in 2
|
|
+ */
|
|
+ if (sw_r32(RTL930X_L2_CTRL) & BIT(1))
|
|
+ h |= (k1 + 2048) << 16;
|
|
+ else
|
|
+ h |= (k0 + 2048) << 16;
|
|
+
|
|
+ return h;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fills an L2 entry structure from the SoC registers
|
|
+ */
|
|
static void rtl930x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
|
|
{
|
|
+ pr_debug("In %s valid?\n", __func__);
|
|
e->valid = !!(r[2] & BIT(31));
|
|
if (!e->valid)
|
|
return;
|
|
|
|
+ pr_debug("In %s is valid\n", __func__);
|
|
+ e->is_ip_mc = false;
|
|
+ e->is_ipv6_mc = false;
|
|
+
|
|
// TODO: Is there not a function to copy directly MAC memory?
|
|
e->mac[0] = (r[0] >> 24);
|
|
e->mac[1] = (r[0] >> 16);
|
|
@@ -182,61 +283,164 @@ static void rtl930x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
|
|
e->mac[4] = (r[1] >> 24);
|
|
e->mac[5] = (r[1] >> 16);
|
|
|
|
+ e->next_hop = !!(r[2] & BIT(12));
|
|
+ e->rvid = r[1] & 0xfff;
|
|
+
|
|
/* Is it a unicast entry? check multicast bit */
|
|
if (!(e->mac[0] & 1)) {
|
|
e->type = L2_UNICAST;
|
|
e->is_static = !!(r[2] & BIT(14));
|
|
- e->vid = r[2] & 0xfff;
|
|
- e->rvid = r[1] & 0xfff;
|
|
e->port = (r[2] >> 20) & 0x3ff;
|
|
// Check for trunk port
|
|
if (r[2] & BIT(30)) {
|
|
- e->stackDev = (e->port >> 9) & 1;
|
|
+ e->is_trunk = true;
|
|
+ e->stack_dev = (e->port >> 9) & 1;
|
|
e->trunk = e->port & 0x3f;
|
|
} else {
|
|
- e->stackDev = (e->port >> 6) & 0xf;
|
|
+ e->is_trunk = false;
|
|
+ e->stack_dev = (e->port >> 6) & 0xf;
|
|
e->port = e->port & 0x3f;
|
|
}
|
|
|
|
e->block_da = !!(r[2] & BIT(15));
|
|
e->block_sa = !!(r[2] & BIT(16));
|
|
e->suspended = !!(r[2] & BIT(13));
|
|
- e->next_hop = !!(r[2] & BIT(12));
|
|
e->age = (r[2] >> 17) & 3;
|
|
e->valid = true;
|
|
-
|
|
+ // the UC_VID field in hardware is used for the VID or for the route id
|
|
+ if (e->next_hop) {
|
|
+ e->nh_route_id = r[2] & 0xfff;
|
|
+ e->vid = 0;
|
|
+ } else {
|
|
+ e->vid = r[2] & 0xfff;
|
|
+ e->nh_route_id = 0;
|
|
+ }
|
|
} else {
|
|
e->valid = true;
|
|
e->type = L2_MULTICAST;
|
|
- e->mc_portmask_index = (r[2]>>6) & 0xfff;
|
|
+ e->mc_portmask_index = (r[2] >> 16) & 0x3ff;
|
|
}
|
|
}
|
|
|
|
-static u64 rtl930x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
|
|
+/*
|
|
+ * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry
|
|
+ */
|
|
+static void rtl930x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u32 port;
|
|
+
|
|
+ if (!e->valid) {
|
|
+ r[0] = r[1] = r[2] = 0;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ r[2] = BIT(31); // Set valid bit
|
|
+
|
|
+ r[0] = ((u32)e->mac[0]) << 24 | ((u32)e->mac[1]) << 16
|
|
+ | ((u32)e->mac[2]) << 8 | ((u32)e->mac[3]);
|
|
+ r[1] = ((u32)e->mac[4]) << 24 | ((u32)e->mac[5]) << 16;
|
|
+
|
|
+ r[2] |= e->next_hop ? BIT(12) : 0;
|
|
+
|
|
+ if (e->type == L2_UNICAST) {
|
|
+ r[2] |= e->is_static ? BIT(14) : 0;
|
|
+ r[1] |= e->rvid & 0xfff;
|
|
+ r[2] |= (e->port & 0x3ff) << 20;
|
|
+ if (e->is_trunk) {
|
|
+ r[2] |= BIT(30);
|
|
+ port = e->stack_dev << 9 | (e->port & 0x3f);
|
|
+ } else {
|
|
+ port = (e->stack_dev & 0xf) << 6;
|
|
+ port |= e->port & 0x3f;
|
|
+ }
|
|
+ r[2] |= port << 20;
|
|
+ r[2] |= e->block_da ? BIT(15) : 0;
|
|
+ r[2] |= e->block_sa ? BIT(17) : 0;
|
|
+ r[2] |= e->suspended ? BIT(13) : 0;
|
|
+ r[2] |= (e->age & 0x3) << 17;
|
|
+ // the UC_VID field in hardware is used for the VID or for the route id
|
|
+ if (e->next_hop)
|
|
+ r[2] |= e->nh_route_id & 0xfff;
|
|
+ else
|
|
+ r[2] |= e->vid & 0xfff;
|
|
+ } else { // L2_MULTICAST
|
|
+ r[2] |= (e->mc_portmask_index & 0x3ff) << 16;
|
|
+ r[2] |= e->mc_mac_index & 0x7ff;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table
|
|
+ * hash is the id of the bucket and pos is the position of the entry in that bucket
|
|
+ * The data read from the SoC is filled into rtl838x_l2_entry
|
|
+ */
|
|
+static u64 rtl930x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
|
|
{
|
|
- u64 entry;
|
|
u32 r[3];
|
|
struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 0);
|
|
- u32 idx = (0 << 14) | (hash << 2) | position;
|
|
+ u32 idx;
|
|
int i;
|
|
+ u64 mac;
|
|
+ u64 seed;
|
|
+
|
|
+ pr_debug("%s: hash %08x, pos: %d\n", __func__, hash, pos);
|
|
+
|
|
+ /* On the RTL93xx, 2 different hash algorithms are used making it a total of
|
|
+ * 8 buckets that need to be searched, 4 for each hash-half
|
|
+ * Use second hash space when bucket is between 4 and 8 */
|
|
+ if (pos >= 4) {
|
|
+ pos -= 4;
|
|
+ hash >>= 16;
|
|
+ } else {
|
|
+ hash &= 0xffff;
|
|
+ }
|
|
+
|
|
+ idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket
|
|
+ pr_debug("%s: NOW hash %08x, pos: %d\n", __func__, hash, pos);
|
|
|
|
rtl_table_read(q, idx);
|
|
- for (i= 0; i < 3; i++)
|
|
+ for (i = 0; i < 3; i++)
|
|
r[i] = sw_r32(rtl_table_data(q, i));
|
|
|
|
rtl_table_release(q);
|
|
|
|
rtl930x_fill_l2_entry(r, e);
|
|
+
|
|
+ pr_debug("%s: valid: %d, nh: %d\n", __func__, e->valid, e->next_hop);
|
|
if (!e->valid)
|
|
return 0;
|
|
|
|
- entry = ((u64)r[0] << 32) | (r[1] & 0xffff0000) | e->vid;
|
|
- return entry;
|
|
+ mac = ((u64)e->mac[0]) << 40 | ((u64)e->mac[1]) << 32 | ((u64)e->mac[2]) << 24
|
|
+ | ((u64)e->mac[3]) << 16 | ((u64)e->mac[4]) << 8 | ((u64)e->mac[5]);
|
|
+
|
|
+ seed = rtl930x_l2_hash_seed(mac, e->rvid);
|
|
+ pr_debug("%s: mac %016llx, seed %016llx\n", __func__, mac, seed);
|
|
+ // return vid with concatenated mac as unique id
|
|
+ return seed;
|
|
+}
|
|
+
|
|
+static void rtl930x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 0);
|
|
+ u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket
|
|
+ int i;
|
|
+
|
|
+ pr_info("%s: hash %d, pos %d\n", __func__, hash, pos);
|
|
+ pr_info("%s: index %d -> mac %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, idx,
|
|
+ e->mac[0], e->mac[1], e->mac[2], e->mac[3],e->mac[4],e->mac[5]);
|
|
+
|
|
+ rtl930x_fill_l2_row(r, e);
|
|
+
|
|
+ for (i= 0; i < 3; i++)
|
|
+ sw_w32(r[i], rtl_table_data(q, i));
|
|
+
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
}
|
|
|
|
static u64 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e)
|
|
{
|
|
- u64 entry;
|
|
u32 r[3];
|
|
struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1);
|
|
int i;
|
|
@@ -251,9 +455,142 @@ static u64 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e)
|
|
if (!e->valid)
|
|
return 0;
|
|
|
|
- entry = ((u64)r[0] << 32) | (r[1] & 0xffff0000) | e->vid;
|
|
+ // return mac with concatenated vid as unique id
|
|
+ return ((u64)r[0] << 28) | ((r[1] & 0xffff0000) >> 4) | e->vid;
|
|
+}
|
|
+
|
|
+static void rtl930x_write_cam(int idx, struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ u32 r[3];
|
|
+ struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1); // Access L2 Table 1
|
|
+ int i;
|
|
+
|
|
+ rtl930x_fill_l2_row(r, e);
|
|
+
|
|
+ for (i= 0; i < 3; i++)
|
|
+ sw_w32(r[i], rtl_table_data(q, i));
|
|
+
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
+}
|
|
|
|
- return entry;
|
|
+static void dump_l2_entry(struct rtl838x_l2_entry *e)
|
|
+{
|
|
+ pr_info("MAC: %02x:%02x:%02x:%02x:%02x:%02x vid: %d, rvid: %d, port: %d, valid: %d\n",
|
|
+ e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5],
|
|
+ e->vid, e->rvid, e->port, e->valid);
|
|
+ pr_info("Type: %d, is_static: %d, is_ip_mc: %d, is_ipv6_mc: %d, block_da: %d\n",
|
|
+ e->type, e->is_static, e->is_ip_mc, e->is_ipv6_mc, e->block_da);
|
|
+ pr_info(" block_sa: %d, suspended: %d, next_hop: %d, age: %d, is_trunk: %d, trunk: %d\n",
|
|
+ e->block_sa, e->suspended, e->next_hop, e->age, e->is_trunk, e->trunk);
|
|
+ if (e->is_ip_mc || e->is_ipv6_mc)
|
|
+ pr_info(" mc_portmask_index: %d, mc_gip: %d, mc_sip: %d\n",
|
|
+ e->mc_portmask_index, e->mc_gip, e->mc_sip);
|
|
+ pr_info(" stac_dev: %d, nh_route_id: %d, port: %d, dev_id\n",
|
|
+ e->stack_dev, e->nh_route_id, e->port);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add an L2 nexthop entry for the L3 routing system in the SoC
|
|
+ * Use VID and MAC in rtl838x_l2_entry to identify either a free slot in the L2 hash table
|
|
+ * or mark an existing entry as a nexthop by setting it's nexthop bit
|
|
+ * Called from the L3 layer
|
|
+ * The index in the L2 hash table is filled into nh->l2_id;
|
|
+ */
|
|
+static int rtl930x_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct rtl838x_nexthop *nh)
|
|
+{
|
|
+ struct rtl838x_l2_entry e;
|
|
+ u64 seed = rtl930x_l2_hash_seed(nh->mac, nh->vid);
|
|
+ u32 key = rtl930x_l2_hash_key(priv, seed);
|
|
+ int i, idx = -1;
|
|
+ u64 entry;
|
|
+
|
|
+ pr_info("%s searching for %08llx vid %d with key %d, seed: %016llx\n",
|
|
+ __func__, nh->mac, nh->vid, key, seed);
|
|
+
|
|
+ e.type = L2_UNICAST;
|
|
+ e.rvid = nh->fid; // Verify its the forwarding ID!!! l2_entry.un.unicast.fid
|
|
+ u64_to_ether_addr(nh->mac, &e.mac[0]);
|
|
+ e.port = RTL930X_PORT_IGNORE;
|
|
+
|
|
+ // Loop over all entries in the hash-bucket and over the second block on 93xx SoCs
|
|
+ for (i = 0; i < priv->l2_bucket_size; i++) {
|
|
+ entry = rtl930x_read_l2_entry_using_hash(key, i, &e);
|
|
+ pr_info("%s i: %d, entry %016llx, seed %016llx\n", __func__, i, entry, seed);
|
|
+ if (e.valid && e.next_hop)
|
|
+ continue;
|
|
+ if (!e.valid || ((entry & 0x0fffffffffffffffULL) == seed)) {
|
|
+ idx = i > 3 ? ((key >> 14) & 0xffff) | i >> 1
|
|
+ : ((key << 2) | i) & 0xffff;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pr_info("%s: found idx %d and i %d\n", __func__, idx, i);
|
|
+
|
|
+ if (idx < 0) {
|
|
+ pr_err("%s: No more L2 forwarding entries available\n", __func__);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ // Found an existing or empty entry, make it a nexthop entry
|
|
+ pr_info("%s BEFORE -> key %d, pos: %d, index: %d\n", __func__, key, i, idx);
|
|
+ dump_l2_entry(&e);
|
|
+ nh->l2_id = idx;
|
|
+
|
|
+ // Found an existing (e->valid is true) or empty entry, make it a nexthop entry
|
|
+ if (e.valid) {
|
|
+ nh->port = e.port;
|
|
+ nh->fid = e.rvid;
|
|
+ nh->vid = e.vid;
|
|
+ nh->dev_id = e.stack_dev;
|
|
+ } else {
|
|
+ e.valid = true;
|
|
+ e.is_static = false;
|
|
+ e.vid = nh->vid;
|
|
+ e.rvid = nh->fid;
|
|
+ e.port = RTL930X_PORT_IGNORE;
|
|
+ u64_to_ether_addr(nh->mac, &e.mac[0]);
|
|
+ }
|
|
+ e.next_hop = true;
|
|
+ // For nexthop entries, the vid field in the table is used to denote the dest mac_id
|
|
+ e.nh_route_id = nh->mac_id;
|
|
+ pr_info("%s AFTER\n", __func__);
|
|
+ dump_l2_entry(&e);
|
|
+
|
|
+ rtl930x_write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e);
|
|
+
|
|
+ // _dal_longan_l2_nexthop_add
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u64 rtl930x_read_mcast_pmask(int idx)
|
|
+{
|
|
+ u32 portmask;
|
|
+ // Read MC_PORTMASK (2) via register RTL9300_TBL_L2
|
|
+ struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 2);
|
|
+
|
|
+ rtl_table_read(q, idx);
|
|
+ portmask = sw_r32(rtl_table_data(q, 0));
|
|
+ portmask >>= 3;
|
|
+ rtl_table_release(q);
|
|
+
|
|
+ pr_debug("%s: Index idx %d has portmask %08x\n", __func__, idx, portmask);
|
|
+ return portmask;
|
|
+}
|
|
+
|
|
+static void rtl930x_write_mcast_pmask(int idx, u64 portmask)
|
|
+{
|
|
+ u32 pm = portmask;
|
|
+
|
|
+ // Access MC_PORTMASK (2) via register RTL9300_TBL_L2
|
|
+ struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 2);
|
|
+
|
|
+ pr_debug("%s: Index idx %d has portmask %08x\n", __func__, idx, pm);
|
|
+ pm <<= 3;
|
|
+ sw_w32(pm, rtl_table_data(q, 0));
|
|
+ rtl_table_write(q, idx);
|
|
+ rtl_table_release(q);
|
|
}
|
|
|
|
u64 rtl930x_traffic_get(int source)
|
|
@@ -382,7 +719,6 @@ int rtl9300_sds_power(int mac, int val)
|
|
return 0;
|
|
}
|
|
|
|
-
|
|
int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val)
|
|
{
|
|
u32 v;
|
|
@@ -418,7 +754,6 @@ int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
|
|
u32 v;
|
|
int err = 0;
|
|
|
|
-// pr_info("In %s\n", __func__);
|
|
if (port > 63 || page > 4095 || reg > 31)
|
|
return -ENOTSUPP;
|
|
|
|
@@ -445,7 +780,6 @@ int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
|
|
return err;
|
|
}
|
|
|
|
-
|
|
/*
|
|
* Write to an mmd register of the PHY
|
|
*/
|
|
@@ -465,12 +799,12 @@ int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val)
|
|
// Set MMD device number and register to write to
|
|
sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3);
|
|
|
|
- v = BIT(2)| BIT(1)| BIT(0); // WRITE | MMD-access | EXEC
|
|
+ v = BIT(2) | BIT(1) | BIT(0); // WRITE | MMD-access | EXEC
|
|
sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1);
|
|
|
|
do {
|
|
v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1);
|
|
- } while ( v & BIT(0));
|
|
+ } while (v & BIT(0));
|
|
|
|
pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err);
|
|
mutex_unlock(&smi_lock);
|
|
@@ -493,12 +827,12 @@ int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
|
|
// Set MMD device number and register to write to
|
|
sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3);
|
|
|
|
- v = BIT(1)| BIT(0); // MMD-access | EXEC
|
|
+ v = BIT(1) | BIT(0); // MMD-access | EXEC
|
|
sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1);
|
|
|
|
do {
|
|
v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1);
|
|
- } while ( v & 0x1);
|
|
+ } while (v & BIT(0));
|
|
// There is no error-checking via BIT 25 of v, as it does not seem to be set correctly
|
|
*val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff);
|
|
pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err);
|
|
@@ -508,7 +842,6 @@ int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
|
|
return err;
|
|
}
|
|
|
|
-
|
|
/*
|
|
* Calculate both the block 0 and the block 1 hash, and return in
|
|
* lower and higher word of the return value since only 12 bit of
|
|
@@ -552,6 +885,96 @@ u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed)
|
|
return h;
|
|
}
|
|
|
|
+/*
|
|
+ * Enables or disables the EEE/EEEP capability of a port
|
|
+ */
|
|
+void rtl930x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ // This works only for Ethernet ports, and on the RTL930X, ports from 26 are SFP
|
|
+ if (port >= 26)
|
|
+ return;
|
|
+
|
|
+ pr_debug("In %s: setting port %d to %d\n", __func__, port, enable);
|
|
+ v = enable ? 0x3f : 0x0;
|
|
+
|
|
+ // Set EEE/EEEP state for 100, 500, 1000MBit and 2.5, 5 and 10GBit
|
|
+ sw_w32_mask(0, v << 10, rtl930x_mac_force_mode_ctrl(port));
|
|
+
|
|
+ // Set TX/RX EEE state
|
|
+ v = enable ? 0x3 : 0x0;
|
|
+ sw_w32(v, RTL930X_EEE_CTRL(port));
|
|
+
|
|
+ priv->ports[port].eee_enabled = enable;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get EEE own capabilities and negotiation result
|
|
+ */
|
|
+int rtl930x_eee_port_ability(struct rtl838x_switch_priv *priv, struct ethtool_eee *e, int port)
|
|
+{
|
|
+ u32 link, a;
|
|
+
|
|
+ if (port >= 26)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ pr_info("In %s, port %d\n", __func__, port);
|
|
+ link = sw_r32(RTL930X_MAC_LINK_STS);
|
|
+ link = sw_r32(RTL930X_MAC_LINK_STS);
|
|
+ if (!(link & BIT(port)))
|
|
+ return 0;
|
|
+
|
|
+ pr_info("Setting advertised\n");
|
|
+ if (sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(10))
|
|
+ e->advertised |= ADVERTISED_100baseT_Full;
|
|
+
|
|
+ if (sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(12))
|
|
+ e->advertised |= ADVERTISED_1000baseT_Full;
|
|
+
|
|
+ if (priv->ports[port].is2G5 && sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(13)) {
|
|
+ pr_info("ADVERTISING 2.5G EEE\n");
|
|
+ e->advertised |= ADVERTISED_2500baseX_Full;
|
|
+ }
|
|
+
|
|
+ if (priv->ports[port].is10G && sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(15))
|
|
+ e->advertised |= ADVERTISED_10000baseT_Full;
|
|
+
|
|
+ a = sw_r32(RTL930X_MAC_EEE_ABLTY);
|
|
+ a = sw_r32(RTL930X_MAC_EEE_ABLTY);
|
|
+ pr_info("Link partner: %08x\n", a);
|
|
+ if (a & BIT(port)) {
|
|
+ e->lp_advertised = ADVERTISED_100baseT_Full;
|
|
+ e->lp_advertised |= ADVERTISED_1000baseT_Full;
|
|
+ if (priv->ports[port].is2G5)
|
|
+ e->lp_advertised |= ADVERTISED_2500baseX_Full;
|
|
+ if (priv->ports[port].is10G)
|
|
+ e->lp_advertised |= ADVERTISED_10000baseT_Full;
|
|
+ }
|
|
+
|
|
+ // Read 2x to clear latched state
|
|
+ a = sw_r32(RTL930X_EEEP_PORT_CTRL(port));
|
|
+ a = sw_r32(RTL930X_EEEP_PORT_CTRL(port));
|
|
+ pr_info("%s RTL930X_EEEP_PORT_CTRL: %08x\n", __func__, a);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rtl930x_init_eee(struct rtl838x_switch_priv *priv, bool enable)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ pr_info("Setting up EEE, state: %d\n", enable);
|
|
+
|
|
+ // Setup EEE on all ports
|
|
+ for (i = 0; i < priv->cpu_port; i++) {
|
|
+ if (priv->ports[i].phy)
|
|
+ rtl930x_port_eee_set(priv, i, enable);
|
|
+ }
|
|
+
|
|
+ priv->eee_enabled = enable;
|
|
+}
|
|
+
|
|
const struct rtl838x_reg rtl930x_reg = {
|
|
.mask_port_reg_be = rtl838x_mask_port_reg,
|
|
.set_port_reg_be = rtl838x_set_port_reg,
|
|
@@ -582,6 +1005,8 @@ const struct rtl838x_reg rtl930x_reg = {
|
|
.vlan_set_tagged = rtl930x_vlan_set_tagged,
|
|
.vlan_set_untagged = rtl930x_vlan_set_untagged,
|
|
.vlan_profile_dump = rtl930x_vlan_profile_dump,
|
|
+ .vlan_profile_setup = rtl930x_vlan_profile_setup,
|
|
+ .vlan_fwd_on_inner = rtl930x_vlan_fwd_on_inner,
|
|
.stp_get = rtl930x_stp_get,
|
|
.stp_set = rtl930x_stp_set,
|
|
.mac_force_mode_ctrl = rtl930x_mac_force_mode_ctrl,
|
|
@@ -597,11 +1022,18 @@ const struct rtl838x_reg rtl930x_reg = {
|
|
.mac_rx_pause_sts = RTL930X_MAC_RX_PAUSE_STS,
|
|
.mac_tx_pause_sts = RTL930X_MAC_TX_PAUSE_STS,
|
|
.read_l2_entry_using_hash = rtl930x_read_l2_entry_using_hash,
|
|
+ .write_l2_entry_using_hash = rtl930x_write_l2_entry_using_hash,
|
|
.read_cam = rtl930x_read_cam,
|
|
+ .write_cam = rtl930x_write_cam,
|
|
.vlan_port_egr_filter = RTL930X_VLAN_PORT_EGR_FLTR,
|
|
.vlan_port_igr_filter = RTL930X_VLAN_PORT_IGR_FLTR(0),
|
|
.vlan_port_pb = RTL930X_VLAN_PORT_PB_VLAN,
|
|
.vlan_port_tag_sts_ctrl = RTL930X_VLAN_PORT_TAG_STS_CTRL,
|
|
.trk_mbr_ctr = rtl930x_trk_mbr_ctr,
|
|
.rma_bpdu_fld_pmask = RTL930X_RMA_BPDU_FLD_PMSK,
|
|
+ .init_eee = rtl930x_init_eee,
|
|
+ .port_eee_set = rtl930x_port_eee_set,
|
|
+ .eee_port_ability = rtl930x_eee_port_ability,
|
|
+ .read_mcast_pmask = rtl930x_read_mcast_pmask,
|
|
+ .write_mcast_pmask = rtl930x_write_mcast_pmask,
|
|
};
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
|
|
index a33941a0eb..f98bf7df29 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
|
|
@@ -175,6 +175,7 @@ static u64 rtl931x_read_cam(int idx, struct rtl838x_l2_entry *e)
|
|
// TODO: Implement
|
|
return entry;
|
|
}
|
|
+
|
|
irqreturn_t rtl931x_switch_irq(int irq, void *dev_id)
|
|
{
|
|
struct dsa_switch *ds = dev_id;
|
|
@@ -199,7 +200,6 @@ irqreturn_t rtl931x_switch_irq(int irq, void *dev_id)
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
-
|
|
int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val)
|
|
{
|
|
u32 v;
|
|
@@ -264,6 +264,73 @@ int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
|
|
return 0;
|
|
}
|
|
|
|
+/*
|
|
+ * Read an mmd register of the PHY
|
|
+ */
|
|
+int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
|
|
+{
|
|
+ int err = 0;
|
|
+ u32 v;
|
|
+ int type = 1; // TODO: For C45 PHYs need to set to 2
|
|
+
|
|
+ mutex_lock(&smi_lock);
|
|
+
|
|
+ // Set PHY to access via port-number
|
|
+ sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL);
|
|
+
|
|
+ // Set MMD device number and register to write to
|
|
+ sw_w32(devnum << 16 | (regnum & 0xffff), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL);
|
|
+
|
|
+ v = type << 2 | BIT(0); // MMD-access-type | EXEC
|
|
+ sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
|
|
+
|
|
+ do {
|
|
+ v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0);
|
|
+ } while (v & BIT(0));
|
|
+
|
|
+ // There is no error-checking via BIT 1 of v, as it does not seem to be set correctly
|
|
+
|
|
+ *val = (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) & 0xffff);
|
|
+
|
|
+ pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err);
|
|
+
|
|
+ mutex_unlock(&smi_lock);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Write to an mmd register of the PHY
|
|
+ */
|
|
+int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val)
|
|
+{
|
|
+ int err = 0;
|
|
+ u32 v;
|
|
+ int type = 1; // TODO: For C45 PHYs need to set to 2
|
|
+
|
|
+ mutex_lock(&smi_lock);
|
|
+
|
|
+ // Set PHY to access via port-number
|
|
+ sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL);
|
|
+
|
|
+ // Set data to write
|
|
+ sw_w32_mask(0xffff << 16, val << 16, RTL931X_SMI_INDRT_ACCESS_CTRL_3);
|
|
+
|
|
+ // Set MMD device number and register to write to
|
|
+ sw_w32(devnum << 16 | (regnum & 0xffff), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL);
|
|
+
|
|
+ v = BIT(4) | type << 2 | BIT(0); // WRITE | MMD-access-type | EXEC
|
|
+ sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
|
|
+
|
|
+ do {
|
|
+ v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0);
|
|
+ } while (v & BIT(0));
|
|
+
|
|
+ pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err);
|
|
+ mutex_unlock(&smi_lock);
|
|
+ return err;
|
|
+}
|
|
+
|
|
void rtl931x_print_matrix(void)
|
|
{
|
|
volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0);
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c
|
|
index 7931daff07..c5c6e3b6b7 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c
|
|
@@ -234,19 +234,21 @@ struct dsa_tag {
|
|
u16 port;
|
|
u8 l2_offloaded;
|
|
u8 prio;
|
|
+ bool crc_error;
|
|
};
|
|
|
|
bool rtl838x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
|
|
{
|
|
t->reason = h->cpu_tag[3] & 0xf;
|
|
- if (t->reason != 15)
|
|
- pr_debug("Reason: %d\n", t->reason);
|
|
t->queue = (h->cpu_tag[0] & 0xe0) >> 5;
|
|
+ t->port = h->cpu_tag[1] & 0x1f;
|
|
+ t->crc_error = t->reason == 13;
|
|
+
|
|
+ pr_debug("Reason: %d\n", t->reason);
|
|
if (t->reason != 4) // NIC_RX_REASON_SPECIAL_TRAP
|
|
t->l2_offloaded = 1;
|
|
else
|
|
t->l2_offloaded = 0;
|
|
- t->port = h->cpu_tag[1] & 0x1f;
|
|
|
|
return t->l2_offloaded;
|
|
}
|
|
@@ -254,37 +256,48 @@ bool rtl838x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
|
|
bool rtl839x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
|
|
{
|
|
t->reason = h->cpu_tag[4] & 0x1f;
|
|
- if (t->reason != 31)
|
|
+ t->queue = (h->cpu_tag[3] & 0xe000) >> 13;
|
|
+ t->port = h->cpu_tag[1] & 0x3f;
|
|
+ t->crc_error = h->cpu_tag[3] & BIT(2);
|
|
+
|
|
pr_debug("Reason: %d\n", t->reason);
|
|
- t->queue = (h->cpu_tag[3] & 0xe000) >> 13;
|
|
if ((t->reason != 7) && (t->reason != 8)) // NIC_RX_REASON_RMA_USR
|
|
t->l2_offloaded = 1;
|
|
else
|
|
t->l2_offloaded = 0;
|
|
-
|
|
- t->port = h->cpu_tag[1] & 0x3f;
|
|
|
|
return t->l2_offloaded;
|
|
}
|
|
|
|
-bool rtl931x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
|
|
+bool rtl930x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
|
|
{
|
|
t->reason = h->cpu_tag[7] & 0x3f;
|
|
- pr_debug("Reason %d\n", t->reason);
|
|
t->queue = (h->cpu_tag[2] >> 11) & 0x1f;
|
|
+ t->port = (h->cpu_tag[0] >> 8) & 0x1f;
|
|
+ t->crc_error = h->cpu_tag[1] & BIT(6);
|
|
+
|
|
+ pr_debug("Reason %d, port %d, queue %d\n", t->reason, t->port, t->queue);
|
|
if (t->reason >= 19 && t->reason <= 27)
|
|
t->l2_offloaded = 0;
|
|
else
|
|
t->l2_offloaded = 1;
|
|
- t->port = (h->cpu_tag[0] >> 8) & 0x3f;
|
|
|
|
return t->l2_offloaded;
|
|
}
|
|
|
|
-bool rtl930x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
|
|
+bool rtl931x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
|
|
{
|
|
- rtl931x_decode_tag(h, t);
|
|
- t->port &= 0x1f;
|
|
+ t->reason = h->cpu_tag[7] & 0x3f;
|
|
+ t->queue = (h->cpu_tag[2] >> 11) & 0x1f;
|
|
+ t->port = (h->cpu_tag[0] >> 8) & 0x3f;
|
|
+ t->crc_error = h->cpu_tag[1] & BIT(6);
|
|
+
|
|
+ pr_debug("Reason %d, port %d, queue %d\n", t->reason, t->port, t->queue);
|
|
+ if (t->reason >= 19 && t->reason <= 27)
|
|
+ t->l2_offloaded = 0;
|
|
+ else
|
|
+ t->l2_offloaded = 1;
|
|
+
|
|
return t->l2_offloaded;
|
|
}
|
|
|
|
@@ -731,8 +744,9 @@ static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv)
|
|
* | MEDIA_SEL
|
|
*/
|
|
sw_w32(0x6192F, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
|
|
- /* allow CRC errors on CPU-port */
|
|
- sw_w32_mask(0, 0x8, priv->r->mac_port_ctrl(priv->cpu_port));
|
|
+
|
|
+ /* Enable CRC checks on CPU-port */
|
|
+ sw_w32_mask(0, BIT(3), priv->r->mac_port_ctrl(priv->cpu_port));
|
|
}
|
|
|
|
static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv)
|
|
@@ -746,8 +760,8 @@ static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv)
|
|
/* Enable DMA */
|
|
sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl);
|
|
|
|
- /* Restart TX/RX to CPU port */
|
|
- sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port));
|
|
+ /* Restart TX/RX to CPU port, enable CRC checking */
|
|
+ sw_w32_mask(0x0, 0x3 | BIT(3), priv->r->mac_port_ctrl(priv->cpu_port));
|
|
|
|
/* CPU port joins Lookup Miss Flooding Portmask */
|
|
// TODO: The code below should also work for the RTL838x
|
|
@@ -784,8 +798,8 @@ static void rtl93xx_hw_en_rxtx(struct rtl838x_eth_priv *priv)
|
|
/* Enable DMA */
|
|
sw_w32_mask(0, RX_EN_93XX | TX_EN_93XX, priv->r->dma_if_ctrl);
|
|
|
|
- /* Restart TX/RX to CPU port */
|
|
- sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port));
|
|
+ /* Restart TX/RX to CPU port, enable CRC checking */
|
|
+ sw_w32_mask(0x0, 0x3 | BIT(4), priv->r->mac_port_ctrl(priv->cpu_port));
|
|
|
|
sw_w32_mask(0, BIT(priv->cpu_port), RTL930X_L2_UNKN_UC_FLD_PMSK);
|
|
sw_w32(0x217, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
|
|
@@ -855,7 +869,7 @@ static int rtl838x_eth_open(struct net_device *ndev)
|
|
struct ring_b *ring = priv->membase;
|
|
int i, err;
|
|
|
|
- pr_info("%s called: RX rings %d(length %d), TX rings %d(length %d)\n",
|
|
+ pr_debug("%s called: RX rings %d(length %d), TX rings %d(length %d)\n",
|
|
__func__, priv->rxrings, priv->rxringlen, TXRINGS, TXRINGLEN);
|
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
|
@@ -883,28 +897,30 @@ static int rtl838x_eth_open(struct net_device *ndev)
|
|
switch (priv->family_id) {
|
|
case RTL8380_FAMILY_ID:
|
|
rtl838x_hw_en_rxtx(priv);
|
|
- /* Trap IGMP traffic to CPU-Port */
|
|
+ /* Trap IGMP/MLD traffic to CPU-Port */
|
|
sw_w32(0x3, RTL838X_SPCL_TRAP_IGMP_CTRL);
|
|
/* Flush learned FDB entries on link down of a port */
|
|
sw_w32_mask(0, BIT(7), RTL838X_L2_CTRL_0);
|
|
break;
|
|
+
|
|
case RTL8390_FAMILY_ID:
|
|
rtl839x_hw_en_rxtx(priv);
|
|
+ // Trap MLD and IGMP messages to CPU_PORT
|
|
sw_w32(0x3, RTL839X_SPCL_TRAP_IGMP_CTRL);
|
|
/* Flush learned FDB entries on link down of a port */
|
|
sw_w32_mask(0, BIT(7), RTL839X_L2_CTRL_0);
|
|
break;
|
|
+
|
|
case RTL9300_FAMILY_ID:
|
|
rtl93xx_hw_en_rxtx(priv);
|
|
/* Flush learned FDB entries on link down of a port */
|
|
sw_w32_mask(0, BIT(7), RTL930X_L2_CTRL);
|
|
- sw_w32_mask(BIT(28), 0, RTL930X_L2_PORT_SABLK_CTRL);
|
|
- sw_w32_mask(BIT(28), 0, RTL930X_L2_PORT_DABLK_CTRL);
|
|
+ // Trap MLD and IGMP messages to CPU_PORT
|
|
+ sw_w32((0x2 << 3) | 0x2, RTL930X_VLAN_APP_PKT_CTRL);
|
|
break;
|
|
|
|
case RTL9310_FAMILY_ID:
|
|
rtl93xx_hw_en_rxtx(priv);
|
|
-// TODO: Add trapping of IGMP frames to CPU-port
|
|
break;
|
|
}
|
|
|
|
@@ -1115,15 +1131,20 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev)
|
|
if (netdev_uses_dsa(dev) && skb->data[len-4] == 0x80 && skb->data[len-3] > 0
|
|
&& skb->data[len-3] < 28 && skb->data[len-2] == 0x10
|
|
&& skb->data[len-1] == 0x00) {
|
|
- /* Reuse tag space for CRC */
|
|
+ /* Reuse tag space for CRC if possible */
|
|
dest_port = skb->data[len-3];
|
|
len -= 4;
|
|
}
|
|
- if (len < ETH_ZLEN)
|
|
- len = ETH_ZLEN;
|
|
|
|
- /* ASIC expects that packet includes CRC, so we extend by 4 bytes */
|
|
- len += 4;
|
|
+ len += 4; // Add space for CRC
|
|
+
|
|
+ // On RTL8380 SoCs, the packet needs extra padding
|
|
+ if (priv->family_id == RTL8380_FAMILY_ID) {
|
|
+ if (len < ETH_ZLEN)
|
|
+ len = ETH_ZLEN; // SoC not automatically padding to ETH_ZLEN
|
|
+ else
|
|
+ len += 4;
|
|
+ }
|
|
|
|
if (skb_padto(skb, len)) {
|
|
ret = NETDEV_TX_OK;
|
|
@@ -1280,6 +1301,12 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
|
|
tag.queue, len, tag.reason, tag.port);
|
|
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
+ if (dev->features & NETIF_F_RXCSUM) {
|
|
+ if (tag.crc_error)
|
|
+ skb_checksum_none_assert(skb);
|
|
+ else
|
|
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
+ }
|
|
dev->stats.rx_packets++;
|
|
dev->stats.rx_bytes += len;
|
|
|
|
@@ -1342,7 +1369,7 @@ static void rtl838x_validate(struct phylink_config *config,
|
|
{
|
|
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
|
|
|
- pr_info("In %s\n", __func__);
|
|
+ pr_debug("In %s\n", __func__);
|
|
|
|
if (!phy_interface_mode_is_rgmii(state->interface) &&
|
|
state->interface != PHY_INTERFACE_MODE_1000BASEX &&
|
|
@@ -1404,7 +1431,7 @@ static void rtl838x_mac_an_restart(struct phylink_config *config)
|
|
if (priv->family_id != RTL8380_FAMILY_ID)
|
|
return;
|
|
|
|
- pr_info("In %s\n", __func__);
|
|
+ pr_debug("In %s\n", __func__);
|
|
/* Restart by disabling and re-enabling link */
|
|
sw_w32(0x6192D, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
|
|
mdelay(20);
|
|
@@ -1419,7 +1446,7 @@ static int rtl838x_mac_pcs_get_state(struct phylink_config *config,
|
|
struct rtl838x_eth_priv *priv = netdev_priv(dev);
|
|
int port = priv->cpu_port;
|
|
|
|
- pr_info("In %s\n", __func__);
|
|
+ pr_debug("In %s\n", __func__);
|
|
|
|
state->link = priv->r->get_mac_link_sts(port) ? 1 : 0;
|
|
state->duplex = priv->r->get_mac_link_dup_sts(port) ? 1 : 0;
|
|
@@ -1456,7 +1483,7 @@ static void rtl838x_mac_link_down(struct phylink_config *config,
|
|
struct net_device *dev = container_of(config->dev, struct net_device, dev);
|
|
struct rtl838x_eth_priv *priv = netdev_priv(dev);
|
|
|
|
- pr_info("In %s\n", __func__);
|
|
+ pr_debug("In %s\n", __func__);
|
|
/* Stop TX/RX to port */
|
|
sw_w32_mask(0x03, 0, priv->r->mac_port_ctrl(priv->cpu_port));
|
|
}
|
|
@@ -1468,7 +1495,7 @@ static void rtl838x_mac_link_up(struct phylink_config *config, unsigned int mode
|
|
struct net_device *dev = container_of(config->dev, struct net_device, dev);
|
|
struct rtl838x_eth_priv *priv = netdev_priv(dev);
|
|
|
|
- pr_info("In %s\n", __func__);
|
|
+ pr_debug("In %s\n", __func__);
|
|
/* Restart TX/RX to port */
|
|
sw_w32_mask(0, 0x03, priv->r->mac_port_ctrl(priv->cpu_port));
|
|
}
|
|
@@ -1479,7 +1506,7 @@ static void rtl838x_set_mac_hw(struct net_device *dev, u8 *mac)
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
|
- pr_info("In %s\n", __func__);
|
|
+ pr_debug("In %s\n", __func__);
|
|
sw_w32((mac[0] << 8) | mac[1], priv->r->mac);
|
|
sw_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], priv->r->mac + 4);
|
|
|
|
@@ -1547,7 +1574,7 @@ static int rtl838x_get_link_ksettings(struct net_device *ndev,
|
|
{
|
|
struct rtl838x_eth_priv *priv = netdev_priv(ndev);
|
|
|
|
- pr_info("%s called\n", __func__);
|
|
+ pr_debug("%s called\n", __func__);
|
|
return phylink_ethtool_ksettings_get(priv->phylink, cmd);
|
|
}
|
|
|
|
@@ -1556,7 +1583,7 @@ static int rtl838x_set_link_ksettings(struct net_device *ndev,
|
|
{
|
|
struct rtl838x_eth_priv *priv = netdev_priv(ndev);
|
|
|
|
- pr_info("%s called\n", __func__);
|
|
+ pr_debug("%s called\n", __func__);
|
|
return phylink_ethtool_ksettings_set(priv->phylink, cmd);
|
|
}
|
|
|
|
@@ -1678,7 +1705,7 @@ static int rtl931x_mdio_write(struct mii_bus *bus, int mii_id,
|
|
|
|
static int rtl838x_mdio_reset(struct mii_bus *bus)
|
|
{
|
|
- pr_info("%s called\n", __func__);
|
|
+ pr_debug("%s called\n", __func__);
|
|
/* Disable MAC polling the PHY so that we can start configuration */
|
|
sw_w32(0x00000000, RTL838X_SMI_POLL_CTRL);
|
|
|
|
@@ -1693,7 +1720,7 @@ static int rtl839x_mdio_reset(struct mii_bus *bus)
|
|
{
|
|
return 0;
|
|
|
|
- pr_info("%s called\n", __func__);
|
|
+ pr_debug("%s called\n", __func__);
|
|
/* BUG: The following does not work, but should! */
|
|
/* Disable MAC polling the PHY so that we can start configuration */
|
|
sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL);
|
|
@@ -1710,7 +1737,7 @@ static int rtl931x_mdio_reset(struct mii_bus *bus)
|
|
sw_w32(0x00000000, RTL931X_SMI_PORT_POLLING_CTRL);
|
|
sw_w32(0x00000000, RTL931X_SMI_PORT_POLLING_CTRL + 4);
|
|
|
|
- pr_info("%s called\n", __func__);
|
|
+ pr_debug("%s called\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
@@ -1767,7 +1794,7 @@ static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv)
|
|
struct device_node *mii_np;
|
|
int ret;
|
|
|
|
- pr_info("%s called\n", __func__);
|
|
+ pr_debug("%s called\n", __func__);
|
|
mii_np = of_get_child_by_name(priv->pdev->dev.of_node, "mdio-bus");
|
|
|
|
if (!mii_np) {
|
|
@@ -1827,7 +1854,7 @@ err_put_node:
|
|
|
|
static int rtl838x_mdio_remove(struct rtl838x_eth_priv *priv)
|
|
{
|
|
- pr_info("%s called\n", __func__);
|
|
+ pr_debug("%s called\n", __func__);
|
|
if (!priv->mii_bus)
|
|
return 0;
|
|
|
|
@@ -1837,6 +1864,40 @@ static int rtl838x_mdio_remove(struct rtl838x_eth_priv *priv)
|
|
return 0;
|
|
}
|
|
|
|
+static netdev_features_t rtl838x_fix_features(struct net_device *dev,
|
|
+ netdev_features_t features)
|
|
+{
|
|
+ return features;
|
|
+}
|
|
+
|
|
+static int rtl83xx_set_features(struct net_device *dev, netdev_features_t features)
|
|
+{
|
|
+ struct rtl838x_eth_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ if ((features ^ dev->features) & NETIF_F_RXCSUM) {
|
|
+ if (!(features & NETIF_F_RXCSUM))
|
|
+ sw_w32_mask(BIT(3), 0, priv->r->mac_port_ctrl(priv->cpu_port));
|
|
+ else
|
|
+ sw_w32_mask(0, BIT(4), priv->r->mac_port_ctrl(priv->cpu_port));
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rtl93xx_set_features(struct net_device *dev, netdev_features_t features)
|
|
+{
|
|
+ struct rtl838x_eth_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ if ((features ^ dev->features) & NETIF_F_RXCSUM) {
|
|
+ if (!(features & NETIF_F_RXCSUM))
|
|
+ sw_w32_mask(BIT(4), 0, priv->r->mac_port_ctrl(priv->cpu_port));
|
|
+ else
|
|
+ sw_w32_mask(0, BIT(4), priv->r->mac_port_ctrl(priv->cpu_port));
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static const struct net_device_ops rtl838x_eth_netdev_ops = {
|
|
.ndo_open = rtl838x_eth_open,
|
|
.ndo_stop = rtl838x_eth_stop,
|
|
@@ -1846,6 +1907,8 @@ static const struct net_device_ops rtl838x_eth_netdev_ops = {
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_set_rx_mode = rtl838x_eth_set_multicast_list,
|
|
.ndo_tx_timeout = rtl838x_eth_tx_timeout,
|
|
+ .ndo_set_features = rtl83xx_set_features,
|
|
+ .ndo_fix_features = rtl838x_fix_features,
|
|
};
|
|
|
|
static const struct net_device_ops rtl839x_eth_netdev_ops = {
|
|
@@ -1857,6 +1920,8 @@ static const struct net_device_ops rtl839x_eth_netdev_ops = {
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_set_rx_mode = rtl839x_eth_set_multicast_list,
|
|
.ndo_tx_timeout = rtl838x_eth_tx_timeout,
|
|
+ .ndo_set_features = rtl83xx_set_features,
|
|
+ .ndo_fix_features = rtl838x_fix_features,
|
|
};
|
|
|
|
static const struct net_device_ops rtl930x_eth_netdev_ops = {
|
|
@@ -1868,6 +1933,8 @@ static const struct net_device_ops rtl930x_eth_netdev_ops = {
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_set_rx_mode = rtl930x_eth_set_multicast_list,
|
|
.ndo_tx_timeout = rtl838x_eth_tx_timeout,
|
|
+ .ndo_set_features = rtl93xx_set_features,
|
|
+ .ndo_fix_features = rtl838x_fix_features,
|
|
};
|
|
|
|
static const struct net_device_ops rtl931x_eth_netdev_ops = {
|
|
@@ -1879,6 +1946,8 @@ static const struct net_device_ops rtl931x_eth_netdev_ops = {
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_set_rx_mode = rtl931x_eth_set_multicast_list,
|
|
.ndo_tx_timeout = rtl838x_eth_tx_timeout,
|
|
+ .ndo_set_features = rtl93xx_set_features,
|
|
+ .ndo_fix_features = rtl838x_fix_features,
|
|
};
|
|
|
|
static const struct phylink_mac_ops rtl838x_phylink_ops = {
|
|
@@ -1975,6 +2044,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
|
|
dev->ethtool_ops = &rtl838x_ethtool_ops;
|
|
dev->min_mtu = ETH_ZLEN;
|
|
dev->max_mtu = 1536;
|
|
+ dev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM;
|
|
+ dev->hw_features = NETIF_F_RXCSUM;
|
|
|
|
priv->id = soc_info.id;
|
|
priv->family_id = soc_info.family;
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h
|
|
index d7b4317cbb..c7e97057b3 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h
|
|
@@ -177,6 +177,7 @@
|
|
#define RTL839X_RMA_CTRL_2 (0x1208)
|
|
#define RTL839X_RMA_CTRL_3 (0x120C)
|
|
|
|
+#define RTL930X_VLAN_APP_PKT_CTRL (0xA23C)
|
|
#define RTL930X_RMA_CTRL_0 (0x9E60)
|
|
#define RTL930X_RMA_CTRL_1 (0x9E64)
|
|
#define RTL930X_RMA_CTRL_2 (0x9E68)
|
|
diff --git a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
|
|
index 78953c6d17..cfddb0aced 100644
|
|
--- a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
|
|
+++ b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
|
|
@@ -18,12 +18,26 @@
|
|
extern struct rtl83xx_soc_info soc_info;
|
|
extern struct mutex smi_lock;
|
|
|
|
+/*
|
|
+ * This lock protects the state of the SoC automatically polling the PHYs over the SMI
|
|
+ * bus to detect e.g. link and media changes. For operations on the PHYs such as
|
|
+ * patching or other configuration changes such as EEE, polling needs to be disabled
|
|
+ * since otherwise these operations may fails or lead to unpredictable results.
|
|
+ */
|
|
+DEFINE_MUTEX(poll_lock);
|
|
+
|
|
static const struct firmware rtl838x_8380_fw;
|
|
static const struct firmware rtl838x_8214fc_fw;
|
|
static const struct firmware rtl838x_8218b_fw;
|
|
|
|
+int rtl838x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
|
|
+int rtl838x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
|
|
+int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
|
|
+int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
|
|
int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
|
|
-int rtl930x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val);
|
|
+int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
|
|
+int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
|
|
+int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
|
|
|
|
static int read_phy(u32 port, u32 page, u32 reg, u32 *val)
|
|
{ switch (soc_info.family) {
|
|
@@ -54,6 +68,93 @@ static int write_phy(u32 port, u32 page, u32 reg, u32 val)
|
|
return -1;
|
|
}
|
|
|
|
+static int read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
|
|
+{
|
|
+ switch (soc_info.family) {
|
|
+ case RTL8380_FAMILY_ID:
|
|
+ return rtl838x_read_mmd_phy(port, devnum, regnum, val);
|
|
+ case RTL8390_FAMILY_ID:
|
|
+ return rtl839x_read_mmd_phy(port, devnum, regnum, val);
|
|
+ case RTL9300_FAMILY_ID:
|
|
+ return rtl930x_read_mmd_phy(port, devnum, regnum, val);
|
|
+ case RTL9310_FAMILY_ID:
|
|
+ return rtl931x_read_mmd_phy(port, devnum, regnum, val);
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val)
|
|
+{
|
|
+ switch (soc_info.family) {
|
|
+ case RTL8380_FAMILY_ID:
|
|
+ return rtl838x_write_mmd_phy(port, devnum, reg, val);
|
|
+ case RTL8390_FAMILY_ID:
|
|
+ return rtl839x_write_mmd_phy(port, devnum, reg, val);
|
|
+ case RTL9300_FAMILY_ID:
|
|
+ return rtl930x_write_mmd_phy(port, devnum, reg, val);
|
|
+ case RTL9310_FAMILY_ID:
|
|
+ return rtl931x_write_mmd_phy(port, devnum, reg, val);
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static u64 disable_polling(int port)
|
|
+{
|
|
+ u64 saved_state;
|
|
+
|
|
+ mutex_lock(&poll_lock);
|
|
+
|
|
+ switch (soc_info.family) {
|
|
+ case RTL8380_FAMILY_ID:
|
|
+ saved_state = sw_r32(RTL838X_SMI_POLL_CTRL);
|
|
+ sw_w32_mask(BIT(port), 0, RTL838X_SMI_POLL_CTRL);
|
|
+ break;
|
|
+ case RTL8390_FAMILY_ID:
|
|
+ saved_state = sw_r32(RTL839X_SMI_PORT_POLLING_CTRL + 4);
|
|
+ saved_state <<= 32;
|
|
+ saved_state |= sw_r32(RTL839X_SMI_PORT_POLLING_CTRL);
|
|
+ sw_w32_mask(BIT(port % 32), 0,
|
|
+ RTL839X_SMI_PORT_POLLING_CTRL + ((port >> 5) << 2));
|
|
+ break;
|
|
+ case RTL9300_FAMILY_ID:
|
|
+ saved_state = sw_r32(RTL930X_SMI_POLL_CTRL);
|
|
+ sw_w32_mask(BIT(port), 0, RTL930X_SMI_POLL_CTRL);
|
|
+ break;
|
|
+ case RTL9310_FAMILY_ID:
|
|
+ pr_warn("%s not implemented for RTL931X\n", __func__);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&poll_lock);
|
|
+
|
|
+ return saved_state;
|
|
+}
|
|
+
|
|
+static int resume_polling(u64 saved_state)
|
|
+{
|
|
+ mutex_lock(&poll_lock);
|
|
+
|
|
+ switch (soc_info.family) {
|
|
+ case RTL8380_FAMILY_ID:
|
|
+ sw_w32(saved_state, RTL838X_SMI_POLL_CTRL);
|
|
+ break;
|
|
+ case RTL8390_FAMILY_ID:
|
|
+ sw_w32(saved_state >> 32, RTL839X_SMI_PORT_POLLING_CTRL + 4);
|
|
+ sw_w32(saved_state, RTL839X_SMI_PORT_POLLING_CTRL);
|
|
+ break;
|
|
+ case RTL9300_FAMILY_ID:
|
|
+ sw_w32(saved_state, RTL930X_SMI_POLL_CTRL);
|
|
+ break;
|
|
+ case RTL9310_FAMILY_ID:
|
|
+ pr_warn("%s not implemented for RTL931X\n", __func__);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&poll_lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static void rtl8380_int_phy_on_off(int mac, bool on)
|
|
{
|
|
u32 val;
|
|
@@ -94,18 +195,6 @@ static void rtl8380_phy_reset(int mac)
|
|
write_phy(mac, 0, 0, val | BIT(15));
|
|
}
|
|
|
|
-static void rtl8380_sds_rst(int mac)
|
|
-{
|
|
- u32 offset = (mac == 24) ? 0 : 0x100;
|
|
-
|
|
- sw_w32_mask(1 << 11, 0, RTL8380_SDS4_FIB_REG0 + offset);
|
|
- sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset);
|
|
- sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset);
|
|
- sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset);
|
|
- sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset);
|
|
- pr_info("SERDES reset: %d\n", mac);
|
|
-}
|
|
-
|
|
/*
|
|
* Reset the SerDes by powering it off and set a new operations mode
|
|
* of the SerDes. 0x1f is off. Other modes are
|
|
@@ -307,6 +396,7 @@ static int rtl8393_read_status(struct phy_device *phydev)
|
|
|
|
return err;
|
|
}
|
|
+
|
|
static int rtl8226_read_page(struct phy_device *phydev)
|
|
{
|
|
return __phy_read(phydev, 0x1f);
|
|
@@ -331,20 +421,20 @@ static int rtl8226_read_status(struct phy_device *phydev)
|
|
|
|
// Link status must be read twice
|
|
for (i = 0; i < 2; i++) {
|
|
- rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA402, &val);
|
|
+ read_mmd_phy(port, MMD_VEND2, 0xA402, &val);
|
|
}
|
|
phydev->link = val & BIT(2) ? 1 : 0;
|
|
if (!phydev->link)
|
|
goto out;
|
|
|
|
// Read duplex status
|
|
- ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
|
|
+ ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
|
|
if (ret)
|
|
goto out;
|
|
phydev->duplex = !!(val & BIT(3));
|
|
|
|
// Read speed
|
|
- ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
|
|
+ ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
|
|
switch (val & 0x0630) {
|
|
case 0x0000:
|
|
phydev->speed = SPEED_10;
|
|
@@ -371,7 +461,7 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
-static int rtl8266_advertise_aneg(struct phy_device *phydev)
|
|
+static int rtl8226_advertise_aneg(struct phy_device *phydev)
|
|
{
|
|
int ret = 0;
|
|
u32 v;
|
|
@@ -379,7 +469,7 @@ static int rtl8266_advertise_aneg(struct phy_device *phydev)
|
|
|
|
pr_info("In %s\n", __func__);
|
|
|
|
- ret = rtl930x_read_mmd_phy(port, MMD_AN, 16, &v);
|
|
+ ret = read_mmd_phy(port, MMD_AN, 16, &v);
|
|
if (ret)
|
|
goto out;
|
|
|
|
@@ -388,31 +478,30 @@ static int rtl8266_advertise_aneg(struct phy_device *phydev)
|
|
v |= BIT(7); // HD 100M
|
|
v |= BIT(8); // FD 100M
|
|
|
|
- ret = rtl930x_write_mmd_phy(port, MMD_AN, 16, v);
|
|
+ ret = write_mmd_phy(port, MMD_AN, 16, v);
|
|
|
|
// Allow 1GBit
|
|
- ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA412, &v);
|
|
+ ret = read_mmd_phy(port, MMD_VEND2, 0xA412, &v);
|
|
if (ret)
|
|
goto out;
|
|
v |= BIT(9); // FD 1000M
|
|
|
|
- ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA412, v);
|
|
+ ret = write_mmd_phy(port, MMD_VEND2, 0xA412, v);
|
|
if (ret)
|
|
goto out;
|
|
|
|
// Allow 2.5G
|
|
- ret = rtl930x_read_mmd_phy(port, MMD_AN, 32, &v);
|
|
+ ret = read_mmd_phy(port, MMD_AN, 32, &v);
|
|
if (ret)
|
|
goto out;
|
|
|
|
v |= BIT(7);
|
|
- ret = rtl930x_write_mmd_phy(port, MMD_AN, 32, v);
|
|
+ ret = write_mmd_phy(port, MMD_AN, 32, v);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
-
|
|
static int rtl8226_config_aneg(struct phy_device *phydev)
|
|
{
|
|
int ret = 0;
|
|
@@ -421,26 +510,26 @@ static int rtl8226_config_aneg(struct phy_device *phydev)
|
|
|
|
pr_info("In %s\n", __func__);
|
|
if (phydev->autoneg == AUTONEG_ENABLE) {
|
|
- ret = rtl8266_advertise_aneg(phydev);
|
|
+ ret = rtl8226_advertise_aneg(phydev);
|
|
if (ret)
|
|
goto out;
|
|
// AutoNegotiationEnable
|
|
- ret = rtl930x_read_mmd_phy(port, MMD_AN, 0, &v);
|
|
+ ret = read_mmd_phy(port, MMD_AN, 0, &v);
|
|
if (ret)
|
|
goto out;
|
|
|
|
v |= BIT(12); // Enable AN
|
|
- ret = rtl930x_write_mmd_phy(port, MMD_AN, 0, v);
|
|
+ ret = write_mmd_phy(port, MMD_AN, 0, v);
|
|
if (ret)
|
|
goto out;
|
|
|
|
// RestartAutoNegotiation
|
|
- ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA400, &v);
|
|
+ ret = read_mmd_phy(port, MMD_VEND2, 0xA400, &v);
|
|
if (ret)
|
|
goto out;
|
|
v |= BIT(9);
|
|
|
|
- ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA400, v);
|
|
+ ret = write_mmd_phy(port, MMD_VEND2, 0xA400, v);
|
|
}
|
|
|
|
pr_info("%s: Ret is already: %d\n", __func__, ret);
|
|
@@ -451,6 +540,68 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
+static int rtl8226_get_eee(struct phy_device *phydev,
|
|
+ struct ethtool_eee *e)
|
|
+{
|
|
+ u32 val;
|
|
+ int addr = phydev->mdio.addr;
|
|
+
|
|
+ pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
|
|
+
|
|
+ read_mmd_phy(addr, MMD_AN, 60, &val);
|
|
+ if (e->eee_enabled) {
|
|
+ e->eee_enabled = !!(val & BIT(1));
|
|
+ if (!e->eee_enabled) {
|
|
+ read_mmd_phy(addr, MMD_AN, 62, &val);
|
|
+ e->eee_enabled = !!(val & BIT(0));
|
|
+ }
|
|
+ }
|
|
+ pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rtl8226_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
|
|
+{
|
|
+ int port = phydev->mdio.addr;
|
|
+ u64 poll_state;
|
|
+ bool an_enabled;
|
|
+ u32 val;
|
|
+
|
|
+ pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled);
|
|
+
|
|
+ poll_state = disable_polling(port);
|
|
+
|
|
+ // Remember aneg state
|
|
+ read_mmd_phy(port, MMD_AN, 0, &val);
|
|
+ an_enabled = !!(val & BIT(12));
|
|
+
|
|
+ // Setup 100/1000MBit
|
|
+ read_mmd_phy(port, MMD_AN, 60, &val);
|
|
+ if (e->eee_enabled)
|
|
+ val |= 0x6;
|
|
+ else
|
|
+ val &= 0x6;
|
|
+ write_mmd_phy(port, MMD_AN, 60, val);
|
|
+
|
|
+ // Setup 2.5GBit
|
|
+ read_mmd_phy(port, MMD_AN, 62, &val);
|
|
+ if (e->eee_enabled)
|
|
+ val |= 0x1;
|
|
+ else
|
|
+ val &= 0x1;
|
|
+ write_mmd_phy(port, MMD_AN, 62, val);
|
|
+
|
|
+ // RestartAutoNegotiation
|
|
+ read_mmd_phy(port, MMD_VEND2, 0xA400, &val);
|
|
+ val |= BIT(9);
|
|
+ write_mmd_phy(port, MMD_VEND2, 0xA400, val);
|
|
+
|
|
+ resume_polling(poll_state);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static struct fw_header *rtl838x_request_fw(struct phy_device *phydev,
|
|
const struct firmware *fw,
|
|
const char *name)
|
|
@@ -750,79 +901,6 @@ static int rtl8218b_ext_match_phy_device(struct phy_device *phydev)
|
|
return phydev->phy_id == PHY_ID_RTL8218B_E;
|
|
}
|
|
|
|
-/*
|
|
- * Read an mmd register of the PHY
|
|
- */
|
|
-static int rtl83xx_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val)
|
|
-{
|
|
- u32 v;
|
|
-
|
|
- mutex_lock(&smi_lock);
|
|
-
|
|
- if (rtl838x_smi_wait_op(10000))
|
|
- goto timeout;
|
|
-
|
|
- sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
|
|
- mdelay(10);
|
|
-
|
|
- sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
|
|
-
|
|
- v = addr << 16 | reg;
|
|
- sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3);
|
|
-
|
|
- /* mmd-access | read | cmd-start */
|
|
- v = 1 << 1 | 0 << 2 | 1;
|
|
- sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
|
|
-
|
|
- if (rtl838x_smi_wait_op(10000))
|
|
- goto timeout;
|
|
-
|
|
- *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff;
|
|
-
|
|
- mutex_unlock(&smi_lock);
|
|
- return 0;
|
|
-
|
|
-timeout:
|
|
- mutex_unlock(&smi_lock);
|
|
- return -ETIMEDOUT;
|
|
-}
|
|
-
|
|
-/*
|
|
- * Write to an mmd register of the PHY
|
|
- */
|
|
-static int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val)
|
|
-{
|
|
- u32 v;
|
|
-
|
|
- pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val);
|
|
- val &= 0xffff;
|
|
- mutex_lock(&smi_lock);
|
|
-
|
|
- if (rtl838x_smi_wait_op(10000))
|
|
- goto timeout;
|
|
-
|
|
- sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
|
|
- mdelay(10);
|
|
-
|
|
- sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
|
|
-
|
|
- sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3);
|
|
- sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3);
|
|
- /* mmd-access | write | cmd-start */
|
|
- v = 1 << 1 | 1 << 2 | 1;
|
|
- sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
|
|
-
|
|
- if (rtl838x_smi_wait_op(10000))
|
|
- goto timeout;
|
|
-
|
|
- mutex_unlock(&smi_lock);
|
|
- return 0;
|
|
-
|
|
-timeout:
|
|
- mutex_unlock(&smi_lock);
|
|
- return -ETIMEDOUT;
|
|
-}
|
|
-
|
|
static int rtl8218b_read_mmd(struct phy_device *phydev,
|
|
int devnum, u16 regnum)
|
|
{
|
|
@@ -830,7 +908,7 @@ static int rtl8218b_read_mmd(struct phy_device *phydev,
|
|
u32 val;
|
|
int addr = phydev->mdio.addr;
|
|
|
|
- ret = rtl83xx_read_mmd_phy(addr, devnum, regnum, &val);
|
|
+ ret = read_mmd_phy(addr, devnum, regnum, &val);
|
|
if (ret)
|
|
return ret;
|
|
return val;
|
|
@@ -850,8 +928,7 @@ static int rtl8226_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
|
|
int err;
|
|
u32 val;
|
|
|
|
- err = rtl930x_read_mmd_phy(port, devnum, regnum, &val);
|
|
-
|
|
+ err = read_mmd_phy(port, devnum, regnum, &val);
|
|
if (err)
|
|
return err;
|
|
return val;
|
|
@@ -861,7 +938,7 @@ static int rtl8226_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
|
|
{
|
|
int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr
|
|
|
|
- return rtl930x_write_mmd_phy(port, devnum, regnum, val);
|
|
+ return write_mmd_phy(port, devnum, regnum, val);
|
|
}
|
|
|
|
static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre)
|
|
@@ -957,90 +1034,46 @@ static int rtl8214fc_get_port(struct phy_device *phydev)
|
|
return PORT_MII;
|
|
}
|
|
|
|
-static void rtl8218b_eee_set_u_boot(int port, bool enable)
|
|
-{
|
|
- u32 val;
|
|
- bool an_enabled;
|
|
-
|
|
- /* Set GPHY page to copper */
|
|
- write_phy(port, 0, 30, 0x0001);
|
|
- read_phy(port, 0, 0, &val);
|
|
- an_enabled = val & (1 << 12);
|
|
-
|
|
- if (enable) {
|
|
- /* 100/1000M EEE Capability */
|
|
- write_phy(port, 0, 13, 0x0007);
|
|
- write_phy(port, 0, 14, 0x003C);
|
|
- write_phy(port, 0, 13, 0x4007);
|
|
- write_phy(port, 0, 14, 0x0006);
|
|
-
|
|
- read_phy(port, 0x0A43, 25, &val);
|
|
- val |= 1 << 4;
|
|
- write_phy(port, 0x0A43, 25, val);
|
|
- } else {
|
|
- /* 100/1000M EEE Capability */
|
|
- write_phy(port, 0, 13, 0x0007);
|
|
- write_phy(port, 0, 14, 0x003C);
|
|
- write_phy(port, 0, 13, 0x0007);
|
|
- write_phy(port, 0, 14, 0x0000);
|
|
-
|
|
- read_phy(port, 0x0A43, 25, &val);
|
|
- val &= ~(1 << 4);
|
|
- write_phy(port, 0x0A43, 25, val);
|
|
- }
|
|
-
|
|
- /* Restart AN if enabled */
|
|
- if (an_enabled) {
|
|
- read_phy(port, 0, 0, &val);
|
|
- val |= (1 << 12) | (1 << 9);
|
|
- write_phy(port, 0, 0, val);
|
|
- }
|
|
-
|
|
- /* GPHY page back to auto*/
|
|
- write_phy(port, 0xa42, 29, 0);
|
|
-}
|
|
-
|
|
-// TODO: unused
|
|
-void rtl8380_rtl8218b_eee_set(int port, bool enable)
|
|
+/*
|
|
+ * Enable EEE on the RTL8218B PHYs
|
|
+ * The method used is not the preferred way (which would be based on the MAC-EEE state,
|
|
+ * but the only way that works since the kernel first enables EEE in the MAC
|
|
+ * and then sets up the PHY. The MAC-based approach would require the oppsite.
|
|
+ */
|
|
+void rtl8218d_eee_set(int port, bool enable)
|
|
{
|
|
u32 val;
|
|
bool an_enabled;
|
|
|
|
pr_debug("In %s %d, enable %d\n", __func__, port, enable);
|
|
/* Set GPHY page to copper */
|
|
- write_phy(port, 0xa42, 29, 0x0001);
|
|
+ write_phy(port, 0xa42, 30, 0x0001);
|
|
|
|
read_phy(port, 0, 0, &val);
|
|
- an_enabled = val & (1 << 12);
|
|
+ an_enabled = val & BIT(12);
|
|
|
|
- /* MAC based EEE */
|
|
- read_phy(port, 0xa43, 25, &val);
|
|
- val &= ~(1 << 5);
|
|
- write_phy(port, 0xa43, 25, val);
|
|
-
|
|
- /* 100M / 1000M EEE */
|
|
- if (enable)
|
|
- rtl838x_write_mmd_phy(port, 7, 60, 0x6);
|
|
- else
|
|
- rtl838x_write_mmd_phy(port, 7, 60, 0);
|
|
+ /* Enable 100M (bit 1) / 1000M (bit 2) EEE */
|
|
+ read_mmd_phy(port, 7, 60, &val);
|
|
+ val |= BIT(2) | BIT(1);
|
|
+ write_mmd_phy(port, 7, 60, enable ? 0x6 : 0);
|
|
|
|
/* 500M EEE ability */
|
|
read_phy(port, 0xa42, 20, &val);
|
|
if (enable)
|
|
- val |= 1 << 7;
|
|
+ val |= BIT(7);
|
|
else
|
|
- val &= ~(1 << 7);
|
|
+ val &= ~BIT(7);
|
|
write_phy(port, 0xa42, 20, val);
|
|
|
|
/* Restart AN if enabled */
|
|
if (an_enabled) {
|
|
read_phy(port, 0, 0, &val);
|
|
- val |= (1 << 12) | (1 << 9);
|
|
+ val |= BIT(9);
|
|
write_phy(port, 0, 0, val);
|
|
}
|
|
|
|
/* GPHY page back to auto*/
|
|
- write_phy(port, 0xa42, 29, 0);
|
|
+ write_phy(port, 0xa42, 30, 0);
|
|
}
|
|
|
|
static int rtl8218b_get_eee(struct phy_device *phydev,
|
|
@@ -1049,16 +1082,21 @@ static int rtl8218b_get_eee(struct phy_device *phydev,
|
|
u32 val;
|
|
int addr = phydev->mdio.addr;
|
|
|
|
- pr_debug("In %s, port %d\n", __func__, addr);
|
|
+ pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
|
|
|
|
/* Set GPHY page to copper */
|
|
write_phy(addr, 0xa42, 29, 0x0001);
|
|
|
|
- rtl83xx_read_mmd_phy(addr, 7, 60, &val);
|
|
- if (e->eee_enabled && (!!(val & (1 << 7))))
|
|
- e->eee_enabled = !!(val & (1 << 7));
|
|
- else
|
|
- e->eee_enabled = 0;
|
|
+ read_phy(addr, 7, 60, &val);
|
|
+ if (e->eee_enabled) {
|
|
+ // Verify vs MAC-based EEE
|
|
+ e->eee_enabled = !!(val & BIT(7));
|
|
+ if (!e->eee_enabled) {
|
|
+ read_phy(addr, 0x0A43, 25, &val);
|
|
+ e->eee_enabled = !!(val & BIT(4));
|
|
+ }
|
|
+ }
|
|
+ pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
|
|
|
|
/* GPHY page to auto */
|
|
write_phy(addr, 0xa42, 29, 0x0000);
|
|
@@ -1066,49 +1104,24 @@ static int rtl8218b_get_eee(struct phy_device *phydev,
|
|
return 0;
|
|
}
|
|
|
|
-// TODO: unused
|
|
-void rtl8380_rtl8218b_green_set(int mac, bool enable)
|
|
-{
|
|
- u32 val;
|
|
-
|
|
- /* Set GPHY page to copper */
|
|
- write_phy(mac, 0xa42, 29, 0x0001);
|
|
-
|
|
- write_phy(mac, 0, 27, 0x8011);
|
|
- read_phy(mac, 0, 28, &val);
|
|
- if (enable) {
|
|
- val |= 1 << 9;
|
|
- write_phy(mac, 0, 27, 0x8011);
|
|
- write_phy(mac, 0, 28, val);
|
|
- } else {
|
|
- val &= ~(1 << 9);
|
|
- write_phy(mac, 0, 27, 0x8011);
|
|
- write_phy(mac, 0, 28, val);
|
|
- }
|
|
-
|
|
- /* GPHY page to auto */
|
|
- write_phy(mac, 0xa42, 29, 0x0000);
|
|
-}
|
|
-
|
|
-// TODO: unused
|
|
-int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e)
|
|
+static int rtl8218d_get_eee(struct phy_device *phydev,
|
|
+ struct ethtool_eee *e)
|
|
{
|
|
u32 val;
|
|
int addr = phydev->mdio.addr;
|
|
|
|
- pr_debug("In %s %d\n", __func__, addr);
|
|
+ pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
|
|
+
|
|
/* Set GPHY page to copper */
|
|
- write_phy(addr, 0xa42, 29, 0x0001);
|
|
+ write_phy(addr, 0xa42, 30, 0x0001);
|
|
|
|
- write_phy(addr, 0, 27, 0x8011);
|
|
- read_phy(addr, 0, 28, &val);
|
|
- if (e->eee_enabled && (!!(val & (1 << 9))))
|
|
- e->eee_enabled = !!(val & (1 << 9));
|
|
- else
|
|
- e->eee_enabled = 0;
|
|
+ read_phy(addr, 7, 60, &val);
|
|
+ if (e->eee_enabled)
|
|
+ e->eee_enabled = !!(val & BIT(7));
|
|
+ pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
|
|
|
|
/* GPHY page to auto */
|
|
- write_phy(addr, 0xa42, 29, 0x0000);
|
|
+ write_phy(addr, 0xa42, 30, 0x0000);
|
|
|
|
return 0;
|
|
}
|
|
@@ -1116,20 +1129,56 @@ int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e
|
|
static int rtl8214fc_set_eee(struct phy_device *phydev,
|
|
struct ethtool_eee *e)
|
|
{
|
|
- u32 pollMask;
|
|
- int addr = phydev->mdio.addr;
|
|
+ u32 poll_state;
|
|
+ int port = phydev->mdio.addr;
|
|
+ bool an_enabled;
|
|
+ u32 val;
|
|
|
|
- pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled);
|
|
+ pr_debug("In %s port %d, enabled %d\n", __func__, port, e->eee_enabled);
|
|
|
|
- if (rtl8380_rtl8214fc_media_is_fibre(addr)) {
|
|
- netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr);
|
|
+ if (rtl8380_rtl8214fc_media_is_fibre(port)) {
|
|
+ netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", port);
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
- pollMask = sw_r32(RTL838X_SMI_POLL_CTRL);
|
|
- sw_w32(0, RTL838X_SMI_POLL_CTRL);
|
|
- rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled);
|
|
- sw_w32(pollMask, RTL838X_SMI_POLL_CTRL);
|
|
+ poll_state = disable_polling(port);
|
|
+
|
|
+ /* Set GPHY page to copper */
|
|
+ write_phy(port, 0xa42, 29, 0x0001);
|
|
+
|
|
+ // Get auto-negotiation status
|
|
+ read_phy(port, 0, 0, &val);
|
|
+ an_enabled = val & BIT(12);
|
|
+
|
|
+ pr_info("%s: aneg: %d\n", __func__, an_enabled);
|
|
+ read_phy(port, 0x0A43, 25, &val);
|
|
+ val &= ~BIT(5); // Use MAC-based EEE
|
|
+ write_phy(port, 0x0A43, 25, val);
|
|
+
|
|
+ /* Enable 100M (bit 1) / 1000M (bit 2) EEE */
|
|
+ write_phy(port, 7, 60, e->eee_enabled ? 0x6 : 0);
|
|
+
|
|
+ /* 500M EEE ability */
|
|
+ read_phy(port, 0xa42, 20, &val);
|
|
+ if (e->eee_enabled)
|
|
+ val |= BIT(7);
|
|
+ else
|
|
+ val &= ~BIT(7);
|
|
+ write_phy(port, 0xa42, 20, val);
|
|
+
|
|
+ /* Restart AN if enabled */
|
|
+ if (an_enabled) {
|
|
+ pr_info("%s: doing aneg\n", __func__);
|
|
+ read_phy(port, 0, 0, &val);
|
|
+ val |= BIT(9);
|
|
+ write_phy(port, 0, 0, val);
|
|
+ }
|
|
+
|
|
+ /* GPHY page back to auto*/
|
|
+ write_phy(port, 0xa42, 29, 0);
|
|
+
|
|
+ resume_polling(poll_state);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1147,18 +1196,72 @@ static int rtl8214fc_get_eee(struct phy_device *phydev,
|
|
return rtl8218b_get_eee(phydev, e);
|
|
}
|
|
|
|
-static int rtl8218b_set_eee(struct phy_device *phydev,
|
|
- struct ethtool_eee *e)
|
|
+static int rtl8218b_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
|
|
+{
|
|
+ int port = phydev->mdio.addr;
|
|
+ u64 poll_state;
|
|
+ u32 val;
|
|
+ bool an_enabled;
|
|
+
|
|
+ pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled);
|
|
+
|
|
+ poll_state = disable_polling(port);
|
|
+
|
|
+ /* Set GPHY page to copper */
|
|
+ write_phy(port, 0, 30, 0x0001);
|
|
+ read_phy(port, 0, 0, &val);
|
|
+ an_enabled = val & BIT(12);
|
|
+
|
|
+ if (e->eee_enabled) {
|
|
+ /* 100/1000M EEE Capability */
|
|
+ write_phy(port, 0, 13, 0x0007);
|
|
+ write_phy(port, 0, 14, 0x003C);
|
|
+ write_phy(port, 0, 13, 0x4007);
|
|
+ write_phy(port, 0, 14, 0x0006);
|
|
+
|
|
+ read_phy(port, 0x0A43, 25, &val);
|
|
+ val |= BIT(4);
|
|
+ write_phy(port, 0x0A43, 25, val);
|
|
+ } else {
|
|
+ /* 100/1000M EEE Capability */
|
|
+ write_phy(port, 0, 13, 0x0007);
|
|
+ write_phy(port, 0, 14, 0x003C);
|
|
+ write_phy(port, 0, 13, 0x0007);
|
|
+ write_phy(port, 0, 14, 0x0000);
|
|
+
|
|
+ read_phy(port, 0x0A43, 25, &val);
|
|
+ val &= ~BIT(4);
|
|
+ write_phy(port, 0x0A43, 25, val);
|
|
+ }
|
|
+
|
|
+ /* Restart AN if enabled */
|
|
+ if (an_enabled) {
|
|
+ read_phy(port, 0, 0, &val);
|
|
+ val |= BIT(9);
|
|
+ write_phy(port, 0, 0, val);
|
|
+ }
|
|
+
|
|
+ /* GPHY page back to auto*/
|
|
+ write_phy(port, 0xa42, 30, 0);
|
|
+
|
|
+ pr_info("%s done\n", __func__);
|
|
+ resume_polling(poll_state);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rtl8218d_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
|
|
{
|
|
- u32 pollMask;
|
|
int addr = phydev->mdio.addr;
|
|
+ u64 poll_state;
|
|
+
|
|
+ pr_info("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled);
|
|
|
|
- pr_debug("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled);
|
|
+ poll_state = disable_polling(addr);
|
|
|
|
- pollMask = sw_r32(RTL838X_SMI_POLL_CTRL);
|
|
- sw_w32(0, RTL838X_SMI_POLL_CTRL);
|
|
- rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled);
|
|
- sw_w32(pollMask, RTL838X_SMI_POLL_CTRL);
|
|
+ rtl8218d_eee_set(addr, (bool) e->eee_enabled);
|
|
+
|
|
+ resume_polling(poll_state);
|
|
|
|
return 0;
|
|
}
|
|
@@ -1296,7 +1399,7 @@ static int rtl8380_configure_rtl8214fc(struct phy_device *phydev)
|
|
for (i = 0; i < 4; i++) {
|
|
for (l = 0; l < 100; l++) {
|
|
read_phy(mac + i, 0xb80, 0x10, &val);
|
|
- if (val & 0x40)
|
|
+ if (val & 0x80)
|
|
break;
|
|
}
|
|
if (l >= 100) {
|
|
@@ -1791,7 +1894,10 @@ static struct phy_driver rtl83xx_phy_driver[] = {
|
|
.suspend = genphy_suspend,
|
|
.resume = genphy_resume,
|
|
.set_loopback = genphy_loopback,
|
|
- }, {
|
|
+ .set_eee = rtl8218d_set_eee,
|
|
+ .get_eee = rtl8218d_get_eee,
|
|
+ },
|
|
+ {
|
|
PHY_ID_MATCH_MODEL(PHY_ID_RTL8226),
|
|
.name = "REALTEK RTL8226",
|
|
.features = PHY_GBIT_FEATURES,
|
|
@@ -1805,6 +1911,8 @@ static struct phy_driver rtl83xx_phy_driver[] = {
|
|
.write_page = rtl8226_write_page,
|
|
.read_status = rtl8226_read_status,
|
|
.config_aneg = rtl8226_config_aneg,
|
|
+ .set_eee = rtl8226_set_eee,
|
|
+ .get_eee = rtl8226_get_eee,
|
|
},
|
|
{
|
|
PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
|
|
diff --git a/target/linux/realtek/image/Makefile b/target/linux/realtek/image/Makefile
|
|
index 424726c8a9..a4b2ea892c 100644
|
|
--- a/target/linux/realtek/image/Makefile
|
|
+++ b/target/linux/realtek/image/Makefile
|
|
@@ -118,4 +118,13 @@ define Device/zyxel_gs1900-8hp-v2
|
|
endef
|
|
TARGET_DEVICES += zyxel_gs1900-8hp-v2
|
|
|
|
+define Device/edgecore_ecs4100-12ph
|
|
+ SOC := rtl8392
|
|
+ IMAGE_SIZE := 14336k
|
|
+ DEVICE_VENDOR := Edgecore
|
|
+ DEVICE_MODEL := ECS4100-12PH
|
|
+ DEVICE_PACKAGES += lua-rs232
|
|
+endef
|
|
+TARGET_DEVICES += edgecore_ecs4100-12ph
|
|
+
|
|
$(eval $(call BuildImage))
|
|
diff --git a/target/linux/realtek/patches-5.4/301-gpio-add-rtl838x-driver.patch b/target/linux/realtek/patches-5.4/301-gpio-add-rtl838x-driver.patch
|
|
index 4f5901d87f..e4bb9d90d2 100644
|
|
--- a/target/linux/realtek/patches-5.4/301-gpio-add-rtl838x-driver.patch
|
|
+++ b/target/linux/realtek/patches-5.4/301-gpio-add-rtl838x-driver.patch
|
|
@@ -26,7 +26,7 @@
|
|
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
|
|
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
|
|
+obj-$(CONFIG_GPIO_RTL8231) += gpio-rtl8231.o
|
|
-+obj-$(CONFIG_GPIO_RTL838X) += gpio-rtl838x.o
|
|
++#obj-$(CONFIG_GPIO_RTL838X) += gpio-rtl838x.o
|
|
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
|
|
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
|
|
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
|
|
diff --git a/target/linux/realtek/patches-5.4/500-gpio-Add-Realtek-Otto-GPIO-support.patch b/target/linux/realtek/patches-5.4/500-gpio-Add-Realtek-Otto-GPIO-support.patch
|
|
new file mode 100644
|
|
index 0000000000..f6964c6c5f
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/patches-5.4/500-gpio-Add-Realtek-Otto-GPIO-support.patch
|
|
@@ -0,0 +1,412 @@
|
|
+From patchwork Tue Mar 30 17:48:43 2021
|
|
+Content-Type: text/plain; charset="utf-8"
|
|
+MIME-Version: 1.0
|
|
+Content-Transfer-Encoding: 7bit
|
|
+Subject: [v6,2/2] gpio: Add Realtek Otto GPIO support
|
|
+X-Patchwork-Submitter: Sander Vanheule <sander@svanheule.net>
|
|
+X-Patchwork-Id: 411993
|
|
+Message-Id: <2d00064530e88fbef4fff2e759a66bdda261cca7.1617126277.git.sander@svanheule.net>
|
|
+To: devicetree@vger.kernel.org, linux-gpio@vger.kernel.org
|
|
+Cc: andy.shevchenko@gmail.com, bert@biot.com,
|
|
+ bgolaszewski@baylibre.com, linus.walleij@linaro.org,
|
|
+ linux-kernel@vger.kernel.org, maz@kernel.org, robh+dt@kernel.org,
|
|
+ Sander Vanheule <sander@svanheule.net>
|
|
+Date: Tue, 30 Mar 2021 19:48:43 +0200
|
|
+From: Sander Vanheule <sander@svanheule.net>
|
|
+List-Id: <linux-gpio.vger.kernel.org>
|
|
+
|
|
+Realtek MIPS SoCs (platform name Otto) have GPIO controllers with up to
|
|
+64 GPIOs, divided over two banks. Each bank has a set of registers for
|
|
+32 GPIOs, with support for edge-triggered interrupts.
|
|
+
|
|
+Each GPIO bank consists of four 8-bit GPIO ports (ABCD and EFGH). Most
|
|
+registers pack one bit per GPIO, except for the IMR register, which
|
|
+packs two bits per GPIO (AB-CD).
|
|
+
|
|
+Although the byte order is currently assumed to have port A..D at offset
|
|
+0x0..0x3, this has been observed to be reversed on other, Lexra-based,
|
|
+SoCs (e.g. RTL8196E/97D/97F).
|
|
+
|
|
+Interrupt support is disabled for the fallback devicetree-compatible
|
|
+'realtek,otto-gpio'. This allows for quick support of GPIO banks in
|
|
+which the byte order would be unknown. In this case, the port ordering
|
|
+in the IMR registers may not match the reversed order in the other
|
|
+registers (DCBA, and BA-DC or DC-BA).
|
|
+
|
|
+Signed-off-by: Sander Vanheule <sander@svanheule.net>
|
|
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
|
|
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
|
|
+---
|
|
+ drivers/gpio/Kconfig | 13 ++
|
|
+ drivers/gpio/Makefile | 1 +
|
|
+ drivers/gpio/gpio-realtek-otto.c | 325 +++++++++++++++++++++++++++++++
|
|
+ 3 files changed, 339 insertions(+)
|
|
+ create mode 100644 drivers/gpio/gpio-realtek-otto.c
|
|
+
|
|
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
|
|
+index e3607ec4c2e8..6fb13d6507db 100644
|
|
+--- a/drivers/gpio/Kconfig
|
|
++++ b/drivers/gpio/Kconfig
|
|
+@@ -502,6 +502,19 @@ config GPIO_RDA
|
|
+ help
|
|
+ Say Y here to support RDA Micro GPIO controller.
|
|
+
|
|
++config GPIO_REALTEK_OTTO
|
|
++ tristate "Realtek Otto GPIO support"
|
|
++ depends on RTL838X
|
|
++ default RTL838X
|
|
++ select GPIO_GENERIC
|
|
++ select GPIOLIB_IRQCHIP
|
|
++ help
|
|
++ The GPIO controller on the Otto MIPS platform supports up to two
|
|
++ banks of 32 GPIOs, with edge triggered interrupts. The 32 GPIOs
|
|
++ are grouped in four 8-bit wide ports.
|
|
++
|
|
++ When built as a module, the module will be called realtek_otto_gpio.
|
|
++
|
|
+ config GPIO_REG
|
|
+ bool
|
|
+ help
|
|
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
|
|
+index c58a90a3c3b1..8ace5934e3c3 100644
|
|
+--- a/drivers/gpio/Makefile
|
|
++++ b/drivers/gpio/Makefile
|
|
+@@ -124,6 +124,7 @@ obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
|
|
+ obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
|
|
+ obj-$(CONFIG_GPIO_RDA) += gpio-rda.o
|
|
+ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
|
|
++obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
|
|
+ obj-$(CONFIG_GPIO_REG) += gpio-reg.o
|
|
+ obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
|
|
+ obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
|
|
+diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c
|
|
+new file mode 100644
|
|
+index 000000000000..cb64fb5a51aa
|
|
+--- /dev/null
|
|
++++ b/drivers/gpio/gpio-realtek-otto.c
|
|
+@@ -0,0 +1,325 @@
|
|
++// SPDX-License-Identifier: GPL-2.0-only
|
|
++
|
|
++#include <linux/gpio/driver.h>
|
|
++#include <linux/irq.h>
|
|
++//#include <linux/minmax.h>
|
|
++#include <linux/mod_devicetable.h>
|
|
++#include <linux/module.h>
|
|
++#include <linux/platform_device.h>
|
|
++#include <linux/property.h>
|
|
++
|
|
++/*
|
|
++ * Total register block size is 0x1C for one bank of four ports (A, B, C, D).
|
|
++ * An optional second bank, with ports E, F, G, and H, may be present, starting
|
|
++ * at register offset 0x1C.
|
|
++ */
|
|
++
|
|
++/*
|
|
++ * Pin select: (0) "normal", (1) "dedicate peripheral"
|
|
++ * Not used on RTL8380/RTL8390, peripheral selection is managed by control bits
|
|
++ * in the peripheral registers.
|
|
++ */
|
|
++#define REALTEK_GPIO_REG_CNR 0x00
|
|
++/* Clear bit (0) for input, set bit (1) for output */
|
|
++#define REALTEK_GPIO_REG_DIR 0x08
|
|
++#define REALTEK_GPIO_REG_DATA 0x0C
|
|
++/* Read bit for IRQ status, write 1 to clear IRQ */
|
|
++#define REALTEK_GPIO_REG_ISR 0x10
|
|
++/* Two bits per GPIO in IMR registers */
|
|
++#define REALTEK_GPIO_REG_IMR 0x14
|
|
++#define REALTEK_GPIO_REG_IMR_AB 0x14
|
|
++#define REALTEK_GPIO_REG_IMR_CD 0x18
|
|
++#define REALTEK_GPIO_IMR_LINE_MASK GENMASK(1, 0)
|
|
++#define REALTEK_GPIO_IRQ_EDGE_FALLING 1
|
|
++#define REALTEK_GPIO_IRQ_EDGE_RISING 2
|
|
++#define REALTEK_GPIO_IRQ_EDGE_BOTH 3
|
|
++
|
|
++#define REALTEK_GPIO_MAX 32
|
|
++#define REALTEK_GPIO_PORTS_PER_BANK 4
|
|
++
|
|
++/**
|
|
++ * realtek_gpio_ctrl - Realtek Otto GPIO driver data
|
|
++ *
|
|
++ * @gc: Associated gpio_chip instance
|
|
++ * @base: Base address of the register block for a GPIO bank
|
|
++ * @lock: Lock for accessing the IRQ registers and values
|
|
++ * @intr_mask: Mask for interrupts lines
|
|
++ * @intr_type: Interrupt type selection
|
|
++ *
|
|
++ * Because the interrupt mask register (IMR) combines the function of IRQ type
|
|
++ * selection and masking, two extra values are stored. @intr_mask is used to
|
|
++ * mask/unmask the interrupts for a GPIO port, and @intr_type is used to store
|
|
++ * the selected interrupt types. The logical AND of these values is written to
|
|
++ * IMR on changes.
|
|
++ */
|
|
++struct realtek_gpio_ctrl {
|
|
++ struct gpio_chip gc;
|
|
++ void __iomem *base;
|
|
++ raw_spinlock_t lock;
|
|
++ u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
|
|
++ u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
|
|
++};
|
|
++
|
|
++/* Expand with more flags as devices with other quirks are added */
|
|
++enum realtek_gpio_flags {
|
|
++ /*
|
|
++ * Allow disabling interrupts, for cases where the port order is
|
|
++ * unknown. This may result in a port mismatch between ISR and IMR.
|
|
++ * An interrupt would appear to come from a different line than the
|
|
++ * line the IRQ handler was assigned to, causing uncaught interrupts.
|
|
++ */
|
|
++ GPIO_INTERRUPTS_DISABLED = BIT(0),
|
|
++};
|
|
++
|
|
++static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
|
|
++{
|
|
++ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
|
|
++
|
|
++ return container_of(gc, struct realtek_gpio_ctrl, gc);
|
|
++}
|
|
++
|
|
++/*
|
|
++ * Normal port order register access
|
|
++ *
|
|
++ * Port information is stored with the first port at offset 0, followed by the
|
|
++ * second, etc. Most registers store one bit per GPIO and use a u8 value per
|
|
++ * port. The two interrupt mask registers store two bits per GPIO, so use u16
|
|
++ * values.
|
|
++ */
|
|
++static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl,
|
|
++ unsigned int port, u16 irq_type, u16 irq_mask)
|
|
++{
|
|
++ iowrite16(irq_type & irq_mask, ctrl->base + REALTEK_GPIO_REG_IMR + 2 * port);
|
|
++}
|
|
++
|
|
++static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl,
|
|
++ unsigned int port, u8 mask)
|
|
++{
|
|
++ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + port);
|
|
++}
|
|
++
|
|
++static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port)
|
|
++{
|
|
++ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + port);
|
|
++}
|
|
++
|
|
++/* Set the rising and falling edge mask bits for a GPIO port pin */
|
|
++static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value)
|
|
++{
|
|
++ return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin;
|
|
++}
|
|
++
|
|
++static void realtek_gpio_irq_ack(struct irq_data *data)
|
|
++{
|
|
++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
|
|
++ irq_hw_number_t line = irqd_to_hwirq(data);
|
|
++ unsigned int port = line / 8;
|
|
++ unsigned int port_pin = line % 8;
|
|
++
|
|
++ realtek_gpio_clear_isr(ctrl, port, BIT(port_pin));
|
|
++}
|
|
++
|
|
++static void realtek_gpio_irq_unmask(struct irq_data *data)
|
|
++{
|
|
++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
|
|
++ unsigned int line = irqd_to_hwirq(data);
|
|
++ unsigned int port = line / 8;
|
|
++ unsigned int port_pin = line % 8;
|
|
++ unsigned long flags;
|
|
++ u16 m;
|
|
++
|
|
++ raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
++ m = ctrl->intr_mask[port];
|
|
++ m |= realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
|
|
++ ctrl->intr_mask[port] = m;
|
|
++ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m);
|
|
++ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
++}
|
|
++
|
|
++static void realtek_gpio_irq_mask(struct irq_data *data)
|
|
++{
|
|
++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
|
|
++ unsigned int line = irqd_to_hwirq(data);
|
|
++ unsigned int port = line / 8;
|
|
++ unsigned int port_pin = line % 8;
|
|
++ unsigned long flags;
|
|
++ u16 m;
|
|
++
|
|
++ raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
++ m = ctrl->intr_mask[port];
|
|
++ m &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
|
|
++ ctrl->intr_mask[port] = m;
|
|
++ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m);
|
|
++ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
++}
|
|
++
|
|
++static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
|
++{
|
|
++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
|
|
++ unsigned int line = irqd_to_hwirq(data);
|
|
++ unsigned int port = line / 8;
|
|
++ unsigned int port_pin = line % 8;
|
|
++ unsigned long flags;
|
|
++ u16 type, t;
|
|
++
|
|
++ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
|
|
++ case IRQ_TYPE_EDGE_FALLING:
|
|
++ type = REALTEK_GPIO_IRQ_EDGE_FALLING;
|
|
++ break;
|
|
++ case IRQ_TYPE_EDGE_RISING:
|
|
++ type = REALTEK_GPIO_IRQ_EDGE_RISING;
|
|
++ break;
|
|
++ case IRQ_TYPE_EDGE_BOTH:
|
|
++ type = REALTEK_GPIO_IRQ_EDGE_BOTH;
|
|
++ break;
|
|
++ default:
|
|
++ return -EINVAL;
|
|
++ }
|
|
++
|
|
++ irq_set_handler_locked(data, handle_edge_irq);
|
|
++
|
|
++ raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
++ t = ctrl->intr_type[port];
|
|
++ t &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
|
|
++ t |= realtek_gpio_imr_bits(port_pin, type);
|
|
++ ctrl->intr_type[port] = t;
|
|
++ realtek_gpio_write_imr(ctrl, port, t, ctrl->intr_mask[port]);
|
|
++ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static void realtek_gpio_irq_handler(struct irq_desc *desc)
|
|
++{
|
|
++ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
|
|
++ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
|
|
++ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
|
|
++ unsigned int lines_done;
|
|
++ unsigned int port_pin_count;
|
|
++ unsigned int irq;
|
|
++ unsigned long status;
|
|
++ int offset;
|
|
++
|
|
++ chained_irq_enter(irq_chip, desc);
|
|
++
|
|
++ for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) {
|
|
++ status = realtek_gpio_read_isr(ctrl, lines_done / 8);
|
|
++ port_pin_count = min(gc->ngpio - lines_done, 8U);
|
|
++ for_each_set_bit(offset, &status, port_pin_count) {
|
|
++ irq = irq_find_mapping(gc->irq.domain, offset);
|
|
++ generic_handle_irq(irq);
|
|
++ }
|
|
++ }
|
|
++
|
|
++ chained_irq_exit(irq_chip, desc);
|
|
++}
|
|
++
|
|
++static int realtek_gpio_irq_init(struct gpio_chip *gc)
|
|
++{
|
|
++ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
|
|
++ unsigned int port;
|
|
++
|
|
++ for (port = 0; (port * 8) < gc->ngpio; port++) {
|
|
++ realtek_gpio_write_imr(ctrl, port, 0, 0);
|
|
++ realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0));
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static struct irq_chip realtek_gpio_irq_chip = {
|
|
++ .name = "realtek-otto-gpio",
|
|
++ .irq_ack = realtek_gpio_irq_ack,
|
|
++ .irq_mask = realtek_gpio_irq_mask,
|
|
++ .irq_unmask = realtek_gpio_irq_unmask,
|
|
++ .irq_set_type = realtek_gpio_irq_set_type,
|
|
++};
|
|
++
|
|
++static const struct of_device_id realtek_gpio_of_match[] = {
|
|
++ {
|
|
++ .compatible = "realtek,otto-gpio",
|
|
++ .data = (void *)GPIO_INTERRUPTS_DISABLED,
|
|
++ },
|
|
++ {
|
|
++ .compatible = "realtek,rtl8380-gpio",
|
|
++ },
|
|
++ {
|
|
++ .compatible = "realtek,rtl8390-gpio",
|
|
++ },
|
|
++ {}
|
|
++};
|
|
++MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
|
|
++
|
|
++static int realtek_gpio_probe(struct platform_device *pdev)
|
|
++{
|
|
++ struct device *dev = &pdev->dev;
|
|
++ unsigned int dev_flags;
|
|
++ struct gpio_irq_chip *girq;
|
|
++ struct realtek_gpio_ctrl *ctrl;
|
|
++ u32 ngpios;
|
|
++ int err, irq;
|
|
++
|
|
++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
|
|
++ if (!ctrl)
|
|
++ return -ENOMEM;
|
|
++
|
|
++ dev_flags = (unsigned int) device_get_match_data(dev);
|
|
++
|
|
++ ngpios = REALTEK_GPIO_MAX;
|
|
++ device_property_read_u32(dev, "ngpios", &ngpios);
|
|
++
|
|
++ if (ngpios > REALTEK_GPIO_MAX) {
|
|
++ dev_err(&pdev->dev, "invalid ngpios (max. %d)\n",
|
|
++ REALTEK_GPIO_MAX);
|
|
++ return -EINVAL;
|
|
++ }
|
|
++
|
|
++ ctrl->base = devm_platform_ioremap_resource(pdev, 0);
|
|
++ if (IS_ERR(ctrl->base))
|
|
++ return PTR_ERR(ctrl->base);
|
|
++
|
|
++ raw_spin_lock_init(&ctrl->lock);
|
|
++
|
|
++ err = bgpio_init(&ctrl->gc, dev, 4,
|
|
++ ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL,
|
|
++ ctrl->base + REALTEK_GPIO_REG_DIR, NULL,
|
|
++ BGPIOF_BIG_ENDIAN_BYTE_ORDER);
|
|
++ if (err) {
|
|
++ dev_err(dev, "unable to init generic GPIO");
|
|
++ return err;
|
|
++ }
|
|
++
|
|
++ ctrl->gc.ngpio = ngpios;
|
|
++ ctrl->gc.owner = THIS_MODULE;
|
|
++
|
|
++ irq = platform_get_irq_optional(pdev, 0);
|
|
++ if (!(dev_flags & GPIO_INTERRUPTS_DISABLED) && irq > 0) {
|
|
++ girq = &ctrl->gc.irq;
|
|
++ girq->chip = &realtek_gpio_irq_chip;
|
|
++ girq->default_type = IRQ_TYPE_NONE;
|
|
++ girq->handler = handle_bad_irq;
|
|
++ girq->parent_handler = realtek_gpio_irq_handler;
|
|
++ girq->num_parents = 1;
|
|
++ girq->parents = devm_kcalloc(dev, girq->num_parents,
|
|
++ sizeof(*girq->parents), GFP_KERNEL);
|
|
++ if (!girq->parents)
|
|
++ return -ENOMEM;
|
|
++ girq->parents[0] = irq;
|
|
++ girq->init_hw = realtek_gpio_irq_init;
|
|
++ }
|
|
++
|
|
++ return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
|
|
++}
|
|
++
|
|
++static struct platform_driver realtek_gpio_driver = {
|
|
++ .driver = {
|
|
++ .name = "realtek-otto-gpio",
|
|
++ .of_match_table = realtek_gpio_of_match,
|
|
++ },
|
|
++ .probe = realtek_gpio_probe,
|
|
++};
|
|
++module_platform_driver(realtek_gpio_driver);
|
|
++
|
|
++MODULE_DESCRIPTION("Realtek Otto GPIO support");
|
|
++MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
|
|
++MODULE_LICENSE("GPL v2");
|
|
diff --git a/target/linux/realtek/patches-5.4/706-sysled.patch b/target/linux/realtek/patches-5.4/706-sysled.patch
|
|
new file mode 100644
|
|
index 0000000000..c13885d5ac
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/patches-5.4/706-sysled.patch
|
|
@@ -0,0 +1,294 @@
|
|
+From c1a89fdf22862379bb4150fc76504e2d3384cd67 Mon Sep 17 00:00:00 2001
|
|
+From: Bert Vermeulen <bert@biot.com>
|
|
+Date: Mon, 1 Mar 2021 12:41:35 +0100
|
|
+Subject: [PATCH] mfd: Add Realtek RTL838x/RTL839x sys-led driver
|
|
+
|
|
+---
|
|
+ drivers/mfd/Kconfig | 11 ++
|
|
+ drivers/mfd/Makefile | 1 +
|
|
+ drivers/mfd/realtek-eio.c | 243 ++++++++++++++++++++++++++++++++++++++
|
|
+ 3 files changed, 255 insertions(+)
|
|
+ create mode 100644 drivers/mfd/realtek-eio.c
|
|
+
|
|
+Index: linux-5.4.92/drivers/mfd/Kconfig
|
|
+===================================================================
|
|
+--- linux-5.4.92.orig/drivers/mfd/Kconfig
|
|
++++ linux-5.4.92/drivers/mfd/Kconfig
|
|
+@@ -923,6 +923,16 @@ config MFD_RETU
|
|
+ Retu and Tahvo are a multi-function devices found on Nokia
|
|
+ Internet Tablets (770, N800 and N810).
|
|
+
|
|
++config MFD_REALTEK_EIO
|
|
++ tristate "Realtek external LED and GPIO driver"
|
|
++ select MFD_CORE
|
|
++ select MFD_SYSCON
|
|
++ select GENERIC_PINCONF
|
|
++ default y
|
|
++ help
|
|
++ Say yes here if you want external LED/GPIO support for Realtek
|
|
++ switch SoCs.
|
|
++
|
|
+ config MFD_PCF50633
|
|
+ tristate "NXP PCF50633"
|
|
+ depends on I2C
|
|
+Index: linux-5.4.92/drivers/mfd/realtek-eio.c
|
|
+===================================================================
|
|
+--- /dev/null
|
|
++++ linux-5.4.92/drivers/mfd/realtek-eio.c
|
|
+@@ -0,0 +1,246 @@
|
|
++// SPDX-License-Identifier: GPL-2.0-or-later
|
|
++
|
|
++#include <linux/leds.h>
|
|
++#include <linux/mfd/core.h>
|
|
++#include <linux/mfd/syscon.h>
|
|
++#include <linux/module.h>
|
|
++#include <linux/of.h>
|
|
++#include <linux/of_address.h>
|
|
++#include <linux/of_platform.h>
|
|
++#include <linux/platform_device.h>
|
|
++#include <linux/regmap.h>
|
|
++
|
|
++#define REALTEK_EIO_GLOBAL_CTRL 0x0
|
|
++
|
|
++/*
|
|
++ * Management of external RTL8231 GPIO expanders.
|
|
++ * One RTL8231's GPIO registers can be shadowed to the internal GPIO_DIR
|
|
++ * and GPIO_DAT registers.
|
|
++ */
|
|
++#define RTL8380_EIO_GPIO_INDIRECT_ACCESS 0x9C
|
|
++#define RTL8380_EIO_GPIO_CTRL 0xE0
|
|
++#define RTL8380_EIO_GPIO_DIR(pin) (0xE4 + 4*((pin)/32))
|
|
++#define RTL8380_EIO_GPIO_DAT(pin) (0xEC + 4*((pin)/32))
|
|
++
|
|
++struct realtek_eio_ctrl;
|
|
++
|
|
++struct realtek_eio_data {
|
|
++ unsigned int sys_led_pos;
|
|
++ const struct mfd_cell *mfd_devices;
|
|
++ unsigned int mfd_device_count;
|
|
++};
|
|
++
|
|
++struct realtek_eio_ctrl {
|
|
++ struct device *dev;
|
|
++ struct regmap *map;
|
|
++ const struct realtek_eio_data *data;
|
|
++ struct led_classdev sys_led;
|
|
++ bool active_low;
|
|
++};
|
|
++
|
|
++
|
|
++#define OF_MFD_CELL(_name, _compat) \
|
|
++ { \
|
|
++ .name = (_name), \
|
|
++ .of_compatible = (_compat), \
|
|
++ }
|
|
++
|
|
++/*
|
|
++ * Realtek hardware system LED
|
|
++ *
|
|
++ * The switch SoC supports one hardware managed direct LED output
|
|
++ * to manage a system LED, with two supported blinking rates.
|
|
++ */
|
|
++enum {
|
|
++ REALTEK_SYS_LED_OFF = 0,
|
|
++ REALTEK_SYS_LED_BLINK_64MS,
|
|
++ REALTEK_SYS_LED_BLINK_1024MS,
|
|
++ REALTEK_SYS_LED_ON
|
|
++};
|
|
++
|
|
++static void realtek_sys_led_set(const struct realtek_eio_ctrl *ctrl,
|
|
++ unsigned int mode)
|
|
++{
|
|
++ regmap_update_bits(ctrl->map, REALTEK_EIO_GLOBAL_CTRL,
|
|
++ (0x3 << ctrl->data->sys_led_pos),
|
|
++ ((mode & 0x3) << ctrl->data->sys_led_pos));
|
|
++}
|
|
++
|
|
++static void realtek_sys_led_brightness_set(struct led_classdev *led_cdev,
|
|
++ enum led_brightness brightness)
|
|
++{
|
|
++ struct realtek_eio_ctrl *ctrl =
|
|
++ container_of(led_cdev, struct realtek_eio_ctrl, sys_led);
|
|
++
|
|
++ if ((!ctrl->active_low && brightness == LED_OFF) ||
|
|
++ (ctrl->active_low && brightness != LED_OFF))
|
|
++ realtek_sys_led_set(ctrl, REALTEK_SYS_LED_OFF);
|
|
++ else
|
|
++ realtek_sys_led_set(ctrl, REALTEK_SYS_LED_ON);
|
|
++}
|
|
++
|
|
++static enum led_brightness realtek_sys_led_brightness_get(
|
|
++ struct led_classdev *led_cdev)
|
|
++{
|
|
++ struct realtek_eio_ctrl *ctrl =
|
|
++ container_of(led_cdev, struct realtek_eio_ctrl, sys_led);
|
|
++ u32 val;
|
|
++
|
|
++ regmap_read(ctrl->map, REALTEK_EIO_GLOBAL_CTRL, &val);
|
|
++ val = (val >> ctrl->data->sys_led_pos) & 0x3;
|
|
++
|
|
++ if ((!ctrl->active_low && val == REALTEK_SYS_LED_OFF) ||
|
|
++ (ctrl->active_low && val == REALTEK_SYS_LED_ON))
|
|
++ return LED_OFF;
|
|
++ else
|
|
++ return LED_ON;
|
|
++}
|
|
++
|
|
++static int realtek_sys_led_blink_set(struct led_classdev *led_cdev,
|
|
++ unsigned long *delay_on, unsigned long *delay_off)
|
|
++{
|
|
++ struct realtek_eio_ctrl *ctrl =
|
|
++ container_of(led_cdev, struct realtek_eio_ctrl, sys_led);
|
|
++ u32 blink_interval = *delay_on + *delay_off;
|
|
++
|
|
++ /* Split range at geometric mean of 64 and 1024 */
|
|
++ if (blink_interval == 0 || blink_interval > 2*256) {
|
|
++ *delay_on = 1024;
|
|
++ *delay_off = 1024;
|
|
++ realtek_sys_led_set(ctrl, REALTEK_SYS_LED_BLINK_1024MS);
|
|
++ }
|
|
++ else {
|
|
++ *delay_on = 64;
|
|
++ *delay_off = 64;
|
|
++ realtek_sys_led_set(ctrl, REALTEK_SYS_LED_BLINK_64MS);
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int realtek_sys_led_probe(struct realtek_eio_ctrl *ctrl,
|
|
++ struct device *parent, struct device_node *np)
|
|
++{
|
|
++ struct led_classdev *sys_led = &ctrl->sys_led;
|
|
++ struct led_init_data init_data = {};
|
|
++
|
|
++ init_data.fwnode = of_fwnode_handle(np);
|
|
++
|
|
++ ctrl->active_low = of_property_read_bool(np, "active-low");
|
|
++
|
|
++ sys_led->max_brightness = 1;
|
|
++ sys_led->brightness_set = realtek_sys_led_brightness_set;
|
|
++ sys_led->brightness_get = realtek_sys_led_brightness_get;
|
|
++ sys_led->blink_set = realtek_sys_led_blink_set;
|
|
++
|
|
++ return devm_led_classdev_register_ext(parent, sys_led, &init_data);
|
|
++}
|
|
++
|
|
++static const struct mfd_cell rtl8380_mfd_devices[] = {
|
|
++ OF_MFD_CELL("realtek-eio-port-leds", "realtek,rtl8380-eio-port-led"),
|
|
++ OF_MFD_CELL("realtek-eio-mdio", "realtek,rtl8380-eio-mdio"),
|
|
++ OF_MFD_CELL("realtek-eio-pinctrl", "realtek,rtl8380-eio-pinctrl"),
|
|
++};
|
|
++
|
|
++static const struct realtek_eio_data rtl8380_eio_data = {
|
|
++ .sys_led_pos = 16,
|
|
++ .mfd_devices = rtl8380_mfd_devices,
|
|
++ .mfd_device_count = ARRAY_SIZE(rtl8380_mfd_devices)
|
|
++};
|
|
++
|
|
++static const struct mfd_cell rtl8390_mfd_devices[] = {
|
|
++ OF_MFD_CELL("realtek-eio-port-leds", "realtek,rtl8390-eio-port-led"),
|
|
++};
|
|
++
|
|
++static struct realtek_eio_data rtl8390_eio_data = {
|
|
++ .sys_led_pos = 15,
|
|
++ .mfd_devices = rtl8390_mfd_devices,
|
|
++ .mfd_device_count = ARRAY_SIZE(rtl8390_mfd_devices)
|
|
++};
|
|
++
|
|
++static const struct of_device_id of_realtek_eio_match[] = {
|
|
++ {
|
|
++ .compatible = "realtek,rtl8380-eio",
|
|
++ .data = &rtl8380_eio_data,
|
|
++ },
|
|
++ {
|
|
++ .compatible = "realtek,rtl8390-eio",
|
|
++ .data = &rtl8390_eio_data,
|
|
++ },
|
|
++};
|
|
++
|
|
++MODULE_DEVICE_TABLE(of, of_realtek_eio_match);
|
|
++
|
|
++static int realtek_eio_probe(struct platform_device *pdev)
|
|
++{
|
|
++ struct device *dev = &pdev->dev;
|
|
++ struct device_node *np = dev->of_node;
|
|
++ struct device_node *np_sys_led;
|
|
++ const struct of_device_id *match;
|
|
++ struct realtek_eio_ctrl *ctrl;
|
|
++ int err, val;
|
|
++ unsigned r;
|
|
++
|
|
++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
|
|
++ if (!ctrl)
|
|
++ return -ENOMEM;
|
|
++
|
|
++ match = of_match_device(of_realtek_eio_match, dev);
|
|
++ if (match)
|
|
++ ctrl->data = (struct realtek_eio_data *) match->data;
|
|
++ else {
|
|
++ dev_err(dev, "no device match\n");
|
|
++ return -EINVAL;
|
|
++ }
|
|
++
|
|
++ ctrl->dev = dev;
|
|
++
|
|
++ if (!np) {
|
|
++ dev_err(dev, "no DT node found\n");
|
|
++ return -EINVAL;
|
|
++ }
|
|
++
|
|
++ ctrl->map = device_node_to_regmap(np);
|
|
++ if (!ctrl->map) {
|
|
++ dev_err(dev, "failed to get regmap\n");
|
|
++ return -EINVAL;
|
|
++ }
|
|
++
|
|
++ /* Parse optional sys-led child */
|
|
++ np_sys_led = of_get_child_by_name(np, "sys-led");
|
|
++ if (IS_ERR(np_sys_led))
|
|
++ return PTR_ERR(np_sys_led);
|
|
++
|
|
++ if (np_sys_led) {
|
|
++ err = realtek_sys_led_probe(ctrl, dev, np_sys_led);
|
|
++ if (err)
|
|
++ return err;
|
|
++ }
|
|
++
|
|
++ /* Find sub-devices */
|
|
++ if (ctrl->data->mfd_devices)
|
|
++ mfd_add_devices(dev, 0, ctrl->data->mfd_devices,
|
|
++ ctrl->data->mfd_device_count, NULL, 0, NULL);
|
|
++
|
|
++ /* Dump register values */
|
|
++ for (r = 0; r <= regmap_get_max_register(ctrl->map); r += 4) {
|
|
++ regmap_read(ctrl->map, r, &val);
|
|
++ dev_info(dev, "%02x %08x\n", r, val);
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static struct platform_driver realtek_eio_driver = {
|
|
++ .probe = realtek_eio_probe,
|
|
++ .driver = {
|
|
++ .name = "realtek-ext-io",
|
|
++ .of_match_table = of_realtek_eio_match
|
|
++ }
|
|
++};
|
|
++
|
|
++module_platform_driver(realtek_eio_driver);
|
|
++
|
|
++MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
|
|
++MODULE_DESCRIPTION("Realtek switch SoC external LED/GPIO driver");
|
|
++MODULE_LICENSE("GPL v2");
|
|
+Index: linux-5.4.92/drivers/mfd/Makefile
|
|
+===================================================================
|
|
+--- linux-5.4.92.orig/drivers/mfd/Makefile
|
|
++++ linux-5.4.92/drivers/mfd/Makefile
|
|
+@@ -255,4 +255,4 @@ obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
|
|
+ obj-$(CONFIG_MFD_ROHM_BD70528) += rohm-bd70528.o
|
|
+ obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
|
|
+ obj-$(CONFIG_MFD_STMFX) += stmfx.o
|
|
+-
|
|
++obj-$(CONFIG_MFD_REALTEK_EIO) += realtek-eio.o
|
|
diff --git a/target/linux/realtek/patches-5.4/707-reboot.patch b/target/linux/realtek/patches-5.4/707-reboot.patch
|
|
new file mode 100644
|
|
index 0000000000..420b91d809
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/patches-5.4/707-reboot.patch
|
|
@@ -0,0 +1,9 @@
|
|
+Index: linux-5.4.92/drivers/gpio/Makefile
|
|
+===================================================================
|
|
+--- linux-5.4.92.orig/drivers/gpio/Makefile
|
|
++++ linux-5.4.92/drivers/gpio/Makefile
|
|
+@@ -171,3 +171,4 @@ obj-$(CONFIG_GPIO_XTENSA) += gpio-xtens
|
|
+ obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
|
|
+ obj-$(CONFIG_GPIO_ZX) += gpio-zx.o
|
|
+ obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
|
|
++obj-y += edgecore_reboot.o
|
|
diff --git a/target/linux/realtek/patches-5.4/708-poor-stp.patch b/target/linux/realtek/patches-5.4/708-poor-stp.patch
|
|
new file mode 100644
|
|
index 0000000000..1980914961
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/patches-5.4/708-poor-stp.patch
|
|
@@ -0,0 +1,16 @@
|
|
+Index: linux-5.4.102/net/bridge/br_fdb.c
|
|
+===================================================================
|
|
+--- linux-5.4.102.orig/net/bridge/br_fdb.c
|
|
++++ linux-5.4.102/net/bridge/br_fdb.c
|
|
+@@ -573,9 +573,9 @@ void br_fdb_update(struct net_bridge *br
|
|
+ if (likely(fdb)) {
|
|
+ /* attempt to update an entry for a local interface */
|
|
+ if (unlikely(fdb->is_local)) {
|
|
+- if (net_ratelimit())
|
|
+- br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n",
|
|
++ br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u) shutting port down\n",
|
|
+ source->dev->name, addr, vid);
|
|
++ br_set_state(source, BR_STATE_BLOCKING);
|
|
+ } else {
|
|
+ unsigned long now = jiffies;
|
|
+
|
|
diff --git a/target/linux/realtek/patches-5.4/710-adt7470.patch b/target/linux/realtek/patches-5.4/710-adt7470.patch
|
|
new file mode 100644
|
|
index 0000000000..b76b0b33cf
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/patches-5.4/710-adt7470.patch
|
|
@@ -0,0 +1,22 @@
|
|
+Index: linux-5.4.92/drivers/hwmon/adt7470.c
|
|
+===================================================================
|
|
+--- linux-5.4.92.orig/drivers/hwmon/adt7470.c
|
|
++++ linux-5.4.92/drivers/hwmon/adt7470.c
|
|
+@@ -1271,10 +1271,17 @@ static const struct i2c_device_id adt747
|
|
+ };
|
|
+ MODULE_DEVICE_TABLE(i2c, adt7470_id);
|
|
+
|
|
++static const struct of_device_id __maybe_unused adt7470_of_match =
|
|
++{
|
|
++ .compatible = "adi,adt7470",
|
|
++};
|
|
++MODULE_DEVICE_TABLE(of, adt7470_of_match);
|
|
++
|
|
+ static struct i2c_driver adt7470_driver = {
|
|
+ .class = I2C_CLASS_HWMON,
|
|
+ .driver = {
|
|
+ .name = "adt7470",
|
|
++ .of_match_table = of_match_ptr(&adt7470_of_match),
|
|
+ },
|
|
+ .probe = adt7470_probe,
|
|
+ .remove = adt7470_remove,
|
|
diff --git a/target/linux/realtek/patches-5.4/711-ec4100.patch b/target/linux/realtek/patches-5.4/711-ec4100.patch
|
|
new file mode 100644
|
|
index 0000000000..f297bb2f3a
|
|
--- /dev/null
|
|
+++ b/target/linux/realtek/patches-5.4/711-ec4100.patch
|
|
@@ -0,0 +1,41 @@
|
|
+Index: linux-5.4.111/drivers/net/dsa/rtl83xx/common.c
|
|
+===================================================================
|
|
+--- linux-5.4.111.orig/drivers/net/dsa/rtl83xx/common.c
|
|
++++ linux-5.4.111/drivers/net/dsa/rtl83xx/common.c
|
|
+@@ -687,6 +687,17 @@ static int __init rtl83xx_sw_probe(struc
|
|
+ rtl838x_dbgfs_init(priv);
|
|
+ }
|
|
+
|
|
++ if (of_machine_is_compatible("edgecore,ecs4100-12ph")) {
|
|
++ sw_w32(0x000000FF, 0x110);
|
|
++ sw_w32(0x00000000, 0x114);
|
|
++ sw_w32(0x00000000, 0x118);
|
|
++ sw_w32(0x000f0000, 0x11c);
|
|
++ sw_w32(0x00000000, 0x120);
|
|
++ sw_w32(0x000f0000, 0x124);
|
|
++ sw_w32(0x3DEA, 0xec);
|
|
++ sw_w32(0x707568, 0xe4);
|
|
++ }
|
|
++
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+Index: linux-5.4.111/drivers/net/phy/rtl83xx-phy.c
|
|
+===================================================================
|
|
+--- linux-5.4.111.orig/drivers/net/phy/rtl83xx-phy.c
|
|
++++ linux-5.4.111/drivers/net/phy/rtl83xx-phy.c
|
|
+@@ -1439,6 +1439,14 @@ static int rtl8380_configure_rtl8214fc(s
|
|
+ write_phy(mac + i, 0xfff, 0x1e, 0x0000);
|
|
+ }
|
|
+
|
|
++ if (of_machine_is_compatible("edgecore,ecs4100-12ph")) {
|
|
++ printk("setting edgecore specific SFP modes\n");
|
|
++ rtl8380_rtl8214fc_media_set(mac + 0, 0);
|
|
++ rtl8380_rtl8214fc_media_set(mac + 1, 0);
|
|
++ rtl8380_rtl8214fc_media_set(mac + 2, 1);
|
|
++ rtl8380_rtl8214fc_media_set(mac + 3, 1);
|
|
++ }
|
|
++
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
--
|
|
2.25.1
|
|
|