[PATCH 0/9] Add support for imx8mm-venice-gw7901 and DSA switch

The GW7901 is an IMX8M Mini based board with a KSZ9897 switch hanging off the IMX8M FEC.
In order to support a fixed-link connection to a U-Boot DSA switch some changes need to be made to the FEC driver.
Additionally, in order to support per-port unique MAC addrs for DSA we must introduce a mechanism to put the master device in promiscuous mode.
All patches are here including adding the dts for the board as well as a dsa driver and necessary patches to fec/dsa so that it is clear how this fits together.
Tim Harvey (9): board: gateworks: venice: add imx8mm-gw7901 support net: fec: use device sequence vs index when fetching fec net: fec: set phy_of_node properly for fixed-link phy net: add set_promisc function to enable/disable Promiscuous mode net: fec: add set_promisc function net: dsa: enable master promisc mode if available and needed net: add support for KSZ9477/KSZ9897/KSZ9567 GbE switch arm: dts: imx8mm-venice-gw7901.dts: fix dsa switch configuration configs: imx8mm_venice_defconfig: add support for gbe switch
arch/arm/dts/Makefile | 1 + arch/arm/dts/imx8mm-venice-gw7901-u-boot.dtsi | 118 ++ arch/arm/dts/imx8mm-venice-gw7901.dts | 1055 +++++++++++++++++ board/gateworks/venice/spl.c | 55 +- configs/imx8mm_venice_defconfig | 6 +- drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/fec_mxc.c | 21 +- drivers/net/fec_mxc.h | 1 + drivers/net/ksz9477.c | 547 +++++++++ include/net.h | 2 + net/dsa-uclass.c | 9 +- 12 files changed, 1818 insertions(+), 5 deletions(-) create mode 100644 arch/arm/dts/imx8mm-venice-gw7901-u-boot.dtsi create mode 100644 arch/arm/dts/imx8mm-venice-gw7901.dts create mode 100644 drivers/net/ksz9477.c

The Gateworks GW7901 is an ARM based single board computer (SBC) featuring: - i.MX8M Mini SoC - LPDDR4 DRAM - eMMC FLASH - SPI FRAM - Gateworks System Controller (GSC) - Atmel ATECC Crypto Authentication - USB 2.0 - Microchip GbE Switch - Multiple multi-protocol RS232/RS485/RS422 Serial ports - onboard 802.11ac WiFi / BT - microSD socket - miniPCIe socket with PCIe, USB 2.0 and dual SIM sockets - Wide range DC power input - 802.3at PoE
To add support for this board: - add dts from Linux (accepted for v5.14) - add SPL PMIC config
Signed-off-by: Tim Harvey tharvey@gateworks.com --- arch/arm/dts/Makefile | 1 + arch/arm/dts/imx8mm-venice-gw7901-u-boot.dtsi | 118 ++ arch/arm/dts/imx8mm-venice-gw7901.dts | 1020 +++++++++++++++++ board/gateworks/venice/spl.c | 55 +- configs/imx8mm_venice_defconfig | 2 +- 5 files changed, 1194 insertions(+), 2 deletions(-) create mode 100644 arch/arm/dts/imx8mm-venice-gw7901-u-boot.dtsi create mode 100644 arch/arm/dts/imx8mm-venice-gw7901.dts
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 9fb38682e6..b390d6bf1a 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -861,6 +861,7 @@ dtb-$(CONFIG_ARCH_IMX8M) += \ imx8mm-venice-gw71xx-0x.dtb \ imx8mm-venice-gw72xx-0x.dtb \ imx8mm-venice-gw73xx-0x.dtb \ + imx8mm-venice-gw7901.dtb \ imx8mm-verdin.dtb \ phycore-imx8mm.dtb \ imx8mn-ddr4-evk.dtb \ diff --git a/arch/arm/dts/imx8mm-venice-gw7901-u-boot.dtsi b/arch/arm/dts/imx8mm-venice-gw7901-u-boot.dtsi new file mode 100644 index 0000000000..3b24ca638a --- /dev/null +++ b/arch/arm/dts/imx8mm-venice-gw7901-u-boot.dtsi @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Gateworks Corporation + */ + +&{/soc@0} { + u-boot,dm-pre-reloc; + u-boot,dm-spl; +}; + +&clk { + u-boot,dm-spl; + u-boot,dm-pre-reloc; + /delete-property/ assigned-clocks; + /delete-property/ assigned-clock-parents; + /delete-property/ assigned-clock-rates; +}; + +&osc_24m { + u-boot,dm-spl; + u-boot,dm-pre-reloc; +}; + +&aips1 { + u-boot,dm-spl; + u-boot,dm-pre-reloc; +}; + +&aips2 { + u-boot,dm-spl; +}; + +&aips3 { + u-boot,dm-spl; +}; + +&iomuxc { + u-boot,dm-spl; +}; + +&gpio1 { + u-boot,dm-spl; +}; + +&gpio2 { + u-boot,dm-spl; +}; + +&gpio3 { + u-boot,dm-spl; +}; + +&gpio4 { + u-boot,dm-spl; +}; + +&gpio5 { + u-boot,dm-spl; +}; + +&uart2 { + u-boot,dm-spl; +}; + +&pinctrl_uart2 { + u-boot,dm-spl; +}; + +&usdhc2 { + u-boot,dm-spl; +}; + +&pinctrl_usdhc2 { + u-boot,dm-spl; +}; + +&usdhc3 { + u-boot,dm-spl; +}; + +&pinctrl_usdhc3 { + u-boot,dm-spl; +}; + +&i2c1 { + u-boot,dm-spl; +}; + +&pinctrl_i2c1 { + u-boot,dm-spl; +}; + +&i2c2 { + u-boot,dm-spl; +}; + +&pinctrl_i2c2 { + u-boot,dm-spl; +}; + +&fec1 { + phy-reset-gpios = <&gpio4 19 GPIO_ACTIVE_LOW>; + phy-reset-duration = <1>; + phy-reset-post-delay = <1>; +}; + +&{/soc@0/bus@30800000/i2c@30a30000/pmic@4b} { + u-boot,dm-spl; +}; + +&{/soc@0/bus@30800000/i2c@30a30000/pmic@4b/regulators} { + u-boot,dm-spl; +}; + +&pinctrl_pmic { + u-boot,dm-spl; +}; + diff --git a/arch/arm/dts/imx8mm-venice-gw7901.dts b/arch/arm/dts/imx8mm-venice-gw7901.dts new file mode 100644 index 0000000000..0216facb2a --- /dev/null +++ b/arch/arm/dts/imx8mm-venice-gw7901.dts @@ -0,0 +1,1020 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2020 Gateworks Corporation + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/input/linux-event-codes.h> +#include <dt-bindings/leds/common.h> + +#include "imx8mm.dtsi" + +/ { + model = "Gateworks Venice GW7901 i.MX8MM board"; + compatible = "gw,imx8mm-gw7901", "fsl,imx8mm"; + + aliases { + ethernet0 = &fec1; + ethernet1 = &lan1; + ethernet2 = &lan2; + ethernet3 = &lan3; + ethernet4 = &lan4; + usb0 = &usbotg1; + usb1 = &usbotg2; + }; + + chosen { + stdout-path = &uart2; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x0 0x40000000 0 0x80000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + + user-pb { + label = "user_pb"; + gpios = <&gpio 2 GPIO_ACTIVE_LOW>; + linux,code = <BTN_0>; + }; + + user-pb1x { + label = "user_pb1x"; + linux,code = <BTN_1>; + interrupt-parent = <&gsc>; + interrupts = <0>; + }; + + key-erased { + label = "key_erased"; + linux,code = <BTN_2>; + interrupt-parent = <&gsc>; + interrupts = <1>; + }; + + eeprom-wp { + label = "eeprom_wp"; + linux,code = <BTN_3>; + interrupt-parent = <&gsc>; + interrupts = <2>; + }; + + tamper { + label = "tamper"; + linux,code = <BTN_4>; + interrupt-parent = <&gsc>; + interrupts = <5>; + }; + + switch-hold { + label = "switch_hold"; + linux,code = <BTN_5>; + interrupt-parent = <&gsc>; + interrupts = <7>; + }; + }; + + led-controller { + compatible = "gpio-leds"; + + led-0 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_RED>; + label = "led01_red"; + gpios = <&leds_gpio 0 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-1 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_GREEN>; + label = "led01_grn"; + gpios = <&leds_gpio 1 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-2 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_RED>; + label = "led02_red"; + gpios = <&leds_gpio 2 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-3 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_GREEN>; + label = "led02_grn"; + gpios = <&leds_gpio 3 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-4 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_RED>; + label = "led03_red"; + gpios = <&leds_gpio 4 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-5 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_GREEN>; + label = "led03_grn"; + gpios = <&leds_gpio 5 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-6 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_RED>; + label = "led04_red"; + gpios = <&leds_gpio 8 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-7 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_GREEN>; + label = "led04_grn"; + gpios = <&leds_gpio 9 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-8 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_RED>; + label = "led05_red"; + gpios = <&leds_gpio 10 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-9 { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_GREEN>; + label = "led05_grn"; + gpios = <&leds_gpio 11 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-a { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_RED>; + label = "led06_red"; + gpios = <&leds_gpio 12 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-b { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_GREEN>; + label = "led06_grn"; + gpios = <&leds_gpio 13 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + regulator-ioexp { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_ioexp>; + compatible = "regulator-fixed"; + regulator-name = "ioexp"; + gpio = <&gpio4 21 GPIO_ACTIVE_HIGH>; + enable-active-high; + startup-delay-us = <100>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + regulator-isouart { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_isouart>; + compatible = "regulator-fixed"; + regulator-name = "iso_uart"; + gpio = <&gpio1 13 GPIO_ACTIVE_LOW>; + startup-delay-us = <100>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + reg_usb2_vbus: regulator-usb2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usb2>; + compatible = "regulator-fixed"; + regulator-name = "usb_usb2_vbus"; + gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_wifi: regulator-wifi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_wl>; + compatible = "regulator-fixed"; + regulator-name = "wifi"; + gpio = <&gpio3 25 GPIO_ACTIVE_HIGH>; + enable-active-high; + startup-delay-us = <100>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; +}; + +&ddrc { + operating-points-v2 = <&ddrc_opp_table>; + + ddrc_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-25M { + opp-hz = /bits/ 64 <25000000>; + }; + + opp-100M { + opp-hz = /bits/ 64 <100000000>; + }; + + opp-750M { + opp-hz = /bits/ 64 <750000000>; + }; + }; +}; + +&ecspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi1>; + cs-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <40000000>; + status = "okay"; + }; +}; + +&fec1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec1>; + phy-mode = "rgmii-id"; + local-mac-address = [00 00 00 00 00 00]; + status = "okay"; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + gsc: gsc@20 { + compatible = "gw,gsc"; + reg = <0x20>; + pinctrl-0 = <&pinctrl_gsc>; + interrupt-parent = <&gpio4>; + interrupts = <16 IRQ_TYPE_EDGE_FALLING>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + adc { + compatible = "gw,gsc-adc"; + #address-cells = <1>; + #size-cells = <0>; + + channel@6 { + gw,mode = <0>; + reg = <0x06>; + label = "temp"; + }; + + channel@8 { + gw,mode = <1>; + reg = <0x08>; + label = "vdd_bat"; + }; + + channel@82 { + gw,mode = <2>; + reg = <0x82>; + label = "vin_aux1"; + gw,voltage-divider-ohms = <22100 1000>; + }; + + channel@84 { + gw,mode = <2>; + reg = <0x84>; + label = "vin_aux2"; + gw,voltage-divider-ohms = <22100 1000>; + }; + + channel@86 { + gw,mode = <2>; + reg = <0x86>; + label = "vdd_vin"; + gw,voltage-divider-ohms = <22100 1000>; + }; + + channel@88 { + gw,mode = <2>; + reg = <0x88>; + label = "vdd_3p3"; + gw,voltage-divider-ohms = <10000 10000>; + }; + + channel@8c { + gw,mode = <2>; + reg = <0x8c>; + label = "vdd_2p5"; + gw,voltage-divider-ohms = <10000 10000>; + }; + + channel@8e { + gw,mode = <2>; + reg = <0x8e>; + label = "vdd_0p95"; + }; + + channel@90 { + gw,mode = <2>; + reg = <0x90>; + label = "vdd_soc"; + }; + + channel@92 { + gw,mode = <2>; + reg = <0x92>; + label = "vdd_arm"; + }; + + channel@98 { + gw,mode = <2>; + reg = <0x98>; + label = "vdd_1p8"; + }; + + channel@9a { + gw,mode = <2>; + reg = <0x9a>; + label = "vdd_1p2"; + }; + + channel@9c { + gw,mode = <2>; + reg = <0x9c>; + label = "vdd_dram"; + }; + + channel@a2 { + gw,mode = <2>; + reg = <0xa2>; + label = "vdd_gsc"; + gw,voltage-divider-ohms = <10000 10000>; + }; + }; + }; + + gpio: gpio@23 { + compatible = "nxp,pca9555"; + reg = <0x23>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&gsc>; + interrupts = <4>; + }; + + eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + pagesize = <16>; + }; + + eeprom@51 { + compatible = "atmel,24c02"; + reg = <0x51>; + pagesize = <16>; + }; + + eeprom@52 { + compatible = "atmel,24c02"; + reg = <0x52>; + pagesize = <16>; + }; + + eeprom@53 { + compatible = "atmel,24c02"; + reg = <0x53>; + pagesize = <16>; + }; + + rtc@68 { + compatible = "dallas,ds1672"; + reg = <0x68>; + }; +}; + +&i2c2 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + pmic@4b { + compatible = "rohm,bd71847"; + reg = <0x4b>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pmic>; + interrupt-parent = <&gpio3>; + interrupts = <20 IRQ_TYPE_LEVEL_LOW>; + rohm,reset-snvs-powered; + #clock-cells = <0>; + clocks = <&osc_32k 0>; + clock-output-names = "clk-32k-out"; + + regulators { + /* vdd_soc: 0.805-0.900V (typ=0.8V) */ + BUCK1 { + regulator-name = "buck1"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <1250>; + }; + + /* vdd_arm: 0.805-1.0V (typ=0.9V) */ + BUCK2 { + regulator-name = "buck2"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <1250>; + rohm,dvs-run-voltage = <1000000>; + rohm,dvs-idle-voltage = <900000>; + }; + + /* vdd_0p9: 0.805-1.0V (typ=0.9V) */ + BUCK3 { + regulator-name = "buck3"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1350000>; + regulator-boot-on; + regulator-always-on; + }; + + /* vdd_3p3 */ + BUCK4 { + regulator-name = "buck4"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + /* vdd_1p8 */ + BUCK5 { + regulator-name = "buck5"; + regulator-min-microvolt = <1605000>; + regulator-max-microvolt = <1995000>; + regulator-boot-on; + regulator-always-on; + }; + + /* vdd_dram */ + BUCK6 { + regulator-name = "buck6"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + regulator-always-on; + }; + + /* nvcc_snvs_1p8 */ + LDO1 { + regulator-name = "ldo1"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1900000>; + regulator-boot-on; + regulator-always-on; + }; + + /* vdd_snvs_0p8 */ + LDO2 { + regulator-name = "ldo2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <900000>; + regulator-boot-on; + regulator-always-on; + }; + + /* vdda_1p8 */ + LDO3 { + regulator-name = "ldo3"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + LDO4 { + regulator-name = "ldo4"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + LDO6 { + regulator-name = "ldo6"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; +}; + +&i2c3 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; + + leds_gpio: gpio@20 { + compatible = "nxp,pca9555"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + }; + + switch: switch@5f { + compatible = "microchip,ksz9897"; + reg = <0x5f>; + pinctrl-0 = <&pinctrl_ksz>; + interrupt-parent = <&gpio4>; + interrupts = <18 IRQ_TYPE_EDGE_FALLING>; + phy-mode = "rgmii-id"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + lan1: port@0 { + reg = <0>; + label = "lan1"; + local-mac-address = [00 00 00 00 00 00]; + }; + + lan2: port@1 { + reg = <1>; + label = "lan2"; + local-mac-address = [00 00 00 00 00 00]; + }; + + lan3: port@2 { + reg = <2>; + label = "lan3"; + local-mac-address = [00 00 00 00 00 00]; + }; + + lan4: port@3 { + reg = <3>; + label = "lan4"; + local-mac-address = [00 00 00 00 00 00]; + }; + + port@5 { + reg = <5>; + label = "cpu"; + ethernet = <&fec1>; + phy-mode = "rgmii-id"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + + crypto@60 { + compatible = "atmel,atecc508a"; + reg = <0x60>; + }; +}; + +&i2c4 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c4>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>, <&pinctrl_uart1_gpio>; + rts-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + cts-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; + dtr-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; + dsr-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; + dcd-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +/* console */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3>, <&pinctrl_uart3_gpio>; + cts-gpios = <&gpio4 10 GPIO_ACTIVE_LOW>; + rts-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>, <&pinctrl_uart4_gpio>; + cts-gpios = <&gpio5 11 GPIO_ACTIVE_LOW>; + rts-gpios = <&gpio5 12 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "host"; + disable-over-current; + status = "okay"; +}; + +&usbotg2 { + dr_mode = "host"; + vbus-supply = <®_usb2_vbus>; + status = "okay"; +}; + +/* SDIO WiFi */ +&usdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1>; + bus-width = <4>; + non-removable; + vmmc-supply = <®_wifi>; + status = "okay"; +}; + +/* microSD */ +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; + cd-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>; + bus-width = <4>; + vmmc-supply = <®_3p3v>; + status = "okay"; +}; + +/* eMMC */ +&usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + bus-width = <8>; + non-removable; + status = "okay"; +}; + +&wdog1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wdog>; + fsl,ext-reset-output; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + pinctrl_hog: hoggrp { + fsl,pins = < + MX8MM_IOMUXC_SPDIF_TX_GPIO5_IO3 0x40000041 /* DIG2_OUT */ + MX8MM_IOMUXC_SPDIF_RX_GPIO5_IO4 0x40000041 /* DIG2_IN */ + MX8MM_IOMUXC_GPIO1_IO06_GPIO1_IO6 0x40000041 /* DIG1_IN */ + MX8MM_IOMUXC_GPIO1_IO07_GPIO1_IO7 0x40000041 /* DIG1_OUT */ + MX8MM_IOMUXC_SAI3_RXD_GPIO4_IO30 0x40000041 /* SIM2DET# */ + MX8MM_IOMUXC_SAI3_RXC_GPIO4_IO29 0x40000041 /* SIM1DET# */ + MX8MM_IOMUXC_SPDIF_EXT_CLK_GPIO5_IO5 0x40000041 /* SIM2SEL */ + >; + }; + + pinctrl_fec1: fec1grp { + fsl,pins = < + MX8MM_IOMUXC_ENET_MDC_ENET1_MDC 0x3 + MX8MM_IOMUXC_ENET_MDIO_ENET1_MDIO 0x3 + MX8MM_IOMUXC_ENET_TD3_ENET1_RGMII_TD3 0x1f + MX8MM_IOMUXC_ENET_TD2_ENET1_RGMII_TD2 0x1f + MX8MM_IOMUXC_ENET_TD1_ENET1_RGMII_TD1 0x1f + MX8MM_IOMUXC_ENET_TD0_ENET1_RGMII_TD0 0x1f + MX8MM_IOMUXC_ENET_RD3_ENET1_RGMII_RD3 0x91 + MX8MM_IOMUXC_ENET_RD2_ENET1_RGMII_RD2 0x91 + MX8MM_IOMUXC_ENET_RD1_ENET1_RGMII_RD1 0x91 + MX8MM_IOMUXC_ENET_RD0_ENET1_RGMII_RD0 0x91 + MX8MM_IOMUXC_ENET_TXC_ENET1_RGMII_TXC 0x1f + MX8MM_IOMUXC_ENET_RXC_ENET1_RGMII_RXC 0x91 + MX8MM_IOMUXC_ENET_RX_CTL_ENET1_RGMII_RX_CTL 0x91 + MX8MM_IOMUXC_ENET_TX_CTL_ENET1_RGMII_TX_CTL 0x1f + MX8MM_IOMUXC_SAI1_TXD6_GPIO4_IO18 0x19 /* IRQ# */ + MX8MM_IOMUXC_SAI1_TXD7_GPIO4_IO19 0x19 /* RST# */ + >; + }; + + pinctrl_gsc: gscgrp { + fsl,pins = < + MX8MM_IOMUXC_SAI1_TXD4_GPIO4_IO16 0x159 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX8MM_IOMUXC_I2C1_SCL_I2C1_SCL 0x400001c3 + MX8MM_IOMUXC_I2C1_SDA_I2C1_SDA 0x400001c3 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX8MM_IOMUXC_I2C2_SCL_I2C2_SCL 0x400001c3 + MX8MM_IOMUXC_I2C2_SDA_I2C2_SDA 0x400001c3 + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL 0x400001c3 + MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA 0x400001c3 + >; + }; + + pinctrl_i2c4: i2c4grp { + fsl,pins = < + MX8MM_IOMUXC_I2C4_SCL_I2C4_SCL 0x400001c3 + MX8MM_IOMUXC_I2C4_SDA_I2C4_SDA 0x400001c3 + >; + }; + + pinctrl_ksz: kszgrp { + fsl,pins = < + MX8MM_IOMUXC_SAI1_TXD6_GPIO4_IO18 0x41 + MX8MM_IOMUXC_SAI1_TXD7_GPIO4_IO19 0x41 /* RST# */ + >; + }; + + pinctrl_pmic: pmicgrp { + fsl,pins = < + MX8MM_IOMUXC_SAI5_RXC_GPIO3_IO20 0x41 + >; + }; + + pinctrl_reg_isouart: regisouartgrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO13_GPIO1_IO13 0x40000041 + >; + }; + + pinctrl_reg_ioexp: regioexpgrp { + fsl,pins = < + MX8MM_IOMUXC_SAI2_RXFS_GPIO4_IO21 0x40000041 + >; + }; + + pinctrl_reg_wl: regwlgrp { + fsl,pins = < + MX8MM_IOMUXC_SAI5_MCLK_GPIO3_IO25 0x40000041 + >; + }; + + pinctrl_reg_usb2: regusb1grp { + fsl,pins = < + MX8MM_IOMUXC_SAI1_RXD0_GPIO4_IO2 0x41 + MX8MM_IOMUXC_SAI1_TXD5_GPIO4_IO17 0x140 + MX8MM_IOMUXC_GPIO1_IO15_USB2_OTG_OC 0x140 + >; + }; + + pinctrl_spi1: spi1grp { + fsl,pins = < + MX8MM_IOMUXC_ECSPI1_SCLK_ECSPI1_SCLK 0x82 + MX8MM_IOMUXC_ECSPI1_MOSI_ECSPI1_MOSI 0x82 + MX8MM_IOMUXC_ECSPI1_MISO_ECSPI1_MISO 0x82 + MX8MM_IOMUXC_ECSPI1_SS0_GPIO5_IO9 0x140 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX8MM_IOMUXC_UART1_RXD_UART1_DCE_RX 0x140 + MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX 0x140 + MX8MM_IOMUXC_GPIO1_IO01_GPIO1_IO1 0x140 + MX8MM_IOMUXC_GPIO1_IO10_GPIO1_IO10 0x140 + MX8MM_IOMUXC_GPIO1_IO11_GPIO1_IO11 0x140 + MX8MM_IOMUXC_GPIO1_IO12_GPIO1_IO12 0x140 + MX8MM_IOMUXC_GPIO1_IO14_GPIO1_IO14 0x140 + >; + }; + + pinctrl_uart1_gpio: uart1gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO00_GPIO1_IO0 0x40000041 /* RS422# */ + MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x40000041 /* RS485# */ + MX8MM_IOMUXC_GPIO1_IO05_GPIO1_IO5 0x40000041 /* RS232# */ + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX8MM_IOMUXC_UART2_RXD_UART2_DCE_RX 0x140 + MX8MM_IOMUXC_UART2_TXD_UART2_DCE_TX 0x140 + >; + }; + + pinctrl_uart3: uart3grp { + fsl,pins = < + MX8MM_IOMUXC_UART3_RXD_UART3_DCE_RX 0x140 + MX8MM_IOMUXC_UART3_TXD_UART3_DCE_TX 0x140 + MX8MM_IOMUXC_SAI1_RXD7_GPIO4_IO9 0x140 + MX8MM_IOMUXC_SAI1_TXFS_GPIO4_IO10 0x140 + >; + }; + + pinctrl_uart3_gpio: uart3gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_SAI1_RXD4_GPIO4_IO6 0x40000110 /* RS232# */ + MX8MM_IOMUXC_SAI1_RXD5_GPIO4_IO7 0x40000110 /* RS422# */ + MX8MM_IOMUXC_SAI1_RXD6_GPIO4_IO8 0x40000110 /* RS485# */ + >; + }; + + pinctrl_uart4: uart4grp { + fsl,pins = < + MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 + MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 + MX8MM_IOMUXC_ECSPI2_MOSI_GPIO5_IO11 0x140 + MX8MM_IOMUXC_ECSPI2_MISO_GPIO5_IO12 0x140 + >; + }; + + pinctrl_uart4_gpio: uart4gpiogrp { + fsl,pins = < + + MX8MM_IOMUXC_ECSPI2_SCLK_GPIO5_IO10 0x40000041 /* RS232# */ + MX8MM_IOMUXC_ECSPI2_SS0_GPIO5_IO13 0x40000041 /* RS422# */ + MX8MM_IOMUXC_SAI2_MCLK_GPIO4_IO27 0x40000041 /* RS485# */ + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x190 + MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d0 + MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d0 + MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d0 + MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d0 + MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d0 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x190 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d0 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d0 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x194 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d4 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x196 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d6 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d6 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 + >; + }; + + pinctrl_usdhc2_gpio: usdhc2-gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x1c4 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x190 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d0 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d0 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d0 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d0 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d0 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d0 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d0 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d0 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d0 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x190 + >; + }; + + pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp { + fsl,pins = < + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x194 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d4 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d4 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d4 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d4 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d4 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d4 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d4 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d4 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d4 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x194 + >; + }; + + pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { + fsl,pins = < + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x196 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d6 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d6 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d6 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d6 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d6 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d6 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d6 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d6 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d6 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x196 + >; + }; + + pinctrl_wdog: wdoggrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO02_WDOG1_WDOG_B 0xc6 + >; + }; +}; + +&cpu_alert0 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; +}; + +&cpu_crit0 { + temperature = <105000>; + hysteresis = <2000>; + type = "critical"; +}; diff --git a/board/gateworks/venice/spl.c b/board/gateworks/venice/spl.c index ea500d4f81..8c357757c1 100644 --- a/board/gateworks/venice/spl.c +++ b/board/gateworks/venice/spl.c @@ -26,6 +26,7 @@ #include <dm/uclass-internal.h> #include <dm/device-internal.h>
+#include <power/bd71837.h> #include <power/mp5416.h>
#include "gsc.h" @@ -88,8 +89,23 @@ int board_early_init_f(void) * Note that we can not use pmic dm drivers here as we have a generic * venice dt that does not have board-specific pmic's defined. * - * Instead we must use dm_i2c. + * Instead we must use dm_i2c so we a helpers to give us + * clrsetbit functions we would otherwise have if we could use PMIC dm + * drivers. */ +static int dm_i2c_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set) +{ + int ret; + u8 val; + + ret = dm_i2c_read(dev, reg, &val, 1); + if (ret) + return ret; + val = (val & ~clr) | set; + + return dm_i2c_write(dev, reg, &val, 1); +} + static int power_init_board(void) { const char *model = gsc_get_model(); @@ -117,6 +133,43 @@ static int power_init_board(void) BIT(7) | MP5416_VSET_SW3_SVAL(920000)); }
+ else if (!strncmp(model, "GW7901", 6)) { + ret = uclass_get_device_by_name(UCLASS_I2C, "i2c@30a30000", &bus); + if (ret) { + printf("PMIC : failed I2C2 probe: %d\n", ret); + return ret; + } + ret = dm_i2c_probe(bus, 0x4b, 0, &dev); + if (ret) { + printf("PMIC : failed probe: %d\n", ret); + return ret; + } + puts("PMIC : BD71847\n"); + + /* unlock the PMIC regs */ + dm_i2c_reg_write(dev, BD718XX_REGLOCK, 0x1); + + /* set switchers to forced PWM mode */ + dm_i2c_clrsetbits(dev, BD718XX_BUCK1_CTRL, 0, 0x8); + dm_i2c_clrsetbits(dev, BD718XX_BUCK2_CTRL, 0, 0x8); + dm_i2c_clrsetbits(dev, BD718XX_1ST_NODVS_BUCK_CTRL, 0, 0x8); + dm_i2c_clrsetbits(dev, BD718XX_2ND_NODVS_BUCK_CTRL, 0, 0x8); + dm_i2c_clrsetbits(dev, BD718XX_3RD_NODVS_BUCK_CTRL, 0, 0x8); + dm_i2c_clrsetbits(dev, BD718XX_4TH_NODVS_BUCK_CTRL, 0, 0x8); + + /* increase VDD_0P95 (VDD_GPU/VPU/DRAM) to 0.975v for 1.5Ghz DDR */ + dm_i2c_reg_write(dev, BD718XX_1ST_NODVS_BUCK_VOLT, 0x83); + + /* increase VDD_SOC to 0.85v before first DRAM access */ + dm_i2c_reg_write(dev, BD718XX_BUCK1_VOLT_RUN, 0x0f); + + /* increase VDD_ARM to 0.92v for 800 and 1600Mhz */ + dm_i2c_reg_write(dev, BD718XX_BUCK2_VOLT_RUN, 0x16); + + /* Lock the PMIC regs */ + dm_i2c_reg_write(dev, BD718XX_REGLOCK, 0x11); + } + return 0; }
diff --git a/configs/imx8mm_venice_defconfig b/configs/imx8mm_venice_defconfig index daffcc46d5..3961db00cb 100644 --- a/configs/imx8mm_venice_defconfig +++ b/configs/imx8mm_venice_defconfig @@ -58,7 +58,7 @@ CONFIG_CMD_EXT4_WRITE=y # CONFIG_SPL_EFI_PARTITION is not set CONFIG_OF_CONTROL=y CONFIG_SPL_OF_CONTROL=y -CONFIG_OF_LIST="imx8mm-venice-gw71xx-0x imx8mm-venice-gw72xx-0x imx8mm-venice-gw73xx-0x" +CONFIG_OF_LIST="imx8mm-venice-gw71xx-0x imx8mm-venice-gw72xx-0x imx8mm-venice-gw73xx-0x imx8mm-venice-gw7901" CONFIG_ENV_IS_IN_MMC=y CONFIG_SYS_REDUNDAND_ENVIRONMENT=y CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y

The Gateworks GW7901 is an ARM based single board computer (SBC) featuring:
- i.MX8M Mini SoC
- LPDDR4 DRAM
- eMMC FLASH
- SPI FRAM
- Gateworks System Controller (GSC)
- Atmel ATECC Crypto Authentication
- USB 2.0
- Microchip GbE Switch
- Multiple multi-protocol RS232/RS485/RS422 Serial ports
- onboard 802.11ac WiFi / BT
- microSD socket
- miniPCIe socket with PCIe, USB 2.0 and dual SIM sockets
- Wide range DC power input
- 802.3at PoE
To add support for this board:
- add dts from Linux (accepted for v5.14)
- add SPL PMIC config
Signed-off-by: Tim Harvey tharvey@gateworks.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

When using uclass_get_device* to get the FEC device we need to use device sequence instead of index into UCLASS_ETH. In systems where for example a I2C based DSA switch exists it will probe before the FEC master and its ports will be registered first and have the first indexes yet the FEC's sequence comes from the device-tree alias.
Take for example the imx8mm-venice-gw7901 board which has an i2c based DSA switch:
u-boot=> net list eth1 : lan1 00:0d:8d:aa:00:2f eth2 : lan2 00:0d:8d:aa:00:30 eth3 : lan3 00:0d:8d:aa:00:31 eth4 : lan4 00:0d:8d:aa:00:32 eth0 : ethernet@30be0000 00:0d:8d:aa:00:2e active
Thus in this case uclass_get_device(UCLASS_ETH, 0, &dev) returns lan1 which is wrong but uclass_get_device_seq(UCLASS_ETH, 0, &dev) returns ethernet@30be000 which is correct.
Signed-off-by: Tim Harvey tharvey@gateworks.com --- drivers/net/fec_mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 4fd5c01b4a..3abf914833 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -146,7 +146,7 @@ static int fec_get_clk_rate(void *udev, int idx) CONFIG_IS_ENABLED(CLK_CCF)) { dev = udev; if (!dev) { - ret = uclass_get_device(UCLASS_ETH, idx, &dev); + ret = uclass_get_device_by_seq(UCLASS_ETH, idx, &dev); if (ret < 0) { debug("Can't get FEC udev: %d\n", ret); return ret;

On Thu, Jul 1, 2021 at 2:50 AM Tim Harvey tharvey@gateworks.com wrote:
When using uclass_get_device* to get the FEC device we need to use device sequence instead of index into UCLASS_ETH. In systems where for example a I2C based DSA switch exists it will probe before the FEC master and its ports will be registered first and have the first indexes yet the FEC's sequence comes from the device-tree alias.
Take for example the imx8mm-venice-gw7901 board which has an i2c based DSA switch:
u-boot=> net list eth1 : lan1 00:0d:8d:aa:00:2f eth2 : lan2 00:0d:8d:aa:00:30 eth3 : lan3 00:0d:8d:aa:00:31 eth4 : lan4 00:0d:8d:aa:00:32 eth0 : ethernet@30be0000 00:0d:8d:aa:00:2e active
Thus in this case uclass_get_device(UCLASS_ETH, 0, &dev) returns lan1 which is wrong but uclass_get_device_seq(UCLASS_ETH, 0, &dev) returns ethernet@30be000 which is correct.
Signed-off-by: Tim Harvey tharvey@gateworks.com
drivers/net/fec_mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 4fd5c01b4a..3abf914833 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -146,7 +146,7 @@ static int fec_get_clk_rate(void *udev, int idx) CONFIG_IS_ENABLED(CLK_CCF)) { dev = udev; if (!dev) {
ret = uclass_get_device(UCLASS_ETH, idx, &dev);
ret = uclass_get_device_by_seq(UCLASS_ETH, idx, &dev); if (ret < 0) { debug("Can't get FEC udev: %d\n", ret); return ret;
-- 2.17.1
Reviewed-by: Ramon Fried rfried.dev@gmail.com

When using uclass_get_device* to get the FEC device we need to use device sequence instead of index into UCLASS_ETH. In systems where for example a I2C based DSA switch exists it will probe before the FEC master and its ports will be registered first and have the first indexes yet the FEC's sequence comes from the device-tree alias. Take for example the imx8mm-venice-gw7901 board which has an i2c based DSA switch: u-boot=> net list eth1 : lan1 00:0d:8d:aa:00:2f eth2 : lan2 00:0d:8d:aa:00:30 eth3 : lan3 00:0d:8d:aa:00:31 eth4 : lan4 00:0d:8d:aa:00:32 eth0 : ethernet@30be0000 00:0d:8d:aa:00:2e active Thus in this case uclass_get_device(UCLASS_ETH, 0, &dev) returns lan1 which is wrong but uclass_get_device_seq(UCLASS_ETH, 0, &dev) returns ethernet@30be000 which is correct. Signed-off-by: Tim Harvey tharvey@gateworks.com Reviewed-by: Ramon Fried rfried.dev@gmail.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

If the FEC is connected to a fixed-link (upstream switch port for example) the phy_of_node should be set to the fixed-link node so that speed and other properties can be found properly.
In addition fix a typo in the debug string.
Signed-off-by: Tim Harvey tharvey@gateworks.com --- drivers/net/fec_mxc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 3abf914833..77680491d0 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -1304,7 +1304,11 @@ static int device_get_phy_addr(struct fec_priv *priv, struct udevice *dev) ret = dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0, &phandle_args); if (ret) { - debug("Failed to find phy-handle (err = %d\n)", ret); + priv->phy_of_node = ofnode_find_subnode(dev_ofnode(dev), + "fixed-link"); + if (ofnode_valid(priv->phy_of_node)) + return 0; + debug("Failed to find phy-handle (err = %d)\n", ret); return ret; }

On Thu, Jul 1, 2021 at 2:50 AM Tim Harvey tharvey@gateworks.com wrote:
If the FEC is connected to a fixed-link (upstream switch port for example) the phy_of_node should be set to the fixed-link node so that speed and other properties can be found properly.
In addition fix a typo in the debug string.
Signed-off-by: Tim Harvey tharvey@gateworks.com
drivers/net/fec_mxc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 3abf914833..77680491d0 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -1304,7 +1304,11 @@ static int device_get_phy_addr(struct fec_priv *priv, struct udevice *dev) ret = dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0, &phandle_args); if (ret) {
debug("Failed to find phy-handle (err = %d\n)", ret);
priv->phy_of_node = ofnode_find_subnode(dev_ofnode(dev),
"fixed-link");
if (ofnode_valid(priv->phy_of_node))
return 0;
debug("Failed to find phy-handle (err = %d)\n", ret); return ret; }
-- 2.17.1
Reviewed-by: Ramon Fried rfried.dev@gmail.com

If the FEC is connected to a fixed-link (upstream switch port for example) the phy_of_node should be set to the fixed-link node so that speed and other properties can be found properly. In addition fix a typo in the debug string. Signed-off-by: Tim Harvey tharvey@gateworks.com Reviewed-by: Ramon Fried rfried.dev@gmail.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

Enabling promiscuous mode can be useful for DSA switches where each port has its own MAC address.
Signed-off-by: Tim Harvey tharvey@gateworks.com --- include/net.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/net.h b/include/net.h index b95d6a6f60..cec8c98618 100644 --- a/include/net.h +++ b/include/net.h @@ -158,6 +158,7 @@ enum eth_recv_flags { * ROM on the board. This is how the driver should expose it * to the network stack. This function should fill in the * eth_pdata::enetaddr field - optional + * set_promisc: Enable or Disable promiscuous mode */ struct eth_ops { int (*start)(struct udevice *dev); @@ -168,6 +169,7 @@ struct eth_ops { int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join); int (*write_hwaddr)(struct udevice *dev); int (*read_rom_hwaddr)(struct udevice *dev); + int (*set_promisc)(struct udevice *dev, bool enable); };
#define eth_get_ops(dev) ((struct eth_ops *)(dev)->driver->ops)

On Thu, Jul 1, 2021 at 2:50 AM Tim Harvey tharvey@gateworks.com wrote:
Enabling promiscuous mode can be useful for DSA switches where each port has its own MAC address.
Signed-off-by: Tim Harvey tharvey@gateworks.com
include/net.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/net.h b/include/net.h index b95d6a6f60..cec8c98618 100644 --- a/include/net.h +++ b/include/net.h @@ -158,6 +158,7 @@ enum eth_recv_flags {
ROM on the board. This is how the driver should expose it
to the network stack. This function should fill in the
eth_pdata::enetaddr field - optional
*/
- set_promisc: Enable or Disable promiscuous mode
struct eth_ops { int (*start)(struct udevice *dev); @@ -168,6 +169,7 @@ struct eth_ops { int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join); int (*write_hwaddr)(struct udevice *dev); int (*read_rom_hwaddr)(struct udevice *dev);
int (*set_promisc)(struct udevice *dev, bool enable);
};
#define eth_get_ops(dev) ((struct eth_ops *)(dev)->driver->ops)
2.17.1
Reviewed-by: Ramon Fried rfried.dev@gmail.com

Enabling promiscuous mode can be useful for DSA switches where each port has its own MAC address. Signed-off-by: Tim Harvey tharvey@gateworks.com Reviewed-by: Ramon Fried rfried.dev@gmail.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

Enabling promiscuous mode is necessary if FEC is the master of a DSA switch driver where each port has their own MAC address.
Signed-off-by: Tim Harvey tharvey@gateworks.com --- drivers/net/fec_mxc.c | 13 +++++++++++++ drivers/net/fec_mxc.h | 1 + 2 files changed, 14 insertions(+)
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 77680491d0..db2cdaf684 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -458,6 +458,9 @@ static void fec_reg_setup(struct fec_priv *fec) else if (fec->xcv_type == RMII) rcntrl |= FEC_RCNTRL_RMII;
+ if (fec->promisc) + rcntrl |= 0x8; + writel(rcntrl, &fec->eth->r_cntrl); }
@@ -1278,6 +1281,15 @@ static int fecmxc_read_rom_hwaddr(struct udevice *dev) return fec_get_hwaddr(priv->dev_id, pdata->enetaddr); }
+static int fecmxc_set_promisc(struct udevice *dev, bool enable) +{ + struct fec_priv *priv = dev_get_priv(dev); + + priv->promisc = enable; + + return 0; +} + static int fecmxc_free_pkt(struct udevice *dev, uchar *packet, int length) { if (packet) @@ -1294,6 +1306,7 @@ static const struct eth_ops fecmxc_ops = { .stop = fecmxc_halt, .write_hwaddr = fecmxc_set_hwaddr, .read_rom_hwaddr = fecmxc_read_rom_hwaddr, + .set_promisc = fecmxc_set_promisc, };
static int device_get_phy_addr(struct fec_priv *priv, struct udevice *dev) diff --git a/drivers/net/fec_mxc.h b/drivers/net/fec_mxc.h index 5ccde9193b..62b55ef395 100644 --- a/drivers/net/fec_mxc.h +++ b/drivers/net/fec_mxc.h @@ -272,6 +272,7 @@ struct fec_priv { struct clk clk_ref; struct clk clk_ptp; u32 clk_rate; + char promisc; };
/**

On Thu, Jul 1, 2021 at 2:50 AM Tim Harvey tharvey@gateworks.com wrote:
Enabling promiscuous mode is necessary if FEC is the master of a DSA switch driver where each port has their own MAC address.
Signed-off-by: Tim Harvey tharvey@gateworks.com
drivers/net/fec_mxc.c | 13 +++++++++++++ drivers/net/fec_mxc.h | 1 + 2 files changed, 14 insertions(+)
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 77680491d0..db2cdaf684 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -458,6 +458,9 @@ static void fec_reg_setup(struct fec_priv *fec) else if (fec->xcv_type == RMII) rcntrl |= FEC_RCNTRL_RMII;
if (fec->promisc)
rcntrl |= 0x8;
writel(rcntrl, &fec->eth->r_cntrl);
}
@@ -1278,6 +1281,15 @@ static int fecmxc_read_rom_hwaddr(struct udevice *dev) return fec_get_hwaddr(priv->dev_id, pdata->enetaddr); }
+static int fecmxc_set_promisc(struct udevice *dev, bool enable) +{
struct fec_priv *priv = dev_get_priv(dev);
priv->promisc = enable;
return 0;
+}
static int fecmxc_free_pkt(struct udevice *dev, uchar *packet, int length) { if (packet) @@ -1294,6 +1306,7 @@ static const struct eth_ops fecmxc_ops = { .stop = fecmxc_halt, .write_hwaddr = fecmxc_set_hwaddr, .read_rom_hwaddr = fecmxc_read_rom_hwaddr,
.set_promisc = fecmxc_set_promisc,
};
static int device_get_phy_addr(struct fec_priv *priv, struct udevice *dev) diff --git a/drivers/net/fec_mxc.h b/drivers/net/fec_mxc.h index 5ccde9193b..62b55ef395 100644 --- a/drivers/net/fec_mxc.h +++ b/drivers/net/fec_mxc.h @@ -272,6 +272,7 @@ struct fec_priv { struct clk clk_ref; struct clk clk_ptp; u32 clk_rate;
char promisc;
};
/**
2.17.1
Reviewed-by: Ramon Fried rfried.dev@gmail.com

Enabling promiscuous mode is necessary if FEC is the master of a DSA switch driver where each port has their own MAC address. Signed-off-by: Tim Harvey tharvey@gateworks.com Reviewed-by: Ramon Fried rfried.dev@gmail.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

If ports have their own unique MAC addrs and master has a set_promisc function, call it so that packets will be received for ports.
Signed-off-by: Tim Harvey tharvey@gateworks.com --- net/dsa-uclass.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/net/dsa-uclass.c b/net/dsa-uclass.c index 7ea1cb6949..694664d81b 100644 --- a/net/dsa-uclass.c +++ b/net/dsa-uclass.c @@ -277,8 +277,15 @@ static int dsa_port_probe(struct udevice *pdev) * has a unique MAC address specified in the environment. */ eth_env_get_enetaddr_by_index("eth", dev_seq(pdev), env_enetaddr); - if (!is_zero_ethaddr(env_enetaddr)) + if (!is_zero_ethaddr(env_enetaddr)) { + /* individual port mac addrs require master to be promisc */ + struct eth_ops *eth_ops = eth_get_ops(master); + + if (eth_ops->set_promisc) + eth_ops->set_promisc(master, 1); + return 0; + }
master_pdata = dev_get_plat(master); eth_pdata = dev_get_plat(pdev);

On Thu, Jul 1, 2021 at 2:50 AM Tim Harvey tharvey@gateworks.com wrote:
If ports have their own unique MAC addrs and master has a set_promisc function, call it so that packets will be received for ports.
Signed-off-by: Tim Harvey tharvey@gateworks.com
net/dsa-uclass.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/net/dsa-uclass.c b/net/dsa-uclass.c index 7ea1cb6949..694664d81b 100644 --- a/net/dsa-uclass.c +++ b/net/dsa-uclass.c @@ -277,8 +277,15 @@ static int dsa_port_probe(struct udevice *pdev) * has a unique MAC address specified in the environment. */ eth_env_get_enetaddr_by_index("eth", dev_seq(pdev), env_enetaddr);
if (!is_zero_ethaddr(env_enetaddr))
if (!is_zero_ethaddr(env_enetaddr)) {
/* individual port mac addrs require master to be promisc */
struct eth_ops *eth_ops = eth_get_ops(master);
if (eth_ops->set_promisc)
eth_ops->set_promisc(master, 1);
return 0;
} master_pdata = dev_get_plat(master); eth_pdata = dev_get_plat(pdev);
-- 2.17.1
Reviewed-by: Ramon Fried rfried.dev@gmail.com

If ports have their own unique MAC addrs and master has a set_promisc function, call it so that packets will be received for ports. Signed-off-by: Tim Harvey tharvey@gateworks.com Reviewed-by: Ramon Fried rfried.dev@gmail.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

The Microchip KSZ9477/KSZ9897/KSZ9567 7-Port Gigabit Ethernet Switches support SGMII/RGMII/MII/RMII with register access via SPI, I2C, or MDIO.
This driver currently supports I2C register access but SPI or MDIO register access can be easily added at a later time.
Tagging is not implemented and instead the active port is tracked to avoid needing a tag to store port information.
This was tested with the imx8mm-venice-gw7901 board which has a KSZ9897S switch with an IMX8MM FEC MAC master connected via RGMII_ID.
Signed-off-by: Tim Harvey tharvey@gateworks.com --- drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/ksz9477.c | 547 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 555 insertions(+) create mode 100644 drivers/net/ksz9477.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9fc28b149d..feeea960e2 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -402,6 +402,13 @@ config KS8851_MLL_BASEADDR endif #DM_ETH endif #KS8851_MLL
+config KSZ9477 + bool "Microchip KSZ9477 I2C controller driver" + depends on DM_DSA && DM_I2C + help + This driver implements a DSA switch driver for the KSZ9477 family + of GbE switches using the I2C interface. + config MVGBE bool "Marvell Orion5x/Kirkwood network interface support" depends on ARCH_KIRKWOOD || ARCH_ORION5X diff --git a/drivers/net/Makefile b/drivers/net/Makefile index d56baa65b2..03900ffa3a 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_FTMAC110) += ftmac110.o obj-$(CONFIG_FTMAC100) += ftmac100.o obj-$(CONFIG_GMAC_ROCKCHIP) += gmac_rockchip.o obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o +obj-$(CONFIG_KSZ9477) += ksz9477.o obj-$(CONFIG_LAN91C96) += lan91c96.o obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o obj-$(CONFIG_MACB) += macb.o diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c new file mode 100644 index 0000000000..ab32f42127 --- /dev/null +++ b/drivers/net/ksz9477.c @@ -0,0 +1,547 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 + * Tim Harvey, Gateworks Corporation + */ + +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <eth_phy.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <i2c.h> +#include <net/dsa.h> + +#include <asm-generic/gpio.h> + +/* Global registers */ + +/* Chip ID */ +#define REG_CHIP_ID0__1 0x0000 + +/* Operation control */ +#define REG_SW_OPERATION 0x0300 +#define SW_RESET BIT(1) +#define SW_START BIT(0) + +/* Port Specific Registers */ +#define PORT_CTRL_ADDR(port, addr) ((addr) | (((port) + 1) << 12)) + +/* Port Control */ +#define REG_PORT_XMII_CTRL_1 0x0301 +#define PORT_MII_NOT_1GBIT BIT(6) +#define PORT_MII_SEL_EDGE BIT(5) +#define PORT_RGMII_ID_IG_ENABLE BIT(4) +#define PORT_RGMII_ID_EG_ENABLE BIT(3) +#define PORT_MII_MAC_MODE BIT(2) +#define PORT_MII_SEL_M 0x3 +#define PORT_RGMII_SEL 0x0 +#define PORT_RMII_SEL 0x1 +#define PORT_GMII_SEL 0x2 +#define PORT_MII_SEL 0x3 + +/* Port MSTP State Register */ +#define REG_PORT_MSTP_STATE 0x0b04 +#define PORT_TX_ENABLE BIT(2) +#define PORT_RX_ENABLE BIT(1) +#define PORT_LEARN_DISABLE BIT(0) + +/* MMD */ +#define REG_PORT_PHY_MMD_SETUP 0x011A +#define PORT_MMD_OP_MODE_M 0x3 +#define PORT_MMD_OP_MODE_S 14 +#define PORT_MMD_OP_INDEX 0 +#define PORT_MMD_OP_DATA_NO_INCR 1 +#define PORT_MMD_OP_DATA_INCR_RW 2 +#define PORT_MMD_OP_DATA_INCR_W 3 +#define PORT_MMD_DEVICE_ID_M 0x1F +#define MMD_SETUP(mode, dev) (((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev)) +#define REG_PORT_PHY_MMD_INDEX_DATA 0x011C + +struct ksz_dsa_priv { + struct udevice *dev; + int active_port; +}; + +static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val) +{ + int ret = dm_i2c_read(dev, reg, val, 1); + + dev_dbg(dev, "%s 0x%04x<<0x%02x\n", __func__, reg, *val); + + return ret; +} + +static inline int ksz_pread8(struct udevice *dev, int port, int reg, u8 *val) +{ + return ksz_read8(dev, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_write8(struct udevice *dev, u32 reg, u8 val) +{ + dev_dbg(dev, "%s 0x%04x>>0x%02x\n", __func__, reg, val); + return dm_i2c_write(dev, reg, &val, 1); +} + +static inline int ksz_pwrite8(struct udevice *dev, int port, int reg, u8 val) +{ + return ksz_write8(dev, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_write16(struct udevice *dev, u32 reg, u16 val) +{ + u8 buf[2]; + + buf[1] = val & 0xff; + buf[0] = val >> 8; + dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val); + + return dm_i2c_write(dev, reg, buf, 2); +} + +static inline int ksz_pwrite16(struct udevice *dev, int port, int reg, u16 val) +{ + return ksz_write16(dev, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_read16(struct udevice *dev, u32 reg, u16 *val) +{ + u8 buf[2]; + int ret; + + ret = dm_i2c_read(dev, reg, buf, 2); + *val = (buf[0] << 8) | buf[1]; + dev_dbg(dev, "%s 0x%04x<<0x%04x\n", __func__, reg, *val); + + return ret; +} + +static inline int ksz_pread16(struct udevice *dev, int port, int reg, u16 *val) +{ + return ksz_read16(dev, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_read32(struct udevice *dev, u32 reg, u32 *val) +{ + return dm_i2c_read(dev, reg, (u8 *)val, 4); +} + +static inline int ksz_pread32(struct udevice *dev, int port, int reg, u32 *val) +{ + return ksz_read32(dev, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_write32(struct udevice *dev, u32 reg, u32 val) +{ + u8 buf[4]; + + buf[3] = val & 0xff; + buf[2] = (val >> 24) & 0xff; + buf[1] = (val >> 16) & 0xff; + buf[0] = (val >> 8) & 0xff; + dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val); + + return dm_i2c_write(dev, reg, buf, 4); +} + +static inline int ksz_pwrite32(struct udevice *dev, int port, int reg, u32 val) +{ + return ksz_write32(dev, PORT_CTRL_ADDR(port, reg), val); +} + +static __maybe_unused void ksz_port_mmd_read(struct udevice *dev, int port, + u8 addr, u16 reg, u16 *val) +{ + ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_INDEX, addr)); + ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, reg); + ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_DATA_NO_INCR, addr)); + ksz_pread16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, val); + dev_dbg(dev, "%s P%d 0x%02x:0x%04x<<0x%04x\n", __func__, port + 1, addr, reg, *val); +} + +static void ksz_port_mmd_write(struct udevice *dev, int port, u8 addr, u16 reg, u16 val) +{ + dev_dbg(dev, "%s P%d 0x%02x:0x%04x>>0x%04x\n", __func__, port + 1, addr, addr, val); + ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_INDEX, addr)); + ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, addr); + ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_DATA_NO_INCR, addr)); + ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, val); +} + +/* Apply PHY settings to address errata listed in KSZ9477, KSZ9897, KSZ9896, KSZ9567 + * Silicon Errata and Data Sheet Clarification documents + */ +static void ksz_phy_errata_setup(struct udevice *dev, int port) +{ + dev_dbg(dev, "%s P%d\n", __func__, port + 1); + + /* Register settings are needed to improve PHY receive performance */ + ksz_port_mmd_write(dev, port, 0x01, 0x6f, 0xdd0b); + ksz_port_mmd_write(dev, port, 0x01, 0x8f, 0x6032); + ksz_port_mmd_write(dev, port, 0x01, 0x9d, 0x248c); + ksz_port_mmd_write(dev, port, 0x01, 0x75, 0x0060); + ksz_port_mmd_write(dev, port, 0x01, 0xd3, 0x7777); + ksz_port_mmd_write(dev, port, 0x1c, 0x06, 0x3008); + ksz_port_mmd_write(dev, port, 0x1c, 0x08, 0x2001); + + /* Transmit waveform amplitude can be improved (1000BASE-T, 100BASE-TX, 10BASE-Te) */ + ksz_port_mmd_write(dev, port, 0x1c, 0x04, 0x00d0); + + /* Energy Efficient Ethernet (EEE) feature select must be manually disabled */ + ksz_port_mmd_write(dev, port, 0x07, 0x3c, 0x0000); + + /* Register settings are required to meet data sheet supply current specifications */ + ksz_port_mmd_write(dev, port, 0x1c, 0x13, 0x6eff); + ksz_port_mmd_write(dev, port, 0x1c, 0x14, 0xe6ff); + ksz_port_mmd_write(dev, port, 0x1c, 0x15, 0x6eff); + ksz_port_mmd_write(dev, port, 0x1c, 0x16, 0xe6ff); + ksz_port_mmd_write(dev, port, 0x1c, 0x17, 0x00ff); + ksz_port_mmd_write(dev, port, 0x1c, 0x18, 0x43ff); + ksz_port_mmd_write(dev, port, 0x1c, 0x19, 0xc3ff); + ksz_port_mmd_write(dev, port, 0x1c, 0x1a, 0x6fff); + ksz_port_mmd_write(dev, port, 0x1c, 0x1b, 0x07ff); + ksz_port_mmd_write(dev, port, 0x1c, 0x1c, 0x0fff); + ksz_port_mmd_write(dev, port, 0x1c, 0x1d, 0xe7ff); + ksz_port_mmd_write(dev, port, 0x1c, 0x1e, 0xefff); + ksz_port_mmd_write(dev, port, 0x1c, 0x20, 0xeeee); +} + +/* + * mii bus driver + */ +#define KSZ_MDIO_CHILD_DRV_NAME "ksz_mdio" + +struct ksz_mdio_priv { + struct ksz_dsa_priv *ksz; +}; + +static int dm_ksz_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct ksz_mdio_priv *priv = dev_get_priv(dev); + struct ksz_dsa_priv *ksz = priv->ksz; + u16 val = 0xffff; + + ksz_pread16(ksz->dev, addr, 0x100 + (reg << 1), &val); + dev_dbg(ksz->dev, "%s P%d reg=0x%04x:0x%04x<<0x%04x\n", __func__, + addr + 1, reg, 0x100 + (reg << 1), val); + + return val; +}; + +static int dm_ksz_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val) +{ + struct ksz_mdio_priv *priv = dev_get_priv(dev); + struct ksz_dsa_priv *ksz = priv->ksz; + + dev_dbg(ksz->dev, "%s P%d reg=0x%04x:%04x>>0x%04x\n", + __func__, addr + 1, reg, 0x100 + (reg << 1), val); + ksz_pwrite16(ksz->dev, addr, 0x100 + (reg << 1), val); + + return 0; +} + +static const struct mdio_ops ksz_mdio_ops = { + .read = dm_ksz_mdio_read, + .write = dm_ksz_mdio_write, +}; + +static int ksz_mdio_bind(struct udevice *dev) +{ + char name[16]; + static int num_devices; + + dev_dbg(dev, "%s\n", __func__); + sprintf(name, "ksz-mdio-%d", num_devices++); + device_set_name(dev, name); + + return 0; +} + +static int ksz_mdio_probe(struct udevice *dev) +{ + struct ksz_mdio_priv *priv = dev_get_priv(dev); + + dev_dbg(dev, "%s\n", __func__); + priv->ksz = dev_get_parent_priv(dev->parent); + + return 0; +} + +static const struct udevice_id ksz_mdio_ids[] = { + { .compatible = "microchip,ksz-mdio" }, + { } +}; + +U_BOOT_DRIVER(ksz_mdio) = { + .name = KSZ_MDIO_CHILD_DRV_NAME, + .id = UCLASS_MDIO, + .of_match = ksz_mdio_ids, + .bind = ksz_mdio_bind, + .probe = ksz_mdio_probe, + .ops = &ksz_mdio_ops, + .priv_auto = sizeof(struct ksz_mdio_priv), + .plat_auto = sizeof(struct mdio_perdev_priv), +}; + +static int ksz_port_setup(struct udevice *dev, int port, + phy_interface_t interface) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(dev); + u8 data8; + + dev_dbg(dev, "%s P%d %s\n", __func__, port + 1, + (port == pdata->cpu_port) ? "cpu" : ""); + + if (port != pdata->cpu_port) { + /* phy port: config errata and leds */ + ksz_phy_errata_setup(dev, port); + } else { + /* cpu port: configure MAC interface mode */ + ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); + dev_dbg(dev, "%s P%d cpu interface %s\n", __func__, port + 1, + phy_string_for_interface(interface)); + switch (interface) { + case PHY_INTERFACE_MODE_MII: + data8 &= ~PORT_MII_SEL_M; + data8 |= PORT_MII_SEL; + data8 |= PORT_MII_NOT_1GBIT; + break; + case PHY_INTERFACE_MODE_RMII: + data8 &= ~PORT_MII_SEL_M; + data8 |= PORT_RMII_SEL; + data8 |= PORT_MII_NOT_1GBIT; + break; + case PHY_INTERFACE_MODE_GMII: + data8 &= ~PORT_MII_SEL_M; + data8 |= PORT_GMII_SEL; + data8 &= ~PORT_MII_NOT_1GBIT; + break; + default: + data8 &= ~PORT_MII_SEL_M; + data8 |= PORT_RGMII_SEL; + data8 &= ~PORT_MII_NOT_1GBIT; + data8 &= ~PORT_RGMII_ID_IG_ENABLE; + data8 &= ~PORT_RGMII_ID_EG_ENABLE; + if (interface == PHY_INTERFACE_MODE_RGMII_ID || + interface == PHY_INTERFACE_MODE_RGMII_RXID) + data8 |= PORT_RGMII_ID_IG_ENABLE; + if (interface == PHY_INTERFACE_MODE_RGMII_ID || + interface == PHY_INTERFACE_MODE_RGMII_TXID) + data8 |= PORT_RGMII_ID_EG_ENABLE; + break; + } + ksz_write8(dev, PORT_CTRL_ADDR(port, REG_PORT_XMII_CTRL_1), data8); + } + + return 0; +} + +static int ksz_port_enable(struct udevice *dev, int port, struct phy_device *phy) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(dev); + struct ksz_dsa_priv *priv = dev_get_priv(dev); + int supported = PHY_GBIT_FEATURES; + u8 data8; + int ret; + + dev_dbg(dev, "%s P%d 0x%x %s\n", __func__, port + 1, phy->phy_id, + phy_string_for_interface(phy->interface)); + + /* setup this port */ + ret = ksz_port_setup(dev, port, phy->interface); + if (ret) { + dev_err(dev, "port setup failed: %d\n", ret); + return ret; + } + + /* enable port forwarding for this port */ + ksz_pread8(priv->dev, port, REG_PORT_MSTP_STATE, &data8); + data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); + data8 |= (PORT_TX_ENABLE | PORT_RX_ENABLE); + ksz_pwrite8(priv->dev, port, REG_PORT_MSTP_STATE, data8); + + /* if cpu master we are done */ + if (port == pdata->cpu_port) + return 0; + + /* configure phy */ + phy->supported &= supported; + phy->advertising &= supported; + ret = phy_config(phy); + if (ret) + return ret; + + ret = phy_startup(phy); + if (ret) + return ret; + + /* start switch */ + ksz_read8(priv->dev, REG_SW_OPERATION, &data8); + data8 |= SW_START; + ksz_write8(priv->dev, REG_SW_OPERATION, data8); + + /* keep track of current enabled non-cpu port */ + priv->active_port = port; + + return 0; +} + +static void ksz_port_disable(struct udevice *dev, int port, struct phy_device *phy) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(dev); + struct ksz_dsa_priv *priv = dev_get_priv(dev); + u8 data8; + + dev_dbg(dev, "%s P%d 0x%x\n", __func__, port + 1, phy->phy_id); + + /* can't disable CPU port without re-configuring/re-starting switch */ + if (port == pdata->cpu_port) + return; + + /* disable port */ + ksz_pread8(priv->dev, port, REG_PORT_MSTP_STATE, &data8); + data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); + data8 |= PORT_LEARN_DISABLE; + ksz_pwrite8(priv->dev, port, REG_PORT_MSTP_STATE, data8); + + /* + * we don't call phy_shutdown here to avoid waiting next time we use + * the port, but the downside is that remote side will think we're + * actively processing traffic although we are not. + */ +} + +static int ksz_xmit(struct udevice *dev, int port, void *packet, int length) +{ + dev_dbg(dev, "%s P%d %d\n", __func__, port + 1, length); + + return 0; +} + +static int ksz_recv(struct udevice *dev, int *port, void *packet, int length) +{ + struct ksz_dsa_priv *priv = dev_get_priv(dev); + + dev_dbg(dev, "%s P%d %d\n", __func__, priv->active_port + 1, length); + *port = priv->active_port; + + return 0; +}; + +static const struct dsa_ops ksz_dsa_ops = { + .port_enable = ksz_port_enable, + .port_disable = ksz_port_disable, + .xmit = ksz_xmit, + .rcv = ksz_recv, +}; + +static int ksz_probe_mdio(struct udevice *dev) +{ + ofnode node, mdios; + int ret; + + mdios = dev_read_subnode(dev, "mdios"); + if (ofnode_valid(mdios)) { + ofnode_for_each_subnode(node, mdios) { + const char *name = ofnode_get_name(node); + struct udevice *pdev; + + ret = device_bind_driver_to_node(dev, + KSZ_MDIO_CHILD_DRV_NAME, + name, node, &pdev); + if (ret) + dev_err(dev, "failed to probe %s: %d\n", name, ret); + } + } + + return 0; +} + +/* + * I2C driver + */ +static int ksz_i2c_probe(struct udevice *dev) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(dev); + struct ksz_dsa_priv *priv = dev_get_priv(dev); + struct udevice *master = dsa_get_master(dev); + int i, ret; + u8 data8; + u32 id; + + if (!master) + return -ENODEV; + + dev_dbg(dev, "%s %s master:%s\n", __func__, dev->name, master->name); + dev_set_parent_priv(dev, priv); + + ret = i2c_set_chip_offset_len(dev, 2); + if (ret) { + printf("i2c_set_chip_offset_len failed: %d\n", ret); + return ret; + } + + /* default config */ + priv->dev = dev; + + /* chip level reset */ + ksz_read8(priv->dev, REG_SW_OPERATION, &data8); + data8 |= SW_RESET; + ksz_write8(priv->dev, REG_SW_OPERATION, data8); + + /* read chip id */ + ret = ksz_read32(dev, REG_CHIP_ID0__1, &id); + if (ret) + return ret; + id = __swab32(id); + dev_dbg(dev, "%s id=0x%08x\n", __func__, id); + switch (id & 0xffffff00) { + case 0x00947700: + puts("KSZ9477S: "); + break; + case 0x00956700: + puts("KSZ9567R: "); + break; + case 0x00989700: + puts("KSZ9897S: "); + break; + default: + dev_err(dev, "invalid chip id: 0x%08x\n", id); + return -EINVAL; + } + + /* probe mdio bus */ + ret = ksz_probe_mdio(dev); + if (ret) + return ret; + + /* disable ports by default */ + for (i = 0; i < pdata->num_ports; i++) { + ksz_pread8(priv->dev, i, REG_PORT_MSTP_STATE, &data8); + data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); + ksz_pwrite8(priv->dev, i, REG_PORT_MSTP_STATE, data8); + } + + dsa_set_tagging(dev, 0, 0); + + return 0; +}; + +static const struct udevice_id ksz_i2c_ids[] = { + { .compatible = "microchip,ksz9897" }, + { .compatible = "microchip,ksz9477" }, + { .compatible = "microchip,ksz9567" }, + { } +}; + +U_BOOT_DRIVER(ksz) = { + .name = "ksz-switch", + .id = UCLASS_DSA, + .of_match = ksz_i2c_ids, + .probe = ksz_i2c_probe, + .ops = &ksz_dsa_ops, + .priv_auto = sizeof(struct ksz_dsa_priv), +}; +

On Thu, Jul 1, 2021 at 2:50 AM Tim Harvey tharvey@gateworks.com wrote:
The Microchip KSZ9477/KSZ9897/KSZ9567 7-Port Gigabit Ethernet Switches support SGMII/RGMII/MII/RMII with register access via SPI, I2C, or MDIO.
This driver currently supports I2C register access but SPI or MDIO register access can be easily added at a later time.
Tagging is not implemented and instead the active port is tracked to avoid needing a tag to store port information.
This was tested with the imx8mm-venice-gw7901 board which has a KSZ9897S switch with an IMX8MM FEC MAC master connected via RGMII_ID.
Signed-off-by: Tim Harvey tharvey@gateworks.com
drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/ksz9477.c | 547 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 555 insertions(+) create mode 100644 drivers/net/ksz9477.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9fc28b149d..feeea960e2 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -402,6 +402,13 @@ config KS8851_MLL_BASEADDR endif #DM_ETH endif #KS8851_MLL
+config KSZ9477
bool "Microchip KSZ9477 I2C controller driver"
depends on DM_DSA && DM_I2C
help
This driver implements a DSA switch driver for the KSZ9477 family
of GbE switches using the I2C interface.
config MVGBE bool "Marvell Orion5x/Kirkwood network interface support" depends on ARCH_KIRKWOOD || ARCH_ORION5X diff --git a/drivers/net/Makefile b/drivers/net/Makefile index d56baa65b2..03900ffa3a 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_FTMAC110) += ftmac110.o obj-$(CONFIG_FTMAC100) += ftmac100.o obj-$(CONFIG_GMAC_ROCKCHIP) += gmac_rockchip.o obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o +obj-$(CONFIG_KSZ9477) += ksz9477.o obj-$(CONFIG_LAN91C96) += lan91c96.o obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o obj-$(CONFIG_MACB) += macb.o diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c new file mode 100644 index 0000000000..ab32f42127 --- /dev/null +++ b/drivers/net/ksz9477.c @@ -0,0 +1,547 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2020
- Tim Harvey, Gateworks Corporation
- */
+#include <dm.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <eth_phy.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <i2c.h> +#include <net/dsa.h>
+#include <asm-generic/gpio.h>
+/* Global registers */
+/* Chip ID */ +#define REG_CHIP_ID0__1 0x0000
+/* Operation control */ +#define REG_SW_OPERATION 0x0300 +#define SW_RESET BIT(1) +#define SW_START BIT(0)
+/* Port Specific Registers */ +#define PORT_CTRL_ADDR(port, addr) ((addr) | (((port) + 1) << 12))
+/* Port Control */ +#define REG_PORT_XMII_CTRL_1 0x0301 +#define PORT_MII_NOT_1GBIT BIT(6) +#define PORT_MII_SEL_EDGE BIT(5) +#define PORT_RGMII_ID_IG_ENABLE BIT(4) +#define PORT_RGMII_ID_EG_ENABLE BIT(3) +#define PORT_MII_MAC_MODE BIT(2) +#define PORT_MII_SEL_M 0x3 +#define PORT_RGMII_SEL 0x0 +#define PORT_RMII_SEL 0x1 +#define PORT_GMII_SEL 0x2 +#define PORT_MII_SEL 0x3
+/* Port MSTP State Register */ +#define REG_PORT_MSTP_STATE 0x0b04 +#define PORT_TX_ENABLE BIT(2) +#define PORT_RX_ENABLE BIT(1) +#define PORT_LEARN_DISABLE BIT(0)
+/* MMD */ +#define REG_PORT_PHY_MMD_SETUP 0x011A +#define PORT_MMD_OP_MODE_M 0x3 +#define PORT_MMD_OP_MODE_S 14 +#define PORT_MMD_OP_INDEX 0 +#define PORT_MMD_OP_DATA_NO_INCR 1 +#define PORT_MMD_OP_DATA_INCR_RW 2 +#define PORT_MMD_OP_DATA_INCR_W 3 +#define PORT_MMD_DEVICE_ID_M 0x1F +#define MMD_SETUP(mode, dev) (((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev)) +#define REG_PORT_PHY_MMD_INDEX_DATA 0x011C
+struct ksz_dsa_priv {
struct udevice *dev;
int active_port;
+};
+static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val) +{
int ret = dm_i2c_read(dev, reg, val, 1);
dev_dbg(dev, "%s 0x%04x<<0x%02x\n", __func__, reg, *val);
return ret;
+}
+static inline int ksz_pread8(struct udevice *dev, int port, int reg, u8 *val) +{
return ksz_read8(dev, PORT_CTRL_ADDR(port, reg), val);
+}
+static inline int ksz_write8(struct udevice *dev, u32 reg, u8 val) +{
dev_dbg(dev, "%s 0x%04x>>0x%02x\n", __func__, reg, val);
return dm_i2c_write(dev, reg, &val, 1);
+}
+static inline int ksz_pwrite8(struct udevice *dev, int port, int reg, u8 val) +{
return ksz_write8(dev, PORT_CTRL_ADDR(port, reg), val);
+}
+static inline int ksz_write16(struct udevice *dev, u32 reg, u16 val) +{
u8 buf[2];
buf[1] = val & 0xff;
buf[0] = val >> 8;
dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val);
return dm_i2c_write(dev, reg, buf, 2);
+}
+static inline int ksz_pwrite16(struct udevice *dev, int port, int reg, u16 val) +{
return ksz_write16(dev, PORT_CTRL_ADDR(port, reg), val);
+}
+static inline int ksz_read16(struct udevice *dev, u32 reg, u16 *val) +{
u8 buf[2];
int ret;
ret = dm_i2c_read(dev, reg, buf, 2);
*val = (buf[0] << 8) | buf[1];
dev_dbg(dev, "%s 0x%04x<<0x%04x\n", __func__, reg, *val);
return ret;
+}
+static inline int ksz_pread16(struct udevice *dev, int port, int reg, u16 *val) +{
return ksz_read16(dev, PORT_CTRL_ADDR(port, reg), val);
+}
+static inline int ksz_read32(struct udevice *dev, u32 reg, u32 *val) +{
return dm_i2c_read(dev, reg, (u8 *)val, 4);
+}
+static inline int ksz_pread32(struct udevice *dev, int port, int reg, u32 *val) +{
return ksz_read32(dev, PORT_CTRL_ADDR(port, reg), val);
+}
+static inline int ksz_write32(struct udevice *dev, u32 reg, u32 val) +{
u8 buf[4];
buf[3] = val & 0xff;
buf[2] = (val >> 24) & 0xff;
buf[1] = (val >> 16) & 0xff;
buf[0] = (val >> 8) & 0xff;
dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val);
return dm_i2c_write(dev, reg, buf, 4);
+}
+static inline int ksz_pwrite32(struct udevice *dev, int port, int reg, u32 val) +{
return ksz_write32(dev, PORT_CTRL_ADDR(port, reg), val);
+}
+static __maybe_unused void ksz_port_mmd_read(struct udevice *dev, int port,
u8 addr, u16 reg, u16 *val)
+{
ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_INDEX, addr));
ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, reg);
ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_DATA_NO_INCR, addr));
ksz_pread16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, val);
dev_dbg(dev, "%s P%d 0x%02x:0x%04x<<0x%04x\n", __func__, port + 1, addr, reg, *val);
+}
+static void ksz_port_mmd_write(struct udevice *dev, int port, u8 addr, u16 reg, u16 val) +{
dev_dbg(dev, "%s P%d 0x%02x:0x%04x>>0x%04x\n", __func__, port + 1, addr, addr, val);
ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_INDEX, addr));
ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, addr);
ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_DATA_NO_INCR, addr));
ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, val);
+}
+/* Apply PHY settings to address errata listed in KSZ9477, KSZ9897, KSZ9896, KSZ9567
- Silicon Errata and Data Sheet Clarification documents
- */
+static void ksz_phy_errata_setup(struct udevice *dev, int port) +{
dev_dbg(dev, "%s P%d\n", __func__, port + 1);
/* Register settings are needed to improve PHY receive performance */
ksz_port_mmd_write(dev, port, 0x01, 0x6f, 0xdd0b);
ksz_port_mmd_write(dev, port, 0x01, 0x8f, 0x6032);
ksz_port_mmd_write(dev, port, 0x01, 0x9d, 0x248c);
ksz_port_mmd_write(dev, port, 0x01, 0x75, 0x0060);
ksz_port_mmd_write(dev, port, 0x01, 0xd3, 0x7777);
ksz_port_mmd_write(dev, port, 0x1c, 0x06, 0x3008);
ksz_port_mmd_write(dev, port, 0x1c, 0x08, 0x2001);
/* Transmit waveform amplitude can be improved (1000BASE-T, 100BASE-TX, 10BASE-Te) */
ksz_port_mmd_write(dev, port, 0x1c, 0x04, 0x00d0);
/* Energy Efficient Ethernet (EEE) feature select must be manually disabled */
ksz_port_mmd_write(dev, port, 0x07, 0x3c, 0x0000);
/* Register settings are required to meet data sheet supply current specifications */
ksz_port_mmd_write(dev, port, 0x1c, 0x13, 0x6eff);
ksz_port_mmd_write(dev, port, 0x1c, 0x14, 0xe6ff);
ksz_port_mmd_write(dev, port, 0x1c, 0x15, 0x6eff);
ksz_port_mmd_write(dev, port, 0x1c, 0x16, 0xe6ff);
ksz_port_mmd_write(dev, port, 0x1c, 0x17, 0x00ff);
ksz_port_mmd_write(dev, port, 0x1c, 0x18, 0x43ff);
ksz_port_mmd_write(dev, port, 0x1c, 0x19, 0xc3ff);
ksz_port_mmd_write(dev, port, 0x1c, 0x1a, 0x6fff);
ksz_port_mmd_write(dev, port, 0x1c, 0x1b, 0x07ff);
ksz_port_mmd_write(dev, port, 0x1c, 0x1c, 0x0fff);
ksz_port_mmd_write(dev, port, 0x1c, 0x1d, 0xe7ff);
ksz_port_mmd_write(dev, port, 0x1c, 0x1e, 0xefff);
ksz_port_mmd_write(dev, port, 0x1c, 0x20, 0xeeee);
+}
+/*
- mii bus driver
- */
+#define KSZ_MDIO_CHILD_DRV_NAME "ksz_mdio"
+struct ksz_mdio_priv {
struct ksz_dsa_priv *ksz;
+};
+static int dm_ksz_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{
struct ksz_mdio_priv *priv = dev_get_priv(dev);
struct ksz_dsa_priv *ksz = priv->ksz;
u16 val = 0xffff;
ksz_pread16(ksz->dev, addr, 0x100 + (reg << 1), &val);
dev_dbg(ksz->dev, "%s P%d reg=0x%04x:0x%04x<<0x%04x\n", __func__,
addr + 1, reg, 0x100 + (reg << 1), val);
return val;
+};
+static int dm_ksz_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val) +{
struct ksz_mdio_priv *priv = dev_get_priv(dev);
struct ksz_dsa_priv *ksz = priv->ksz;
dev_dbg(ksz->dev, "%s P%d reg=0x%04x:%04x>>0x%04x\n",
__func__, addr + 1, reg, 0x100 + (reg << 1), val);
ksz_pwrite16(ksz->dev, addr, 0x100 + (reg << 1), val);
return 0;
+}
+static const struct mdio_ops ksz_mdio_ops = {
.read = dm_ksz_mdio_read,
.write = dm_ksz_mdio_write,
+};
+static int ksz_mdio_bind(struct udevice *dev) +{
char name[16];
static int num_devices;
dev_dbg(dev, "%s\n", __func__);
sprintf(name, "ksz-mdio-%d", num_devices++);
device_set_name(dev, name);
return 0;
+}
+static int ksz_mdio_probe(struct udevice *dev) +{
struct ksz_mdio_priv *priv = dev_get_priv(dev);
dev_dbg(dev, "%s\n", __func__);
priv->ksz = dev_get_parent_priv(dev->parent);
return 0;
+}
+static const struct udevice_id ksz_mdio_ids[] = {
{ .compatible = "microchip,ksz-mdio" },
{ }
+};
+U_BOOT_DRIVER(ksz_mdio) = {
.name = KSZ_MDIO_CHILD_DRV_NAME,
.id = UCLASS_MDIO,
.of_match = ksz_mdio_ids,
.bind = ksz_mdio_bind,
.probe = ksz_mdio_probe,
.ops = &ksz_mdio_ops,
.priv_auto = sizeof(struct ksz_mdio_priv),
.plat_auto = sizeof(struct mdio_perdev_priv),
+};
+static int ksz_port_setup(struct udevice *dev, int port,
phy_interface_t interface)
+{
struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
u8 data8;
dev_dbg(dev, "%s P%d %s\n", __func__, port + 1,
(port == pdata->cpu_port) ? "cpu" : "");
if (port != pdata->cpu_port) {
/* phy port: config errata and leds */
ksz_phy_errata_setup(dev, port);
} else {
/* cpu port: configure MAC interface mode */
ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
dev_dbg(dev, "%s P%d cpu interface %s\n", __func__, port + 1,
phy_string_for_interface(interface));
switch (interface) {
case PHY_INTERFACE_MODE_MII:
data8 &= ~PORT_MII_SEL_M;
data8 |= PORT_MII_SEL;
data8 |= PORT_MII_NOT_1GBIT;
break;
case PHY_INTERFACE_MODE_RMII:
data8 &= ~PORT_MII_SEL_M;
data8 |= PORT_RMII_SEL;
data8 |= PORT_MII_NOT_1GBIT;
break;
case PHY_INTERFACE_MODE_GMII:
data8 &= ~PORT_MII_SEL_M;
data8 |= PORT_GMII_SEL;
data8 &= ~PORT_MII_NOT_1GBIT;
break;
default:
data8 &= ~PORT_MII_SEL_M;
data8 |= PORT_RGMII_SEL;
data8 &= ~PORT_MII_NOT_1GBIT;
data8 &= ~PORT_RGMII_ID_IG_ENABLE;
data8 &= ~PORT_RGMII_ID_EG_ENABLE;
if (interface == PHY_INTERFACE_MODE_RGMII_ID ||
interface == PHY_INTERFACE_MODE_RGMII_RXID)
data8 |= PORT_RGMII_ID_IG_ENABLE;
if (interface == PHY_INTERFACE_MODE_RGMII_ID ||
interface == PHY_INTERFACE_MODE_RGMII_TXID)
data8 |= PORT_RGMII_ID_EG_ENABLE;
break;
}
ksz_write8(dev, PORT_CTRL_ADDR(port, REG_PORT_XMII_CTRL_1), data8);
}
return 0;
+}
+static int ksz_port_enable(struct udevice *dev, int port, struct phy_device *phy) +{
struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
struct ksz_dsa_priv *priv = dev_get_priv(dev);
int supported = PHY_GBIT_FEATURES;
u8 data8;
int ret;
dev_dbg(dev, "%s P%d 0x%x %s\n", __func__, port + 1, phy->phy_id,
phy_string_for_interface(phy->interface));
/* setup this port */
ret = ksz_port_setup(dev, port, phy->interface);
if (ret) {
dev_err(dev, "port setup failed: %d\n", ret);
return ret;
}
/* enable port forwarding for this port */
ksz_pread8(priv->dev, port, REG_PORT_MSTP_STATE, &data8);
data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
data8 |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
ksz_pwrite8(priv->dev, port, REG_PORT_MSTP_STATE, data8);
/* if cpu master we are done */
if (port == pdata->cpu_port)
return 0;
/* configure phy */
phy->supported &= supported;
phy->advertising &= supported;
ret = phy_config(phy);
if (ret)
return ret;
ret = phy_startup(phy);
if (ret)
return ret;
/* start switch */
ksz_read8(priv->dev, REG_SW_OPERATION, &data8);
data8 |= SW_START;
ksz_write8(priv->dev, REG_SW_OPERATION, data8);
/* keep track of current enabled non-cpu port */
priv->active_port = port;
return 0;
+}
+static void ksz_port_disable(struct udevice *dev, int port, struct phy_device *phy) +{
struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
struct ksz_dsa_priv *priv = dev_get_priv(dev);
u8 data8;
dev_dbg(dev, "%s P%d 0x%x\n", __func__, port + 1, phy->phy_id);
/* can't disable CPU port without re-configuring/re-starting switch */
if (port == pdata->cpu_port)
return;
/* disable port */
ksz_pread8(priv->dev, port, REG_PORT_MSTP_STATE, &data8);
data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
data8 |= PORT_LEARN_DISABLE;
ksz_pwrite8(priv->dev, port, REG_PORT_MSTP_STATE, data8);
/*
* we don't call phy_shutdown here to avoid waiting next time we use
* the port, but the downside is that remote side will think we're
* actively processing traffic although we are not.
*/
+}
+static int ksz_xmit(struct udevice *dev, int port, void *packet, int length) +{
dev_dbg(dev, "%s P%d %d\n", __func__, port + 1, length);
return 0;
+}
+static int ksz_recv(struct udevice *dev, int *port, void *packet, int length) +{
struct ksz_dsa_priv *priv = dev_get_priv(dev);
dev_dbg(dev, "%s P%d %d\n", __func__, priv->active_port + 1, length);
*port = priv->active_port;
return 0;
+};
+static const struct dsa_ops ksz_dsa_ops = {
.port_enable = ksz_port_enable,
.port_disable = ksz_port_disable,
.xmit = ksz_xmit,
.rcv = ksz_recv,
+};
+static int ksz_probe_mdio(struct udevice *dev) +{
ofnode node, mdios;
int ret;
mdios = dev_read_subnode(dev, "mdios");
if (ofnode_valid(mdios)) {
ofnode_for_each_subnode(node, mdios) {
const char *name = ofnode_get_name(node);
struct udevice *pdev;
ret = device_bind_driver_to_node(dev,
KSZ_MDIO_CHILD_DRV_NAME,
name, node, &pdev);
if (ret)
dev_err(dev, "failed to probe %s: %d\n", name, ret);
}
}
return 0;
+}
+/*
- I2C driver
- */
+static int ksz_i2c_probe(struct udevice *dev) +{
struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
struct ksz_dsa_priv *priv = dev_get_priv(dev);
struct udevice *master = dsa_get_master(dev);
int i, ret;
u8 data8;
u32 id;
if (!master)
return -ENODEV;
dev_dbg(dev, "%s %s master:%s\n", __func__, dev->name, master->name);
dev_set_parent_priv(dev, priv);
ret = i2c_set_chip_offset_len(dev, 2);
if (ret) {
printf("i2c_set_chip_offset_len failed: %d\n", ret);
return ret;
}
/* default config */
priv->dev = dev;
/* chip level reset */
ksz_read8(priv->dev, REG_SW_OPERATION, &data8);
data8 |= SW_RESET;
ksz_write8(priv->dev, REG_SW_OPERATION, data8);
/* read chip id */
ret = ksz_read32(dev, REG_CHIP_ID0__1, &id);
if (ret)
return ret;
id = __swab32(id);
dev_dbg(dev, "%s id=0x%08x\n", __func__, id);
switch (id & 0xffffff00) {
case 0x00947700:
puts("KSZ9477S: ");
break;
case 0x00956700:
puts("KSZ9567R: ");
break;
case 0x00989700:
puts("KSZ9897S: ");
break;
default:
dev_err(dev, "invalid chip id: 0x%08x\n", id);
return -EINVAL;
}
/* probe mdio bus */
ret = ksz_probe_mdio(dev);
if (ret)
return ret;
/* disable ports by default */
for (i = 0; i < pdata->num_ports; i++) {
ksz_pread8(priv->dev, i, REG_PORT_MSTP_STATE, &data8);
data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
ksz_pwrite8(priv->dev, i, REG_PORT_MSTP_STATE, data8);
}
dsa_set_tagging(dev, 0, 0);
return 0;
+};
+static const struct udevice_id ksz_i2c_ids[] = {
{ .compatible = "microchip,ksz9897" },
{ .compatible = "microchip,ksz9477" },
{ .compatible = "microchip,ksz9567" },
{ }
+};
+U_BOOT_DRIVER(ksz) = {
.name = "ksz-switch",
.id = UCLASS_DSA,
.of_match = ksz_i2c_ids,
.probe = ksz_i2c_probe,
.ops = &ksz_dsa_ops,
.priv_auto = sizeof(struct ksz_dsa_priv),
+};
-- 2.17.1
Reviewed-by: Ramon Fried rfried.dev@gmail.com

The Microchip KSZ9477/KSZ9897/KSZ9567 7-Port Gigabit Ethernet Switches support SGMII/RGMII/MII/RMII with register access via SPI, I2C, or MDIO. This driver currently supports I2C register access but SPI or MDIO register access can be easily added at a later time. Tagging is not implemented and instead the active port is tracked to avoid needing a tag to store port information. This was tested with the imx8mm-venice-gw7901 board which has a KSZ9897S switch with an IMX8MM FEC MAC master connected via RGMII_ID. Signed-off-by: Tim Harvey tharvey@gateworks.com Reviewed-by: Ramon Fried rfried.dev@gmail.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

Fix the dsa switch config: - remove the unnecessary phy-mode from the switch itself - added the necessary fixed-link node to the non-cpu ports required for U-Boot DSA
Signed-off-by: Tim Harvey tharvey@gateworks.com --- arch/arm/dts/imx8mm-venice-gw7901.dts | 37 ++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/arch/arm/dts/imx8mm-venice-gw7901.dts b/arch/arm/dts/imx8mm-venice-gw7901.dts index 0216facb2a..124e1e4e70 100644 --- a/arch/arm/dts/imx8mm-venice-gw7901.dts +++ b/arch/arm/dts/imx8mm-venice-gw7901.dts @@ -577,7 +577,6 @@ pinctrl-0 = <&pinctrl_ksz>; interrupt-parent = <&gpio4>; interrupts = <18 IRQ_TYPE_EDGE_FALLING>; - phy-mode = "rgmii-id";
ports { #address-cells = <1>; @@ -587,24 +586,32 @@ reg = <0>; label = "lan1"; local-mac-address = [00 00 00 00 00 00]; + phy-handle = <&sw_phy0>; + phy-mode = "internal"; };
lan2: port@1 { reg = <1>; label = "lan2"; local-mac-address = [00 00 00 00 00 00]; + phy-handle = <&sw_phy1>; + phy-mode = "internal"; };
lan3: port@2 { reg = <2>; label = "lan3"; local-mac-address = [00 00 00 00 00 00]; + phy-handle = <&sw_phy2>; + phy-mode = "internal"; };
lan4: port@3 { reg = <3>; label = "lan4"; local-mac-address = [00 00 00 00 00 00]; + phy-handle = <&sw_phy3>; + phy-mode = "internal"; };
port@5 { @@ -619,6 +626,34 @@ }; }; }; + + mdios { + #address-cells = <1>; + #size-cells = <0>; + + mdio@0 { + reg = <0>; + compatible = "microchip,ksz-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + sw_phy0: ethernet-phy@0 { + reg = <0x0>; + }; + + sw_phy1: ethernet-phy@1 { + reg = <0x1>; + }; + + sw_phy2: ethernet-phy@2 { + reg = <0x2>; + }; + + sw_phy3: ethernet-phy@3 { + reg = <0x3>; + }; + }; + }; };
crypto@60 {

Fix the dsa switch config:
- remove the unnecessary phy-mode from the switch itself
- added the necessary fixed-link node to the non-cpu ports required for U-Boot DSA
Signed-off-by: Tim Harvey tharvey@gateworks.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic

The imx8mm-venice-gw7901 board has an I2C connected KSZ9897S GbE switch with an IMX8MM FEC MAC master connected via RGMII_ID.
Signed-off-by: Tim Harvey tharvey@gateworks.com --- configs/imx8mm_venice_defconfig | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/configs/imx8mm_venice_defconfig b/configs/imx8mm_venice_defconfig index 3961db00cb..49affccec4 100644 --- a/configs/imx8mm_venice_defconfig +++ b/configs/imx8mm_venice_defconfig @@ -87,9 +87,13 @@ CONFIG_SPL_MMC_HS400_SUPPORT=y CONFIG_FSL_USDHC=y CONFIG_PHYLIB=y CONFIG_PHY_TI_DP83867=y +CONFIG_PHY_FIXED=y CONFIG_DM_ETH=y +CONFIG_DM_MDIO=y +CONFIG_DM_DSA=y CONFIG_PHY_GIGE=y CONFIG_FEC_MXC=y +CONFIG_KSZ9477=y CONFIG_MII=y CONFIG_PINCTRL=y CONFIG_SPL_PINCTRL=y

The imx8mm-venice-gw7901 board has an I2C connected KSZ9897S GbE switch with an IMX8MM FEC MAC master connected via RGMII_ID. Signed-off-by: Tim Harvey tharvey@gateworks.com
Applied to u-boot-imx, master, thanks !
Best regards, Stefano Babic
participants (3)
-
Ramon Fried
-
sbabic@denx.de
-
Tim Harvey