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:
commit
496f49464d
38 changed files with 1834 additions and 105 deletions
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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,14 +252,13 @@ 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)
|
||||
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();
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
414
drivers/net/mscc_eswitch/felix_switch.c
Normal file
414
drivers/net/mscc_eswitch/felix_switch.c
Normal 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);
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
90
drivers/tee/optee/i2c.c
Normal 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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -216,6 +252,98 @@ static u32 ta_avb_invoke_func(struct udevice *dev, u32 func, uint num_params,
|
|||
}
|
||||
#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
|
||||
{ .uuid = TA_AVB_UUID,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
165
include/net/dsa.h
Normal 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__ */
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -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".
|
||||
|
|
30
include/tee/optee_ta_rpc_test.h
Normal file
30
include/tee/optee_ta_rpc_test.h
Normal 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 */
|
|
@ -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
478
net/dsa-uclass.c
Normal 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", ðernet))
|
||||
continue;
|
||||
|
||||
pdata->master_node = ofnode_get_by_phandle(ethernet);
|
||||
pdata->cpu_port_node = pnode;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ofnode_valid(pdata->master_node)) {
|
||||
dev_err(dev, "master eth node missing!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ofnode_read_u32(pnode, "reg", &pdata->cpu_port)) {
|
||||
dev_err(dev, "CPU port node not valid!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "master node %s on port %d\n",
|
||||
ofnode_get_name(pdata->master_node), pdata->cpu_port);
|
||||
|
||||
for (i = 0; i < pdata->num_ports; i++) {
|
||||
char name[DSA_PORT_NAME_LENGTH];
|
||||
struct udevice *pdev;
|
||||
|
||||
/*
|
||||
* If this is the CPU port don't register it as an ETH device,
|
||||
* we skip it on purpose since I/O to/from it from the CPU
|
||||
* isn't useful.
|
||||
*/
|
||||
if (i == pdata->cpu_port)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Set up default port names. If present, DT port labels
|
||||
* will override the default port names.
|
||||
*/
|
||||
snprintf(name, DSA_PORT_NAME_LENGTH, "%s@%d", dev->name, i);
|
||||
|
||||
ofnode_for_each_subnode(pnode, node) {
|
||||
u32 reg;
|
||||
|
||||
if (ofnode_read_u32(pnode, "reg", ®))
|
||||
continue;
|
||||
|
||||
if (reg == i)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* skip registration if port id not found or if the port
|
||||
* is explicitly disabled in DT
|
||||
*/
|
||||
if (!ofnode_valid(pnode) || !ofnode_is_available(pnode))
|
||||
continue;
|
||||
|
||||
err = device_bind_driver_to_node(dev, DSA_PORT_CHILD_DRV_NAME,
|
||||
name, pnode, &pdev);
|
||||
if (pdev) {
|
||||
struct dsa_port_pdata *port_pdata;
|
||||
|
||||
port_pdata = dev_get_parent_plat(pdev);
|
||||
strncpy(port_pdata->name, name, DSA_PORT_NAME_LENGTH);
|
||||
pdev->name = port_pdata->name;
|
||||
}
|
||||
|
||||
/* try to bind all ports but keep 1st error */
|
||||
if (err && !first_err)
|
||||
first_err = err;
|
||||
}
|
||||
|
||||
if (first_err)
|
||||
return first_err;
|
||||
|
||||
dev_dbg(dev, "DSA ports successfully bound\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the uclass per device internal state structure (priv).
|
||||
* TODO: pick up references to other switch devices here, if we're cascaded.
|
||||
*/
|
||||
static int dsa_pre_probe(struct udevice *dev)
|
||||
{
|
||||
struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
|
||||
struct dsa_priv *priv = dev_get_uclass_priv(dev);
|
||||
|
||||
if (!pdata || !priv)
|
||||
return -ENODEV;
|
||||
|
||||
priv->num_ports = pdata->num_ports;
|
||||
priv->cpu_port = pdata->cpu_port;
|
||||
priv->cpu_port_fixed_phy = fixed_phy_create(pdata->cpu_port_node);
|
||||
if (!priv->cpu_port_fixed_phy) {
|
||||
dev_err(dev, "Failed to register fixed-link for CPU port\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
uclass_find_device_by_ofnode(UCLASS_ETH, pdata->master_node,
|
||||
&priv->master_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(dsa) = {
|
||||
.id = UCLASS_DSA,
|
||||
.name = "dsa",
|
||||
.post_bind = dsa_post_bind,
|
||||
.pre_probe = dsa_pre_probe,
|
||||
.per_device_auto = sizeof(struct dsa_priv),
|
||||
.per_device_plat_auto = sizeof(struct dsa_pdata),
|
||||
.per_child_plat_auto = sizeof(struct dsa_port_pdata),
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
};
|
116
test/dm/tee.c
116
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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue