Merge branch '2021-02-16-assorted-improvements'

- DSA switch support (Layerscape platforms)
- IOMUX cleanup / fixes
- i2c OP-TEE trampoline driver
This commit is contained in:
Tom Rini 2021-02-16 15:14:34 -05:00
commit 496f49464d
38 changed files with 1834 additions and 105 deletions

View File

@ -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>;
};
};

View File

@ -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";
};
};

View File

@ -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();

View File

@ -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 */

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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/

View File

@ -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

View File

@ -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

90
drivers/tee/optee/i2c.c Normal file
View File

@ -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;
}

View File

@ -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 */

View File

@ -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
*/

View File

@ -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 */

View File

@ -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;
}

View File

@ -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,

View File

@ -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 */

View File

@ -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 */

View File

@ -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

165
include/net/dsa.h Normal file
View File

@ -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__ */

View File

@ -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
/**

View File

@ -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".

View File

@ -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 */

View File

@ -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

478
net/dsa-uclass.c Normal file
View File

@ -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,
};

View File

@ -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);

View File

@ -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