summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2021-02-16 15:14:34 -0500
committerTom Rini <trini@konsulko.com>2021-02-16 15:14:34 -0500
commit496f49464d90b564da5f1a2f4eecb5553e01edf9 (patch)
treee084452c2b7e9f2d0ede789aa840e9e3ae2338ed
parent767582cd4c755c52bce3e1813bc462f37047cb5c (diff)
parentd003434525767145d152b54573ef8e030b63a6fe (diff)
downloadu-boot-496f49464d90b564da5f1a2f4eecb5553e01edf9.tar.gz
Merge branch '2021-02-16-assorted-improvements'
- DSA switch support (Layerscape platforms) - IOMUX cleanup / fixes - i2c OP-TEE trampoline driver
-rw-r--r--arch/arm/dts/fsl-ls1028a-rdb.dts64
-rw-r--r--arch/arm/dts/fsl-ls1028a.dtsi56
-rw-r--r--common/console.c59
-rw-r--r--common/iomux.c75
-rw-r--r--common/stdio.c57
-rw-r--r--common/usb_kbd.c16
-rw-r--r--configs/kontron_sl28_defconfig3
-rw-r--r--configs/ls1028aqds_tfa_SECURE_BOOT_defconfig3
-rw-r--r--configs/ls1028aqds_tfa_defconfig3
-rw-r--r--configs/ls1028ardb_tfa_SECURE_BOOT_defconfig3
-rw-r--r--configs/ls1028ardb_tfa_defconfig3
-rw-r--r--drivers/net/Kconfig15
-rw-r--r--drivers/net/fsl_enetc.h5
-rw-r--r--drivers/net/mscc_eswitch/Kconfig8
-rw-r--r--drivers/net/mscc_eswitch/Makefile1
-rw-r--r--drivers/net/mscc_eswitch/felix_switch.c414
-rw-r--r--drivers/net/phy/fixed.c3
-rw-r--r--drivers/net/phy/phy.c31
-rw-r--r--drivers/tee/Makefile2
-rw-r--r--drivers/tee/optee/Kconfig9
-rw-r--r--drivers/tee/optee/Makefile1
-rw-r--r--drivers/tee/optee/i2c.c90
-rw-r--r--drivers/tee/optee/optee_msg.h21
-rw-r--r--drivers/tee/optee/optee_msg_supplicant.h5
-rw-r--r--drivers/tee/optee/optee_private.h17
-rw-r--r--drivers/tee/optee/supplicant.c3
-rw-r--r--drivers/tee/sandbox.c142
-rw-r--r--include/dm/uclass-id.h1
-rw-r--r--include/iomux.h7
-rw-r--r--include/net.h6
-rw-r--r--include/net/dsa.h165
-rw-r--r--include/phy.h21
-rw-r--r--include/stdio_dev.h3
-rw-r--r--include/tee/optee_ta_rpc_test.h30
-rw-r--r--net/Makefile1
-rw-r--r--net/dsa-uclass.c478
-rw-r--r--test/dm/tee.c116
-rw-r--r--test/py/requirements.txt2
38 files changed, 1834 insertions, 105 deletions
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 <dm/device_compat.h>
+#include <linux/delay.h>
+#include <net/dsa.h>
+#include <asm/io.h>
+#include <miiphy.h>
+#include <pci.h>
+
+/* 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 <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <tee.h>
+#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
@@ -148,6 +148,11 @@
#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
*/
#define OPTEE_MSG_RPC_CMD_SQL_FS_RESERVED 8
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 <sandboxtee.h>
#include <tee.h>
#include <tee/optee_ta_avb.h>
+#include <tee/optee_ta_rpc_test.h>
+
+#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 <phy.h>
+#include <net.h>
+
+/**
+ * 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 <net/dsa.h>
+#include <dm/lists.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <linux/bitmap.h>
+#include <miiphy.h>
+
+#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", &ethernet))
+ 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", &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 <test/test.h>
#include <test/ut.h>
#include <tee/optee_ta_avb.h>
+#include <tee/optee_ta_rpc_test.h>
-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