diff --git a/build-tgr.sh b/build-tgr.sh index 01560a3..b7858cb 100755 --- a/build-tgr.sh +++ b/build-tgr.sh @@ -49,8 +49,8 @@ openssl rsa -in target/keys/signingkey.pem -outform PEM -pubout -out target/keys cp target/keys/signingpub.pem target/overlay/pub.pem echo "[+] Generating sample update package" -mkdir -p home/update -echo "sample update" > home/update/sample.txt +mkdir -p home/upgrade +echo "sample update" > home/upgrade/sample.txt tar -cvf update.tar home openssl dgst -sha256 -sign target/keys/signingkey.pem -out update.tar.sig update.tar cat update.tar > update.tar.cc diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1000-imx8mm-Add-imx8mm-tgr-device-tree.patch b/buildroot/board/tiesse/tgr/kernel-patches/1000-imx8mm-Add-imx8mm-tgr-device-tree.patch index b4102f3..32a5f1e 100644 --- a/buildroot/board/tiesse/tgr/kernel-patches/1000-imx8mm-Add-imx8mm-tgr-device-tree.patch +++ b/buildroot/board/tiesse/tgr/kernel-patches/1000-imx8mm-Add-imx8mm-tgr-device-tree.patch @@ -1,8 +1,8 @@ -diff --git arch/arm64/boot/dts/freescale/Makefile arch/arm64/boot/dts/freescale/Makefile -index da7ede2f5744..2a0a0f56b9a8 100644 ---- linux-imx/arch/arm64/boot/dts/freescale/Makefile +Index: arch/arm64/boot/dts/freescale/Makefile +=================================================================== +--- arch/arm64/boot/dts/freescale/Makefile.orig +++ linux-imx/arch/arm64/boot/dts/freescale/Makefile -@@ -116,7 +116,8 @@ dtb-$(CONFIG_ARCH_FSL_IMX8MQ) += fsl-imx8mq-ddr3l-arm2.dtb \ +@@ -116,7 +116,8 @@ dtb-$(CONFIG_ARCH_FSL_IMX8MQ) += fsl-imx fsl-imx8mq-evk-inmate.dtb \ fsl-imx8mq-evk-dp.dtb \ fsl-imx8mq-evk-edp.dtb @@ -12,12 +12,11 @@ index da7ede2f5744..2a0a0f56b9a8 100644 fsl-imx8mm-evk-ak4497.dtb \ fsl-imx8mm-evk-m4.dtb \ fsl-imx8mm-evk-ak5558.dtb \ -diff --git arch/arm64/boot/dts/freescale/fsl-imx8mm-tgr.dts arch/arm64/boot/dts/freescale/fsl-imx8mm-tgr.dts -new file mode 100755 -index 000000000000..411de1c8c620 +Index: arch/arm64/boot/dts/freescale/fsl-imx8mm-tgr.dts +=================================================================== --- /dev/null +++ linux-imx/arch/arm64/boot/dts/freescale/fsl-imx8mm-tgr.dts -@@ -0,0 +1,629 @@ +@@ -0,0 +1,646 @@ +/* + * Copyright 2018 NXP + * @@ -52,30 +51,12 @@ index 000000000000..411de1c8c620 + + pwr { + label = "pwr"; -+ gpios = <&gpio4 8 GPIO_ACTIVE_HIGH>; ++ gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + + lte { + label = "lte"; -+ gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>; -+ default-state = "off"; -+ }; -+ -+ ble { -+ label = "ble"; -+ gpios = <&gpio4 6 GPIO_ACTIVE_HIGH>; -+ default-state = "off"; -+ }; -+ -+ wifi { -+ label = "wifi"; -+ gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; -+ default-state = "off"; -+ }; -+ -+ wan { -+ label = "wan"; + gpios = <&gpio4 4 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; @@ -103,15 +84,6 @@ index 000000000000..411de1c8c620 + }; +}; + -+&memory { -+ reg = <0x0 0x40000000 0 0x40000000>; -+}; -+ -+&linux_cma { -+ size = <0 0x14000000>; -+ alloc-ranges = <0 0x40000000 0 0x30000000>; -+}; -+ +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; @@ -130,7 +102,8 @@ index 000000000000..411de1c8c620 + MX8MM_IOMUXC_SAI1_TXFS_GPIO4_IO10 0x80 /* OC HUB USB */ + MX8MM_IOMUXC_SAI1_TXD7_GPIO4_IO19 0x10 /* USB PWR */ + MX8MM_IOMUXC_SAI1_MCLK_GPIO4_IO20 0x80 /* OC USB */ -+ MX8MM_IOMUXC_GPIO1_IO14_GPIO1_IO14 0x10 /* RST HUB */ ++ ++ MX8MM_IOMUXC_GPIO1_IO14_GPIO1_IO14 0x10 + + /* SDIO WIFI */ + MX8MM_IOMUXC_SAI1_TXD1_GPIO4_IO13 0x10 /* SDIO WIFI ENABLE */ @@ -140,6 +113,9 @@ index 000000000000..411de1c8c620 + MX8MM_IOMUXC_SAI1_RXC_GPIO4_IO1 0x10 /* MODEM RST */ + MX8MM_IOMUXC_SAI1_RXD0_GPIO4_IO2 0x10 /* SIM SELECT */ + MX8MM_IOMUXC_SAI1_RXD1_GPIO4_IO3 0x10 /* CMD_POWER */ ++ ++ /* RST BUTTON */ ++ MX8MM_IOMUXC_SAI1_RXD7_GPIO4_IO9 0x1c0 + >; + }; + @@ -519,12 +495,6 @@ index 000000000000..411de1c8c620 + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; -+ -+ usb2422@2c { -+ compatible = "microchip,usb2422"; -+ reg = <0x2c>; -+ reset-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; -+ }; +}; + + @@ -536,26 +506,72 @@ index 000000000000..411de1c8c620 + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec1>, <&pinctrl_fec1_gpio>; + phy-mode = "rmii"; -+ phy-handle = <ðphy0>; + fsl,magic-packet; ++ fsl,devname = "ethsw0"; + status = "okay"; + -+ phy-reset-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>; -+ phy-reset-duration = <10>; -+ + assigned-clocks = <&clk IMX8MM_CLK_ENET_REF_SRC>; + assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_50M>; + assigned-clock-rates = <50000000>; + ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ + mdio { + #address-cells = <1>; + #size-cells = <0>; + -+ ethphy0: ethernet-phy@5 { -+ compatible = "ethernet-phy-ieee802.3-c22"; -+ reg = <0x5>; -+ clocks = <&clk IMX8MM_CLK_ENET_REF_SRC>; -+ clock-names = "rmii-ref"; ++ switch0: switch@16 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ compatible = "marvell,mv88e6250"; ++ reg = <0x10>; ++ dsa,member = <0 0>; ++ reset-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@5 { ++ reg = <5>; ++ ethernet = <&fec1>; ++ phy-mode = "rmii"; ++ ++ fixed-link { ++ speed = <100>; ++ full-duplex; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ label = "eth0"; ++ phy-handle = <&switch0phy1>; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ label = "eth1"; ++ phy-handle = <&switch0phy2>; ++ }; ++ ++ }; ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ switch0phy1: switch0phy1@11 { ++ reg = <0x11>; ++ }; ++ switch0phy2: switch0phy2@12 { ++ reg = <0x12>; ++ }; ++ }; + }; + }; +}; @@ -647,25 +663,3 @@ index 000000000000..411de1c8c620 +&A53_0 { + arm-supply = <&buck2_reg>; +}; -diff --git a/arch/arm64/boot/dts/freescale/fsl-imx8mm.dtsi b/arch/arm64/boot/dts/freescale/fsl-imx8mm.dtsi -index e200219ea8bb..333c9fbaf07f 100644 ---- linux-imx/arch/arm64/boot/dts/freescale/fsl-imx8mm.dtsi -+++ linux-imx/arch/arm64/boot/dts/freescale/fsl-imx8mm.dtsi -@@ -65,7 +65,7 @@ - }; - }; - -- memory@40000000 { -+ memory: memory@40000000 { - device_type = "memory"; - reg = <0x0 0x40000000 0 0x80000000>; - }; -@@ -76,7 +76,7 @@ - ranges; - - /* global autoconfigured region for contiguous allocations */ -- linux,cma { -+ linux_cma: linux,cma { - compatible = "shared-dma-pool"; - reusable; - size = <0 0x28000000>; diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1001-imx8mm-Add-Microchip-USB2422-support.patch b/buildroot/board/tiesse/tgr/kernel-patches/1001-imx8mm-Add-Microchip-USB2422-support.patch index 2823a24..9630c32 100644 --- a/buildroot/board/tiesse/tgr/kernel-patches/1001-imx8mm-Add-Microchip-USB2422-support.patch +++ b/buildroot/board/tiesse/tgr/kernel-patches/1001-imx8mm-Add-Microchip-USB2422-support.patch @@ -1,6 +1,6 @@ diff --git drivers/usb/misc/usb251xb.c drivers/usb/misc/usb251xb.c index 135c91c434bf..a1ca97c07266 100644 ---- linux-imx/drivers/usb/misc/usb251xb.c +--- drivers/usb/misc/usb251xb.c +++ linux-imx/drivers/usb/misc/usb251xb.c @@ -155,6 +155,11 @@ struct usb251xb_data { char product_str[USB251XB_STRING_BUFSIZE / 2]; /* ASCII string */ diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1002-imx8mm-Add-PCA9450-PMIC-support.patch b/buildroot/board/tiesse/tgr/kernel-patches/1002-imx8mm-Add-PCA9450-PMIC-support.patch index 3f08869..acd584c 100644 --- a/buildroot/board/tiesse/tgr/kernel-patches/1002-imx8mm-Add-PCA9450-PMIC-support.patch +++ b/buildroot/board/tiesse/tgr/kernel-patches/1002-imx8mm-Add-PCA9450-PMIC-support.patch @@ -1,6 +1,6 @@ diff --git drivers/mfd/Kconfig drivers/mfd/Kconfig index 997a6172735e..e33bd218301b 100644 ---- linux-imx/drivers/mfd/Kconfig +--- drivers/mfd/Kconfig +++ linux-imx/drivers/mfd/Kconfig @@ -1818,6 +1818,14 @@ config MFD_BD71837 if you say yes here you get support for the BD71837 @@ -19,7 +19,7 @@ index 997a6172735e..e33bd218301b 100644 diff --git drivers/mfd/Makefile drivers/mfd/Makefile index c6755df735ba..962dcc88d99c 100644 ---- linux-imx/drivers/mfd/Makefile +--- drivers/mfd/Makefile +++ linux-imx/drivers/mfd/Makefile @@ -232,3 +232,4 @@ obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o @@ -338,7 +338,7 @@ index 000000000000..85ce6e3eef68 +MODULE_LICENSE("GPL"); diff --git drivers/regulator/Kconfig drivers/regulator/Kconfig index 5361947ea726..e5b3c9ffb982 100644 ---- linux-imx/drivers/regulator/Kconfig +--- drivers/regulator/Kconfig +++ linux-imx/drivers/regulator/Kconfig @@ -983,5 +983,11 @@ config REGULATOR_BD71837 help @@ -354,7 +354,7 @@ index 5361947ea726..e5b3c9ffb982 100644 diff --git drivers/regulator/Makefile drivers/regulator/Makefile index 1bddbefbc8e7..0072ad5666f8 100644 ---- linux-imx/drivers/regulator/Makefile +--- drivers/regulator/Makefile +++ linux-imx/drivers/regulator/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1003-imx8mm-mv88e6071.patch b/buildroot/board/tiesse/tgr/kernel-patches/1003-imx8mm-mv88e6071.patch new file mode 100644 index 0000000..eb4c59a --- /dev/null +++ b/buildroot/board/tiesse/tgr/kernel-patches/1003-imx8mm-mv88e6071.patch @@ -0,0 +1,1003 @@ +Index: drivers/net/dsa/mv88e6xxx/chip.c +=================================================================== +--- drivers/net/dsa/mv88e6xxx/chip.c ++++ linux-imx/drivers/net/dsa/mv88e6xxx/chip.c +@@ -39,6 +39,7 @@ + #include "phy.h" + #include "port.h" + #include "serdes.h" ++#include "smi.h" + + static void assert_reg_lock(struct mv88e6xxx_chip *chip) + { +@@ -48,149 +49,6 @@ static void assert_reg_lock(struct mv88e + } + } + +-/* The switch ADDR[4:1] configuration pins define the chip SMI device address +- * (ADDR[0] is always zero, thus only even SMI addresses can be strapped). +- * +- * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it +- * is the only device connected to the SMI master. In this mode it responds to +- * all 32 possible SMI addresses, and thus maps directly the internal devices. +- * +- * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing +- * multiple devices to share the SMI interface. In this mode it responds to only +- * 2 registers, used to indirectly access the internal SMI devices. +- */ +- +-static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip, +- int addr, int reg, u16 *val) +-{ +- if (!chip->smi_ops) +- return -EOPNOTSUPP; +- +- return chip->smi_ops->read(chip, addr, reg, val); +-} +- +-static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip, +- int addr, int reg, u16 val) +-{ +- if (!chip->smi_ops) +- return -EOPNOTSUPP; +- +- return chip->smi_ops->write(chip, addr, reg, val); +-} +- +-static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip, +- int addr, int reg, u16 *val) +-{ +- int ret; +- +- ret = mdiobus_read_nested(chip->bus, addr, reg); +- if (ret < 0) +- return ret; +- +- *val = ret & 0xffff; +- +- return 0; +-} +- +-static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip, +- int addr, int reg, u16 val) +-{ +- int ret; +- +- ret = mdiobus_write_nested(chip->bus, addr, reg, val); +- if (ret < 0) +- return ret; +- +- return 0; +-} +- +-static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = { +- .read = mv88e6xxx_smi_single_chip_read, +- .write = mv88e6xxx_smi_single_chip_write, +-}; +- +-static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip) +-{ +- int ret; +- int i; +- +- for (i = 0; i < 16; i++) { +- ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD); +- if (ret < 0) +- return ret; +- +- if ((ret & SMI_CMD_BUSY) == 0) +- return 0; +- } +- +- return -ETIMEDOUT; +-} +- +-static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip, +- int addr, int reg, u16 *val) +-{ +- int ret; +- +- /* Wait for the bus to become free. */ +- ret = mv88e6xxx_smi_multi_chip_wait(chip); +- if (ret < 0) +- return ret; +- +- /* Transmit the read command. */ +- ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD, +- SMI_CMD_OP_22_READ | (addr << 5) | reg); +- if (ret < 0) +- return ret; +- +- /* Wait for the read command to complete. */ +- ret = mv88e6xxx_smi_multi_chip_wait(chip); +- if (ret < 0) +- return ret; +- +- /* Read the data. */ +- ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA); +- if (ret < 0) +- return ret; +- +- *val = ret & 0xffff; +- +- return 0; +-} +- +-static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip, +- int addr, int reg, u16 val) +-{ +- int ret; +- +- /* Wait for the bus to become free. */ +- ret = mv88e6xxx_smi_multi_chip_wait(chip); +- if (ret < 0) +- return ret; +- +- /* Transmit the data to write. */ +- ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val); +- if (ret < 0) +- return ret; +- +- /* Transmit the write command. */ +- ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD, +- SMI_CMD_OP_22_WRITE | (addr << 5) | reg); +- if (ret < 0) +- return ret; +- +- /* Wait for the write command to complete. */ +- ret = mv88e6xxx_smi_multi_chip_wait(chip); +- if (ret < 0) +- return ret; +- +- return 0; +-} +- +-static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = { +- .read = mv88e6xxx_smi_multi_chip_read, +- .write = mv88e6xxx_smi_multi_chip_write, +-}; +- + int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val) + { + int err; +@@ -650,6 +508,12 @@ static void mv88e6095_stats_get_strings( + STATS_TYPE_BANK0 | STATS_TYPE_PORT); + } + ++static void mv88e6250_stats_get_strings(struct mv88e6xxx_chip *chip, ++ uint8_t *data) ++{ ++ mv88e6xxx_stats_get_strings(chip, data, STATS_TYPE_BANK0); ++} ++ + static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data) + { +@@ -686,6 +550,11 @@ static int mv88e6095_stats_get_sset_coun + STATS_TYPE_PORT); + } + ++static int mv88e6250_stats_get_sset_count(struct mv88e6xxx_chip *chip) ++{ ++ return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0); ++} ++ + static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip) + { + return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 | +@@ -728,6 +597,13 @@ static void mv88e6095_stats_get_stats(st + 0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX); + } + ++static void mv88e6250_stats_get_stats(struct mv88e6xxx_chip *chip, int port, ++ uint64_t *data) ++{ ++ mv88e6xxx_stats_get_stats(chip, port, data, STATS_TYPE_BANK0, ++ 0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX); ++} ++ + static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) + { +@@ -2901,6 +2777,40 @@ static const struct mv88e6xxx_ops mv88e6 + .serdes_power = mv88e6352_serdes_power, + }; + ++static const struct mv88e6xxx_ops mv88e6250_ops = { ++ /* MV88E6XXX_FAMILY_6250 */ ++ .irl_init_all = mv88e6352_g2_irl_init_all, ++ .get_eeprom = mv88e6xxx_g2_get_eeprom16, ++ .set_eeprom = mv88e6xxx_g2_set_eeprom16, ++ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, ++ .phy_read = mv88e6xxx_g2_smi_phy_read, ++ .phy_write = mv88e6xxx_g2_smi_phy_write, ++ .port_set_link = mv88e6xxx_port_set_link, ++ .port_set_duplex = mv88e6xxx_port_set_duplex, ++ .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, ++ .port_set_speed = mv88e6250_port_set_speed, ++ .port_tag_remap = mv88e6095_port_tag_remap, ++ .port_set_frame_mode = mv88e6351_port_set_frame_mode, ++ .port_set_egress_floods = mv88e6352_port_set_egress_floods, ++ .port_set_ether_type = mv88e6351_port_set_ether_type, ++ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, ++ .port_pause_limit = mv88e6097_port_pause_limit, ++ .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, ++ .stats_snapshot = mv88e6320_g1_stats_snapshot, ++ .stats_set_histogram = mv88e6095_g1_stats_set_histogram, ++ .stats_get_sset_count = mv88e6250_stats_get_sset_count, ++ .stats_get_strings = mv88e6250_stats_get_strings, ++ .stats_get_stats = mv88e6250_stats_get_stats, ++ .set_cpu_port = mv88e6095_g1_set_cpu_port, ++ .set_egress_port = mv88e6095_g1_set_egress_port, ++ .watchdog_ops = &mv88e6250_watchdog_ops, ++ .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, ++ .pot_clear = mv88e6xxx_g2_pot_clear, ++ .reset = mv88e6250_g1_reset, ++ .vtu_getnext = mv88e6250_g1_vtu_getnext, ++ .vtu_loadpurge = mv88e6250_g1_vtu_loadpurge, ++}; ++ + static const struct mv88e6xxx_ops mv88e6290_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .setup_errata = mv88e6390_setup_errata, +@@ -3218,6 +3128,25 @@ static const struct mv88e6xxx_ops mv88e6 + }; + + static const struct mv88e6xxx_info mv88e6xxx_table[] = { ++ [MV88E6071] = { ++ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6071, ++ .family = MV88E6XXX_FAMILY_6250, ++ .name = "Marvell 88E6071", ++ .num_databases = 64, ++ .num_ports = 7, ++ .max_vid = 4095, ++ .port_base_addr = 0x08, ++ .global1_addr = 0x0f, ++ .global2_addr = 0x07, ++ .age_time_coeff = 15000, ++ .g1_irqs = 9, ++ .g2_irqs = 10, ++ .atu_move_port_mask = 0xf, ++ .dual_chip = true, ++ .tag_protocol = DSA_TAG_PROTO_DSA, ++ .ops = &mv88e6250_ops, ++ }, ++ + [MV88E6085] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085, + .family = MV88E6XXX_FAMILY_6097, +@@ -3551,6 +3480,25 @@ static const struct mv88e6xxx_info mv88e + .ops = &mv88e6240_ops, + }, + ++ [MV88E6250] = { ++ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6250, ++ .family = MV88E6XXX_FAMILY_6250, ++ .name = "Marvell 88E6250", ++ .num_databases = 64, ++ .num_ports = 7, ++ .max_vid = 4095, ++ .port_base_addr = 0x08, ++ .global1_addr = 0x0f, ++ .global2_addr = 0x07, ++ .age_time_coeff = 15000, ++ .g1_irqs = 9, ++ .g2_irqs = 10, ++ .atu_move_port_mask = 0xf, ++ .dual_chip = true, ++ .tag_protocol = DSA_TAG_PROTO_DSA, ++ .ops = &mv88e6250_ops, ++ }, ++ + [MV88E6290] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6290, + .family = MV88E6XXX_FAMILY_6390, +@@ -3786,22 +3734,6 @@ static struct mv88e6xxx_chip *mv88e6xxx_ + return chip; + } + +-static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, +- struct mii_bus *bus, int sw_addr) +-{ +- if (sw_addr == 0) +- chip->smi_ops = &mv88e6xxx_smi_single_chip_ops; +- else if (chip->info->multi_chip) +- chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops; +- else +- return -EINVAL; +- +- chip->bus = bus; +- chip->sw_addr = sw_addr; +- +- return 0; +-} +- + static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds) + { + struct mv88e6xxx_chip *chip = ds->priv; +@@ -3832,6 +3764,7 @@ static const char *mv88e6xxx_drv_probe(s + if (err) + goto free; + ++ mv88e6xxx_hardware_reset(chip); + err = mv88e6xxx_detect(chip); + if (err) + goto free; +@@ -3988,6 +3921,7 @@ static int mv88e6xxx_probe(struct mdio_d + if (IS_ERR(chip->reset)) + return PTR_ERR(chip->reset); + ++ mv88e6xxx_hardware_reset(chip); + err = mv88e6xxx_detect(chip); + if (err) + return err; +@@ -4081,6 +4015,10 @@ static const struct of_device_id mv88e6x + .compatible = "marvell,mv88e6190", + .data = &mv88e6xxx_table[MV88E6190], + }, ++ { ++ .compatible = "marvell,mv88e6250", ++ .data = &mv88e6xxx_table[MV88E6250], ++ }, + { /* sentinel */ }, + }; + +Index: drivers/net/dsa/mv88e6xxx/chip.h +=================================================================== +--- drivers/net/dsa/mv88e6xxx/chip.h ++++ linux-imx/drivers/net/dsa/mv88e6xxx/chip.h +@@ -22,17 +22,6 @@ + #define UINT64_MAX (u64)(~((u64)0)) + #endif + +-#define SMI_CMD 0x00 +-#define SMI_CMD_BUSY BIT(15) +-#define SMI_CMD_CLAUSE_22 BIT(12) +-#define SMI_CMD_OP_22_WRITE ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22) +-#define SMI_CMD_OP_22_READ ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22) +-#define SMI_CMD_OP_45_WRITE_ADDR ((0 << 10) | SMI_CMD_BUSY) +-#define SMI_CMD_OP_45_WRITE_DATA ((1 << 10) | SMI_CMD_BUSY) +-#define SMI_CMD_OP_45_READ_DATA ((2 << 10) | SMI_CMD_BUSY) +-#define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY) +-#define SMI_DATA 0x01 +- + #define MV88E6XXX_N_FID 4096 + + /* PVT limits for 4-bit port and 5-bit switch */ +@@ -55,6 +44,7 @@ enum mv88e6xxx_frame_mode { + + /* List of supported models */ + enum mv88e6xxx_model { ++ MV88E6071, + MV88E6085, + MV88E6095, + MV88E6097, +@@ -72,6 +62,7 @@ enum mv88e6xxx_model { + MV88E6190X, + MV88E6191, + MV88E6240, ++ MV88E6250, + MV88E6290, + MV88E6320, + MV88E6321, +@@ -90,6 +81,7 @@ enum mv88e6xxx_family { + MV88E6XXX_FAMILY_6097, /* 6046 6085 6096 6097 */ + MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */ + MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */ ++ MV88E6XXX_FAMILY_6250, /* 6071 6250 */ + MV88E6XXX_FAMILY_6320, /* 6320 6321 */ + MV88E6XXX_FAMILY_6341, /* 6141 6341 */ + MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ +@@ -119,6 +111,11 @@ struct mv88e6xxx_info { + * when it is non-zero, and use indirect access to internal registers. + */ + bool multi_chip; ++ /* Dual-chip Addressing Mode ++ * Some chips respond to only half of the 32 SMI addresses, ++ * allowing two to coexist on the same SMI interface. ++ */ ++ bool dual_chip; + enum dsa_tag_protocol tag_protocol; + + /* Mask for FromPort and ToPort value of PortVec used in ATU Move +Index: drivers/net/dsa/mv88e6xxx/global1.c +=================================================================== +--- drivers/net/dsa/mv88e6xxx/global1.c ++++ linux-imx/drivers/net/dsa/mv88e6xxx/global1.c +@@ -31,6 +31,12 @@ int mv88e6xxx_g1_write(struct mv88e6xxx_ + return mv88e6xxx_write(chip, addr, reg, val); + } + ++int mv88e6250_g1_ieee_pri_map(struct mv88e6xxx_chip *chip) ++{ ++ /* Reset the IEEE Tag priorities to defaults */ ++ return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, 0xfa50); ++} ++ + int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) + { + return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask); +@@ -182,6 +188,25 @@ int mv88e6185_g1_reset(struct mv88e6xxx_ + return mv88e6185_g1_wait_ppu_polling(chip); + } + ++int mv88e6250_g1_reset(struct mv88e6xxx_chip *chip) ++{ ++ u16 val; ++ int err; ++ ++ /* Set the SWReset bit 15 */ ++ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &val); ++ if (err) ++ return err; ++ ++ val |= MV88E6XXX_G1_CTL1_SW_RESET; ++ ++ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, val); ++ if (err) ++ return err; ++ ++ return mv88e6xxx_g1_wait_init_ready(chip); ++} ++ + int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip) + { + u16 val; +@@ -374,6 +399,22 @@ int mv88e6xxx_g1_stats_wait(struct mv88e + MV88E6XXX_G1_STATS_OP_BUSY); + } + ++int mv88e6095_g1_stats_set_histogram(struct mv88e6xxx_chip *chip) ++{ ++ u16 val; ++ int err; ++ ++ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STATS_OP, &val); ++ if (err) ++ return err; ++ ++ val |= MV88E6XXX_G1_STATS_OP_HIST_RX_TX; ++ ++ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP, val); ++ ++ return err; ++} ++ + int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) + { + int err; +Index: drivers/net/dsa/mv88e6xxx/global1.h +=================================================================== +--- drivers/net/dsa/mv88e6xxx/global1.h ++++ linux-imx/drivers/net/dsa/mv88e6xxx/global1.h +@@ -227,6 +227,7 @@ int mv88e6xxx_g1_set_switch_mac(struct m + + int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip); + int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip); ++int mv88e6250_g1_reset(struct mv88e6xxx_chip *chip); + + int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip); + int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip); +@@ -236,6 +237,7 @@ int mv88e6xxx_g1_stats_snapshot(struct m + int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); + int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); + int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip); ++int mv88e6095_g1_stats_set_histogram(struct mv88e6xxx_chip *chip); + void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val); + int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); + int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); +@@ -258,6 +260,12 @@ int mv88e6185_g1_vtu_getnext(struct mv88 + struct mv88e6xxx_vtu_entry *entry); + int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); ++int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip, ++ struct mv88e6xxx_vtu_entry *entry); ++int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, ++ struct mv88e6xxx_vtu_entry *entry); ++int mv88e6250_g1_ieee_pri_map(struct mv88e6xxx_chip *chip); ++ + int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); + int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, +Index: drivers/net/dsa/mv88e6xxx/port.h +=================================================================== +--- drivers/net/dsa/mv88e6xxx/port.h ++++ linux-imx/drivers/net/dsa/mv88e6xxx/port.h +@@ -23,6 +23,16 @@ + #define MV88E6XXX_PORT_STS_MY_PAUSE 0x4000 + #define MV88E6XXX_PORT_STS_HD_FLOW 0x2000 + #define MV88E6XXX_PORT_STS_PHY_DETECT 0x1000 ++#define MV88E6250_PORT_STS_LINK 0x1000 ++#define MV88E6250_PORT_STS_PORTMODE_MASK 0x0f00 ++#define MV88E6250_PORT_STS_PORTMODE_PHY_10_HALF 0x0800 ++#define MV88E6250_PORT_STS_PORTMODE_PHY_100_HALF 0x0900 ++#define MV88E6250_PORT_STS_PORTMODE_PHY_10_FULL 0x0a00 ++#define MV88E6250_PORT_STS_PORTMODE_PHY_100_FULL 0x0b00 ++#define MV88E6250_PORT_STS_PORTMODE_MII_10_HALF 0x0c00 ++#define MV88E6250_PORT_STS_PORTMODE_MII_100_HALF 0x0d00 ++#define MV88E6250_PORT_STS_PORTMODE_MII_10_FULL 0x0e00 ++#define MV88E6250_PORT_STS_PORTMODE_MII_100_FULL 0x0f00 + #define MV88E6XXX_PORT_STS_LINK 0x0800 + #define MV88E6XXX_PORT_STS_DUPLEX 0x0400 + #define MV88E6XXX_PORT_STS_SPEED_MASK 0x0300 +@@ -79,6 +89,7 @@ + /* Offset 0x03: Switch Identifier Register */ + #define MV88E6XXX_PORT_SWITCH_ID 0x03 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_MASK 0xfff0 ++#define MV88E6XXX_PORT_SWITCH_ID_PROD_6071 0x0710 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6085 0x04a0 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6095 0x0950 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6097 0x0990 +@@ -97,6 +108,7 @@ + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 ++#define MV88E6XXX_PORT_SWITCH_ID_PROD_6250 0x2500 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400 +@@ -262,6 +274,7 @@ int mv88e6xxx_port_set_duplex(struct mv8 + + int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); + int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); ++int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); + int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); + int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); + int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +Index: drivers/net/dsa/mv88e6xxx/port.c +=================================================================== +--- drivers/net/dsa/mv88e6xxx/port.c ++++ linux-imx/drivers/net/dsa/mv88e6xxx/port.c +@@ -266,6 +266,18 @@ int mv88e6185_port_set_speed(struct mv88 + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); + } + ++/* Support 10, 100 Mbps (e.g. 88E6250 family) */ ++int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) ++{ ++ if (speed == SPEED_MAX) ++ speed = 100; ++ ++ if (speed > 100) ++ return -EOPNOTSUPP; ++ ++ return mv88e6xxx_port_set_speed(chip, port, speed, false, false); ++} ++ + /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ + int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) + { +Index: drivers/net/dsa/mv88e6xxx/global1_atu.c +=================================================================== +--- drivers/net/dsa/mv88e6xxx/global1_atu.c ++++ linux-imx/drivers/net/dsa/mv88e6xxx/global1_atu.c +@@ -92,7 +92,7 @@ static int mv88e6xxx_g1_atu_op(struct mv + if (err) + return err; + } else { +- if (mv88e6xxx_num_databases(chip) > 16) { ++ if (mv88e6xxx_num_databases(chip) > 64) { + /* ATU DBNum[7:4] are located in ATU Control 15:12 */ + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, + &val); +@@ -104,6 +104,9 @@ static int mv88e6xxx_g1_atu_op(struct mv + val); + if (err) + return err; ++ } else if (mv88e6xxx_num_databases(chip) > 16) { ++ /* ATU DBNum[5:4] are located in ATU Operation 9:8 */ ++ op |= (fid & 0x30) << 4; + } + + /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ +Index: drivers/net/dsa/mv88e6xxx/global1_vtu.c +=================================================================== +--- drivers/net/dsa/mv88e6xxx/global1_vtu.c ++++ linux-imx/drivers/net/dsa/mv88e6xxx/global1_vtu.c +@@ -304,6 +304,35 @@ static int mv88e6xxx_g1_vtu_getnext(stru + return mv88e6xxx_g1_vtu_vid_read(chip, entry); + } + ++int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip, ++ struct mv88e6xxx_vtu_entry *entry) ++{ ++ u16 val; ++ int err; ++ ++ err = mv88e6xxx_g1_vtu_getnext(chip, entry); ++ if (err) ++ return err; ++ ++ if (entry->valid) { ++ err = mv88e6185_g1_vtu_data_read(chip, entry); ++ if (err) ++ return err; ++ ++ /* VTU DBNum[3:0] are located in VTU Operation 3:0 ++ * VTU DBNum[5:4] are located in VTU Operation 9:8 ++ */ ++ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val); ++ if (err) ++ return err; ++ ++ entry->fid = val & 0x000f; ++ entry->fid |= (val & 0x0300) >> 4; ++ } ++ ++ return 0; ++} ++ + int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) + { +@@ -393,6 +422,35 @@ int mv88e6390_g1_vtu_getnext(struct mv88 + return 0; + } + ++int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, ++ struct mv88e6xxx_vtu_entry *entry) ++{ ++ u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE; ++ int err; ++ ++ err = mv88e6xxx_g1_vtu_op_wait(chip); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_g1_vtu_vid_write(chip, entry); ++ if (err) ++ return err; ++ ++ if (entry->valid) { ++ err = mv88e6185_g1_vtu_data_write(chip, entry); ++ if (err) ++ return err; ++ ++ /* VTU DBNum[3:0] are located in VTU Operation 3:0 ++ * VTU DBNum[5:4] are located in VTU Operation 9:8 ++ */ ++ op |= entry->fid & 0x000f; ++ op |= (entry->fid & 0x0030) << 4; ++ } ++ ++ return mv88e6xxx_g1_vtu_op(chip, op); ++} ++ + int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) + { +Index: drivers/net/dsa/mv88e6xxx/global2.c +=================================================================== +--- drivers/net/dsa/mv88e6xxx/global2.c ++++ linux-imx/drivers/net/dsa/mv88e6xxx/global2.c +@@ -835,6 +835,32 @@ const struct mv88e6xxx_irq_ops mv88e6097 + .irq_free = mv88e6097_watchdog_free, + }; + ++static void mv88e6250_watchdog_free(struct mv88e6xxx_chip *chip) ++{ ++ u16 reg; ++ ++ mv88e6xxx_g2_read(chip, MV88E6250_G2_WDOG_CTL, ®); ++ ++ reg &= ~(MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE | ++ MV88E6250_G2_WDOG_CTL_QC_ENABLE); ++ ++ mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL, reg); ++} ++ ++static int mv88e6250_watchdog_setup(struct mv88e6xxx_chip *chip) ++{ ++ return mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL, ++ MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE | ++ MV88E6250_G2_WDOG_CTL_QC_ENABLE | ++ MV88E6250_G2_WDOG_CTL_SWRESET); ++} ++ ++const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = { ++ .irq_action = mv88e6097_watchdog_action, ++ .irq_setup = mv88e6250_watchdog_setup, ++ .irq_free = mv88e6250_watchdog_free, ++}; ++ + static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip) + { + return mv88e6xxx_g2_update(chip, MV88E6390_G2_WDOG_CTL, +Index: drivers/net/dsa/mv88e6xxx/global2.h +=================================================================== +--- drivers/net/dsa/mv88e6xxx/global2.h ++++ linux-imx/drivers/net/dsa/mv88e6xxx/global2.h +@@ -185,6 +185,18 @@ + #define MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK 0x00ff + + /* Offset 0x1B: Watch Dog Control Register */ ++#define MV88E6250_G2_WDOG_CTL 0x1b ++#define MV88E6250_G2_WDOG_CTL_QC_HISTORY 0x0100 ++#define MV88E6250_G2_WDOG_CTL_QC_EVENT 0x0080 ++#define MV88E6250_G2_WDOG_CTL_QC_ENABLE 0x0040 ++#define MV88E6250_G2_WDOG_CTL_EGRESS_HISTORY 0x0020 ++#define MV88E6250_G2_WDOG_CTL_EGRESS_EVENT 0x0010 ++#define MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE 0x0008 ++#define MV88E6250_G2_WDOG_CTL_FORCE_IRQ 0x0004 ++#define MV88E6250_G2_WDOG_CTL_HISTORY 0x0002 ++#define MV88E6250_G2_WDOG_CTL_SWRESET 0x0001 ++ ++/* Offset 0x1B: Watch Dog Control Register */ + #define MV88E6352_G2_WDOG_CTL 0x1b + #define MV88E6352_G2_WDOG_CTL_EGRESS_EVENT 0x0080 + #define MV88E6352_G2_WDOG_CTL_RMU_TIMEOUT 0x0040 +@@ -265,6 +277,7 @@ int mv88e6352_g2_mgmt_rsvd2cpu(struct mv + int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip); + + extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops; ++extern const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops; + extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; + + #else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ +@@ -380,6 +393,7 @@ static inline int mv88e6xxx_g2_pot_clear + } + + static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {}; ++static const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = {}; + static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {}; + + #endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ +Index: drivers/net/dsa/mv88e6xxx/Makefile +=================================================================== +--- drivers/net/dsa/mv88e6xxx/Makefile ++++ linux-imx/drivers/net/dsa/mv88e6xxx/Makefile +@@ -8,3 +8,4 @@ mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLO + mv88e6xxx-objs += phy.o + mv88e6xxx-objs += port.o + mv88e6xxx-objs += serdes.o ++mv88e6xxx-objs += smi.o +Index: drivers/net/dsa/mv88e6xxx/smi.c +=================================================================== +--- /dev/null ++++ linux-imx/drivers/net/dsa/mv88e6xxx/smi.c +@@ -0,0 +1,181 @@ ++/* ++ * Marvell 88E6xxx System Management Interface (SMI) support ++ * ++ * Copyright (c) 2008 Marvell Semiconductor ++ * ++ * Copyright (c) 2019 Vivien Didelot ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include "chip.h" ++#include "smi.h" ++ ++/* The switch ADDR[4:1] configuration pins define the chip SMI device address ++ * (ADDR[0] is always zero, thus only even SMI addresses can be strapped). ++ * ++ * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it ++ * is the only device connected to the SMI master. In this mode it responds to ++ * all 32 possible SMI addresses, and thus maps directly the internal devices. ++ * ++ * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing ++ * multiple devices to share the SMI interface. In this mode it responds to only ++ * 2 registers, used to indirectly access the internal SMI devices. ++ * ++ * Some chips use a different scheme: Only the ADDR4 pin is used for ++ * configuration, and the device responds to 16 of the 32 SMI ++ * addresses, allowing two to coexist on the same SMI interface. ++ */ ++ ++static int mv88e6xxx_smi_direct_read(struct mv88e6xxx_chip *chip, ++ int dev, int reg, u16 *data) ++{ ++ int ret; ++ ++ ret = mdiobus_read_nested(chip->bus, dev, reg); ++ if (ret < 0) ++ return ret; ++ ++ *data = ret & 0xffff; ++ ++ return 0; ++} ++ ++static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip, ++ int dev, int reg, u16 data) ++{ ++ int ret; ++ ++ ret = mdiobus_write_nested(chip->bus, dev, reg, data); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip, ++ int dev, int reg, int bit, int val) ++{ ++ u16 data; ++ int err; ++ int i; ++ ++ for (i = 0; i < 16; i++) { ++ err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data); ++ if (err) ++ return err; ++ ++ if (!!(data >> bit) == !!val) ++ return 0; ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = { ++ .read = mv88e6xxx_smi_direct_read, ++ .write = mv88e6xxx_smi_direct_write, ++}; ++ ++static int mv88e6xxx_smi_dual_direct_read(struct mv88e6xxx_chip *chip, ++ int dev, int reg, u16 *data) ++{ ++ return mv88e6xxx_smi_direct_read(chip, chip->sw_addr + dev, reg, data); ++} ++ ++static int mv88e6xxx_smi_dual_direct_write(struct mv88e6xxx_chip *chip, ++ int dev, int reg, u16 data) ++{ ++ return mv88e6xxx_smi_direct_write(chip, chip->sw_addr + dev, reg, data); ++} ++ ++static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_dual_direct_ops = { ++ .read = mv88e6xxx_smi_dual_direct_read, ++ .write = mv88e6xxx_smi_dual_direct_write, ++}; ++ ++/* Offset 0x00: SMI Command Register ++ * Offset 0x01: SMI Data Register ++ */ ++ ++static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip, ++ int dev, int reg, u16 *data) ++{ ++ int err; ++ ++ err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, ++ MV88E6XXX_SMI_CMD, 15, 0); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, ++ MV88E6XXX_SMI_CMD, ++ MV88E6XXX_SMI_CMD_BUSY | ++ MV88E6XXX_SMI_CMD_MODE_22 | ++ MV88E6XXX_SMI_CMD_OP_22_READ | ++ (dev << 5) | reg); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, ++ MV88E6XXX_SMI_CMD, 15, 0); ++ if (err) ++ return err; ++ ++ return mv88e6xxx_smi_direct_read(chip, chip->sw_addr, ++ MV88E6XXX_SMI_DATA, data); ++} ++ ++static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip, ++ int dev, int reg, u16 data) ++{ ++ int err; ++ ++ err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, ++ MV88E6XXX_SMI_CMD, 15, 0); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, ++ MV88E6XXX_SMI_DATA, data); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, ++ MV88E6XXX_SMI_CMD, ++ MV88E6XXX_SMI_CMD_BUSY | ++ MV88E6XXX_SMI_CMD_MODE_22 | ++ MV88E6XXX_SMI_CMD_OP_22_WRITE | ++ (dev << 5) | reg); ++ if (err) ++ return err; ++ ++ return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, ++ MV88E6XXX_SMI_CMD, 15, 0); ++} ++ ++static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = { ++ .read = mv88e6xxx_smi_indirect_read, ++ .write = mv88e6xxx_smi_indirect_write, ++}; ++ ++int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, ++ struct mii_bus *bus, int sw_addr) ++{ ++ if (chip->info->dual_chip) ++ chip->smi_ops = &mv88e6xxx_smi_dual_direct_ops; ++ else if (sw_addr == 0) ++ chip->smi_ops = &mv88e6xxx_smi_direct_ops; ++ else if (chip->info->multi_chip) ++ chip->smi_ops = &mv88e6xxx_smi_indirect_ops; ++ else ++ return -EINVAL; ++ ++ chip->bus = bus; ++ chip->sw_addr = sw_addr; ++ ++ return 0; ++} +Index: drivers/net/dsa/mv88e6xxx/smi.h +=================================================================== +--- /dev/null ++++ linux-imx/drivers/net/dsa/mv88e6xxx/smi.h +@@ -0,0 +1,59 @@ ++/* ++ * Marvell 88E6xxx System Management Interface (SMI) support ++ * ++ * Copyright (c) 2008 Marvell Semiconductor ++ * ++ * Copyright (c) 2019 Vivien Didelot ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef _MV88E6XXX_SMI_H ++#define _MV88E6XXX_SMI_H ++ ++#include "chip.h" ++ ++/* Offset 0x00: SMI Command Register */ ++#define MV88E6XXX_SMI_CMD 0x00 ++#define MV88E6XXX_SMI_CMD_BUSY 0x8000 ++#define MV88E6XXX_SMI_CMD_MODE_MASK 0x1000 ++#define MV88E6XXX_SMI_CMD_MODE_45 0x0000 ++#define MV88E6XXX_SMI_CMD_MODE_22 0x1000 ++#define MV88E6XXX_SMI_CMD_OP_MASK 0x0c00 ++#define MV88E6XXX_SMI_CMD_OP_22_WRITE 0x0400 ++#define MV88E6XXX_SMI_CMD_OP_22_READ 0x0800 ++#define MV88E6XXX_SMI_CMD_OP_45_WRITE_ADDR 0x0000 ++#define MV88E6XXX_SMI_CMD_OP_45_WRITE_DATA 0x0400 ++#define MV88E6XXX_SMI_CMD_OP_45_READ_DATA 0x0800 ++#define MV88E6XXX_SMI_CMD_OP_45_READ_DATA_INC 0x0c00 ++#define MV88E6XXX_SMI_CMD_DEV_ADDR_MASK 0x003e ++#define MV88E6XXX_SMI_CMD_REG_ADDR_MASK 0x001f ++ ++/* Offset 0x01: SMI Data Register */ ++#define MV88E6XXX_SMI_DATA 0x01 ++ ++int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, ++ struct mii_bus *bus, int sw_addr); ++ ++static inline int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip, ++ int dev, int reg, u16 *data) ++{ ++ if (chip->smi_ops && chip->smi_ops->read) ++ return chip->smi_ops->read(chip, dev, reg, data); ++ ++ return -EOPNOTSUPP; ++} ++ ++static inline int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip, ++ int dev, int reg, u16 data) ++{ ++ if (chip->smi_ops && chip->smi_ops->write) ++ return chip->smi_ops->write(chip, dev, reg, data); ++ ++ return -EOPNOTSUPP; ++} ++ ++#endif /* _MV88E6XXX_SMI_H */ diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1004-imx8mm-custom-devname.patch b/buildroot/board/tiesse/tgr/kernel-patches/1004-imx8mm-custom-devname.patch new file mode 100644 index 0000000..8422ad6 --- /dev/null +++ b/buildroot/board/tiesse/tgr/kernel-patches/1004-imx8mm-custom-devname.patch @@ -0,0 +1,26 @@ +Index: drivers/net/ethernet/freescale/fec_main.c +=================================================================== +--- drivers/net/ethernet/freescale/fec_main.c ++++ linux-imx/drivers/net/ethernet/freescale/fec_main.c +@@ -3630,12 +3630,19 @@ fec_probe(struct platform_device *pdev) + struct device_node *np = pdev->dev.of_node, *phy_node; + int num_tx_qs; + int num_rx_qs; ++ const char *dev_name; + + fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); + + /* Init network device */ +- ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + +- FEC_STATS_SIZE, num_tx_qs, num_rx_qs); ++ dev_name = of_get_property(np, "fsl,devname", NULL); ++ if (dev_name) ++ ndev = alloc_netdev_mqs(sizeof(struct fec_enet_private) + ++ FEC_STATS_SIZE, dev_name, NET_NAME_UNKNOWN, ++ ether_setup, num_tx_qs, num_rx_qs); ++ else ++ ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + ++ FEC_STATS_SIZE, num_tx_qs, num_rx_qs); + if (!ndev) + return -ENOMEM; + diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1005-imx8mm-dsa-slave-address.patch b/buildroot/board/tiesse/tgr/kernel-patches/1005-imx8mm-dsa-slave-address.patch new file mode 100644 index 0000000..8283c6f --- /dev/null +++ b/buildroot/board/tiesse/tgr/kernel-patches/1005-imx8mm-dsa-slave-address.patch @@ -0,0 +1,49 @@ +Index: net/dsa/Kconfig +=================================================================== +--- net/dsa/Kconfig ++++ linux-imx/net/dsa/Kconfig +@@ -40,4 +40,9 @@ config NET_DSA_TAG_TRAILER + config NET_DSA_TAG_QCA + bool + ++config NET_DSA_SLAVE_MAC ++ bool "DSA Extension for Tiesse TGR" ++ ---help--- ++ This enables a Tiesse TGR specific mac assignement schema ++ + endif +Index: net/dsa/slave.c +=================================================================== +--- net/dsa/slave.c ++++ linux-imx/net/dsa/slave.c +@@ -27,6 +27,18 @@ + + static bool dsa_slave_dev_check(struct net_device *dev); + ++#ifdef CONFIG_NET_DSA_SLAVE_MAC ++static inline void eth_hw_addr_assign(int port_index, ++ struct net_device *dst, ++ struct net_device *src) ++{ ++ u64 temp = ether_addr_to_u64(src->dev_addr); ++ temp += port_index-1; ++ u64_to_ether_addr(temp, dst->dev_addr); ++ dst->addr_assign_type = src->addr_assign_type; ++} ++#endif ++ + /* slave mii_bus handling ***************************************************/ + static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) + { +@@ -1277,7 +1289,11 @@ int dsa_slave_create(struct dsa_port *po + slave_dev->features = master->vlan_features | NETIF_F_HW_TC; + slave_dev->hw_features |= NETIF_F_HW_TC; + slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; ++#ifdef CONFIG_NET_DSA_SLAVE_MAC ++ eth_hw_addr_assign(port->index, slave_dev, master); ++#else + eth_hw_addr_inherit(slave_dev, master); ++#endif + slave_dev->priv_flags |= IFF_NO_QUEUE; + slave_dev->netdev_ops = &dsa_slave_netdev_ops; + slave_dev->switchdev_ops = &dsa_slave_switchdev_ops; diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1006-imx8mm-shut.patch b/buildroot/board/tiesse/tgr/kernel-patches/1006-imx8mm-shut.patch new file mode 100644 index 0000000..565dc68 --- /dev/null +++ b/buildroot/board/tiesse/tgr/kernel-patches/1006-imx8mm-shut.patch @@ -0,0 +1,322 @@ +Index: drivers/net/phy/phy.c +=================================================================== +--- drivers/net/phy/phy.c ++++ linux-imx/drivers/net/phy/phy.c +@@ -55,6 +55,7 @@ static const char *phy_state_to_str(enum + PHY_STATE_STR(NOLINK) + PHY_STATE_STR(FORCING) + PHY_STATE_STR(CHANGELINK) ++ PHY_STATE_STR(POWERED_DOWN) + PHY_STATE_STR(HALTED) + PHY_STATE_STR(RESUMING) + } +@@ -241,6 +242,33 @@ static void phy_sanitize_settings(struct + } + } + ++static int phy_shut(struct phy_device *phydev) ++{ ++ mutex_lock(&phydev->lock); ++ phydev->power = PHY_POWER_DOWN; ++ if (PHY_HALTED == phydev->state) { ++ mutex_unlock(&phydev->lock); ++ return 0; ++ } ++ phydev->state = PHY_POWERED_DOWN; ++ mutex_unlock(&phydev->lock); ++ return 0; ++} ++ ++static int phy_unshut(struct phy_device *phydev) ++{ ++ mutex_lock(&phydev->lock); ++ phydev->power = PHY_POWER_UP; ++ if (PHY_HALTED == phydev->state) { ++ mutex_unlock(&phydev->lock); ++ return 0; ++ } ++ phy_power_up(phydev); ++ phydev->state = PHY_CHANGELINK; ++ mutex_unlock(&phydev->lock); ++ return 0; ++} ++ + /** + * phy_ethtool_sset - generic ethtool sset function, handles all the details + * @phydev: target phy_device struct +@@ -257,10 +285,28 @@ static void phy_sanitize_settings(struct + int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) + { + u32 speed = ethtool_cmd_speed(cmd); ++ int ret; + + if (cmd->phy_address != phydev->mdio.addr) + return -EINVAL; + ++ if (PHY_POWERED_DOWN == phydev->state) { ++ if (cmd->power == PHY_POWER_UP) { ++ ret = phy_unshut(phydev); ++ if (ret < 0) ++ return -EIO; ++ phy_trigger_machine(phydev, true); ++ } ++ return 0; ++ } ++ else if (cmd->power == PHY_POWER_DOWN) { ++ ret = phy_shut(phydev); ++ if (ret < 0) ++ return -EIO; ++ phy_trigger_machine(phydev, true); ++ return 0; ++ } ++ + /* We make sure that we don't pass unsupported values in to the PHY */ + cmd->advertising &= phydev->supported; + +@@ -308,10 +354,28 @@ int phy_ethtool_ksettings_set(struct phy + u8 duplex = cmd->base.duplex; + u32 speed = cmd->base.speed; + u32 advertising; ++ int ret; + + if (cmd->base.phy_address != phydev->mdio.addr) + return -EINVAL; + ++ if (PHY_POWERED_DOWN == phydev->state) { ++ if (cmd->base.power == PHY_POWER_UP) { ++ ret = phy_unshut(phydev); ++ if (ret < 0) ++ return -EIO; ++ phy_trigger_machine(phydev, true); ++ } ++ return 0; ++ } ++ else if (cmd->base.power == PHY_POWER_DOWN) { ++ ret = phy_shut(phydev); ++ if (ret < 0) ++ return -EIO; ++ phy_trigger_machine(phydev, true); ++ return 0; ++ } ++ + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); + +@@ -379,6 +443,7 @@ void phy_ethtool_ksettings_get(struct ph + cmd->base.autoneg = phydev->autoneg; + cmd->base.eth_tp_mdix_ctrl = phydev->mdix_ctrl; + cmd->base.eth_tp_mdix = phydev->mdix; ++ cmd->base.power = phydev->power; + } + EXPORT_SYMBOL(phy_ethtool_ksettings_get); + +@@ -881,7 +946,7 @@ void phy_state_machine(struct work_struc + struct delayed_work *dwork = to_delayed_work(work); + struct phy_device *phydev = + container_of(dwork, struct phy_device, state_queue); +- bool needs_aneg = false, do_suspend = false; ++ bool needs_aneg = false, do_suspend = false, do_power_down = false; + enum phy_state old_state; + int err = 0; + int old_link; +@@ -1014,6 +1079,14 @@ void phy_state_machine(struct work_struc + do_suspend = true; + } + break; ++ case PHY_POWERED_DOWN: ++ phy_read_status(phydev); ++ if (phydev->link) { ++ phydev->link = 0; ++ phy_link_down(phydev, true); ++ do_power_down = true; ++ } ++ break; + case PHY_RESUMING: + if (AUTONEG_ENABLE == phydev->autoneg) { + err = phy_aneg_done(phydev); +@@ -1059,8 +1132,14 @@ void phy_state_machine(struct work_struc + + if (needs_aneg) + err = phy_start_aneg_priv(phydev, false); +- else if (do_suspend) +- phy_suspend(phydev); ++ else { ++ if (do_suspend) ++ phy_suspend(phydev); ++ if (do_power_down) { ++ phy_power_down(phydev); ++ phy_read_status(phydev); ++ } ++ } + + if (err < 0) + phy_error(phydev); +Index: drivers/net/phy/phy_device.c +=================================================================== +--- drivers/net/phy/phy_device.c ++++ linux-imx/drivers/net/phy/phy_device.c +@@ -1230,6 +1230,31 @@ out: + } + EXPORT_SYMBOL(phy_loopback); + ++int phy_power_down(struct phy_device *phydev) ++{ ++ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); ++ int ret = 0; ++ ++ if (phydev->drv && phydrv->suspend) ++ ret = phydrv->suspend(phydev); ++ ++ return ret; ++ ++} ++EXPORT_SYMBOL(phy_power_down); ++ ++int phy_power_up(struct phy_device *phydev) ++{ ++ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); ++ int ret = 0; ++ ++ if (phydev->drv && phydrv->resume) ++ ret = phydrv->resume(phydev); ++ ++ return ret; ++} ++EXPORT_SYMBOL(phy_power_up); ++ + /* Generic PHY support and helper functions */ + + /** +@@ -1501,6 +1526,7 @@ EXPORT_SYMBOL(genphy_update_link); + */ + int genphy_read_status(struct phy_device *phydev) + { ++ int bmcr; + int adv; + int err; + int lpa; +@@ -1513,6 +1539,10 @@ int genphy_read_status(struct phy_device + if (err) + return err; + ++ bmcr = phy_read(phydev, MII_BMCR); ++ if (bmcr < 0) ++ return bmcr; ++ + phydev->lp_advertising = 0; + + if (AUTONEG_ENABLE == phydev->autoneg) { +@@ -1567,11 +1597,6 @@ int genphy_read_status(struct phy_device + phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; + } + } else { +- int bmcr = phy_read(phydev, MII_BMCR); +- +- if (bmcr < 0) +- return bmcr; +- + if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + else +@@ -1588,6 +1613,11 @@ int genphy_read_status(struct phy_device + phydev->asym_pause = 0; + } + ++ if (bmcr & BMCR_PDOWN) ++ phydev->power = PHY_POWER_DOWN; ++ else ++ phydev->power = PHY_POWER_UP; ++ + return 0; + } + EXPORT_SYMBOL(genphy_read_status); +Index: include/linux/phy.h +=================================================================== +--- include/linux/phy.h ++++ linux-imx/include/linux/phy.h +@@ -348,6 +348,7 @@ enum phy_state { + PHY_NOLINK, + PHY_FORCING, + PHY_CHANGELINK, ++ PHY_POWERED_DOWN, + PHY_HALTED, + PHY_RESUMING + }; +@@ -447,6 +448,8 @@ struct phy_device { + + int link_timeout; + ++ int power; ++ + #ifdef CONFIG_LED_TRIGGER_PHY + struct phy_led_trigger *phy_led_triggers; + unsigned int phy_num_led_triggers; +@@ -819,6 +822,8 @@ int phy_suspend(struct phy_device *phyde + int phy_resume(struct phy_device *phydev); + int __phy_resume(struct phy_device *phydev); + int phy_loopback(struct phy_device *phydev, bool enable); ++int phy_power_up(struct phy_device *phydev); ++int phy_power_down(struct phy_device *phydev); + struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, + phy_interface_t interface); + struct phy_device *phy_find_first(struct mii_bus *bus); +Index: include/uapi/linux/ethtool.h +=================================================================== +--- include/uapi/linux/ethtool.h ++++ linux-imx/include/uapi/linux/ethtool.h +@@ -114,7 +114,10 @@ struct ethtool_cmd { + __u8 eth_tp_mdix; + __u8 eth_tp_mdix_ctrl; + __u32 lp_advertising; +- __u32 reserved[2]; ++ __u8 power; ++ __u8 reserved2; ++ __u16 reserved3; ++ __u32 reserved[1]; + }; + + static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep, +@@ -1664,6 +1667,11 @@ static inline int ethtool_validate_duple + #define ETH_MODULE_SFF_8436 0x4 + #define ETH_MODULE_SFF_8436_LEN 256 + ++/* TIESSE Power, up or down. */ ++#define PHY_POWER_IGNORE 0x0 ++#define PHY_POWER_DOWN 0x1 ++#define PHY_POWER_UP 0x2 ++ + /* Reset flags */ + /* The reset() operation must clear the flags for the components which + * were actually reset. On successful return, the flags indicate the +@@ -1808,7 +1816,8 @@ struct ethtool_link_settings { + __u8 eth_tp_mdix_ctrl; + __s8 link_mode_masks_nwords; + __u8 transceiver; +- __u8 reserved1[3]; ++ __u8 power; ++ __u8 reserved1[2]; + __u32 reserved[7]; + __u32 link_mode_masks[0]; + /* layout of link_mode_masks fields: +Index: net/core/ethtool.c +=================================================================== +--- net/core/ethtool.c ++++ linux-imx/net/core/ethtool.c +@@ -480,6 +480,8 @@ convert_legacy_settings_to_link_ksetting + = legacy_settings->eth_tp_mdix; + link_ksettings->base.eth_tp_mdix_ctrl + = legacy_settings->eth_tp_mdix_ctrl; ++ link_ksettings->base.power ++ = legacy_settings->power; + return retval; + } + +@@ -526,6 +528,8 @@ convert_link_ksettings_to_legacy_setting + = link_ksettings->base.eth_tp_mdix_ctrl; + legacy_settings->transceiver + = link_ksettings->base.transceiver; ++ legacy_settings->power ++ = link_ksettings->base.power; + return retval; + } + diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1007-imx8mm-phy-speed.patch b/buildroot/board/tiesse/tgr/kernel-patches/1007-imx8mm-phy-speed.patch new file mode 100644 index 0000000..57a22f0 --- /dev/null +++ b/buildroot/board/tiesse/tgr/kernel-patches/1007-imx8mm-phy-speed.patch @@ -0,0 +1,13 @@ +Index: drivers/net/phy/phy_device.c +=================================================================== +--- drivers/net/phy/phy_device.c ++++ linux-imx/drivers/net/phy/phy_device.c +@@ -1389,6 +1389,8 @@ int genphy_setup_forced(struct phy_devic + if (DUPLEX_FULL == phydev->duplex) + ctl |= BMCR_FULLDPLX; + ++ ctl |= BMCR_RESET; ++ + return phy_write(phydev, MII_BMCR, ctl); + } + EXPORT_SYMBOL(genphy_setup_forced); diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1008-imx8mm-clear-stats.patch b/buildroot/board/tiesse/tgr/kernel-patches/1008-imx8mm-clear-stats.patch new file mode 100644 index 0000000..e47a331 --- /dev/null +++ b/buildroot/board/tiesse/tgr/kernel-patches/1008-imx8mm-clear-stats.patch @@ -0,0 +1,235 @@ +Index: include/uapi/linux/ethtool.h +=================================================================== +--- include/uapi/linux/ethtool.h ++++ linux-imx/include/uapi/linux/ethtool.h +@@ -1377,6 +1377,7 @@ enum ethtool_fec_config_bits { + #define ETHTOOL_PHY_STUNABLE 0x0000004f /* Set PHY tunable configuration */ + #define ETHTOOL_GFECPARAM 0x00000050 /* Get FEC settings */ + #define ETHTOOL_SFECPARAM 0x00000051 /* Set FEC settings */ ++#define ETHTOOL_CSTATS 0x00000052 /* Clear statistics */ + + /* compatibility with older code */ + #define SPARC_ETH_GSET ETHTOOL_GSET +Index: net/core/ethtool.c +=================================================================== +--- net/core/ethtool.c ++++ linux-imx/net/core/ethtool.c +@@ -1938,6 +1938,15 @@ static int ethtool_get_stats(struct net_ + return ret; + } + ++static int ethtool_clear_stats(struct net_device *dev) ++{ ++ if (!dev->ethtool_ops->clear_ethtool_stats) ++ return -EOPNOTSUPP; ++ ++ dev->ethtool_ops->clear_ethtool_stats(dev); ++ return 0; ++} ++ + static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr) + { + struct ethtool_stats stats; +@@ -2577,6 +2586,7 @@ int dev_ethtool(struct net *net, struct + case ETHTOOL_GSSET_INFO: + case ETHTOOL_GSTRINGS: + case ETHTOOL_GSTATS: ++ case ETHTOOL_CSTATS: + case ETHTOOL_GPHYSTATS: + case ETHTOOL_GTSO: + case ETHTOOL_GPERMADDR: +@@ -2688,6 +2698,9 @@ int dev_ethtool(struct net *net, struct + case ETHTOOL_GSTATS: + rc = ethtool_get_stats(dev, useraddr); + break; ++ case ETHTOOL_CSTATS: ++ rc = ethtool_clear_stats(dev); ++ break; + case ETHTOOL_GPERMADDR: + rc = ethtool_get_perm_addr(dev, useraddr); + break; +Index: include/linux/ethtool.h +=================================================================== +--- include/linux/ethtool.h ++++ linux-imx/include/linux/ethtool.h +@@ -347,6 +347,7 @@ struct ethtool_ops { + int (*set_phys_id)(struct net_device *, enum ethtool_phys_id_state); + void (*get_ethtool_stats)(struct net_device *, + struct ethtool_stats *, u64 *); ++ void (*clear_ethtool_stats)(struct net_device *); + int (*begin)(struct net_device *); + void (*complete)(struct net_device *); + u32 (*get_priv_flags)(struct net_device *); +Index: net/dsa/slave.c +=================================================================== +--- net/dsa/slave.c ++++ linux-imx/net/dsa/slave.c +@@ -1019,6 +1019,30 @@ static int dsa_slave_set_rxnfc(struct ne + return ds->ops->set_rxnfc(ds, p->dp->index, nfc); + } + ++static void dsa_slave_clear_ethtool_stats(struct net_device *dev) ++{ ++ struct dsa_slave_priv *p = netdev_priv(dev); ++ struct dsa_switch *ds = p->dp->ds; ++ struct pcpu_sw_netstats *s; ++ unsigned int start; ++ int i; ++ ++ memset(&dev->stats, 0, sizeof(struct net_device_stats)); ++ for_each_possible_cpu(i) { ++ s = per_cpu_ptr(p->stats64, i); ++ do { ++ start = u64_stats_fetch_begin_irq(&s->syncp); ++ s->tx_packets = 0L; ++ s->tx_bytes = 0L; ++ s->rx_packets = 0L; ++ s->rx_bytes = 0L; ++ } while (u64_stats_fetch_retry_irq(&s->syncp, start)); ++ } ++ ++ if (ds->ops->clear_ethtool_stats) ++ ds->ops->clear_ethtool_stats(ds, p->dp->index); ++} ++ + static const struct ethtool_ops dsa_slave_ethtool_ops = { + .get_drvinfo = dsa_slave_get_drvinfo, + .get_regs_len = dsa_slave_get_regs_len, +@@ -1039,6 +1063,7 @@ static const struct ethtool_ops dsa_slav + .set_link_ksettings = dsa_slave_set_link_ksettings, + .get_rxnfc = dsa_slave_get_rxnfc, + .set_rxnfc = dsa_slave_set_rxnfc, ++ .clear_ethtool_stats = dsa_slave_clear_ethtool_stats, + }; + + static const struct net_device_ops dsa_slave_netdev_ops = { +Index: drivers/net/dsa/mv88e6xxx/chip.h +=================================================================== +--- drivers/net/dsa/mv88e6xxx/chip.h ++++ linux-imx/drivers/net/dsa/mv88e6xxx/chip.h +@@ -330,6 +330,7 @@ struct mv88e6xxx_ops { + void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data); + void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port, + uint64_t *data); ++ void (*stats_clear_stats)(struct mv88e6xxx_chip *chip, int port); + int (*set_cpu_port)(struct mv88e6xxx_chip *chip, int port); + int (*set_egress_port)(struct mv88e6xxx_chip *chip, int port); + const struct mv88e6xxx_irq_ops *watchdog_ops; +Index: include/net/dsa.h +=================================================================== +--- include/net/dsa.h ++++ linux-imx/include/net/dsa.h +@@ -320,6 +320,7 @@ struct dsa_switch_ops { + void (*get_ethtool_stats)(struct dsa_switch *ds, + int port, uint64_t *data); + int (*get_sset_count)(struct dsa_switch *ds); ++ void (*clear_ethtool_stats)(struct dsa_switch *ds, int port); + + /* + * ethtool Wake-on-LAN +Index: drivers/net/dsa/mv88e6xxx/chip.c +=================================================================== +--- drivers/net/dsa/mv88e6xxx/chip.c ++++ linux-imx/drivers/net/dsa/mv88e6xxx/chip.c +@@ -648,6 +648,46 @@ static void mv88e6xxx_get_ethtool_stats( + mutex_unlock(&chip->reg_lock); + } + ++static void mv88e6xxx_stats_clear_stats(struct mv88e6xxx_chip *chip, int port) ++{ ++ u16 histogram = 0; ++ int err; ++ ++ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STATS_OP, &histogram); ++ if (err) ++ return; ++ ++ histogram &= 0xC00; ++ /* Clear the statistics counters for given port */ ++ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP, ++ MV88E6XXX_G1_STATS_OP_BUSY | ++ MV88E6XXX_G1_STATS_OP_FLUSH_PORT | ++ (port +1) << 5 | ++ histogram); ++} ++ ++static void mv88e6250_stats_clear_stats(struct mv88e6xxx_chip *chip, int port) ++{ ++ mv88e6xxx_stats_clear_stats(chip, port); ++} ++ ++static void mv88e6xxx_clear_stats(struct mv88e6xxx_chip *chip, int port) ++{ ++ if (chip->info->ops->stats_clear_stats) ++ chip->info->ops->stats_clear_stats(chip, port); ++} ++ ++static void mv88e6xxx_clear_ethtool_stats(struct dsa_switch *ds, int port) ++{ ++ struct mv88e6xxx_chip *chip = ds->priv; ++ ++ mutex_lock(&chip->reg_lock); ++ ++ mv88e6xxx_clear_stats(chip, port); ++ ++ mutex_unlock(&chip->reg_lock); ++} ++ + static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip) + { + if (chip->info->ops->stats_set_histogram) +@@ -2801,6 +2841,7 @@ static const struct mv88e6xxx_ops mv88e6 + .stats_get_sset_count = mv88e6250_stats_get_sset_count, + .stats_get_strings = mv88e6250_stats_get_strings, + .stats_get_stats = mv88e6250_stats_get_stats, ++ .stats_clear_stats = mv88e6250_stats_clear_stats, + .set_cpu_port = mv88e6095_g1_set_cpu_port, + .set_egress_port = mv88e6095_g1_set_egress_port, + .watchdog_ops = &mv88e6250_watchdog_ops, +@@ -3837,6 +3878,7 @@ static const struct dsa_switch_ops mv88e + .adjust_link = mv88e6xxx_adjust_link, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, ++ .clear_ethtool_stats = mv88e6xxx_clear_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, + .port_enable = mv88e6xxx_port_enable, + .port_disable = mv88e6xxx_port_disable, +Index: net/8021q/vlan_dev.c +=================================================================== +--- net/8021q/vlan_dev.c ++++ linux-imx/net/8021q/vlan_dev.c +@@ -681,6 +681,26 @@ static int vlan_ethtool_get_ts_info(stru + return 0; + } + ++static void vlan_clear_ethtool_stats(struct net_device *dev) ++{ ++ struct vlan_pcpu_stats *p; ++ unsigned int start; ++ int i; ++ ++ memset(&dev->stats, 0, sizeof(struct net_device_stats)); ++ for_each_possible_cpu(i) { ++ p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i); ++ do { ++ start = u64_stats_fetch_begin_irq(&p->syncp); ++ p->rx_packets = 0L; ++ p->rx_bytes = 0L; ++ p->rx_multicast = 0L; ++ p->tx_packets = 0L; ++ p->tx_bytes = 0L; ++ } while (u64_stats_fetch_retry_irq(&p->syncp, start)); ++ } ++} ++ + static void vlan_dev_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) + { +@@ -771,6 +791,7 @@ static const struct ethtool_ops vlan_eth + .get_drvinfo = vlan_ethtool_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ts_info = vlan_ethtool_get_ts_info, ++ .clear_ethtool_stats = vlan_clear_ethtool_stats, + }; + + static const struct net_device_ops vlan_netdev_ops = { diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1009-imx8mm-mv88e6xxx-sysfs-interface.patch b/buildroot/board/tiesse/tgr/kernel-patches/1009-imx8mm-mv88e6xxx-sysfs-interface.patch new file mode 100644 index 0000000..e78d084 --- /dev/null +++ b/buildroot/board/tiesse/tgr/kernel-patches/1009-imx8mm-mv88e6xxx-sysfs-interface.patch @@ -0,0 +1,229 @@ +Index: drivers/net/dsa/mv88e6xxx/chip.c +=================================================================== +--- drivers/net/dsa/mv88e6xxx/chip.c ++++ linux-imx/drivers/net/dsa/mv88e6xxx/chip.c +@@ -3936,6 +3936,212 @@ static void mv88e6xxx_unregister_switch( + dsa_unregister_switch(chip->ds); + } + ++/* ++ * Marvell switch mv88e6xxx sysfs interface ++ */ ++static int mv88e6xxx_port; ++static u16 mv88e6xxx_reg; ++static ssize_t sysfs_reg_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return scnprintf(buf, PAGE_SIZE, "0x%x", mv88e6xxx_reg); ++} ++ ++static ssize_t sysfs_reg_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ if (kstrtou16(buf, 0, &mv88e6xxx_reg)) { ++ dev_err(dev, "Invalid reg\n"); ++ return -EINVAL; ++ } ++ return (ssize_t)count; ++} ++ ++static ssize_t sysfs_port_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return scnprintf(buf, PAGE_SIZE, "0x%x", mv88e6xxx_port); ++} ++ ++static ssize_t sysfs_port_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ if (kstrtoint(buf, 0, &mv88e6xxx_port)) { ++ dev_err(dev, "Invalid port \n"); ++ return -EINVAL; ++ } ++ return (ssize_t)count; ++} ++ ++/* ++ * Read the specified register of a specified port ++ */ ++static ssize_t sysfs_port_reg_val_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dsa_switch *ds = (struct dsa_switch *)dev_get_drvdata(dev); ++ struct mv88e6xxx_chip *chip = ds->priv; ++ u16 reg; ++ int err; ++ ++ mutex_lock(&chip->reg_lock); ++ err = mv88e6xxx_port_read(chip, mv88e6xxx_port, mv88e6xxx_reg, ®); ++ mutex_unlock(&chip->reg_lock); ++ ++ if (err) { ++ dev_err(dev, "Port %d register %x failed\n", mv88e6xxx_port, mv88e6xxx_reg); ++ return -EIO; ++ } ++ ++ return scnprintf(buf, PAGE_SIZE, "0x%x\n", reg); ++} ++ ++/* ++ * Purge the switch ATU cache ++ */ ++static ssize_t sysfs_atu_flush(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct dsa_switch *ds = (struct dsa_switch *)dev_get_drvdata(dev); ++ struct mv88e6xxx_chip *chip = ds->priv; ++ int err; ++ ++ mutex_lock(&chip->reg_lock); ++ err = mv88e6xxx_g1_atu_flush(chip, 0, true); ++ mutex_unlock(&chip->reg_lock); ++ ++ if (err) { ++ dev_err(dev, "ATU flush failed\n"); ++ return -EIO; ++ } ++ ++ return (ssize_t)count; ++} ++ ++/* ++ * Returns the ATU cache content ++ */ ++static ssize_t sysfs_atu_dump(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dsa_switch *ds = (struct dsa_switch *)dev_get_drvdata(dev); ++ struct mv88e6xxx_chip *chip = ds->priv; ++ struct mv88e6xxx_atu_entry addr; ++ u16 fid = 0; ++ ssize_t n = 0; ++ int err; ++ ++ addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; ++ eth_broadcast_addr(addr.mac); ++ ++ n += scnprintf(buf + n, PAGE_SIZE - n, ++ "\nATU List:\n"); ++ ++ mutex_lock(&chip->reg_lock); ++ while (1) { ++ err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr); ++ if (err) ++ break; ++ ++ if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) ++ break; ++ n += scnprintf(buf + n, PAGE_SIZE - n, ++ "(%02x:%02x:%02x:%02x:%02x:%02x), PortVec %#x, State %#x\n", ++ addr.mac[0], addr.mac[1], addr.mac[2], ++ addr.mac[3], addr.mac[4], addr.mac[5], ++ addr.portvec, ++ addr.state); ++ } ++ mutex_unlock(&chip->reg_lock); ++ n += scnprintf(buf + n, PAGE_SIZE - n,"\n"); ++ if (err) ++ return 0; ++ else ++ return n; ++} ++ ++static ssize_t sysfs_atu_uc_static_entry_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct dsa_switch *ds = (struct dsa_switch *)dev_get_drvdata(dev); ++ struct mv88e6xxx_chip *chip = ds->priv; ++ const char *name = attr->attr.name; ++ struct mv88e6xxx_atu_entry entry; ++ u8 mac[ETH_ALEN]; ++ u8 state; ++ int values[ETH_ALEN], dpv; ++ int i, err; ++ ++ if(7 != sscanf(buf, "%x:%x:%x:%x:%x:%x %x%*c", ++ &values[0], &values[1], &values[2], ++ &values[3], &values[4], &values[5], ++ &dpv)) { ++ dev_err(dev, "Invalid input %s\n", buf); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i 255) { ++ dev_err(dev, "Invalid mac %s\n", buf); ++ return -EINVAL; ++ } ++ mac[i] = (u8)values[i]; ++ } ++ ++ if (!strcmp(name, "atu_uc_static_entry_add")) ++ state = MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC; ++ else ++ state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; ++ ++ memset(&entry, 0, sizeof(entry)); ++ ether_addr_copy(entry.mac, mac); ++ entry.portvec = dpv; ++ entry.state = state; ++ ++ mutex_lock(&chip->reg_lock); ++ err = mv88e6xxx_g1_atu_loadpurge(chip, 0, &entry); ++ mutex_unlock(&chip->reg_lock); ++ ++ if (err) { ++ dev_err(dev, "Load static entry failed\n"); ++ return -EIO; ++ } ++ ++ return (ssize_t)count; ++} ++ ++static DEVICE_ATTR(reg, S_IWUSR | S_IRUSR, ++ sysfs_reg_show, sysfs_reg_store); ++static DEVICE_ATTR(port, S_IWUSR | S_IRUSR, ++ sysfs_port_show, sysfs_port_store); ++static DEVICE_ATTR(port_reg_val, S_IWUSR | S_IRUSR, ++ sysfs_port_reg_val_show, NULL); ++static DEVICE_ATTR(atu, S_IWUSR | S_IRUSR, ++ sysfs_atu_dump, sysfs_atu_flush); ++static DEVICE_ATTR(atu_uc_static_entry_add, S_IWUSR | S_IRUSR, ++ NULL, sysfs_atu_uc_static_entry_store); ++static DEVICE_ATTR(atu_uc_static_entry_del, S_IWUSR | S_IRUSR, ++ NULL, sysfs_atu_uc_static_entry_store); ++ ++static struct attribute *mv88e6xxx_sysfs_entries[] = { ++ &dev_attr_reg.attr, ++ &dev_attr_port.attr, ++ &dev_attr_port_reg_val.attr, ++ &dev_attr_atu.attr, ++ &dev_attr_atu_uc_static_entry_add.attr, ++ &dev_attr_atu_uc_static_entry_del.attr, ++ NULL ++}; ++ ++static struct attribute_group mv88e6xxx_attribute_group = { ++ .name = "config", ++ .attrs = mv88e6xxx_sysfs_entries, ++}; ++ + static int mv88e6xxx_probe(struct mdio_device *mdiodev) + { + struct device *dev = &mdiodev->dev; +@@ -4013,6 +4219,11 @@ static int mv88e6xxx_probe(struct mdio_d + if (err) + goto out_mdio; + ++ /* register sysfs files */ ++ err = sysfs_create_group(&dev->kobj, &mv88e6xxx_attribute_group); ++ if (err) ++ dev_err(chip->dev, "sysfs_create_group failed\n"); ++ + return 0; + + out_mdio: diff --git a/buildroot/board/tiesse/tgr/kernel-patches/1010-imx8mm-netdev-trigger.patch b/buildroot/board/tiesse/tgr/kernel-patches/1010-imx8mm-netdev-trigger.patch new file mode 100644 index 0000000..664d61f --- /dev/null +++ b/buildroot/board/tiesse/tgr/kernel-patches/1010-imx8mm-netdev-trigger.patch @@ -0,0 +1,527 @@ +Index: drivers/leds/trigger/Kconfig +=================================================================== +--- drivers/leds/trigger/Kconfig ++++ linux-imx/drivers/leds/trigger/Kconfig +@@ -126,4 +126,11 @@ config LEDS_TRIGGER_PANIC + a different trigger. + If unsure, say Y. + ++config LEDS_TRIGGER_NETDEV ++ tristate "LED Netdev Trigger" ++ depends on NET ++ help ++ This allows LEDs to be controlled by network device activity. ++ If unsure, say Y. ++ + endif # LEDS_TRIGGERS +Index: drivers/leds/trigger/Makefile +=================================================================== +--- drivers/leds/trigger/Makefile ++++ linux-imx/drivers/leds/trigger/Makefile +@@ -11,3 +11,4 @@ obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += + obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o + obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o + obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o ++obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o +Index: drivers/leds/trigger/ledtrig-netdev.c +=================================================================== +--- /dev/null ++++ linux-imx/drivers/leds/trigger/ledtrig-netdev.c +@@ -0,0 +1,497 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright 2017 Ben Whitten ++// Copyright 2007 Oliver Jowett ++// ++// LED Kernel Netdev Trigger ++// ++// Toggles the LED to reflect the link and traffic state of a named net device ++// ++// Derived from ledtrig-timer.c which is: ++// Copyright 2005-2006 Openedhand Ltd. ++// Author: Richard Purdie ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../leds.h" ++ ++/* ++ * Configurable sysfs attributes: ++ * ++ * device_name - network device name to monitor ++ * interval - duration of LED blink, in milliseconds ++ * link - LED's normal state reflects whether the link is up ++ * (has carrier) or not ++ * tx - LED blinks on transmitted data ++ * rx - LED blinks on receive data ++ * ++ */ ++ ++struct led_netdev_data { ++ spinlock_t lock; ++ ++ struct delayed_work work; ++ struct notifier_block notifier; ++ ++ struct led_classdev *led_cdev; ++ struct net_device *net_dev; ++ ++ char device_name[IFNAMSIZ]; ++ atomic_t interval; ++ unsigned int last_activity; ++ ++ unsigned long mode; ++#define NETDEV_LED_LINK 0 ++#define NETDEV_LED_TX 1 ++#define NETDEV_LED_RX 2 ++#define NETDEV_LED_MODE_LINKUP 3 ++}; ++ ++enum netdev_led_attr { ++ NETDEV_ATTR_LINK, ++ NETDEV_ATTR_TX, ++ NETDEV_ATTR_RX ++}; ++ ++static void set_baseline_state(struct led_netdev_data *trigger_data) ++{ ++ int current_brightness; ++ struct led_classdev *led_cdev = trigger_data->led_cdev; ++ ++ current_brightness = led_cdev->brightness; ++ if (current_brightness) ++ led_cdev->blink_brightness = current_brightness; ++ if (!led_cdev->blink_brightness) ++ led_cdev->blink_brightness = led_cdev->max_brightness; ++ ++ if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode)) ++ led_set_brightness(led_cdev, LED_OFF); ++ else { ++ if (test_bit(NETDEV_LED_LINK, &trigger_data->mode)) ++ led_set_brightness(led_cdev, ++ led_cdev->blink_brightness); ++ else ++ led_set_brightness(led_cdev, LED_OFF); ++ ++ /* If we are looking for RX/TX start periodically ++ * checking stats ++ */ ++ if (test_bit(NETDEV_LED_TX, &trigger_data->mode) || ++ test_bit(NETDEV_LED_RX, &trigger_data->mode)) ++ schedule_delayed_work(&trigger_data->work, 0); ++ } ++} ++ ++static ssize_t device_name_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct led_netdev_data *trigger_data = led_cdev->trigger_data; ++ ssize_t len; ++ ++ spin_lock_bh(&trigger_data->lock); ++ len = sprintf(buf, "%s\n", trigger_data->device_name); ++ spin_unlock_bh(&trigger_data->lock); ++ ++ return len; ++} ++ ++static ssize_t device_name_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t size) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct led_netdev_data *trigger_data = led_cdev->trigger_data; ++ ++ if (size >= IFNAMSIZ) ++ return -EINVAL; ++ ++ cancel_delayed_work_sync(&trigger_data->work); ++ ++ spin_lock_bh(&trigger_data->lock); ++ ++ if (trigger_data->net_dev) { ++ dev_put(trigger_data->net_dev); ++ trigger_data->net_dev = NULL; ++ } ++ ++ memcpy(trigger_data->device_name, buf, size); ++ trigger_data->device_name[size] = 0; ++ if (size > 0 && trigger_data->device_name[size - 1] == '\n') ++ trigger_data->device_name[size - 1] = 0; ++ ++ if (trigger_data->device_name[0] != 0) ++ trigger_data->net_dev = ++ dev_get_by_name(&init_net, trigger_data->device_name); ++ ++ clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); ++ if (trigger_data->net_dev != NULL) ++ if (netif_carrier_ok(trigger_data->net_dev)) ++ set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); ++ ++ trigger_data->last_activity = 0; ++ ++ set_baseline_state(trigger_data); ++ spin_unlock_bh(&trigger_data->lock); ++ ++ return size; ++} ++ ++static DEVICE_ATTR_RW(device_name); ++ ++static ssize_t netdev_led_attr_show(struct device *dev, char *buf, ++ enum netdev_led_attr attr) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct led_netdev_data *trigger_data = led_cdev->trigger_data; ++ int bit; ++ ++ switch (attr) { ++ case NETDEV_ATTR_LINK: ++ bit = NETDEV_LED_LINK; ++ break; ++ case NETDEV_ATTR_TX: ++ bit = NETDEV_LED_TX; ++ break; ++ case NETDEV_ATTR_RX: ++ bit = NETDEV_LED_RX; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode)); ++} ++ ++static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, ++ size_t size, enum netdev_led_attr attr) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct led_netdev_data *trigger_data = led_cdev->trigger_data; ++ unsigned long state; ++ int ret; ++ int bit; ++ ++ ret = kstrtoul(buf, 0, &state); ++ if (ret) ++ return ret; ++ ++ switch (attr) { ++ case NETDEV_ATTR_LINK: ++ bit = NETDEV_LED_LINK; ++ break; ++ case NETDEV_ATTR_TX: ++ bit = NETDEV_LED_TX; ++ break; ++ case NETDEV_ATTR_RX: ++ bit = NETDEV_LED_RX; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ cancel_delayed_work_sync(&trigger_data->work); ++ ++ if (state) ++ set_bit(bit, &trigger_data->mode); ++ else ++ clear_bit(bit, &trigger_data->mode); ++ ++ set_baseline_state(trigger_data); ++ ++ return size; ++} ++ ++static ssize_t link_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK); ++} ++ ++static ssize_t link_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK); ++} ++ ++static DEVICE_ATTR_RW(link); ++ ++static ssize_t tx_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX); ++} ++ ++static ssize_t tx_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX); ++} ++ ++static DEVICE_ATTR_RW(tx); ++ ++static ssize_t rx_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX); ++} ++ ++static ssize_t rx_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX); ++} ++ ++static DEVICE_ATTR_RW(rx); ++ ++static ssize_t interval_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct led_netdev_data *trigger_data = led_cdev->trigger_data; ++ ++ return sprintf(buf, "%u\n", ++ jiffies_to_msecs(atomic_read(&trigger_data->interval))); ++} ++ ++static ssize_t interval_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t size) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct led_netdev_data *trigger_data = led_cdev->trigger_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul(buf, 0, &value); ++ if (ret) ++ return ret; ++ ++ /* impose some basic bounds on the timer interval */ ++ if (value >= 5 && value <= 10000) { ++ cancel_delayed_work_sync(&trigger_data->work); ++ ++ atomic_set(&trigger_data->interval, msecs_to_jiffies(value)); ++ set_baseline_state(trigger_data); /* resets timer */ ++ } ++ ++ return size; ++} ++ ++static DEVICE_ATTR_RW(interval); ++ ++static int netdev_trig_notify(struct notifier_block *nb, ++ unsigned long evt, void *dv) ++{ ++ struct net_device *dev = ++ netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv); ++ struct led_netdev_data *trigger_data = container_of(nb, ++ struct ++ led_netdev_data, ++ notifier); ++ ++ if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE ++ && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER ++ && evt != NETDEV_CHANGENAME) ++ return NOTIFY_DONE; ++ ++ if (!(dev == trigger_data->net_dev || ++ (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) || ++ (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name)))) ++ return NOTIFY_DONE; ++ ++ cancel_delayed_work_sync(&trigger_data->work); ++ ++ spin_lock_bh(&trigger_data->lock); ++ ++ clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); ++ switch (evt) { ++ case NETDEV_CHANGENAME: ++ case NETDEV_REGISTER: ++ if (trigger_data->net_dev) ++ dev_put(trigger_data->net_dev); ++ dev_hold(dev); ++ trigger_data->net_dev = dev; ++ break; ++ case NETDEV_UNREGISTER: ++ dev_put(trigger_data->net_dev); ++ trigger_data->net_dev = NULL; ++ break; ++ case NETDEV_UP: ++ case NETDEV_CHANGE: ++ if (netif_carrier_ok(dev)) ++ set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); ++ break; ++ } ++ ++ set_baseline_state(trigger_data); ++ ++ spin_unlock_bh(&trigger_data->lock); ++ ++ return NOTIFY_DONE; ++} ++ ++/* here's the real work! */ ++static void netdev_trig_work(struct work_struct *work) ++{ ++ struct led_netdev_data *trigger_data = container_of(work, ++ struct ++ led_netdev_data, ++ work.work); ++ struct rtnl_link_stats64 *dev_stats; ++ unsigned int new_activity; ++ struct rtnl_link_stats64 temp; ++ unsigned long interval; ++ int invert; ++ ++ /* If we dont have a device, insure we are off */ ++ if (!trigger_data->net_dev) { ++ led_set_brightness(trigger_data->led_cdev, LED_OFF); ++ return; ++ } ++ ++ /* If we are not looking for RX/TX then return */ ++ if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) && ++ !test_bit(NETDEV_LED_RX, &trigger_data->mode)) ++ return; ++ ++ dev_stats = dev_get_stats(trigger_data->net_dev, &temp); ++ new_activity = ++ (test_bit(NETDEV_LED_TX, &trigger_data->mode) ? ++ dev_stats->tx_packets : 0) + ++ (test_bit(NETDEV_LED_RX, &trigger_data->mode) ? ++ dev_stats->rx_packets : 0); ++ ++ if (trigger_data->last_activity != new_activity) { ++ led_stop_software_blink(trigger_data->led_cdev); ++ ++ invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode); ++ interval = jiffies_to_msecs( ++ atomic_read(&trigger_data->interval)); ++ /* base state is ON (link present) */ ++ led_blink_set_oneshot(trigger_data->led_cdev, ++ &interval, ++ &interval, ++ invert); ++ trigger_data->last_activity = new_activity; ++ } ++ ++ schedule_delayed_work(&trigger_data->work, ++ (atomic_read(&trigger_data->interval)*2)); ++} ++ ++static void netdev_trig_activate(struct led_classdev *led_cdev) ++{ ++ struct led_netdev_data *trigger_data; ++ int rc; ++ ++ trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL); ++ if (!trigger_data) ++ return; ++ ++ spin_lock_init(&trigger_data->lock); ++ ++ trigger_data->notifier.notifier_call = netdev_trig_notify; ++ trigger_data->notifier.priority = 10; ++ ++ INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work); ++ ++ trigger_data->led_cdev = led_cdev; ++ trigger_data->net_dev = NULL; ++ trigger_data->device_name[0] = 0; ++ ++ trigger_data->mode = 0; ++ atomic_set(&trigger_data->interval, msecs_to_jiffies(50)); ++ trigger_data->last_activity = 0; ++ ++ led_cdev->trigger_data = trigger_data; ++ ++ rc = device_create_file(led_cdev->dev, &dev_attr_device_name); ++ if (rc) ++ goto err_out; ++ rc = device_create_file(led_cdev->dev, &dev_attr_link); ++ if (rc) ++ goto err_out_device_name; ++ rc = device_create_file(led_cdev->dev, &dev_attr_rx); ++ if (rc) ++ goto err_out_link; ++ rc = device_create_file(led_cdev->dev, &dev_attr_tx); ++ if (rc) ++ goto err_out_rx; ++ rc = device_create_file(led_cdev->dev, &dev_attr_interval); ++ if (rc) ++ goto err_out_tx; ++ rc = register_netdevice_notifier(&trigger_data->notifier); ++ if (rc) ++ goto err_out_interval; ++ return; ++ ++err_out_interval: ++ device_remove_file(led_cdev->dev, &dev_attr_interval); ++err_out_tx: ++ device_remove_file(led_cdev->dev, &dev_attr_tx); ++err_out_rx: ++ device_remove_file(led_cdev->dev, &dev_attr_rx); ++err_out_link: ++ device_remove_file(led_cdev->dev, &dev_attr_link); ++err_out_device_name: ++ device_remove_file(led_cdev->dev, &dev_attr_device_name); ++err_out: ++ led_cdev->trigger_data = NULL; ++ kfree(trigger_data); ++} ++ ++static void netdev_trig_deactivate(struct led_classdev *led_cdev) ++{ ++ struct led_netdev_data *trigger_data = led_cdev->trigger_data; ++ ++ if (trigger_data) { ++ unregister_netdevice_notifier(&trigger_data->notifier); ++ ++ device_remove_file(led_cdev->dev, &dev_attr_device_name); ++ device_remove_file(led_cdev->dev, &dev_attr_link); ++ device_remove_file(led_cdev->dev, &dev_attr_rx); ++ device_remove_file(led_cdev->dev, &dev_attr_tx); ++ device_remove_file(led_cdev->dev, &dev_attr_interval); ++ ++ cancel_delayed_work_sync(&trigger_data->work); ++ ++ if (trigger_data->net_dev) ++ dev_put(trigger_data->net_dev); ++ ++ kfree(trigger_data); ++ } ++} ++ ++static struct led_trigger netdev_led_trigger = { ++ .name = "netdev", ++ .activate = netdev_trig_activate, ++ .deactivate = netdev_trig_deactivate, ++}; ++ ++static int __init netdev_trig_init(void) ++{ ++ return led_trigger_register(&netdev_led_trigger); ++} ++ ++static void __exit netdev_trig_exit(void) ++{ ++ led_trigger_unregister(&netdev_led_trigger); ++} ++ ++module_init(netdev_trig_init); ++module_exit(netdev_trig_exit); ++ ++MODULE_AUTHOR("Ben Whitten "); ++MODULE_AUTHOR("Oliver Jowett "); ++MODULE_DESCRIPTION("Netdev LED trigger"); ++MODULE_LICENSE("GPL v2"); diff --git a/buildroot/board/tiesse/tgr/linux.config b/buildroot/board/tiesse/tgr/linux.config index 2b01b87..758808d 100644 --- a/buildroot/board/tiesse/tgr/linux.config +++ b/buildroot/board/tiesse/tgr/linux.config @@ -158,6 +158,8 @@ CONFIG_IP6_NF_NAT=m CONFIG_IP6_NF_TARGET_MASQUERADE=m CONFIG_BRIDGE=m CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_SLAVE_MAC=y CONFIG_VLAN_8021Q=m CONFIG_VLAN_8021Q_GVRP=y CONFIG_VLAN_8021Q_MVRP=y @@ -269,6 +271,7 @@ CONFIG_VXLAN=m CONFIG_TUN=y CONFIG_VETH=m CONFIG_VIRTIO_NET=y +CONFIG_NET_DSA_MV88E6XXX=m CONFIG_AMD_XGBE=y CONFIG_NET_XGENE=y CONFIG_MACB=y @@ -290,7 +293,6 @@ CONFIG_AT803X_PHY=y CONFIG_MARVELL_PHY=m CONFIG_MESON_GXL_PHY=m CONFIG_MICREL_PHY=y -CONFIG_NXP_TJA110X_PHY=y CONFIG_REALTEK_PHY=m CONFIG_ROCKCHIP_PHY=y CONFIG_PPP=y @@ -314,7 +316,6 @@ CONFIG_USB_NET_QMI_WWAN=m CONFIG_BRCMFMAC=m CONFIG_BRCMFMAC_PCIE=y CONFIG_HOSTAP=y -CONFIG_RFKILL=y CONFIG_RTL_CARDS=m # CONFIG_WLAN_VENDOR_TI is not set CONFIG_XEN_NETDEV_BACKEND=m @@ -618,7 +619,6 @@ CONFIG_USB_SERIAL=y CONFIG_USB_SERIAL_OPTION=y CONFIG_USB_TEST=m CONFIG_USB_EHSET_TEST_FIXTURE=y -CONFIG_USB_HUB_USB251XB=y CONFIG_USB_HSIC_USB3503=y CONFIG_NOP_USB_XCEIV=y CONFIG_USB_GPIO_VBUS=y @@ -687,6 +687,7 @@ CONFIG_LEDS_SYSCON=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y CONFIG_LEDS_TRIGGER_CPU=y CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_NETDEV=y CONFIG_EDAC=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_MAX77686=y diff --git a/buildroot/board/tiesse/tgr/uboot-patches/0000-Add-imx8mm-tgr-support.patch b/buildroot/board/tiesse/tgr/uboot-patches/0000-Add-imx8mm-tgr-support.patch index c5c7bbb..30870db 100644 --- a/buildroot/board/tiesse/tgr/uboot-patches/0000-Add-imx8mm-tgr-support.patch +++ b/buildroot/board/tiesse/tgr/uboot-patches/0000-Add-imx8mm-tgr-support.patch @@ -69,7 +69,7 @@ Index: u-boot-imx/arch/arm/dts/fsl-imx8mm-tgr.dts + MX8MM_IOMUXC_SAI1_RXD5_GPIO4_IO7 0x10 + MX8MM_IOMUXC_SAI1_RXD6_GPIO4_IO8 0x10 + MX8MM_IOMUXC_GPIO1_IO08_GPIO1_IO8 0x10 -+ MX8MM_IOMUXC_SAI1_RXD7_GPIO4_IO9 0x80 ++ MX8MM_IOMUXC_SAI1_RXD7_GPIO4_IO9 0x1c0 + MX8MM_IOMUXC_SAI1_RXD1_GPIO4_IO3 0x10 + >; + }; @@ -4125,7 +4125,7 @@ Index: u-boot-imx/board/tiesse/tgr/spl.c +#endif + +static iomux_v3_cfg_t btn_pads[] = { -+ IMX8MM_PAD_SAI1_RXD7_GPIO4_IO9 | MUX_PAD_CTRL(PAD_CTL_HYS), ++ IMX8MM_PAD_SAI1_RXD7_GPIO4_IO9 | MUX_PAD_CTRL(PAD_CTL_HYS | PAD_CTL_PUE | PAD_CTL_PE ), +}; + +#define RST_BTN IMX_GPIO_NR(4, 9) @@ -4141,8 +4141,8 @@ Index: u-boot-imx/board/tiesse/tgr/spl.c + IMX8MM_PAD_SAI1_RXD6_GPIO4_IO8 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + -+#define LED_MODEM IMX_GPIO_NR(4, 7) -+#define LED_PWR IMX_GPIO_NR(4, 8) ++#define LED_MODEM IMX_GPIO_NR(4, 4) ++#define LED_PWR IMX_GPIO_NR(4, 5) +static void leds_init(void) +{ + imx_iomux_v3_setup_multiple_pads(led_pads, ARRAY_SIZE(led_pads)); @@ -4720,7 +4720,7 @@ Index: u-boot-imx/include/configs/tgr.h + +#define CONFIG_SYS_SDRAM_BASE 0x40000000 +#define PHYS_SDRAM 0x40000000 -+#define PHYS_SDRAM_SIZE SZ_1G /* 1GB DDR */ ++#define PHYS_SDRAM_SIZE SZ_2G /* 1GB DDR */ +#define CONFIG_NR_DRAM_BANKS 1 + +#define CONFIG_SYS_MEMTEST_START PHYS_SDRAM diff --git a/conf/etc/network/interfaces b/conf/etc/network/interfaces index 193c0a0..81ef206 100644 --- a/conf/etc/network/interfaces +++ b/conf/etc/network/interfaces @@ -2,8 +2,8 @@ auto lo iface lo inet loopback auto eth0 - pre-up sleep 10 iface eth0 inet dhcp + pre-up sleep 20 auto ap0 iface ap0 inet static diff --git a/conf/lib/modules/4.14.98/extra/qca6174.ko b/conf/lib/modules/4.14.98/extra/qca6174.ko index 485118a..5edc203 100644 Binary files a/conf/lib/modules/4.14.98/extra/qca6174.ko and b/conf/lib/modules/4.14.98/extra/qca6174.ko differ diff --git a/update/update.sh b/update/update.sh index d7b08b0..1e141bd 100755 --- a/update/update.sh +++ b/update/update.sh @@ -34,9 +34,9 @@ then /bin/echo "[+] Signature is valid!" /bin/echo "[+] Upgrading..." /bin/tar -xvf $file -C / - /bin/rm $file /tmp/sig + /bin/rm /tmp/sig /bin/echo "[+] Done" else /bin/echo "[-] Signature error, exiting..." - /bin/rm $file /tmp/sig + /bin/rm /tmp/sig fi