diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index 2220aff4b..b969fbbc4 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -37,6 +37,7 @@ jobs: tar cfz "$TAR_NAME" -C openwrt/bin/targets/ . curl -u $GH_BUILD_USERNAME:$GH_BUILD_PASSWORD -T "$TAR_NAME" "https://tip.jfrog.io/artifactory/tip-wlan-ap-firmware/uCentral/$LOWERCASE_TARGET/"$TAR_NAME"" IMG_NAME="$(date +%Y%m%d)-$LOWERCASE_TARGET-$BRANCH-$HASH-upgrade.bin"; - echo -e "{\n\t\"image\":\""${IMG_NAME}"\",\n\t\"revision\": \""${HASH}"\"\n}" > latest-upgrade.json - [ -f openwrt/image-file ] && curl -u $GH_BUILD_USERNAME:$GH_BUILD_PASSWORD -T "openwrt/$(cat openwrt/image-file)" "https://tip.jfrog.io/artifactory/tip-wlan-ap-firmware/uCentral/$LOWERCASE_TARGET/"$IMG_NAME"" - [ -f openwrt/image-file ] && curl -u $GH_BUILD_USERNAME:$GH_BUILD_PASSWORD -T "latest-upgrade.json" "https://tip.jfrog.io/artifactory/tip-wlan-ap-firmware/uCentral/$LOWERCASE_TARGET/latest-upgrade.json" + TIP_VERSION="$(grep DISTRIB_TIP= openwrt/tmp/openwrt_release | cut -d\' -f2)" + echo -e "{\n\t\"image\":\""${IMG_NAME}"\",\n\t\"revision\": \""${TIP_VERSION}"\",\n\t\"timestamp\":\""$(date +%s)"\",\n\n\t\"compatible\": \""${LOWERCASE_TARGET}"\"\n}" > latest-upgrade.json + [ -f openwrt/tmp/image-file ] && curl -u $GH_BUILD_USERNAME:$GH_BUILD_PASSWORD -T "openwrt/$(cat openwrt/tmp/image-file)" "https://tip.jfrog.io/artifactory/tip-wlan-ap-firmware/uCentral/$LOWERCASE_TARGET/"$IMG_NAME"" + [ -f openwrt/tmp/image-file ] && curl -u $GH_BUILD_USERNAME:$GH_BUILD_PASSWORD -T "latest-upgrade.json" "https://tip.jfrog.io/artifactory/tip-wlan-ap-firmware/uCentral/$LOWERCASE_TARGET/latest-upgrade.json" diff --git a/backports/0008-realtek-update-to-latest-owrt-HEAD.patch b/backports/0008-realtek-update-to-latest-owrt-HEAD.patch index c9568f395..755ad76cd 100644 --- a/backports/0008-realtek-update-to-latest-owrt-HEAD.patch +++ b/backports/0008-realtek-update-to-latest-owrt-HEAD.patch @@ -1,38 +1,49 @@ -From 9f20103ebfb0b3a07b87157299353ff4984f94bc Mon Sep 17 00:00:00 2001 +From 4846fa664105f251140cca009480944b70579170 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 16 Mar 2021 10:46:51 +0100 -Subject: [PATCH 8/9] realtek: update to latest owrt HEAD +Subject: [PATCH 01/38] realtek: update to latest owrt HEAD Signed-off-by: John Crispin --- 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 | 30 +- + 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 | 198 ++++++++++++ - .../files-5.4/arch/mips/rtl838x/setup.c | 18 -- - .../files-5.4/drivers/gpio/edgecore_reboot.c | 61 ++++ + .../dts/rtl8392_edgecore_ecs4100-12ph.dts | 297 +++++++++ + target/linux/realtek/dts/rtl839x.dtsi | 201 ++++++ + .../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 | 22 +- - .../files-5.4/drivers/net/dsa/rtl83xx/dsa.c | 56 +++- - .../drivers/net/dsa/rtl83xx/rtl839x.c | 8 +- - .../drivers/net/ethernet/rtl838x_eth.c | 4 +- - .../files-5.4/drivers/net/phy/rtl83xx-phy.c | 9 +- + .../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 + - .../realtek/patches-5.4/706-sysled.patch | 294 +++++++++++++++++ + .../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 ++ - 20 files changed, 1024 insertions(+), 63 deletions(-) + .../realtek/patches-5.4/710-adt7470.patch | 22 + + .../realtek/patches-5.4/711-ec4100.patch | 41 ++ + 29 files changed, 3789 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 @@ -82,7 +93,7 @@ index 2568fd2e0e..4d025b0975 100755 ucidef_set_poe 130 "$lan_list" ;; diff --git a/target/linux/realtek/config-5.4 b/target/linux/realtek/config-5.4 -index 2fbd904376..3b455f17e9 100644 +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 @@ -122,17 +133,18 @@ index 2fbd904376..3b455f17e9 100644 CONFIG_FW_LOADER_PAGED_BUF=y CONFIG_GENERIC_ATOMIC64=y CONFIG_GENERIC_CLOCKEVENTS=y -@@ -74,7 +71,8 @@ CONFIG_GENERIC_TIME_VSYSCALL=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 +80,14 @@ CONFIG_HAS_DMA=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 @@ -147,7 +159,7 @@ index 2fbd904376..3b455f17e9 100644 CONFIG_I2C_GPIO=y CONFIG_INITRAMFS_SOURCE="" CONFIG_IRQCHIP=y -@@ -101,10 +101,13 @@ CONFIG_LEGACY_PTYS=y +@@ -101,10 +102,13 @@ CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y @@ -161,7 +173,7 @@ index 2fbd904376..3b455f17e9 100644 CONFIG_MFD_SYSCON=y CONFIG_MIGRATION=y CONFIG_MIPS=y -@@ -159,11 +162,17 @@ CONFIG_PINCTRL=y +@@ -159,11 +163,17 @@ CONFIG_PINCTRL=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_SYSCON=y CONFIG_PSB6970_PHY=y @@ -179,7 +191,7 @@ index 2fbd904376..3b455f17e9 100644 CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SFP=y -@@ -172,7 +181,7 @@ CONFIG_SPI_MASTER=y +@@ -172,7 +182,7 @@ CONFIG_SPI_MASTER=y CONFIG_SPI_MEM=y CONFIG_SPI_RTL838X=y CONFIG_SRCU=y @@ -188,7 +200,7 @@ index 2fbd904376..3b455f17e9 100644 CONFIG_SWPHY=y CONFIG_SYSCTL_EXCEPTION_TRACE=y CONFIG_SYS_HAS_CPU_MIPS32_R1=y -@@ -184,8 +193,11 @@ CONFIG_SYS_SUPPORTS_BIG_ENDIAN=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 @@ -240,7 +252,7 @@ index 92d0e25fc4..a590450055 100644 }; 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..7a1bccfa00 +index 0000000000..303b79ac83 --- /dev/null +++ b/target/linux/realtek/dts/rtl8392_edgecore_ecs4100-12ph.dts @@ -0,0 +1,297 @@ @@ -419,7 +431,7 @@ index 0000000000..7a1bccfa00 + }; + partition@100000 { + label = "u-boot-env"; -+ reg = <0x100000 0x120000>; ++ reg = <0x100000 0x100000>; + read-only; + }; + partition@b260000 { @@ -543,10 +555,10 @@ index 0000000000..7a1bccfa00 +}; diff --git a/target/linux/realtek/dts/rtl839x.dtsi b/target/linux/realtek/dts/rtl839x.dtsi new file mode 100644 -index 0000000000..1eda5b77b4 +index 0000000000..9b41804ecf --- /dev/null +++ b/target/linux/realtek/dts/rtl839x.dtsi -@@ -0,0 +1,198 @@ +@@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; @@ -686,10 +698,13 @@ index 0000000000..1eda5b77b4 + }; + + gpio0: gpio-controller@b8003500 { -+ compatible = "realtek,rtl838x-gpio"; ++ compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio"; + reg = <0xb8003500 0x20>; + gpio-controller; + #gpio-cells = <2>; ++ ngpios = <24>; ++ interrupt-controller; ++ #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <23>; + }; @@ -745,6 +760,41 @@ index 0000000000..1eda5b77b4 + + }; +}; +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 @@ -870,10 +920,55 @@ index 8207e4bb73..60b6f08834 100644 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..2f0e568bc2 100644 +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 -@@ -368,8 +368,8 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv) +@@ -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) { @@ -884,7 +979,7 @@ index 698f2892ea..2f0e568bc2 100644 } else { /* Disable PHY polling via SoC */ sw_w32_mask(BIT(7), 0, RTL839X_SMI_GLB_CTRL); -@@ -555,7 +555,6 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) +@@ -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; @@ -892,7 +987,39 @@ index 698f2892ea..2f0e568bc2 100644 u64 bpdu_mask; pr_debug("Probing RTL838X switch device\n"); -@@ -650,9 +649,9 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) +@@ -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); @@ -905,29 +1032,62 @@ index 698f2892ea..2f0e568bc2 100644 priv->link_state_irq = platform_get_irq(pdev, 0); pr_info("LINK state irq: %d\n", priv->link_state_irq); -@@ -708,6 +707,17 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) - 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; - } - 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..9c088ea8b1 100644 +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 -@@ -79,7 +79,7 @@ static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv) +@@ -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) @@ -936,7 +1096,44 @@ index e0832c42b8..9c088ea8b1 100644 } pr_debug("%s: %16llx\n", __func__, v); -@@ -174,7 +174,7 @@ static int rtl83xx_setup(struct dsa_switch *ds) +@@ -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) { @@ -945,7 +1142,16 @@ index e0832c42b8..9c088ea8b1 100644 priv->r->port_iso_ctrl(i)); port_bitmap |= BIT_ULL(i); } -@@ -218,8 +218,8 @@ static int rtl930x_setup(struct dsa_switch *ds) +@@ -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) { @@ -956,15 +1162,28 @@ index e0832c42b8..9c088ea8b1 100644 } } priv->r->traffic_set(priv->cpu_port, port_bitmap); -@@ -245,6 +245,7 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port, - pr_debug("In %s port %d", __func__, port); +@@ -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 +279,10 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port, +@@ -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); @@ -975,20 +1194,29 @@ index e0832c42b8..9c088ea8b1 100644 phylink_set(mask, 10baseT_Half); phylink_set(mask, 10baseT_Full); phylink_set(mask, 100baseT_Half); -@@ -310,7 +315,7 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port, +@@ -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_info("%s: link state port %d: %llx\n", __func__, port, 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)) -@@ -343,6 +348,44 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int 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; @@ -1029,7 +1257,7 @@ index e0832c42b8..9c088ea8b1 100644 static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) -@@ -376,10 +419,11 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port, +@@ -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) { @@ -1042,11 +1270,1861 @@ index e0832c42b8..9c088ea8b1 100644 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..91947a20ed 100644 +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 -@@ -275,7 +275,7 @@ void rtl839x_traffic_enable(int source, int dest) +@@ -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) { @@ -1055,7 +3133,7 @@ index 5106bd2e9d..91947a20ed 100644 } irqreturn_t rtl839x_switch_irq(int irq, void *dev_id) -@@ -290,10 +290,10 @@ 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); @@ -1069,33 +3147,2236 @@ index 5106bd2e9d..91947a20ed 100644 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..7d0f692ee3 100644 +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 -@@ -1580,7 +1580,7 @@ static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) - int err; - struct rtl838x_eth_priv *priv = bus->priv; +@@ -234,19 +234,21 @@ struct dsa_tag { + u16 port; + u8 l2_offloaded; + u8 prio; ++ bool crc_error; + }; -- if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) -+ if (mii_id >= 48 && mii_id <= 51 && priv->id == 0x8393) - return rtl839x_read_sds_phy(mii_id, regnum); - - err = rtl839x_read_phy(mii_id, 0, regnum, &val); -@@ -1644,7 +1644,7 @@ static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id, + bool rtl838x_decode_tag(struct p_hdr *h, struct dsa_tag *t) { - struct rtl838x_eth_priv *priv = bus->priv; + 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; -- if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) -+ if (mii_id >= 48 && mii_id <= 51 && priv->id == 0x8393) - return rtl839x_write_sds_phy(mii_id, regnum, value); + 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 rtl839x_write_phy(mii_id, 0, regnum, value); + 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..7a153ec7bd 100644 +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 -@@ -1296,7 +1296,7 @@ static int rtl8380_configure_rtl8214fc(struct phy_device *phydev) +@@ -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); @@ -1104,22 +5385,29 @@ index 78953c6d17..7a153ec7bd 100644 break; } if (l >= 100) { -@@ -1336,6 +1336,13 @@ static int rtl8380_configure_rtl8214fc(struct phy_device *phydev) - 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; - } - +@@ -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..760ebc6bc1 100644 +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 @@ -1128,7 +5416,7 @@ index 424726c8a9..760ebc6bc1 100644 +define Device/edgecore_ecs4100-12ph + SOC := rtl8392 -+ IMAGE_SIZE := 6976k ++ IMAGE_SIZE := 14336k + DEVICE_VENDOR := Edgecore + DEVICE_MODEL := ECS4100-12PH + DEVICE_PACKAGES += lua-rs232 @@ -1136,6 +5424,437 @@ index 424726c8a9..760ebc6bc1 100644 +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 ++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 ++Date: Tue, 30 Mar 2021 19:48:43 +0200 ++From: Sander Vanheule ++List-Id: ++ ++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 ++Reviewed-by: Linus Walleij ++Reviewed-by: Andy Shevchenko ++--- ++ 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 +++#include +++//#include +++#include +++#include +++#include +++#include +++ +++/* +++ * 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 "); +++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 @@ -1501,6 +6220,53 @@ index 0000000000..b76b0b33cf + }, + .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 diff --git a/feeds/realtek/realtek-poe/Makefile b/feeds/realtek/realtek-poe/Makefile new file mode 100644 index 000000000..343218ca9 --- /dev/null +++ b/feeds/realtek/realtek-poe/Makefile @@ -0,0 +1,25 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=realtek-poe +PKG_RELEASE:=1 + +PKG_LICENSE:=GPL-2.0 +PKG_MAINTAINER:=John Crispin + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +define Package/realtek-poe + SECTION:=net + CATEGORY:=Network + TITLE:=Realtek PoE Switch Port daemon + DEPENDS:=+libubox +libubus +libuci +endef + +define Package/realtek-poe/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/realtek-poe $(1)/usr/bin/ + $(CP) ./files/* $(1) +endef + +$(eval $(call BuildPackage,realtek-poe)) diff --git a/feeds/realtek/realtek-poe/files/etc/config/poe b/feeds/realtek/realtek-poe/files/etc/config/poe new file mode 100644 index 000000000..6ef9d40ad --- /dev/null +++ b/feeds/realtek/realtek-poe/files/etc/config/poe @@ -0,0 +1,9 @@ +config global + option budget '65' + +config port + option enable '1' + option id '1' + option name 'lan1' + option poe_plus '1' + option priority '2' diff --git a/feeds/realtek/realtek-poe/files/etc/init.d/poe b/feeds/realtek/realtek-poe/files/etc/init.d/poe new file mode 100755 index 000000000..66f77d6ef --- /dev/null +++ b/feeds/realtek/realtek-poe/files/etc/init.d/poe @@ -0,0 +1,20 @@ +#!/bin/sh /etc/rc.common + +START=80 +USE_PROCD=1 +PROG=/usr/bin/realtek-poe + +reload_service() { + ubus call poe reload +} + +service_triggers() { + procd_add_config_trigger "config.change" "poe" ubus call poe reload +} + +start_service() { + procd_open_instance + procd_set_param command "$PROG" + procd_set_param respawn + procd_close_instance +} diff --git a/feeds/realtek/realtek-poe/src/CMakeLists.txt b/feeds/realtek/realtek-poe/src/CMakeLists.txt new file mode 100644 index 000000000..4eb81f457 --- /dev/null +++ b/feeds/realtek/realtek-poe/src/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(realtek-poe C) +ADD_DEFINITIONS(-Os -ggdb -Wextra -Wall -Werror --std=gnu99 -Wmissing-declarations -Wno-unused-parameter) + +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + +SET(SOURCES main.c) + +IF(DEBUG) + ADD_DEFINITIONS(-DDEBUG -g3) +ENDIF() + +FIND_LIBRARY(ubus NAMES ubus) +FIND_LIBRARY(uci NAMES uci) +FIND_LIBRARY(ubox NAMES ubox) +FIND_PATH(uci_include_dir NAMES uci.h) +FIND_PATH(ubus_include_dir NAMES libubus.h) +FIND_PATH(ubox_include_dir NAMES libubox/usock.h) +INCLUDE_DIRECTORIES(${ubox_include_dir} ${ubus_include_dir} ${uci_include_dir}) + +ADD_EXECUTABLE(realtek-poe ${SOURCES}) + +TARGET_LINK_LIBRARIES(realtek-poe ${ubox} ${ubus} ${uci}) + +INSTALL(TARGETS realtek-poe + RUNTIME DESTINATION sbin +) diff --git a/feeds/realtek/realtek-poe/src/main.c b/feeds/realtek/realtek-poe/src/main.c new file mode 100644 index 000000000..a44f86726 --- /dev/null +++ b/feeds/realtek/realtek-poe/src/main.c @@ -0,0 +1,845 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define ULOG_DBG(fmt, ...) ulog(LOG_DEBUG, fmt, ## __VA_ARGS__) + +typedef int (*poe_reply_handler)(unsigned char *reply); + +#define MAX_PORT 8 +#define GET_STR(a, b) (a < ARRAY_SIZE(b) ? b[a] : NULL) + +struct port_config { + char name[16]; + unsigned char enable; + unsigned char priority; + unsigned char power_up_mode; + unsigned char power_budget; +}; + +struct config { + int debug; + + int budget; + int budget_guard; + + int port_count; + struct port_config ports[MAX_PORT]; +}; + +struct port_state { + char *status; + float watt; + char *poe_mode; +}; + +struct state { + char *sys_mode; + unsigned char sys_version; + char *sys_mcu; + char *sys_status; + unsigned char sys_ext_version; + float power_consumption; + + struct port_state ports[MAX_PORT]; +}; + +struct cmd { + struct list_head list; + unsigned char cmd[12]; +}; + +static struct uloop_timeout state_timeout; +static struct ubus_auto_conn conn; +static struct ustream_fd stream; +static LIST_HEAD(cmd_pending); +static unsigned char cmd_seq; +static struct state state; +static struct blob_buf b; + +static struct config config = { + .budget = 65, + .budget_guard = 7, + .port_count = 8, +}; + +static void +config_load_port(struct uci_section *s) +{ + enum { + PORT_ATTR_ID, + PORT_ATTR_NAME, + PORT_ATTR_ENABLE, + PORT_ATTR_PRIO, + PORT_ATTR_POE_PLUS, + __PORT_ATTR_MAX, + }; + + static const struct blobmsg_policy port_attrs[__PORT_ATTR_MAX] = { + [PORT_ATTR_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, + [PORT_ATTR_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, + [PORT_ATTR_ENABLE] = { .name = "enable", .type = BLOBMSG_TYPE_INT32 }, + [PORT_ATTR_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 }, + [PORT_ATTR_POE_PLUS] = { .name = "poe_plus", .type = BLOBMSG_TYPE_INT32 }, + }; + + const struct uci_blob_param_list port_attr_list = { + .n_params = __PORT_ATTR_MAX, + .params = port_attrs, + }; + + struct blob_attr *tb[__PORT_ATTR_MAX] = { 0 }; + unsigned int id; + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, &port_attr_list); + blobmsg_parse(port_attrs, __PORT_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head)); + + if (!tb[PORT_ATTR_ID] || !tb[PORT_ATTR_NAME]) { + ULOG_ERR("invalid port settings"); + return; + } + + id = blobmsg_get_u32(tb[PORT_ATTR_ID]); + if (!id || id > MAX_PORT) { + ULOG_ERR("invalid port id"); + return; + } + id--; + + strncpy(config.ports[id].name, blobmsg_get_string(tb[PORT_ATTR_NAME]), 16); + + if (tb[PORT_ATTR_ENABLE]) + config.ports[id].enable = !!blobmsg_get_u32(tb[PORT_ATTR_ENABLE]); + + if (tb[PORT_ATTR_PRIO]) + config.ports[id].priority = blobmsg_get_u32(tb[PORT_ATTR_PRIO]); + if (config.ports[id].priority > 3) + config.ports[id].priority = 3; + + if (tb[PORT_ATTR_POE_PLUS] && blobmsg_get_u32(tb[PORT_ATTR_POE_PLUS])) + config.ports[id].power_up_mode = 3; +} + +static void +config_load_global(struct uci_section *s) +{ + enum { + GLOBAL_ATTR_BUDGET, + GLOBAL_ATTR_GUARD, + __GLOBAL_ATTR_MAX, + }; + + static const struct blobmsg_policy global_attrs[__GLOBAL_ATTR_MAX] = { + [GLOBAL_ATTR_BUDGET] = { .name = "budget", .type = BLOBMSG_TYPE_INT32 }, + [GLOBAL_ATTR_GUARD] = { .name = "guard", .type = BLOBMSG_TYPE_INT32 }, + }; + + const struct uci_blob_param_list global_attr_list = { + .n_params = __GLOBAL_ATTR_MAX, + .params = global_attrs, + }; + + struct blob_attr *tb[__GLOBAL_ATTR_MAX] = { 0 }; + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, &global_attr_list); + blobmsg_parse(global_attrs, __GLOBAL_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head)); + + config.budget = 65; + if (tb[GLOBAL_ATTR_BUDGET]) + config.budget = blobmsg_get_u32(tb[GLOBAL_ATTR_BUDGET]); + + if (tb[GLOBAL_ATTR_GUARD]) + config.budget_guard = blobmsg_get_u32(tb[GLOBAL_ATTR_GUARD]); + else + config.budget_guard = config.budget / 10; +} + +static void +config_load(int init) +{ + struct uci_context *uci = uci_alloc_context(); + struct uci_package *package = NULL; + + memset(config.ports, 0, sizeof(config.ports)); + + if (!uci_load(uci, "poe", &package)) { + struct uci_element *e; + + if (init) + uci_foreach_element(&package->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "global")) + config_load_global(s); + } + uci_foreach_element(&package->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "port")) + config_load_port(s); + } + } + + uci_unload(uci, package); + uci_free_context(uci); +} + +static void +poe_cmd_dump(char *type, unsigned char *data) +{ + int i; + + if (!config.debug) + return; + + fprintf(stderr, "%s ->", type); + for (i = 0; i < 12; i++) + fprintf(stderr, " 0x%02x", data[i]); + fprintf(stderr, "\n"); +} + +static int +poe_cmd_send(struct cmd *cmd) +{ + poe_cmd_dump("TX", cmd->cmd); + ustream_write(&stream.stream, (char *)cmd->cmd, 12, false); + + return 0; +} + +static int +poe_cmd_next(void) +{ + struct cmd *cmd; + + if (list_empty(&cmd_pending)) + return -1; + + cmd = list_first_entry(&cmd_pending, struct cmd, list); + + return poe_cmd_send(cmd); +} + +static int +poe_cmd_queue(unsigned char *_cmd, int len) +{ + int i, empty = list_empty(&cmd_pending); + struct cmd *cmd = malloc(sizeof(*cmd)); + + memset(cmd, 0, sizeof(*cmd)); + memset(cmd->cmd, 0xff, 12); + memcpy(cmd->cmd, _cmd, len); + + cmd_seq++; + cmd->cmd[1] = cmd_seq; + cmd->cmd[11] = 0; + + for (i = 0; i < 11; i++) + cmd->cmd[11] += cmd->cmd[i]; + + list_add_tail(&cmd->list, &cmd_pending); + + if (empty) + return poe_cmd_send(cmd); + + return 0; +} + +/* 0x00 - Set port enable + * 0: Disable + * 1: Enable + */ +static int +poe_cmd_port_enable(unsigned char port, unsigned char enable) +{ + unsigned char cmd[] = { 0x00, 0x00, port, enable }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x06 - Set global port enable + * 0: Disable PSE Functionality on all Ports + * 1: Enable PSE Functionality on all Ports + * 2: Enable Force power Functionality on all ports + * 3: Enable Force Power with Disconnect Functionality on all Ports + */ +static int +poe_cmd_global_port_enable(unsigned char enable) +{ + unsigned char cmd[] = { 0x06, 0x00, enable }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x10 - Set port detection type + * 1: Legacy Capacitive Detection only + * 2: IEEE 802.3af 4-Point Detection only (Default) + * 3: IEEE 802.3af 4-Point followed by Legacy + * 4: IEEE 802.3af 2-Point detection (Not Supported) + * 5: IEEE 802.3af 2-Point followed by Legacy + */ +static int +poe_cmd_port_detection_type(unsigned char port, unsigned char type) +{ + unsigned char cmd[] = { 0x10, 0x00, port, type }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x11 - Set port classification + * 0: Disable + * 1: Enable + */ +static int +poe_cmd_port_classification(unsigned char port, unsigned char classification) +{ + unsigned char cmd[] = { 0x11, 0x00, port, classification }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x13 - Set port disconnect type + * 0: none + * 1: AC-disconnect + * 2: DC-disconnect + * 3: DC with delay + */ +static int +poe_cmd_port_disconnect_type(unsigned char port, unsigned char type) +{ + unsigned char cmd[] = { 0x13, 0x00, port, type }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x15 - Set port power limit type + * 0: None. Power limit is 16.2W if the connected device is “low power”, + * or the set high power limit if the device is “high power”. + * 1: Class based. The power limit for class 4 devices is determined by the high power limit. + * 2: User defined + */ +static int +poe_cmd_port_power_limit_type(unsigned char port, unsigned char type) +{ + unsigned char cmd[] = { 0x15, 0x00, port, type }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x16 - Set port power budget + * values in 0.2W increments + */ +static int +poe_cmd_port_power_budget(unsigned char port, unsigned char budget) +{ + unsigned char cmd[] = { 0x16, 0x00, port, budget }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x17 - Set power management mode + * 0: None (No Power Management mode) (Default in Semi-Auto mode) + * 1: Static Power Management with Port Priority(Default in Automode) + * 2: Dynamic Power Management with Port Priority + * 3: Static Power Management without Port Priority + * 4: Dynamic Power Management without Port Priority + */ +static int +poe_cmd_power_mgmt_mode(unsigned char mode) +{ + unsigned char cmd[] = { 0x18, 0x00, mode }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x18 - Set global power budget */ +static int +poe_cmd_global_power_budget(int budget, int guard) +{ + unsigned char cmd[] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + cmd[3] = budget * 10 / 256; + cmd[4] = budget * 10 % 256; + cmd[5] = guard * 10 / 256; + cmd[6] = guard * 10 % 256; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x1a - Set port priority + * 0: Low + * 1: Normal + * 2: High + * 3: Critical + */ +static int +poe_set_port_priority(unsigned char port, unsigned char priority) +{ + unsigned char cmd[] = { 0x1a, 0x00, port, priority }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x1c - Set port power-up mode + * 0: PoE + * 1: legacy + * 2: pre-PoE+ + * 3: PoE+ + */ +static int +poe_set_port_power_up_mode(unsigned char port, unsigned char mode) +{ + unsigned char cmd[] = { 0x1c, 0x00, port, mode }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +/* 0x20 - Get system info */ +static int +poe_cmd_status(void) +{ + unsigned char cmd[] = { 0x20 }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +static int +poe_reply_status(unsigned char *reply) +{ + static char *mode[]={ + "Semi-auto I2C", + "Semi-auto UART", + "Auto I2C", + "Auto UART" + }; + static char *mcu[]={ + "ST Micro ST32F100 Microcontroller", + "Nuvoton M05xx LAN Microcontroller", + "ST Micro STF030C8 Microcontroller", + "Nuvoton M058SAN Microcontroller", + "Nuvoton NUC122 Microcontroller" + }; + static char *status[]={ + "Global Disable pin is de-asserted:No system reset from the previous query cmd:Configuration saved", + "Global Disable pin is de-asserted:No system reset from the previous query cmd:Configuration Dirty", + "Global Disable pin is de-asserted:System reseted:Configuration saved", + "Global Disable pin is de-asserted:System reseted:Configuration Dirty", + "Global Disable Pin is asserted:No system reset from the previous query cmd:Configuration saved", + "Global Disable Pin is asserted:No system reset from the previous query cmd:Configuration Dirty", + "Global Disable Pin is asserted:System reseted:Configuration saved", + "Global Disable Pin is asserted:System reseted:Configuration Dirty" + }; + + state.sys_mode = GET_STR(reply[2], mode); + config.port_count = reply[3]; + state.sys_version = reply[7]; + state.sys_mcu = GET_STR(reply[8], mcu); + state.sys_status = GET_STR(reply[9], status); + state.sys_ext_version = reply[10]; + + return 0; +} + +/* 0x23 - Get power statistics */ +static int +poe_cmd_power_stats(void) +{ + unsigned char cmd[] = { 0x23 }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +static int +poe_reply_power_stats(unsigned char *reply) +{ + state.power_consumption = reply[2]; + state.power_consumption *= 256; + state.power_consumption += reply[3]; + state.power_consumption /= 10; + + return 0; +} + +/* 0x26 - Get extended port config */ +static int +poe_cmd_port_ext_config(unsigned char port) +{ + unsigned char cmd[] = { 0x26, 0x00, port }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +static int +poe_reply_port_ext_config(unsigned char *reply) +{ + static char *mode[] = { + "PoE", + "Legacy", + "pre-PoE+", + "PoE+" + }; + + state.ports[reply[2]].poe_mode = GET_STR(reply[3], mode); + + return 0; +} + +/* 0x2a - Get all port status */ +static int +poe_cmd_port_overview(void) +{ + unsigned char cmd[] = { 0x2a, 0x00, 0x00 }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +static int +poe_reply_port_overview(unsigned char *reply) +{ + static char *status[]={ + "Disabled", + "Searching", + "Delivering power", + "Fault", + "Other fault", + "Requesting power", + }; + int i; + + for (i = 0; i < 8; i++) + state.ports[i].status = GET_STR((reply[3 + i] & 0xf), status); + + return 0; +} + +/* 0x30 - Get port power statistics */ +static int +poe_cmd_port_power_stats(unsigned char port) +{ + unsigned char cmd[] = { 0x30, 0x00, port }; + + return poe_cmd_queue(cmd, sizeof(cmd)); +} + +static int +poe_reply_port_power_stats(unsigned char *reply) +{ + float watt; + + watt = reply[9]; + watt *= 256; + watt += reply[10]; + watt /= 10; + + state.ports[reply[2]].watt = watt; + + return 0; +} + +static poe_reply_handler reply_handler[] = { + [0x20] = poe_reply_status, + [0x23] = poe_reply_power_stats, + [0x26] = poe_reply_port_ext_config, + [0x2a] = poe_reply_port_overview, + [0x30] = poe_reply_port_power_stats, +}; + +static int +poe_reply_consume(unsigned char *reply) +{ + struct cmd *cmd = NULL; + unsigned char sum = 0, i; + + poe_cmd_dump("RX", reply); + + if (list_empty(&cmd_pending)) { + ULOG_ERR("received unsolicited reply\n"); + return -1; + } + + cmd = list_first_entry(&cmd_pending, struct cmd, list); + list_del(&cmd->list); + + for (i = 0; i < 11; i++) + sum += reply[i]; + + if (reply[11] != sum) { + ULOG_DBG("received reply with bad checksum\n"); + return -1; + } + + if (reply[0] != cmd->cmd[0]) { + ULOG_DBG("received reply with bad command id\n"); + return -1; + } + + if (reply[1] != cmd->cmd[1]) { + ULOG_DBG("received reply with bad sequence number\n"); + return -1; + } + + free(cmd); + + if (reply_handler[reply[0]]) + return reply_handler[reply[0]](reply); + + return 0; +} + +static void +poe_stream_msg_cb(struct ustream *s, int bytes) +{ + int len; + unsigned char *reply = (unsigned char *)ustream_get_read_buf(s, &len); + + if (len < 12) + return; + + poe_reply_consume(reply); + ustream_consume(s, 12); + poe_cmd_next(); +} + +static void +poe_stream_notify_cb(struct ustream *s) +{ + if (!s->eof) + return; + + ULOG_ERR("tty error, shutting down\n"); + exit(-1); +} + +static int +poe_stream_open(char *dev, struct ustream_fd *s, speed_t speed) +{ + struct termios tio; + int tty; + + tty = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (tty < 0) { + ULOG_ERR("%s: device open failed: %s\n", dev, strerror(errno)); + return -1; + } + + tcgetattr(tty, &tio); + tio.c_cflag |= CREAD; + tio.c_cflag |= CS8; + tio.c_iflag |= IGNPAR; + tio.c_lflag &= ~(ICANON); + tio.c_lflag &= ~(ECHO); + tio.c_lflag &= ~(ECHOE); + tio.c_lflag &= ~(ISIG); + tio.c_iflag &= ~(IXON | IXOFF | IXANY); + tio.c_cflag &= ~CRTSCTS; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + cfsetispeed(&tio, speed); + cfsetospeed(&tio, speed); + tcsetattr(tty, TCSANOW, &tio); + + s->stream.string_data = false; + s->stream.notify_read = poe_stream_msg_cb; + s->stream.notify_state = poe_stream_notify_cb; + + ustream_fd_init(s, tty); + tcflush(tty, TCIFLUSH); + + return 0; +} + +static int +poe_port_setup(void) +{ + int i; + + for (i = 0; i < config.port_count; i++) { + if (!config.ports[i].enable) { + poe_cmd_port_enable(i, 0); + continue; + } + + poe_set_port_priority(i, config.ports[i].priority); + poe_set_port_power_up_mode(i, config.ports[i].power_up_mode); + if (config.ports[i].power_budget) { + poe_cmd_port_power_budget(i, config.ports[i].power_budget); + poe_cmd_port_power_limit_type(i, 2); + } else { + poe_cmd_port_power_limit_type(i, 1); + } + poe_cmd_port_disconnect_type(i, 2); + poe_cmd_port_classification(i, 1); + poe_cmd_port_detection_type(i, 3); + poe_cmd_port_enable(i, 1); + } + + return 0; +} + +static int +poe_initial_setup(void) +{ + poe_cmd_status(); + poe_cmd_power_mgmt_mode(2); + poe_cmd_global_power_budget(0, 0); + poe_cmd_global_port_enable(0); + poe_cmd_global_power_budget(config.budget, config.budget_guard); + + poe_port_setup(); + + return 0; +} + +static void +state_timeout_cb(struct uloop_timeout *t) +{ + int i; + + poe_cmd_power_stats(); + poe_cmd_port_overview(); + + for (i = 0; i < config.port_count; i++) { + poe_cmd_port_ext_config(i); + poe_cmd_port_power_stats(i); + } + + uloop_timeout_set(&state_timeout, 2 * 1000); +} + +static int +ubus_poe_info_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + char tmp[16]; + void *c; + int i; + + blob_buf_init(&b, 0); + + snprintf(tmp, sizeof(tmp), "v%u.%u.%u.%u", + state.sys_version >> 4, state.sys_version & 0xf, + state.sys_ext_version >> 4, state.sys_ext_version & 0xf); + blobmsg_add_string(&b, "firmware", tmp); + if (state.sys_mcu) + blobmsg_add_string(&b, "mcu", state.sys_mcu); + blobmsg_add_double(&b, "budget", config.budget); + blobmsg_add_double(&b, "consumption", state.power_consumption); + + c = blobmsg_open_table(&b, "ports"); + for (i = 0; i < config.port_count; i++) { + void *p; + + if (!config.ports[i].enable) + continue; + + p = blobmsg_open_table(&b, config.ports[i].name); + + blobmsg_add_u32(&b, "priority", config.ports[i].priority); + + if (state.ports[i].poe_mode) + blobmsg_add_string(&b, "mode", state.ports[i].poe_mode); + if (state.ports[i].status) + blobmsg_add_string(&b, "status", state.ports[i].status); + else + blobmsg_add_string(&b, "status", "unknown"); + if (state.ports[i].watt) + blobmsg_add_double(&b, "consumption", state.ports[i].watt); + + blobmsg_close_table(&b, p); + } + blobmsg_close_table(&b, c); + + ubus_send_reply(ctx, req, b.head); + + return UBUS_STATUS_OK; +} + +static int +ubus_poe_reload_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + config_load(0); + poe_port_setup(); + + return UBUS_STATUS_OK; +} + +static const struct ubus_method ubus_poe_methods[] = { + UBUS_METHOD_NOARG("info", ubus_poe_info_cb), + UBUS_METHOD_NOARG("reload", ubus_poe_reload_cb), +}; + +static struct ubus_object_type ubus_poe_object_type = + UBUS_OBJECT_TYPE("poe", ubus_poe_methods); + +static struct ubus_object ubus_poe_object = { + .name = "poe", + .type = &ubus_poe_object_type, + .methods = ubus_poe_methods, + .n_methods = ARRAY_SIZE(ubus_poe_methods), +}; + +static void +ubus_connect_handler(struct ubus_context *ctx) +{ + int ret; + + ret = ubus_add_object(ctx, &ubus_poe_object); + if (ret) + fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); +} + +int +main(int argc, char ** argv) +{ + int ch; + + ulog_open(ULOG_STDIO | ULOG_SYSLOG, LOG_DAEMON, "realtek-poe"); + ulog_threshold(LOG_INFO); + + while ((ch = getopt(argc, argv, "d")) != -1) { + switch (ch) { + case 'd': + config.debug = 1; + break; + } + } + + config_load(1); + + uloop_init(); + conn.cb = ubus_connect_handler; + ubus_auto_connect(&conn); + + if (poe_stream_open("/dev/ttyS1", &stream, B19200) < 0) + return -1; + + poe_initial_setup(); + state_timeout.cb = state_timeout_cb; + uloop_timeout_set(&state_timeout, 1000); + uloop_run(); + uloop_done(); + + return 0; +} diff --git a/feeds/tip/tip-defaults/files/etc/init.d/tip-version b/feeds/tip/tip-defaults/files/etc/init.d/tip-version index 40c7ed330..c193373fc 100755 --- a/feeds/tip/tip-defaults/files/etc/init.d/tip-version +++ b/feeds/tip/tip-defaults/files/etc/init.d/tip-version @@ -3,8 +3,5 @@ START=80 boot() { - DESC=$(cat /etc/openwrt_release | grep DISTRIB_DESCRIPTION= | cut -d\' -f2) - HASH=$(cat /etc/openwrt_release | grep DISTRIB_TIP= | cut -d\' -f2) - VERSION=$(cat /etc/openwrt_release | grep DISTRIB_TIP_VERSION= | cut -d\' -f2) - echo "$DESC / TIP-$VERSION-$HASH" > /tmp/ucentral.version + echo $(cat /etc/openwrt_release | grep DISTRIB_TIP= | cut -d\' -f2) > /tmp/ucentral.version } diff --git a/patches/0003-pending-scripts-add-gen_config.py.patch b/patches/0003-pending-scripts-add-gen_config.py.patch index 979b2a161..31f110aa3 100644 --- a/patches/0003-pending-scripts-add-gen_config.py.patch +++ b/patches/0003-pending-scripts-add-gen_config.py.patch @@ -206,7 +206,7 @@ index 0000000000..124dfd596a + setup_feeds(profile) + generate_config(profile) + if profile["image"]: -+ Path("image-file").write_text(profile["image"]) ++ Path("tmp/image-file").write_text(profile["image"]) + print("Running make defconfig") + if run(["make", "defconfig"]).returncode: + die(f"Error running make defconfig") diff --git a/patches/0008-base-files-add-the-wlan-ap-repo-hash.patch b/patches/0008-base-files-add-the-wlan-ap-repo-hash.patch index 403bc0431..5bd7468cd 100644 --- a/patches/0008-base-files-add-the-wlan-ap-repo-hash.patch +++ b/patches/0008-base-files-add-the-wlan-ap-repo-hash.patch @@ -1,7 +1,7 @@ -From af8a5fcd98e0d1d0f0cca85c62a3551d3d589d88 Mon Sep 17 00:00:00 2001 +From 5ed64f1cde06fa3f0fb27c618c86aeeb9309d845 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sat, 29 Aug 2020 08:25:41 +0200 -Subject: [PATCH 01/25] base-files: add the wlan-ap repo hash +Subject: [PATCH 12/38] base-files: add the wlan-ap repo hash currently the banner will show the revision of the build tree. This patch adds the hash of the wlan-ap tree. @@ -9,13 +9,14 @@ This patch adds the hash of the wlan-ap tree. Signed-off-by: John Crispin --- include/version.mk | 7 +++++ + package/base-files/Makefile | 1 + package/base-files/files/etc/banner | 1 + package/base-files/files/etc/openwrt_release | 2 ++ package/base-files/files/etc/openwrt_version | 1 + ...p-revision-info-to-system.board-call.patch | 28 +++++++++++++++++++ scripts/gen_config.py | 2 +- scripts/getver.sh | 12 ++++++++ - 7 files changed, 52 insertions(+), 1 deletion(-) + 8 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 package/system/procd/patches/0001-procd-add-tip-revision-info-to-system.board-call.patch diff --git a/include/version.mk b/include/version.mk @@ -42,6 +43,18 @@ index a4b47c8a57..214f32974b 100644 + -e 's,%x,$(call sed_escape,$(VERSION_TIP_VERSION)),g' \ -e 's,%h,$(call sed_escape,$(VERSION_HWREV)),g' +diff --git a/package/base-files/Makefile b/package/base-files/Makefile +index 8a1ddf96f5..0ff6fa84c1 100644 +--- a/package/base-files/Makefile ++++ b/package/base-files/Makefile +@@ -205,6 +205,7 @@ define Package/base-files/install + + $(if $(CONFIG_TARGET_PREINIT_DISABLE_FAILSAFE), \ + rm -f $(1)/etc/banner.failsafe,) ++ $(CP) $(1)/etc/openwrt_release $(TOPDIR)/tmp/ + endef + + ifneq ($(DUMP),1) diff --git a/package/base-files/files/etc/banner b/package/base-files/files/etc/banner index f73423bad4..65c175e1ef 100644 --- a/package/base-files/files/etc/banner @@ -54,14 +67,14 @@ index f73423bad4..65c175e1ef 100644 %D %V, %C --------------------------------------------------- diff --git a/package/base-files/files/etc/openwrt_release b/package/base-files/files/etc/openwrt_release -index d03400ca05..0327c21f68 100644 +index d03400ca05..3652b1a49a 100644 --- a/package/base-files/files/etc/openwrt_release +++ b/package/base-files/files/etc/openwrt_release @@ -5,3 +5,5 @@ DISTRIB_TARGET='%S' DISTRIB_ARCH='%A' DISTRIB_DESCRIPTION='%D %V %C' DISTRIB_TAINTS='%t' -+DISTRIB_TIP='%a' ++DISTRIB_TIP='%D %V %C / TIP-%x-%a' +DISTRIB_TIP_VERSION='%x' diff --git a/package/base-files/files/etc/openwrt_version b/package/base-files/files/etc/openwrt_version index 48157ed97f..bb0ef233ac 100644 @@ -105,7 +118,7 @@ index 0000000000..4e8f05a700 +2.25.1 + diff --git a/scripts/gen_config.py b/scripts/gen_config.py -index 5bc7068ae7..b380a2e605 100755 +index 124dfd596a..e9418d776c 100755 --- a/scripts/gen_config.py +++ b/scripts/gen_config.py @@ -62,7 +62,7 @@ def load_yaml(fname: str, profile: dict, include = True): diff --git a/profiles/realtek.yml b/profiles/realtek.yml index 0f74f4fb2..35fe083b7 100644 --- a/profiles/realtek.yml +++ b/profiles/realtek.yml @@ -5,4 +5,4 @@ feeds: path: ../../feeds/realtek packages: - - rtl83xx-poe=y + - realtek-poe=y diff --git a/profiles/ucentral-switch.yml b/profiles/ucentral-switch.yml index 874441628..58529f159 100644 --- a/profiles/ucentral-switch.yml +++ b/profiles/ucentral-switch.yml @@ -4,17 +4,25 @@ feeds: - name: ucentral path: ../../feeds/ucentral +include: + - webui + packages: - libmosquitto-ssl - lldpd + - curl - ip-bridge - ucentral-client - ucentral-jsonschema - - ucentral-mqtt - ucentral-schema - ucentral-defaults - ucode - rtty-openssl + - tcpdump + - tip-defaults + - tmate + - udevmand + - umdns - vxlan diffconfig: | CONFIG_IMAGEOPT=y