diff --git a/arch/arm/dts/fsl-ls1028a-rdb.dts b/arch/arm/dts/fsl-ls1028a-rdb.dts index 85b4815b2e..3432fca352 100644 --- a/arch/arm/dts/fsl-ls1028a-rdb.dts +++ b/arch/arm/dts/fsl-ls1028a-rdb.dts @@ -15,6 +15,12 @@ compatible = "fsl,ls1028a-rdb", "fsl,ls1028a"; aliases { spi0 = &fspi; + eth0 = &enetc0; + eth1 = &enetc2; + eth2 = &mscc_felix_port0; + eth3 = &mscc_felix_port1; + eth4 = &mscc_felix_port2; + eth5 = &mscc_felix_port3; }; }; @@ -131,9 +137,67 @@ phy-handle = <&rdb_phy0>; }; +&enetc2 { + status = "okay"; +}; + +&mscc_felix { + status = "okay"; +}; + +&mscc_felix_port0 { + label = "swp0"; + phy-handle = <&sw_phy0>; + phy-mode = "qsgmii"; + status = "okay"; +}; + +&mscc_felix_port1 { + label = "swp1"; + phy-handle = <&sw_phy1>; + phy-mode = "qsgmii"; + status = "okay"; +}; + +&mscc_felix_port2 { + label = "swp2"; + phy-handle = <&sw_phy2>; + phy-mode = "qsgmii"; + status = "okay"; +}; + +&mscc_felix_port3 { + label = "swp3"; + phy-handle = <&sw_phy3>; + phy-mode = "qsgmii"; + status = "okay"; +}; + +&mscc_felix_port4 { + ethernet = <&enetc2>; + status = "okay"; +}; + &mdio0 { status = "okay"; rdb_phy0: phy@2 { reg = <2>; }; + + /* VSC8514 QSGMII PHY */ + sw_phy0: phy@10 { + reg = <0x10>; + }; + + sw_phy1: phy@11 { + reg = <0x11>; + }; + + sw_phy2: phy@12 { + reg = <0x12>; + }; + + sw_phy3: phy@13 { + reg = <0x13>; + }; }; diff --git a/arch/arm/dts/fsl-ls1028a.dtsi b/arch/arm/dts/fsl-ls1028a.dtsi index 5171bf28c7..c7c725a4fc 100644 --- a/arch/arm/dts/fsl-ls1028a.dtsi +++ b/arch/arm/dts/fsl-ls1028a.dtsi @@ -151,9 +151,63 @@ reg = <0x000300 0 0 0 0>; status = "disabled"; }; + + mscc_felix: pci@0,5 { + reg = <0x000500 0 0 0 0>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + mscc_felix_port0: port@0 { + reg = <0>; + status = "disabled"; + }; + + mscc_felix_port1: port@1 { + reg = <1>; + status = "disabled"; + }; + + mscc_felix_port2: port@2 { + reg = <2>; + status = "disabled"; + }; + + mscc_felix_port3: port@3 { + reg = <3>; + status = "disabled"; + }; + + mscc_felix_port4: port@4 { + reg = <4>; + phy-mode = "internal"; + status = "disabled"; + + fixed-link { + speed = <2500>; + full-duplex; + }; + }; + + mscc_felix_port5: port@5 { + reg = <5>; + phy-mode = "internal"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + + }; + }; + }; + enetc6: pci@0,6 { reg = <0x000600 0 0 0 0>; - status = "okay"; + status = "disabled"; phy-mode = "internal"; }; }; diff --git a/common/console.c b/common/console.c index 567273a0ce..561cdf36a7 100644 --- a/common/console.c +++ b/common/console.c @@ -233,9 +233,10 @@ static struct stdio_dev *tstcdev; struct stdio_dev **console_devices[MAX_FILES]; int cd_count[MAX_FILES]; -static void __maybe_unused console_devices_set(int file, struct stdio_dev *dev) +static void console_devices_set(int file, struct stdio_dev *dev) { console_devices[file][0] = dev; + cd_count[file] = 1; } /** @@ -251,15 +252,14 @@ static void __maybe_unused console_devices_set(int file, struct stdio_dev *dev) */ static bool console_needs_start_stop(int file, struct stdio_dev *sdev) { - int i, j; + int i; for (i = 0; i < ARRAY_SIZE(cd_count); i++) { if (i == file) continue; - for (j = 0; j < cd_count[i]; j++) - if (console_devices[i][j] == sdev) - return false; + if (iomux_match_device(console_devices[i], cd_count[i], sdev) >= 0) + return false; } return true; } @@ -293,8 +293,7 @@ static int console_tstc(int file) int prev; prev = disable_ctrlc(1); - for (i = 0; i < cd_count[file]; i++) { - dev = console_devices[file][i]; + for_each_console_dev(i, file, dev) { if (dev->tstc != NULL) { ret = dev->tstc(dev); if (ret > 0) { @@ -314,8 +313,7 @@ static void console_putc(int file, const char c) int i; struct stdio_dev *dev; - for (i = 0; i < cd_count[file]; i++) { - dev = console_devices[file][i]; + for_each_console_dev(i, file, dev) { if (dev->putc != NULL) dev->putc(dev, c); } @@ -334,11 +332,9 @@ static void console_puts_select(int file, bool serial_only, const char *s) int i; struct stdio_dev *dev; - for (i = 0; i < cd_count[file]; i++) { - bool is_serial; + for_each_console_dev(i, file, dev) { + bool is_serial = console_dev_is_serial(dev); - dev = console_devices[file][i]; - is_serial = console_dev_is_serial(dev); if (dev->puts && serial_only == is_serial) dev->puts(dev, s); } @@ -354,8 +350,7 @@ static void console_puts(int file, const char *s) int i; struct stdio_dev *dev; - for (i = 0; i < cd_count[file]; i++) { - dev = console_devices[file][i]; + for_each_console_dev(i, file, dev) { if (dev->puts != NULL) dev->puts(dev, s); } @@ -369,7 +364,7 @@ static inline void console_doenv(int file, struct stdio_dev *dev) #endif #else -static void __maybe_unused console_devices_set(int file, struct stdio_dev *dev) +static void console_devices_set(int file, struct stdio_dev *dev) { } @@ -417,6 +412,12 @@ static inline void console_doenv(int file, struct stdio_dev *dev) #endif #endif /* CONIFIG_IS_ENABLED(CONSOLE_MUX) */ +static void __maybe_unused console_setfile_and_devices(int file, struct stdio_dev *dev) +{ + console_setfile(file, dev); + console_devices_set(file, dev); +} + int console_start(int file, struct stdio_dev *sdev) { int error; @@ -855,17 +856,9 @@ int console_assign(int file, const char *devname) struct stdio_dev *dev; /* Check for valid file */ - switch (file) { - case stdin: - flag = DEV_FLAGS_INPUT; - break; - case stdout: - case stderr: - flag = DEV_FLAGS_OUTPUT; - break; - default: - return -1; - } + flag = stdio_file_to_flags(file); + if (flag < 0) + return flag; /* Check for valid device name */ @@ -1079,17 +1072,13 @@ int console_init_r(void) /* Initializes output console first */ if (outputdev != NULL) { - console_setfile(stdout, outputdev); - console_setfile(stderr, outputdev); - console_devices_set(stdout, outputdev); - console_devices_set(stderr, outputdev); + console_setfile_and_devices(stdout, outputdev); + console_setfile_and_devices(stderr, outputdev); } /* Initializes input console */ - if (inputdev != NULL) { - console_setfile(stdin, inputdev); - console_devices_set(stdin, inputdev); - } + if (inputdev != NULL) + console_setfile_and_devices(stdin, inputdev); if (!IS_ENABLED(CONFIG_SYS_CONSOLE_INFO_QUIET)) stdio_print_current_devices(); diff --git a/common/iomux.c b/common/iomux.c index 15bf533885..b9088aa3b5 100644 --- a/common/iomux.c +++ b/common/iomux.c @@ -15,18 +15,26 @@ void iomux_printdevs(const int console) int i; struct stdio_dev *dev; - for (i = 0; i < cd_count[console]; i++) { - dev = console_devices[console][i]; + for_each_console_dev(i, console, dev) printf("%s ", dev->name); - } printf("\n"); } +int iomux_match_device(struct stdio_dev **set, const int n, struct stdio_dev *sdev) +{ + int i; + + for (i = 0; i < n; i++) + if (sdev == set[i]) + return i; + return -ENOENT; +} + /* This tries to preserve the old list if an error occurs. */ int iomux_doenv(const int console, const char *arg) { char *console_args, *temp, **start; - int i, j, k, io_flag, cs_idx, repeat; + int i, j, io_flag, cs_idx, repeat; struct stdio_dev **cons_set, **old_set; struct stdio_dev *dev; @@ -75,15 +83,8 @@ int iomux_doenv(const int console, const char *arg) return 1; } - switch (console) { - case stdin: - io_flag = DEV_FLAGS_INPUT; - break; - case stdout: - case stderr: - io_flag = DEV_FLAGS_OUTPUT; - break; - default: + io_flag = stdio_file_to_flags(console); + if (io_flag < 0) { free(start); free(console_args); free(cons_set); @@ -103,14 +104,8 @@ int iomux_doenv(const int console, const char *arg) /* * Prevent multiple entries for a device. */ - repeat = 0; - for (k = 0; k < cs_idx; k++) { - if (dev == cons_set[k]) { - repeat++; - break; - } - } - if (repeat) + repeat = iomux_match_device(cons_set, cs_idx, dev); + if (repeat >= 0) continue; /* * Try assigning the specified device. @@ -136,10 +131,7 @@ int iomux_doenv(const int console, const char *arg) /* Stop dropped consoles */ for (i = 0; i < repeat; i++) { - for (j = 0; j < cs_idx; j++) { - if (old_set[i] == cons_set[j]) - break; - } + j = iomux_match_device(cons_set, cs_idx, old_set[i]); if (j == cs_idx) console_stop(console, old_set[i]); } @@ -147,4 +139,37 @@ int iomux_doenv(const int console, const char *arg) free(old_set); return 0; } + +int iomux_replace_device(const int console, const char *old, const char *new) +{ + struct stdio_dev *dev; + char *arg = NULL; /* Initial empty list */ + int size = 1; /* For NUL terminator */ + int i, ret; + + for_each_console_dev(i, console, dev) { + const char *name = strcmp(dev->name, old) ? dev->name : new; + char *tmp; + + /* Append name with a ',' (comma) separator */ + tmp = realloc(arg, size + strlen(name) + 1); + if (!tmp) { + free(arg); + return -ENOMEM; + } + + strcat(tmp, ","); + strcat(tmp, name); + + arg = tmp; + size = strlen(tmp) + 1; + } + + ret = iomux_doenv(console, arg); + if (ret) + ret = -EINVAL; + + free(arg); + return ret; +} #endif /* CONSOLE_MUX */ diff --git a/common/stdio.c b/common/stdio.c index 2b883fddbe..d4acc5256c 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -28,6 +28,20 @@ static struct stdio_dev devs; struct stdio_dev *stdio_devices[] = { NULL, NULL, NULL }; char *stdio_names[MAX_FILES] = { "stdin", "stdout", "stderr" }; +int stdio_file_to_flags(const int file) +{ + switch (file) { + case stdin: + return DEV_FLAGS_INPUT; + case stdout: + case stderr: + return DEV_FLAGS_OUTPUT; + default: + return -EINVAL; + } +} + +#if CONFIG_IS_ENABLED(SYS_DEVICE_NULLDEV) static void nulldev_putc(struct stdio_dev *dev, const char c) { /* nulldev is empty! */ @@ -44,6 +58,25 @@ static int nulldev_input(struct stdio_dev *dev) return 0; } +static void nulldev_register(void) +{ + struct stdio_dev dev; + + memset(&dev, '\0', sizeof(dev)); + + strcpy(dev.name, "nulldev"); + dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; + dev.putc = nulldev_putc; + dev.puts = nulldev_puts; + dev.getc = nulldev_input; + dev.tstc = nulldev_input; + + stdio_register(&dev); +} +#else +static inline void nulldev_register(void) {} +#endif /* SYS_DEVICE_NULLDEV */ + static void stdio_serial_putc(struct stdio_dev *dev, const char c) { serial_putc(c); @@ -83,18 +116,7 @@ static void drv_system_init (void) dev.tstc = stdio_serial_tstc; stdio_register (&dev); - if (CONFIG_IS_ENABLED(SYS_DEVICE_NULLDEV)) { - memset(&dev, '\0', sizeof(dev)); - - strcpy(dev.name, "nulldev"); - dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; - dev.putc = nulldev_putc; - dev.puts = nulldev_puts; - dev.getc = nulldev_input; - dev.tstc = nulldev_input; - - stdio_register(&dev); - } + nulldev_register(); } /************************************************************************** @@ -261,17 +283,6 @@ int stdio_deregister_dev(struct stdio_dev *dev, int force) return 0; } -int stdio_deregister(const char *devname, int force) -{ - struct stdio_dev *dev; - - dev = stdio_get_by_name(devname); - if (!dev) /* device not found */ - return -ENODEV; - - return stdio_deregister_dev(dev, force); -} - int stdio_init_tables(void) { #if defined(CONFIG_NEEDS_MANUAL_RELOC) diff --git a/common/usb_kbd.c b/common/usb_kbd.c index b316807844..60c6027e04 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -617,12 +617,12 @@ int usb_kbd_deregister(int force) if (dev) { usb_kbd_dev = (struct usb_device *)dev->priv; data = usb_kbd_dev->privptr; - if (stdio_deregister_dev(dev, force) != 0) - return 1; #if CONFIG_IS_ENABLED(CONSOLE_MUX) - if (iomux_doenv(stdin, env_get("stdin")) != 0) + if (iomux_replace_device(stdin, DEVNAME, force ? "nulldev" : "")) return 1; #endif + if (stdio_deregister_dev(dev, force) != 0) + return 1; #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE destroy_int_queue(usb_kbd_dev, data->intq); #endif @@ -660,16 +660,16 @@ static int usb_kbd_remove(struct udevice *dev) goto err; } data = udev->privptr; - if (stdio_deregister_dev(sdev, true)) { - ret = -EPERM; - goto err; - } #if CONFIG_IS_ENABLED(CONSOLE_MUX) - if (iomux_doenv(stdin, env_get("stdin"))) { + if (iomux_replace_device(stdin, DEVNAME, "nulldev")) { ret = -ENOLINK; goto err; } #endif + if (stdio_deregister_dev(sdev, true)) { + ret = -EPERM; + goto err; + } #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE destroy_int_queue(udev, data->intq); #endif diff --git a/configs/kontron_sl28_defconfig b/configs/kontron_sl28_defconfig index 1759d5e1b9..d56709689b 100644 --- a/configs/kontron_sl28_defconfig +++ b/configs/kontron_sl28_defconfig @@ -84,6 +84,9 @@ CONFIG_DM_MDIO=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_FSL_ENETC=y +CONFIG_PHY_FIXED=y +CONFIG_DM_DSA=y +CONFIG_MSCC_FELIX_SWITCH=y CONFIG_NVME=y CONFIG_PCI=y CONFIG_DM_PCI=y diff --git a/configs/ls1028aqds_tfa_SECURE_BOOT_defconfig b/configs/ls1028aqds_tfa_SECURE_BOOT_defconfig index eaf903f654..93b13b4729 100644 --- a/configs/ls1028aqds_tfa_SECURE_BOOT_defconfig +++ b/configs/ls1028aqds_tfa_SECURE_BOOT_defconfig @@ -62,6 +62,9 @@ CONFIG_DM_MDIO_MUX=y CONFIG_E1000=y CONFIG_FSL_ENETC=y CONFIG_MDIO_MUX_I2CREG=y +CONFIG_PHY_FIXED=y +CONFIG_DM_DSA=y +CONFIG_MSCC_FELIX_SWITCH=y CONFIG_NVME=y CONFIG_PCI=y CONFIG_DM_PCI=y diff --git a/configs/ls1028aqds_tfa_defconfig b/configs/ls1028aqds_tfa_defconfig index 248c640482..c915069f28 100644 --- a/configs/ls1028aqds_tfa_defconfig +++ b/configs/ls1028aqds_tfa_defconfig @@ -68,6 +68,9 @@ CONFIG_DM_MDIO_MUX=y CONFIG_E1000=y CONFIG_FSL_ENETC=y CONFIG_MDIO_MUX_I2CREG=y +CONFIG_PHY_FIXED=y +CONFIG_DM_DSA=y +CONFIG_MSCC_FELIX_SWITCH=y CONFIG_NVME=y CONFIG_PCI=y CONFIG_DM_PCI=y diff --git a/configs/ls1028ardb_tfa_SECURE_BOOT_defconfig b/configs/ls1028ardb_tfa_SECURE_BOOT_defconfig index 240e4204ae..45d9f40043 100644 --- a/configs/ls1028ardb_tfa_SECURE_BOOT_defconfig +++ b/configs/ls1028ardb_tfa_SECURE_BOOT_defconfig @@ -59,6 +59,9 @@ CONFIG_DM_MDIO=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_FSL_ENETC=y +CONFIG_PHY_FIXED=y +CONFIG_DM_DSA=y +CONFIG_MSCC_FELIX_SWITCH=y CONFIG_NVME=y CONFIG_PCI=y CONFIG_DM_PCI=y diff --git a/configs/ls1028ardb_tfa_defconfig b/configs/ls1028ardb_tfa_defconfig index 493fe7c377..cff68a3cc8 100644 --- a/configs/ls1028ardb_tfa_defconfig +++ b/configs/ls1028ardb_tfa_defconfig @@ -65,6 +65,9 @@ CONFIG_DM_MDIO=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_FSL_ENETC=y +CONFIG_PHY_FIXED=y +CONFIG_DM_DSA=y +CONFIG_MSCC_FELIX_SWITCH=y CONFIG_NVME=y CONFIG_PCI=y CONFIG_DM_PCI=y diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 971a572248..0e84c22b50 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -37,6 +37,21 @@ config DM_MDIO_MUX This is currently implemented in net/mdio-mux-uclass.c Look in include/miiphy.h for details. +config DM_DSA + bool "Enable Driver Model for DSA switches" + depends on DM_ETH && DM_MDIO + depends on PHY_FIXED + help + Enable driver model for DSA switches + + Adds UCLASS_DSA class supporting switches that follow the Distributed + Switch Architecture (DSA). These switches rely on the presence of a + management switch port connected to an Ethernet controller capable of + receiving frames from the switch. This host Ethernet controller is + called the "master" Ethernet interface in DSA terminology. + This is currently implemented in net/dsa-uclass.c, refer to + include/net/dsa.h for API details. + config MDIO_SANDBOX depends on DM_MDIO && SANDBOX default y diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index 37e7e85843..110c1d78fb 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -201,6 +201,11 @@ struct enetc_priv { /* PCS replicator block for USXGMII */ #define ENETC_PCS_DEVAD_REPL 0x1f +#define ENETC_PCS_REPL_LINK_TIMER_1 0x12 +#define ENETC_PCS_REPL_LINK_TIMER_1_DEF 0x0003 +#define ENETC_PCS_REPL_LINK_TIMER_2 0x13 +#define ENETC_PCS_REPL_LINK_TIMER_2_DEF 0x06a0 + /* ENETC external MDIO registers */ #define ENETC_MDIO_BASE 0x1c00 #define ENETC_MDIO_CFG 0x00 diff --git a/drivers/net/mscc_eswitch/Kconfig b/drivers/net/mscc_eswitch/Kconfig index 80dd22f98b..ccf7822dbe 100644 --- a/drivers/net/mscc_eswitch/Kconfig +++ b/drivers/net/mscc_eswitch/Kconfig @@ -36,3 +36,11 @@ config MSCC_SERVAL_SWITCH select PHYLIB help This driver supports the Serval network switch device. + +config MSCC_FELIX_SWITCH + bool "Felix switch driver" + depends on DM_DSA && DM_PCI + select FSL_ENETC + help + This driver supports the Ethernet switch integrated in the + NXP LS1028A SoC. diff --git a/drivers/net/mscc_eswitch/Makefile b/drivers/net/mscc_eswitch/Makefile index d583fe9fc4..22342ed114 100644 --- a/drivers/net/mscc_eswitch/Makefile +++ b/drivers/net/mscc_eswitch/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_xfer.o mscc_mac_table.o m obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o mscc_miim.o obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o mscc_miim.o obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o +obj-$(CONFIG_MSCC_FELIX_SWITCH) += felix_switch.o diff --git a/drivers/net/mscc_eswitch/felix_switch.c b/drivers/net/mscc_eswitch/felix_switch.c new file mode 100644 index 0000000000..f20e84e0f1 --- /dev/null +++ b/drivers/net/mscc_eswitch/felix_switch.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Felix (VSC9959) Ethernet switch driver + * Copyright 2018-2021 NXP Semiconductors + */ + +/* + * This driver is used for the Ethernet switch integrated into NXP LS1028A. + * Felix switch is derived from Microsemi Ocelot but there are several NXP + * adaptations that makes the two U-Boot drivers largely incompatible. + * + * Felix on LS1028A has 4 front panel ports and two internal ports, connected + * to ENETC interfaces. We're using one of the ENETC interfaces to push traffic + * into the switch. Injection/extraction headers are used to identify + * egress/ingress ports in the switch for Tx/Rx. + */ + +#include +#include +#include +#include +#include +#include + +/* defines especially around PCS are reused from enetc */ +#include "../fsl_enetc.h" + +#define PCI_DEVICE_ID_FELIX_ETHSW 0xEEF0 + +/* Felix has in fact 6 ports, but we don't use the last internal one */ +#define FELIX_PORT_COUNT 5 +/* Front panel port mask */ +#define FELIX_FP_PORT_MASK 0xf + +/* Register map for BAR4 */ +#define FELIX_SYS 0x010000 +#define FELIX_ES0 0x040000 +#define FELIX_IS1 0x050000 +#define FELIX_IS2 0x060000 +#define FELIX_GMII(port) (0x100000 + (port) * 0x10000) +#define FELIX_QSYS 0x200000 + +#define FELIX_SYS_SYSTEM (FELIX_SYS + 0x00000E00) +#define FELIX_SYS_SYSTEM_EN BIT(0) +#define FELIX_SYS_RAM_CTRL (FELIX_SYS + 0x00000F24) +#define FELIX_SYS_RAM_CTRL_INIT BIT(1) +#define FELIX_SYS_SYSTEM_PORT_MODE(a) (FELIX_SYS_SYSTEM + 0xC + (a) * 4) +#define FELIX_SYS_SYSTEM_PORT_MODE_CPU 0x0000001e + +#define FELIX_ES0_TCAM_CTRL (FELIX_ES0 + 0x000003C0) +#define FELIX_ES0_TCAM_CTRL_EN BIT(0) +#define FELIX_IS1_TCAM_CTRL (FELIX_IS1 + 0x000003C0) +#define FELIX_IS1_TCAM_CTRL_EN BIT(0) +#define FELIX_IS2_TCAM_CTRL (FELIX_IS2 + 0x000003C0) +#define FELIX_IS2_TCAM_CTRL_EN BIT(0) + +#define FELIX_GMII_CLOCK_CFG(port) (FELIX_GMII(port) + 0x00000000) +#define FELIX_GMII_CLOCK_CFG_LINK_1G 1 +#define FELIX_GMII_CLOCK_CFG_LINK_100M 2 +#define FELIX_GMII_CLOCK_CFG_LINK_10M 3 +#define FELIX_GMII_MAC_ENA_CFG(port) (FELIX_GMII(port) + 0x0000001C) +#define FELIX_GMII_MAX_ENA_CFG_TX BIT(0) +#define FELIX_GMII_MAX_ENA_CFG_RX BIT(4) +#define FELIX_GMII_MAC_IFG_CFG(port) (FELIX_GMII(port) + 0x0000001C + 0x14) +#define FELIX_GMII_MAC_IFG_CFG_DEF 0x515 + +#define FELIX_QSYS_SYSTEM (FELIX_QSYS + 0x0000F460) +#define FELIX_QSYS_SYSTEM_SW_PORT_MODE(a) \ + (FELIX_QSYS_SYSTEM + 0x20 + (a) * 4) +#define FELIX_QSYS_SYSTEM_SW_PORT_ENA BIT(14) +#define FELIX_QSYS_SYSTEM_SW_PORT_LOSSY BIT(9) +#define FELIX_QSYS_SYSTEM_SW_PORT_SCH(a) (((a) & 0x3800) << 11) +#define FELIX_QSYS_SYSTEM_EXT_CPU_CFG (FELIX_QSYS_SYSTEM + 0x80) +#define FELIX_QSYS_SYSTEM_EXT_CPU_PORT(a) (((a) & 0xf) << 8 | 0xff) + +/* internal MDIO in BAR0 */ +#define FELIX_PM_IMDIO_BASE 0x8030 + +/* Serdes block on LS1028A */ +#define FELIX_SERDES_BASE 0x1ea0000L +#define FELIX_SERDES_LNATECR0(lane) (FELIX_SERDES_BASE + 0x818 + \ + (lane) * 0x40) +#define FELIX_SERDES_LNATECR0_ADPT_EQ 0x00003000 +#define FELIX_SERDES_SGMIICR1(lane) (FELIX_SERDES_BASE + 0x1804 + \ + (lane) * 0x10) +#define FELIX_SERDES_SGMIICR1_SGPCS BIT(11) +#define FELIX_SERDES_SGMIICR1_MDEV(a) (((a) & 0x1f) << 27) + +#define FELIX_PCS_CTRL 0 +#define FELIX_PCS_CTRL_RST BIT(15) + +/* + * The long prefix format used here contains two dummy MAC addresses, a magic + * value in place of a VLAN tag followed by the extraction/injection header and + * the original L2 frame. Out of all this we only use the port ID. + */ +#define FELIX_DSA_TAG_LEN sizeof(struct felix_dsa_tag) +#define FELIX_DSA_TAG_MAGIC 0x0a008088 +#define FELIX_DSA_TAG_INJ_PORT 7 +#define FELIX_DSA_TAG_INJ_PORT_SET(a) (0x1 << ((a) & FELIX_FP_PORT_MASK)) +#define FELIX_DSA_TAG_EXT_PORT 10 +#define FELIX_DSA_TAG_EXT_PORT_GET(a) ((a) >> 3) + +struct felix_dsa_tag { + uchar d_mac[6]; + uchar s_mac[6]; + u32 magic; + uchar meta[16]; +}; + +struct felix_priv { + void *regs_base; + void *imdio_base; + struct mii_dev imdio; +}; + +/* MDIO wrappers, we're using these to drive internal MDIO to get to serdes */ +static int felix_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct enetc_mdio_priv priv; + + priv.regs_base = bus->priv; + return enetc_mdio_read_priv(&priv, addr, devad, reg); +} + +static int felix_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct enetc_mdio_priv priv; + + priv.regs_base = bus->priv; + return enetc_mdio_write_priv(&priv, addr, devad, reg, val); +} + +/* set up serdes for SGMII */ +static void felix_init_sgmii(struct mii_dev *imdio, int pidx, bool an) +{ + u16 reg; + + /* set up PCS lane address */ + out_le32(FELIX_SERDES_SGMIICR1(pidx), FELIX_SERDES_SGMIICR1_SGPCS | + FELIX_SERDES_SGMIICR1_MDEV(pidx)); + + /* + * Set to SGMII mode, for 1Gbps enable AN, for 2.5Gbps set fixed speed. + * Although fixed speed is 1Gbps, we could be running at 2.5Gbps based + * on PLL configuration. Setting 1G for 2.5G here is counter intuitive + * but intentional. + */ + reg = ENETC_PCS_IF_MODE_SGMII; + reg |= an ? ENETC_PCS_IF_MODE_SGMII_AN : ENETC_PCS_IF_MODE_SPEED_1G; + felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, + ENETC_PCS_IF_MODE, reg); + + /* Dev ability - SGMII */ + felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SGMII); + + /* Adjust link timer for SGMII */ + felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, + ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL); + felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, + ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL); + + reg = ENETC_PCS_CR_DEF_VAL; + reg |= an ? ENETC_PCS_CR_RESET_AN : ENETC_PCS_CR_RST; + /* restart PCS AN */ + felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, + ENETC_PCS_CR, reg); +} + +/* set up MAC and serdes for (Q)SXGMII */ +static int felix_init_sxgmii(struct mii_dev *imdio, int pidx) +{ + int timeout = 1000; + + /* set up transit equalization control on serdes lane */ + out_le32(FELIX_SERDES_LNATECR0(1), FELIX_SERDES_LNATECR0_ADPT_EQ); + + /*reset lane */ + felix_mdio_write(imdio, pidx, MDIO_MMD_PCS, FELIX_PCS_CTRL, + FELIX_PCS_CTRL_RST); + while (felix_mdio_read(imdio, pidx, MDIO_MMD_PCS, + FELIX_PCS_CTRL) & FELIX_PCS_CTRL_RST && + --timeout) { + mdelay(10); + } + if (felix_mdio_read(imdio, pidx, MDIO_MMD_PCS, + FELIX_PCS_CTRL) & FELIX_PCS_CTRL_RST) + return -ETIME; + + /* Dev ability - SXGMII */ + felix_mdio_write(imdio, pidx, ENETC_PCS_DEVAD_REPL, + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII); + + /* Restart PCS AN */ + felix_mdio_write(imdio, pidx, ENETC_PCS_DEVAD_REPL, ENETC_PCS_CR, + ENETC_PCS_CR_RST | ENETC_PCS_CR_RESET_AN); + felix_mdio_write(imdio, pidx, ENETC_PCS_DEVAD_REPL, + ENETC_PCS_REPL_LINK_TIMER_1, + ENETC_PCS_REPL_LINK_TIMER_1_DEF); + felix_mdio_write(imdio, pidx, ENETC_PCS_DEVAD_REPL, + ENETC_PCS_REPL_LINK_TIMER_2, + ENETC_PCS_REPL_LINK_TIMER_2_DEF); + + return 0; +} + +/* Apply protocol specific configuration to MAC, serdes as needed */ +static void felix_start_pcs(struct udevice *dev, int port, + struct phy_device *phy, struct mii_dev *imdio) +{ + bool autoneg = true; + + if (phy->phy_id == PHY_FIXED_ID || + phy->interface == PHY_INTERFACE_MODE_SGMII_2500) + autoneg = false; + + switch (phy->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_SGMII_2500: + case PHY_INTERFACE_MODE_QSGMII: + felix_init_sgmii(imdio, port, autoneg); + break; + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_XFI: + case PHY_INTERFACE_MODE_USXGMII: + if (felix_init_sxgmii(imdio, port)) + dev_err(dev, "PCS reset timeout on port %d\n", port); + break; + default: + break; + } +} + +void felix_init(struct udevice *dev) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(dev); + struct felix_priv *priv = dev_get_priv(dev); + void *base = priv->regs_base; + int timeout = 100; + + /* Init core memories */ + out_le32(base + FELIX_SYS_RAM_CTRL, FELIX_SYS_RAM_CTRL_INIT); + while (in_le32(base + FELIX_SYS_RAM_CTRL) & FELIX_SYS_RAM_CTRL_INIT && + --timeout) + udelay(10); + if (in_le32(base + FELIX_SYS_RAM_CTRL) & FELIX_SYS_RAM_CTRL_INIT) + dev_err(dev, "Timeout waiting for switch memories\n"); + + /* Start switch core, set up ES0, IS1, IS2 */ + out_le32(base + FELIX_SYS_SYSTEM, FELIX_SYS_SYSTEM_EN); + out_le32(base + FELIX_ES0_TCAM_CTRL, FELIX_ES0_TCAM_CTRL_EN); + out_le32(base + FELIX_IS1_TCAM_CTRL, FELIX_IS1_TCAM_CTRL_EN); + out_le32(base + FELIX_IS2_TCAM_CTRL, FELIX_IS2_TCAM_CTRL_EN); + udelay(20); + + priv->imdio.read = felix_mdio_read; + priv->imdio.write = felix_mdio_write; + priv->imdio.priv = priv->imdio_base + FELIX_PM_IMDIO_BASE; + strncpy(priv->imdio.name, dev->name, MDIO_NAME_LEN); + + /* set up CPU port */ + out_le32(base + FELIX_QSYS_SYSTEM_EXT_CPU_CFG, + FELIX_QSYS_SYSTEM_EXT_CPU_PORT(pdata->cpu_port)); + out_le32(base + FELIX_SYS_SYSTEM_PORT_MODE(pdata->cpu_port), + FELIX_SYS_SYSTEM_PORT_MODE_CPU); +} + +/* + * Probe Felix: + * - enable the PCI function + * - map BAR 4 + * - init switch core and port registers + */ +static int felix_probe(struct udevice *dev) +{ + struct felix_priv *priv = dev_get_priv(dev); + + if (ofnode_valid(dev_ofnode(dev)) && + !ofnode_is_available(dev_ofnode(dev))) { + dev_dbg(dev, "switch disabled\n"); + return -ENODEV; + } + + priv->imdio_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); + if (!priv->imdio_base) { + dev_err(dev, "failed to map BAR0\n"); + return -EINVAL; + } + + priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_4, 0); + if (!priv->regs_base) { + dev_err(dev, "failed to map BAR4\n"); + return -EINVAL; + } + + /* register internal MDIO for debug */ + if (!miiphy_get_dev_by_name(dev->name)) { + struct mii_dev *mii_bus; + + mii_bus = mdio_alloc(); + mii_bus->read = felix_mdio_read; + mii_bus->write = felix_mdio_write; + mii_bus->priv = priv->imdio_base + FELIX_PM_IMDIO_BASE; + strncpy(mii_bus->name, dev->name, MDIO_NAME_LEN); + mdio_register(mii_bus); + } + + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); + + dsa_set_tagging(dev, FELIX_DSA_TAG_LEN, 0); + + /* set up registers */ + felix_init(dev); + + return 0; +} + +static int felix_port_enable(struct udevice *dev, int port, + struct phy_device *phy) +{ + int supported = PHY_GBIT_FEATURES | SUPPORTED_2500baseX_Full; + struct felix_priv *priv = dev_get_priv(dev); + void *base = priv->regs_base; + + /* Set up MAC registers */ + out_le32(base + FELIX_GMII_CLOCK_CFG(port), + FELIX_GMII_CLOCK_CFG_LINK_1G); + + out_le32(base + FELIX_GMII_MAC_IFG_CFG(port), + FELIX_GMII_MAC_IFG_CFG_DEF); + + out_le32(base + FELIX_GMII_MAC_ENA_CFG(port), + FELIX_GMII_MAX_ENA_CFG_TX | FELIX_GMII_MAX_ENA_CFG_RX); + + out_le32(base + FELIX_QSYS_SYSTEM_SW_PORT_MODE(port), + FELIX_QSYS_SYSTEM_SW_PORT_ENA | + FELIX_QSYS_SYSTEM_SW_PORT_LOSSY | + FELIX_QSYS_SYSTEM_SW_PORT_SCH(1)); + + felix_start_pcs(dev, port, phy, &priv->imdio); + + phy->supported &= supported; + phy->advertising &= supported; + phy_config(phy); + + phy_startup(phy); + + return 0; +} + +static void felix_port_disable(struct udevice *dev, int pidx, + struct phy_device *phy) +{ + struct felix_priv *priv = dev_get_priv(dev); + void *base = priv->regs_base; + + out_le32(base + FELIX_GMII_MAC_ENA_CFG(pidx), 0); + + out_le32(base + FELIX_QSYS_SYSTEM_SW_PORT_MODE(pidx), + FELIX_QSYS_SYSTEM_SW_PORT_LOSSY | + FELIX_QSYS_SYSTEM_SW_PORT_SCH(1)); + + /* + * 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 felix_xmit(struct udevice *dev, int pidx, void *packet, int length) +{ + struct felix_dsa_tag *tag = packet; + + tag->magic = FELIX_DSA_TAG_MAGIC; + tag->meta[FELIX_DSA_TAG_INJ_PORT] = FELIX_DSA_TAG_INJ_PORT_SET(pidx); + + return 0; +} + +static int felix_rcv(struct udevice *dev, int *pidx, void *packet, int length) +{ + struct felix_dsa_tag *tag = packet; + + if (tag->magic != FELIX_DSA_TAG_MAGIC) + return -EINVAL; + + *pidx = FELIX_DSA_TAG_EXT_PORT_GET(tag->meta[FELIX_DSA_TAG_EXT_PORT]); + + return 0; +} + +static const struct dsa_ops felix_dsa_ops = { + .port_enable = felix_port_enable, + .port_disable = felix_port_disable, + .xmit = felix_xmit, + .rcv = felix_rcv, +}; + +U_BOOT_DRIVER(felix_ethsw) = { + .name = "felix-switch", + .id = UCLASS_DSA, + .probe = felix_probe, + .ops = &felix_dsa_ops, + .priv_auto = sizeof(struct felix_priv), +}; + +static struct pci_device_id felix_ethsw_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_FELIX_ETHSW) }, + {} +}; + +U_BOOT_PCI_DEVICE(felix_ethsw, felix_ethsw_ids); diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 3228672fc4..1a38c29469 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -24,7 +24,8 @@ int fixedphy_probe(struct phy_device *phydev) /* check for mandatory properties within fixed-link node */ val = fdt_getprop_u32_default_node(gd->fdt_blob, ofnode, 0, "speed", 0); - if (val != SPEED_10 && val != SPEED_100 && val != SPEED_1000) { + if (val != SPEED_10 && val != SPEED_100 && val != SPEED_1000 && + val != SPEED_2500 && val != SPEED_10000) { printf("ERROR: no/invalid speed given in fixed-link node!"); return -EINVAL; } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a2be398736..89e3076bfd 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -977,6 +977,37 @@ static struct phy_device *phy_connect_gmii2rgmii(struct mii_dev *bus, #endif #ifdef CONFIG_PHY_FIXED +/** + * fixed_phy_create() - create an unconnected fixed-link pseudo-PHY device + * @node: OF node for the container of the fixed-link node + * + * Description: Creates a struct phy_device based on a fixed-link of_node + * description. Can be used without phy_connect by drivers which do not expose + * a UCLASS_ETH udevice. + */ +struct phy_device *fixed_phy_create(ofnode node) +{ + phy_interface_t interface = PHY_INTERFACE_MODE_NONE; + const char *if_str; + ofnode subnode; + + if_str = ofnode_read_string(node, "phy-mode"); + if (!if_str) { + if_str = ofnode_read_string(node, "phy-interface-type"); + } + if (if_str) { + interface = phy_get_interface_by_name(if_str); + } + + subnode = ofnode_find_subnode(node, "fixed-link"); + if (!ofnode_valid(subnode)) { + return NULL; + } + + return phy_device_create(NULL, ofnode_to_offset(subnode), PHY_FIXED_ID, + false, interface); +} + #ifdef CONFIG_DM_ETH static struct phy_device *phy_connect_fixed(struct mii_dev *bus, struct udevice *dev, diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile index 5c8ffdbce8..ff844195ae 100644 --- a/drivers/tee/Makefile +++ b/drivers/tee/Makefile @@ -2,5 +2,7 @@ obj-y += tee-uclass.o obj-$(CONFIG_SANDBOX) += sandbox.o +obj-$(CONFIG_OPTEE_TA_RPC_TEST) += optee/supplicant.o +obj-$(CONFIG_OPTEE_TA_RPC_TEST) += optee/i2c.o obj-$(CONFIG_OPTEE) += optee/ obj-y += broadcom/ diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig index d489834df9..65622f30b1 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -22,6 +22,15 @@ config OPTEE_TA_AVB The TA can support the "avb" subcommands "read_rb", "write"rb" and "is_unlocked". +config OPTEE_TA_RPC_TEST + bool "Support RPC TEST TA" + depends on SANDBOX_TEE + default y + help + Enables support for RPC test trusted application emulation, which + permits to test reverse RPC calls to TEE supplicant. Should + be used only in sandbox env. + endmenu endif diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 928d3f8002..068c6e7aa1 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -2,4 +2,5 @@ obj-y += core.o obj-y += supplicant.o +obj-$(CONFIG_DM_I2C) += i2c.o obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o diff --git a/drivers/tee/optee/i2c.c b/drivers/tee/optee/i2c.c new file mode 100644 index 0000000000..ef4e10f991 --- /dev/null +++ b/drivers/tee/optee/i2c.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2020 Foundries.io Ltd + */ + +#include +#include +#include +#include +#include "optee_msg.h" +#include "optee_private.h" + +static int check_xfer_flags(struct udevice *chip, uint tee_flags) +{ + uint flags; + int ret; + + ret = i2c_get_chip_flags(chip, &flags); + if (ret) + return ret; + + if (tee_flags & OPTEE_MSG_RPC_CMD_I2C_FLAGS_TEN_BIT) { + if (!(flags & DM_I2C_CHIP_10BIT)) + return -EINVAL; + } else { + if (flags & DM_I2C_CHIP_10BIT) + return -EINVAL; + } + + return 0; +} + +void optee_suppl_cmd_i2c_transfer(struct optee_msg_arg *arg) +{ + const u8 attr[] = { + OPTEE_MSG_ATTR_TYPE_VALUE_INPUT, + OPTEE_MSG_ATTR_TYPE_VALUE_INPUT, + OPTEE_MSG_ATTR_TYPE_RMEM_INOUT, + OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT, + }; + struct udevice *chip_dev; + struct tee_shm *shm; + u8 *buf; + int ret; + + if (arg->num_params != ARRAY_SIZE(attr) || + arg->params[0].attr != attr[0] || + arg->params[1].attr != attr[1] || + arg->params[2].attr != attr[2] || + arg->params[3].attr != attr[3]) { + goto bad; + } + + shm = (struct tee_shm *)(unsigned long)arg->params[2].u.rmem.shm_ref; + buf = shm->addr; + if (!buf) + goto bad; + + if (i2c_get_chip_for_busnum((int)arg->params[0].u.value.b, + (int)arg->params[0].u.value.c, + 0, &chip_dev)) + goto bad; + + if (check_xfer_flags(chip_dev, arg->params[1].u.value.a)) + goto bad; + + switch (arg->params[0].u.value.a) { + case OPTEE_MSG_RPC_CMD_I2C_TRANSFER_RD: + ret = dm_i2c_read(chip_dev, 0, buf, + (size_t)arg->params[2].u.rmem.size); + break; + case OPTEE_MSG_RPC_CMD_I2C_TRANSFER_WR: + ret = dm_i2c_write(chip_dev, 0, buf, + (size_t)arg->params[2].u.rmem.size); + break; + default: + goto bad; + } + + if (ret) { + arg->ret = TEE_ERROR_COMMUNICATION; + } else { + arg->params[3].u.value.a = arg->params[2].u.rmem.size; + arg->ret = TEE_SUCCESS; + } + + return; +bad: + arg->ret = TEE_ERROR_BAD_PARAMETERS; +} diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 24c60960fc..8d40ce60c2 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -422,4 +422,25 @@ struct optee_msg_arg { */ #define OPTEE_MSG_RPC_CMD_SHM_FREE 7 +/* + * Access a device on an i2c bus + * + * [in] param[0].u.value.a mode: RD(0), WR(1) + * [in] param[0].u.value.b i2c adapter + * [in] param[0].u.value.c i2c chip + * + * [in] param[1].u.value.a i2c control flags + * + * [in/out] memref[2] buffer to exchange the transfer data + * with the secure world + * + * [out] param[3].u.value.a bytes transferred by the driver + */ +#define OPTEE_MSG_RPC_CMD_I2C_TRANSFER 21 +/* I2C master transfer modes */ +#define OPTEE_MSG_RPC_CMD_I2C_TRANSFER_RD 0 +#define OPTEE_MSG_RPC_CMD_I2C_TRANSFER_WR 1 +/* I2C master control flags */ +#define OPTEE_MSG_RPC_CMD_I2C_FLAGS_TEN_BIT BIT(0) + #endif /* _OPTEE_MSG_H */ diff --git a/drivers/tee/optee/optee_msg_supplicant.h b/drivers/tee/optee/optee_msg_supplicant.h index a0fb8063c8..963cfd4782 100644 --- a/drivers/tee/optee/optee_msg_supplicant.h +++ b/drivers/tee/optee/optee_msg_supplicant.h @@ -147,6 +147,11 @@ #define OPTEE_MSG_RPC_CMD_SHM_ALLOC 6 #define OPTEE_MSG_RPC_CMD_SHM_FREE 7 +/* + * I2C bus access + */ +#define OPTEE_MSG_RPC_CMD_I2C_TRANSFER 21 + /* * Was OPTEE_MSG_RPC_CMD_SQL_FS, which isn't supported any longer */ diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 9442d1c176..1f07a27ee4 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -60,6 +60,23 @@ static inline void optee_suppl_rpmb_release(struct udevice *dev) } #endif +#ifdef CONFIG_DM_I2C +/** + * optee_suppl_cmd_i2c_transfer() - route I2C requests to an I2C chip + * @arg: OP-TEE message (layout specified in optee_msg.h) defining the + * transfer mode (read/write), adapter, chip and control flags. + * + * Handles OP-TEE requests to transfer data to the I2C chip on the I2C adapter. + */ +void optee_suppl_cmd_i2c_transfer(struct optee_msg_arg *arg); +#else +static inline void optee_suppl_cmd_i2c_transfer(struct optee_msg_arg *arg) +{ + debug("OPTEE_MSG_RPC_CMD_I2C_TRANSFER not implemented\n"); + arg->ret = TEE_ERROR_NOT_IMPLEMENTED; +} +#endif + void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr); #endif /* __OPTEE_PRIVATE_H */ diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c index ae042b9a20..f9dd874b59 100644 --- a/drivers/tee/optee/supplicant.c +++ b/drivers/tee/optee/supplicant.c @@ -89,6 +89,9 @@ void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg, case OPTEE_MSG_RPC_CMD_RPMB: optee_suppl_cmd_rpmb(dev, arg); break; + case OPTEE_MSG_RPC_CMD_I2C_TRANSFER: + optee_suppl_cmd_i2c_transfer(arg); + break; default: arg->ret = TEE_ERROR_NOT_IMPLEMENTED; } diff --git a/drivers/tee/sandbox.c b/drivers/tee/sandbox.c index e1ba027fd6..3a1d34d6fc 100644 --- a/drivers/tee/sandbox.c +++ b/drivers/tee/sandbox.c @@ -7,11 +7,15 @@ #include #include #include +#include + +#include "optee/optee_msg.h" +#include "optee/optee_private.h" /* * The sandbox tee driver tries to emulate a generic Trusted Exectution - * Environment (TEE) with the Trusted Application (TA) OPTEE_TA_AVB - * available. + * Environment (TEE) with the Trusted Applications (TA) OPTEE_TA_AVB and + * OPTEE_TA_RPC_TEST available. */ static const u32 pstorage_max = 16; @@ -32,7 +36,38 @@ struct ta_entry { struct tee_param *params); }; -#ifdef CONFIG_OPTEE_TA_AVB +static int get_msg_arg(struct udevice *dev, uint num_params, + struct tee_shm **shmp, struct optee_msg_arg **msg_arg) +{ + int rc; + struct optee_msg_arg *ma; + + rc = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL, + OPTEE_MSG_GET_ARG_SIZE(num_params), TEE_SHM_ALLOC, + shmp); + if (rc) + return rc; + + ma = (*shmp)->addr; + memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); + ma->num_params = num_params; + *msg_arg = ma; + + return 0; +} + +void *optee_alloc_and_init_page_list(void *buf, ulong len, + u64 *phys_buf_ptr) +{ + /* + * An empty stub is added just to fix linking issues. + * This function isn't supposed to be called in sandbox + * setup, otherwise replace this with a proper + * implementation from optee/core.c + */ + return NULL; +} + static u32 get_attr(uint n, uint num_params, struct tee_param *params) { if (n >= num_params) @@ -63,6 +98,7 @@ bad_params: return TEE_ERROR_BAD_PARAMETERS; } +#ifdef CONFIG_OPTEE_TA_AVB static u32 ta_avb_open_session(struct udevice *dev, uint num_params, struct tee_param *params) { @@ -214,7 +250,99 @@ static u32 ta_avb_invoke_func(struct udevice *dev, u32 func, uint num_params, return TEE_ERROR_NOT_SUPPORTED; } } -#endif /*OPTEE_TA_AVB*/ +#endif /* OPTEE_TA_AVB */ + +#ifdef CONFIG_OPTEE_TA_RPC_TEST +static u32 ta_rpc_test_open_session(struct udevice *dev, uint num_params, + struct tee_param *params) +{ + /* + * We don't expect additional parameters when opening a session to + * this TA. + */ + return check_params(TEE_PARAM_ATTR_TYPE_NONE, TEE_PARAM_ATTR_TYPE_NONE, + TEE_PARAM_ATTR_TYPE_NONE, TEE_PARAM_ATTR_TYPE_NONE, + num_params, params); +} + +static void fill_i2c_rpc_params(struct optee_msg_arg *msg_arg, u64 bus_num, + u64 chip_addr, u64 xfer_flags, u64 op, + struct tee_param_memref memref) +{ + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + msg_arg->params[2].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INOUT; + msg_arg->params[3].attr = OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT; + + /* trigger I2C services of TEE supplicant */ + msg_arg->cmd = OPTEE_MSG_RPC_CMD_I2C_TRANSFER; + + msg_arg->params[0].u.value.a = op; + msg_arg->params[0].u.value.b = bus_num; + msg_arg->params[0].u.value.c = chip_addr; + msg_arg->params[1].u.value.a = xfer_flags; + + /* buffer to read/write data */ + msg_arg->params[2].u.rmem.shm_ref = (ulong)memref.shm; + msg_arg->params[2].u.rmem.size = memref.size; + msg_arg->params[2].u.rmem.offs = memref.shm_offs; + + msg_arg->num_params = 4; +} + +static u32 ta_rpc_test_invoke_func(struct udevice *dev, u32 func, + uint num_params, + struct tee_param *params) +{ + struct tee_shm *shm; + struct tee_param_memref memref_data; + struct optee_msg_arg *msg_arg; + int chip_addr, bus_num, op, xfer_flags; + int res; + + res = check_params(TEE_PARAM_ATTR_TYPE_VALUE_INPUT, + TEE_PARAM_ATTR_TYPE_MEMREF_INOUT, + TEE_PARAM_ATTR_TYPE_NONE, + TEE_PARAM_ATTR_TYPE_NONE, + num_params, params); + if (res) + return TEE_ERROR_BAD_PARAMETERS; + + bus_num = params[0].u.value.a; + chip_addr = params[0].u.value.b; + xfer_flags = params[0].u.value.c; + memref_data = params[1].u.memref; + + switch (func) { + case TA_RPC_TEST_CMD_I2C_READ: + op = OPTEE_MSG_RPC_CMD_I2C_TRANSFER_RD; + break; + case TA_RPC_TEST_CMD_I2C_WRITE: + op = OPTEE_MSG_RPC_CMD_I2C_TRANSFER_WR; + break; + default: + return TEE_ERROR_NOT_SUPPORTED; + } + + /* + * Fill params for an RPC call to tee supplicant + */ + res = get_msg_arg(dev, 4, &shm, &msg_arg); + if (res) + goto out; + + fill_i2c_rpc_params(msg_arg, bus_num, chip_addr, xfer_flags, op, + memref_data); + + /* Make an RPC call to tee supplicant */ + optee_suppl_cmd(dev, shm, 0); + res = msg_arg->ret; +out: + tee_shm_free(shm); + + return res; +} +#endif /* CONFIG_OPTEE_TA_RPC_TEST */ static const struct ta_entry ta_entries[] = { #ifdef CONFIG_OPTEE_TA_AVB @@ -223,6 +351,12 @@ static const struct ta_entry ta_entries[] = { .invoke_func = ta_avb_invoke_func, }, #endif +#ifdef CONFIG_OPTEE_TA_RPC_TEST + { .uuid = TA_RPC_TEST_UUID, + .open_session = ta_rpc_test_open_session, + .invoke_func = ta_rpc_test_invoke_func, + }, +#endif }; static void sandbox_tee_get_version(struct udevice *dev, diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index ae4425d7a5..d75de368c5 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -46,6 +46,7 @@ enum uclass_id { UCLASS_DISPLAY, /* Display (e.g. DisplayPort, HDMI) */ UCLASS_DSI_HOST, /* Display Serial Interface host */ UCLASS_DMA, /* Direct Memory Access */ + UCLASS_DSA, /* Distributed (Ethernet) Switch Architecture */ UCLASS_EFI, /* EFI managed devices */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ diff --git a/include/iomux.h b/include/iomux.h index da7ff697d2..37f5f6dee6 100644 --- a/include/iomux.h +++ b/include/iomux.h @@ -24,7 +24,14 @@ extern struct stdio_dev **console_devices[MAX_FILES]; */ extern int cd_count[MAX_FILES]; +#define for_each_console_dev(i, file, dev) \ + for (i = 0, dev = console_devices[file][i]; \ + i < cd_count[file]; \ + i++, dev = console_devices[file][i]) + +int iomux_match_device(struct stdio_dev **, const int, struct stdio_dev *); int iomux_doenv(const int, const char *); +int iomux_replace_device(const int, const char *, const char *); void iomux_printdevs(const int); #endif /* _IO_MUX_H */ diff --git a/include/net.h b/include/net.h index 13da69b7c1..b95d6a6f60 100644 --- a/include/net.h +++ b/include/net.h @@ -499,7 +499,13 @@ struct icmp_hdr { * maximum packet size and multiple of 32 bytes = 1536 */ #define PKTSIZE 1522 +#ifndef CONFIG_DM_DSA #define PKTSIZE_ALIGN 1536 +#else +/* Maximum DSA tagging overhead (headroom and/or tailroom) */ +#define DSA_MAX_OVR 256 +#define PKTSIZE_ALIGN (1536 + DSA_MAX_OVR) +#endif /* * Maximum receive ring size; that is, the number of packets diff --git a/include/net/dsa.h b/include/net/dsa.h new file mode 100644 index 0000000000..0f31a908c9 --- /dev/null +++ b/include/net/dsa.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019-2021 NXP Semiconductors + */ + +#ifndef __DSA_H__ +#define __DSA_H__ + +#include +#include + +/** + * DSA stands for Distributed Switch Architecture and it is infrastructure + * intended to support drivers for Switches that rely on an intermediary + * Ethernet device for I/O. These switches may support cascading allowing + * them to be arranged as a tree. + * DSA is documented in detail in the Linux kernel documentation under + * Documentation/networking/dsa/dsa.txt + * The network layout of such a switch is shown below: + * + * |------| + * | eth0 | <--- master eth device (regular eth driver) + * |------| + * ^ | + * tag added by switch -->| | + * | | + * | |<-- tag added by DSA driver + * | v + * |--------------------------------------| + * | | CPU port | | <-- DSA (switch) device + * | ------------ | (DSA driver) + * | _________ _________ _________ | + * | | port0 | | port1 | ... | portn | | <-- ports as eth devices + * |-+-------+--+-------+-------+-------+-| ('dsa-port' eth driver) + * + * In U-Boot the intent is to allow access to front panel ports (shown at the + * bottom of the picture) through the master Ethernet dev (eth0 in the picture). + * Front panel ports are presented as regular Ethernet devices in U-Boot and + * they are expected to support the typical networking commands. + * In general DSA switches require the use of tags, extra headers added both by + * software on Tx and by the switch on Rx. These tags carry at a minimum port + * information and switch information for cascaded set-ups. + * In U-Boot these tags are inserted and parsed by the DSA switch driver, the + * class code helps with headroom/tailroom for the extra headers. + * + * TODO: + * - handle switch cascading, for now U-Boot only supports stand-alone switches. + * - Add support to probe DSA switches connected to a MDIO bus, this is needed + * to convert switch drivers that are now under drivers/net/phy. + */ + +#define DSA_PORT_NAME_LENGTH 16 + +/* Maximum number of ports each DSA device can have */ +#define DSA_MAX_PORTS 12 + +/** + * struct dsa_ops - DSA operations + * + * @port_enable: Initialize a switch port for I/O. + * @port_disable: Disable I/O for a port. + * @xmit: Insert the DSA tag for transmission. + * DSA drivers receive a copy of the packet with headroom and + * tailroom reserved and set to 0. 'packet' points to headroom + * and 'length' is updated to include both head and tailroom. + * @rcv: Process the DSA tag on reception and return the port index + * from the h/w provided tag. Return the index via 'portp'. + * 'packet' and 'length' describe the frame as received from + * master including any additional headers. + */ +struct dsa_ops { + int (*port_enable)(struct udevice *dev, int port, + struct phy_device *phy); + void (*port_disable)(struct udevice *dev, int port, + struct phy_device *phy); + int (*xmit)(struct udevice *dev, int port, void *packet, int length); + int (*rcv)(struct udevice *dev, int *portp, void *packet, int length); +}; + +#define dsa_get_ops(dev) ((struct dsa_ops *)(dev)->driver->ops) + +/** + * struct dsa_port_pdata - DSA port platform data + * + * @phy: PHY device associated with this port. + * The uclass code attempts to set this field for all ports except CPU + * port, based on DT information. It may be NULL. + * @index: Port index in the DSA switch, set by the uclass code. + * @name: Name of the port Eth device. If a label property is present in the + * port DT node, it is used as name. + */ +struct dsa_port_pdata { + struct phy_device *phy; + u32 index; + char name[DSA_PORT_NAME_LENGTH]; +}; + +/** + * struct dsa_pdata - Per-device platform data for DSA DM + * + * @num_ports: Number of ports the device has, must be <= DSA_MAX_PORTS. + * This number is extracted from the DT 'ports' node of this + * DSA device, and it counts the CPU port and all the other + * port subnodes including the disabled ones. + * @cpu_port: Index of the switch port linked to the master Ethernet. + * The uclass code sets this based on DT information. + * @master_node: OF node of the host Ethernet controller. + * @cpu_port_node: DT node of the switch's CPU port. + */ +struct dsa_pdata { + int num_ports; + u32 cpu_port; + ofnode master_node; + ofnode cpu_port_node; +}; + +/** + * dsa_set_tagging() - Configure the headroom and/or tailroom sizes + * + * The DSA class code allocates headroom and tailroom on Tx before + * calling the DSA driver's xmit function. + * All drivers must call this at probe time. + * + * @dev: DSA device pointer + * @headroom: Size, in bytes, of headroom needed for the DSA tag. + * @tailroom: Size, in bytes, of tailroom needed for the DSA tag. + * Total headroom and tailroom size should not exceed + * DSA_MAX_OVR. + * @return 0 if OK, -ve on error + */ +int dsa_set_tagging(struct udevice *dev, ushort headroom, ushort tailroom); + +/* DSA helpers */ + +/** + * dsa_get_master() - Return a reference to the master Ethernet device + * + * Can be called at driver probe time or later. + * + * @dev: DSA device pointer + * @return Master Eth 'udevice' pointer if OK, NULL on error + */ +struct udevice *dsa_get_master(struct udevice *dev); + +/** + * dsa_port_get_pdata() - Helper that returns the platdata of an active + * (non-CPU) DSA port device. + * + * Can be called at driver probe time or later. + * + * @pdev: DSA port device pointer + * @return 'dsa_port_pdata' pointer if OK, NULL on error + */ +static inline struct dsa_port_pdata * + dsa_port_get_pdata(struct udevice *pdev) +{ + struct eth_pdata *eth = dev_get_plat(pdev); + + if (!eth) + return NULL; + + return eth->priv_pdata; +} + +#endif /* __DSA_H__ */ diff --git a/include/phy.h b/include/phy.h index 7750efd8bb..2754421ed4 100644 --- a/include/phy.h +++ b/include/phy.h @@ -402,6 +402,27 @@ int phy_reset(struct phy_device *phydev); struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask, phy_interface_t interface); +#ifdef CONFIG_PHY_FIXED + +/** + * fixed_phy_create() - create an unconnected fixed-link pseudo-PHY device + * @node: OF node for the container of the fixed-link node + * + * Description: Creates a struct phy_device based on a fixed-link of_node + * description. Can be used without phy_connect by drivers which do not expose + * a UCLASS_ETH udevice. + */ +struct phy_device *fixed_phy_create(ofnode node); + +#else + +static inline struct phy_device *fixed_phy_create(ofnode node) +{ + return NULL; +} + +#endif + #ifdef CONFIG_DM_ETH /** diff --git a/include/stdio_dev.h b/include/stdio_dev.h index 48871a6a22..8fb9a12dd8 100644 --- a/include/stdio_dev.h +++ b/include/stdio_dev.h @@ -18,6 +18,8 @@ #define DEV_FLAGS_OUTPUT 0x00000002 /* Device can be used as output console */ #define DEV_FLAGS_DM 0x00000004 /* Device priv is a struct udevice * */ +int stdio_file_to_flags(const int file); + /* Device information */ struct stdio_dev { int flags; /* Device flags: input/output/system */ @@ -83,7 +85,6 @@ int stdio_add_devices(void); int stdio_init(void); void stdio_print_current_devices(void); -int stdio_deregister(const char *devname, int force); /** * stdio_deregister_dev() - deregister the device "devname". diff --git a/include/tee/optee_ta_rpc_test.h b/include/tee/optee_ta_rpc_test.h new file mode 100644 index 0000000000..9491fbab1d --- /dev/null +++ b/include/tee/optee_ta_rpc_test.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* Copyright (c) 2020 Foundries Ltd */ + +#ifndef __TA_RPC_TEST_H +#define __TA_RPC_TEST_H + +#define TA_RPC_TEST_UUID { 0x48420575, 0x96ca, 0x401a, \ + { 0x89, 0x91, 0x1e, 0xfd, 0xce, 0xbd, 0x7d, 0x04 } } + +/* + * Does a reverse RPC call for I2C read + * + * in params[0].value.a: bus number + * in params[0].value.b: chip address + * in params[0].value.c: control flags + * inout params[1].u.memref: buffer to read data + */ +#define TA_RPC_TEST_CMD_I2C_READ 0 + +/* + * Does a reverse RPC call for I2C write + * + * in params[0].value.a: bus number + * in params[0].value.b: chip address + * in params[0].value.c: control flags + * inout params[1].u.memref: buffer with data to write + */ +#define TA_RPC_TEST_CMD_I2C_WRITE 1 + +#endif /* __TA_RPC_TEST_H */ diff --git a/net/Makefile b/net/Makefile index 76527f704c..fb3eba840f 100644 --- a/net/Makefile +++ b/net/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_NET) += arp.o obj-$(CONFIG_CMD_BOOTP) += bootp.o obj-$(CONFIG_CMD_CDP) += cdp.o obj-$(CONFIG_CMD_DNS) += dns.o +obj-$(CONFIG_DM_DSA) += dsa-uclass.o ifdef CONFIG_DM_ETH obj-$(CONFIG_NET) += eth-uclass.o else diff --git a/net/dsa-uclass.c b/net/dsa-uclass.c new file mode 100644 index 0000000000..2ce9ddb90d --- /dev/null +++ b/net/dsa-uclass.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019-2021 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DSA_PORT_CHILD_DRV_NAME "dsa-port" + +/* per-device internal state structure */ +struct dsa_priv { + struct phy_device *cpu_port_fixed_phy; + struct udevice *master_dev; + int num_ports; + u32 cpu_port; + int headroom; + int tailroom; +}; + +/* external API */ +int dsa_set_tagging(struct udevice *dev, ushort headroom, ushort tailroom) +{ + struct dsa_priv *priv; + + if (!dev || !dev_get_uclass_priv(dev)) + return -ENODEV; + + if (headroom + tailroom > DSA_MAX_OVR) + return -EINVAL; + + priv = dev_get_uclass_priv(dev); + + if (headroom > 0) + priv->headroom = headroom; + if (tailroom > 0) + priv->tailroom = tailroom; + + return 0; +} + +/* returns the DSA master Ethernet device */ +struct udevice *dsa_get_master(struct udevice *dev) +{ + struct dsa_priv *priv = dev_get_uclass_priv(dev); + + if (!priv) + return NULL; + + return priv->master_dev; +} + +/* + * Start the desired port, the CPU port and the master Eth interface. + * TODO: if cascaded we may need to _start ports in other switches too + */ +static int dsa_port_start(struct udevice *pdev) +{ + struct udevice *dev = dev_get_parent(pdev); + struct dsa_priv *priv = dev_get_uclass_priv(dev); + struct udevice *master = dsa_get_master(dev); + struct dsa_ops *ops = dsa_get_ops(dev); + int err; + + if (!priv) + return -ENODEV; + + if (!master) { + dev_err(pdev, "DSA master Ethernet device not found!\n"); + return -EINVAL; + } + + if (ops->port_enable) { + struct dsa_port_pdata *port_pdata; + + port_pdata = dev_get_parent_plat(pdev); + err = ops->port_enable(dev, port_pdata->index, + port_pdata->phy); + if (err) + return err; + + err = ops->port_enable(dev, priv->cpu_port, + priv->cpu_port_fixed_phy); + if (err) + return err; + } + + return eth_get_ops(master)->start(master); +} + +/* Stop the desired port, the CPU port and the master Eth interface */ +static void dsa_port_stop(struct udevice *pdev) +{ + struct udevice *dev = dev_get_parent(pdev); + struct dsa_priv *priv = dev_get_uclass_priv(dev); + struct udevice *master = dsa_get_master(dev); + struct dsa_ops *ops = dsa_get_ops(dev); + + if (!priv) + return; + + if (ops->port_disable) { + struct dsa_port_pdata *port_pdata; + + port_pdata = dev_get_parent_plat(pdev); + ops->port_disable(dev, port_pdata->index, port_pdata->phy); + ops->port_disable(dev, priv->cpu_port, NULL); + } + + /* + * stop master only if it's active, don't probe it otherwise. + * Under normal usage it would be active because we're using it, but + * during tear-down it may have been removed ahead of us. + */ + if (master && device_active(master)) + eth_get_ops(master)->stop(master); +} + +/* + * Insert a DSA tag and call master Ethernet send on the resulting packet + * We copy the frame to a stack buffer where we have reserved headroom and + * tailroom space. Headroom and tailroom are set to 0. + */ +static int dsa_port_send(struct udevice *pdev, void *packet, int length) +{ + struct udevice *dev = dev_get_parent(pdev); + struct dsa_priv *priv = dev_get_uclass_priv(dev); + int head = priv->headroom, tail = priv->tailroom; + struct udevice *master = dsa_get_master(dev); + struct dsa_ops *ops = dsa_get_ops(dev); + uchar dsa_packet_tmp[PKTSIZE_ALIGN]; + struct dsa_port_pdata *port_pdata; + int err; + + if (!master) + return -EINVAL; + + if (length + head + tail > PKTSIZE_ALIGN) + return -EINVAL; + + memset(dsa_packet_tmp, 0, head); + memset(dsa_packet_tmp + head + length, 0, tail); + memcpy(dsa_packet_tmp + head, packet, length); + length += head + tail; + /* copy back to preserve original buffer alignment */ + memcpy(packet, dsa_packet_tmp, length); + + port_pdata = dev_get_parent_plat(pdev); + err = ops->xmit(dev, port_pdata->index, packet, length); + if (err) + return err; + + return eth_get_ops(master)->send(master, packet, length); +} + +/* Receive a frame from master Ethernet, process it and pass it on */ +static int dsa_port_recv(struct udevice *pdev, int flags, uchar **packetp) +{ + struct udevice *dev = dev_get_parent(pdev); + struct dsa_priv *priv = dev_get_uclass_priv(dev); + int head = priv->headroom, tail = priv->tailroom; + struct udevice *master = dsa_get_master(dev); + struct dsa_ops *ops = dsa_get_ops(dev); + struct dsa_port_pdata *port_pdata; + int length, port_index, err; + + if (!master) + return -EINVAL; + + length = eth_get_ops(master)->recv(master, flags, packetp); + if (length <= 0) + return length; + + /* + * If we receive frames from a different port or frames that DSA driver + * doesn't like we discard them here. + * In case of discard we return with no frame and expect to be called + * again instead of looping here, so upper layer can deal with timeouts. + */ + port_pdata = dev_get_parent_plat(pdev); + err = ops->rcv(dev, &port_index, *packetp, length); + if (err || port_index != port_pdata->index || (length <= head + tail)) { + if (eth_get_ops(master)->free_pkt) + eth_get_ops(master)->free_pkt(master, *packetp, length); + return -EAGAIN; + } + + /* + * We move the pointer over headroom here to avoid a copy. If free_pkt + * gets called we move the pointer back before calling master free_pkt. + */ + *packetp += head; + + return length - head - tail; +} + +static int dsa_port_free_pkt(struct udevice *pdev, uchar *packet, int length) +{ + struct udevice *dev = dev_get_parent(pdev); + struct udevice *master = dsa_get_master(dev); + struct dsa_priv *priv; + + if (!master) + return -EINVAL; + + priv = dev_get_uclass_priv(dev); + if (eth_get_ops(master)->free_pkt) { + /* return the original pointer and length to master Eth */ + packet -= priv->headroom; + length += priv->headroom - priv->tailroom; + + return eth_get_ops(master)->free_pkt(master, packet, length); + } + + return 0; +} + +static int dsa_port_of_to_pdata(struct udevice *pdev) +{ + struct dsa_port_pdata *port_pdata; + struct dsa_pdata *dsa_pdata; + struct eth_pdata *eth_pdata; + struct udevice *dev; + const char *label; + u32 index; + int err; + + if (!pdev) + return -ENODEV; + + err = ofnode_read_u32(dev_ofnode(pdev), "reg", &index); + if (err) + return err; + + dev = dev_get_parent(pdev); + dsa_pdata = dev_get_uclass_plat(dev); + + port_pdata = dev_get_parent_plat(pdev); + port_pdata->index = index; + + label = ofnode_read_string(dev_ofnode(pdev), "label"); + if (label) + strncpy(port_pdata->name, label, DSA_PORT_NAME_LENGTH); + + eth_pdata = dev_get_plat(pdev); + eth_pdata->priv_pdata = port_pdata; + + dev_dbg(pdev, "port %d node %s\n", port_pdata->index, + ofnode_get_name(dev_ofnode(pdev))); + + return 0; +} + +static const struct eth_ops dsa_port_ops = { + .start = dsa_port_start, + .send = dsa_port_send, + .recv = dsa_port_recv, + .stop = dsa_port_stop, + .free_pkt = dsa_port_free_pkt, +}; + +static int dsa_port_probe(struct udevice *pdev) +{ + struct udevice *dev = dev_get_parent(pdev); + struct eth_pdata *eth_pdata, *master_pdata; + unsigned char env_enetaddr[ARP_HLEN]; + struct dsa_port_pdata *port_pdata; + struct dsa_priv *dsa_priv; + struct udevice *master; + + port_pdata = dev_get_parent_plat(pdev); + dsa_priv = dev_get_uclass_priv(dev); + + port_pdata->phy = dm_eth_phy_connect(pdev); + if (!port_pdata->phy) + return -ENODEV; + + /* + * Inherit port's hwaddr from the DSA master, unless the port already + * 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)) + return 0; + + master = dsa_get_master(dev); + if (!master) + return 0; + + master_pdata = dev_get_plat(master); + eth_pdata = dev_get_plat(pdev); + memcpy(eth_pdata->enetaddr, master_pdata->enetaddr, ARP_HLEN); + eth_env_set_enetaddr_by_index("eth", dev_seq(pdev), + master_pdata->enetaddr); + + return 0; +} + +static int dsa_port_remove(struct udevice *pdev) +{ + struct udevice *dev = dev_get_parent(pdev); + struct dsa_port_pdata *port_pdata; + struct dsa_priv *dsa_priv; + + port_pdata = dev_get_parent_plat(pdev); + dsa_priv = dev_get_uclass_priv(dev); + + port_pdata->phy = NULL; + + return 0; +} + +U_BOOT_DRIVER(dsa_port) = { + .name = DSA_PORT_CHILD_DRV_NAME, + .id = UCLASS_ETH, + .ops = &dsa_port_ops, + .probe = dsa_port_probe, + .remove = dsa_port_remove, + .of_to_plat = dsa_port_of_to_pdata, + .plat_auto = sizeof(struct eth_pdata), +}; + +/* + * This function mostly deals with pulling information out of the device tree + * into the pdata structure. + * It goes through the list of switch ports, registers an eth device for each + * front panel port and identifies the cpu port connected to master eth device. + * TODO: support cascaded switches + */ +static int dsa_post_bind(struct udevice *dev) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(dev); + ofnode node = dev_ofnode(dev), pnode; + int i, err, first_err = 0; + + if (!pdata || !ofnode_valid(node)) + return -ENODEV; + + pdata->master_node = ofnode_null(); + + node = ofnode_find_subnode(node, "ports"); + if (!ofnode_valid(node)) + node = ofnode_find_subnode(node, "ethernet-ports"); + if (!ofnode_valid(node)) { + dev_err(dev, "ports node is missing under DSA device!\n"); + return -EINVAL; + } + + pdata->num_ports = ofnode_get_child_count(node); + if (pdata->num_ports <= 0 || pdata->num_ports > DSA_MAX_PORTS) { + dev_err(dev, "invalid number of ports (%d)\n", + pdata->num_ports); + return -EINVAL; + } + + /* look for the CPU port */ + ofnode_for_each_subnode(pnode, node) { + u32 ethernet; + + if (ofnode_read_u32(pnode, "ethernet", ðernet)) + continue; + + pdata->master_node = ofnode_get_by_phandle(ethernet); + pdata->cpu_port_node = pnode; + break; + } + + if (!ofnode_valid(pdata->master_node)) { + dev_err(dev, "master eth node missing!\n"); + return -EINVAL; + } + + if (ofnode_read_u32(pnode, "reg", &pdata->cpu_port)) { + dev_err(dev, "CPU port node not valid!\n"); + return -EINVAL; + } + + dev_dbg(dev, "master node %s on port %d\n", + ofnode_get_name(pdata->master_node), pdata->cpu_port); + + for (i = 0; i < pdata->num_ports; i++) { + char name[DSA_PORT_NAME_LENGTH]; + struct udevice *pdev; + + /* + * If this is the CPU port don't register it as an ETH device, + * we skip it on purpose since I/O to/from it from the CPU + * isn't useful. + */ + if (i == pdata->cpu_port) + continue; + + /* + * Set up default port names. If present, DT port labels + * will override the default port names. + */ + snprintf(name, DSA_PORT_NAME_LENGTH, "%s@%d", dev->name, i); + + ofnode_for_each_subnode(pnode, node) { + u32 reg; + + if (ofnode_read_u32(pnode, "reg", ®)) + continue; + + if (reg == i) + break; + } + + /* + * skip registration if port id not found or if the port + * is explicitly disabled in DT + */ + if (!ofnode_valid(pnode) || !ofnode_is_available(pnode)) + continue; + + err = device_bind_driver_to_node(dev, DSA_PORT_CHILD_DRV_NAME, + name, pnode, &pdev); + if (pdev) { + struct dsa_port_pdata *port_pdata; + + port_pdata = dev_get_parent_plat(pdev); + strncpy(port_pdata->name, name, DSA_PORT_NAME_LENGTH); + pdev->name = port_pdata->name; + } + + /* try to bind all ports but keep 1st error */ + if (err && !first_err) + first_err = err; + } + + if (first_err) + return first_err; + + dev_dbg(dev, "DSA ports successfully bound\n"); + + return 0; +} + +/** + * Initialize the uclass per device internal state structure (priv). + * TODO: pick up references to other switch devices here, if we're cascaded. + */ +static int dsa_pre_probe(struct udevice *dev) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(dev); + struct dsa_priv *priv = dev_get_uclass_priv(dev); + + if (!pdata || !priv) + return -ENODEV; + + priv->num_ports = pdata->num_ports; + priv->cpu_port = pdata->cpu_port; + priv->cpu_port_fixed_phy = fixed_phy_create(pdata->cpu_port_node); + if (!priv->cpu_port_fixed_phy) { + dev_err(dev, "Failed to register fixed-link for CPU port\n"); + return -ENODEV; + } + + uclass_find_device_by_ofnode(UCLASS_ETH, pdata->master_node, + &priv->master_dev); + return 0; +} + +UCLASS_DRIVER(dsa) = { + .id = UCLASS_DSA, + .name = "dsa", + .post_bind = dsa_post_bind, + .pre_probe = dsa_pre_probe, + .per_device_auto = sizeof(struct dsa_priv), + .per_device_plat_auto = sizeof(struct dsa_pdata), + .per_child_plat_auto = sizeof(struct dsa_port_pdata), + .flags = DM_UC_FLAG_SEQ_ALIAS, +}; diff --git a/test/dm/tee.c b/test/dm/tee.c index ddbdcfb0cf..7a11bf8913 100644 --- a/test/dm/tee.c +++ b/test/dm/tee.c @@ -13,15 +13,16 @@ #include #include #include +#include -static int open_session(struct udevice *dev, u32 *session) +static int open_session(struct udevice *dev, u32 *session, + struct tee_optee_ta_uuid *uuid) { struct tee_open_session_arg arg; - const struct tee_optee_ta_uuid uuid = TA_AVB_UUID; int rc; memset(&arg, 0, sizeof(arg)); - tee_optee_ta_uuid_to_octets(arg.uuid, &uuid); + tee_optee_ta_uuid_to_octets(arg.uuid, uuid); rc = tee_open_session(dev, &arg, 0, NULL); if (rc) return rc; @@ -32,7 +33,7 @@ static int open_session(struct udevice *dev, u32 *session) return 0; } -static int invoke_func(struct udevice *dev, u32 session) +static int invoke_func_avb(struct udevice *dev, u32 session) { struct tee_param param = { .attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT }; struct tee_invoke_arg arg; @@ -47,6 +48,48 @@ static int invoke_func(struct udevice *dev, u32 session) return 0; } +static int invoke_func_rpc_test(struct udevice *dev, u32 session, + u64 op, u64 busnum, u64 chip_addr, + u64 xfer_flags, u8 *buf, size_t buf_size) +{ + struct tee_param param[2]; + struct tee_invoke_arg arg; + struct tee_shm *shm_buf; + int rc; + + memset(&arg, 0, sizeof(arg)); + arg.session = session; + arg.func = op; + + rc = tee_shm_alloc(dev, buf_size, + TEE_SHM_ALLOC, &shm_buf); + if (rc) + return rc; + + if (op == TA_RPC_TEST_CMD_I2C_WRITE) + memcpy(shm_buf->addr, buf, buf_size); + + memset(param, 0, sizeof(param)); + param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = busnum; + param[0].u.value.b = chip_addr; + param[0].u.value.c = xfer_flags; + param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT; + param[1].u.memref.shm = shm_buf; + param[1].u.memref.size = buf_size; + + if (tee_invoke_func(dev, &arg, 2, param) || arg.ret) { + rc = -1; + goto out; + } + + if (op == TA_RPC_TEST_CMD_I2C_READ) + memcpy(buf, shm_buf->addr, buf_size); +out: + tee_shm_free(shm_buf); + return rc; +} + static int match(struct tee_version_data *vers, const void *data) { return vers->gen_caps & TEE_GEN_CAP_GP; @@ -62,6 +105,7 @@ static int test_tee(struct unit_test_state *uts, struct test_tee_vars *vars) struct tee_version_data vers; struct udevice *dev; struct sandbox_tee_state *state; + struct tee_optee_ta_uuid avb_uuid = TA_AVB_UUID; u32 session = 0; int rc; u8 data[128]; @@ -71,11 +115,11 @@ static int test_tee(struct unit_test_state *uts, struct test_tee_vars *vars) state = dev_get_priv(dev); ut_assert(!state->session); - rc = open_session(dev, &session); + rc = open_session(dev, &session, &avb_uuid); ut_assert(!rc); ut_assert(session == state->session); - rc = invoke_func(dev, session); + rc = invoke_func_avb(dev, session); ut_assert(!rc); rc = tee_close_session(dev, session); @@ -100,7 +144,59 @@ static int test_tee(struct unit_test_state *uts, struct test_tee_vars *vars) vars->alloc_shm = NULL; ut_assert(!state->num_shms); - return 0; + return rc; +} + +#define I2C_BUF_SIZE 64 + +static int test_tee_rpc(struct unit_test_state *uts) +{ + struct tee_version_data vers; + struct udevice *dev; + struct sandbox_tee_state *state; + struct tee_optee_ta_uuid rpc_test_uuid = TA_RPC_TEST_UUID; + u32 session = 0; + int rc; + + char *test_str = "Test string"; + u8 data[I2C_BUF_SIZE] = {0}; + u8 data_from_eeprom[I2C_BUF_SIZE] = {0}; + + /* Use sandbox I2C EEPROM emulation; bus: 0, chip: 0x2c */ + u64 bus = 0; + u64 chip = 0x2c; + u64 xfer_flags = 0; + + dev = tee_find_device(NULL, match, NULL, &vers); + ut_assert(dev); + state = dev_get_priv(dev); + ut_assert(!state->session); + + /* Test RPC call asking for I2C service */ + rc = open_session(dev, &session, &rpc_test_uuid); + ut_assert(!rc); + ut_assert(session == state->session); + + /* Write buffer */ + strncpy((char *)data, test_str, strlen(test_str)); + rc = invoke_func_rpc_test(dev, session, TA_RPC_TEST_CMD_I2C_WRITE, + bus, chip, xfer_flags, data, sizeof(data)); + ut_assert(!rc); + + /* Read buffer */ + rc = invoke_func_rpc_test(dev, session, TA_RPC_TEST_CMD_I2C_READ, + bus, chip, xfer_flags, data_from_eeprom, + sizeof(data_from_eeprom)); + ut_assert(!rc); + + /* Compare */ + ut_assert(!memcmp(data, data_from_eeprom, sizeof(data))); + + rc = tee_close_session(dev, session); + ut_assert(!rc); + ut_assert(!state->session); + + return rc; } static int dm_test_tee(struct unit_test_state *uts) @@ -108,6 +204,12 @@ static int dm_test_tee(struct unit_test_state *uts) struct test_tee_vars vars = { NULL, NULL }; int rc = test_tee(uts, &vars); + if (rc) + goto out; + + if (IS_ENABLED(CONFIG_OPTEE_TA_RPC_TEST)) + rc = test_tee_rpc(uts); +out: /* In case test_tee() asserts these may still remain allocated */ tee_shm_free(vars.reg_shm); tee_shm_free(vars.alloc_shm); diff --git a/test/py/requirements.txt b/test/py/requirements.txt index cf251186f4..926bccad69 100644 --- a/test/py/requirements.txt +++ b/test/py/requirements.txt @@ -10,6 +10,8 @@ packaging==19.2 pbr==5.4.3 pluggy==0.13.0 py==1.8.0 +pyelftools==0.27 +pygit2==1.4.0 pyparsing==2.4.2 pytest==5.2.1 python-mimeparse==1.6.0