[PATCH v4 1/6] net: introduce DSA class for Ethernet switches
Priyanka Jain (OSS)
priyanka.jain at oss.nxp.com
Wed Jul 22 11:15:27 CEST 2020
>-----Original Message-----
>From: U-Boot <u-boot-bounces at lists.denx.de> On Behalf Of Claudiu Manoil
>Sent: Tuesday, May 5, 2020 11:13 PM
>To: u-boot at lists.denx.de
>Cc: joe.hershberger at ni.com; Alexandru Marginean
><alexandru.marginean at nxp.com>; Vladimir Oltean
><vladimir.oltean at nxp.com>
>Subject: [PATCH v4 1/6] net: introduce DSA class for Ethernet switches
>
>From: Alex Marginean <alexandru.marginean at nxp.com>
>
>DSA stands for Distributed Switch Architecture and it covers switches that are
>connected to the CPU through an Ethernet link and generally use frame tags to
>pass information about the source/destination ports to/from CPU.
>Front panel ports are presented as regular ethernet devices in U-Boot and
>they are expected to support the typical networking commands.
>DSA switches may be cascaded, DSA class code does not currently support
>this.
>
>Signed-off-by: Alex Marginean <alexandru.marginean at nxp.com>
>Signed-off-by: Vladimir Oltean <vladimir.oltean at nxp.com>
>Signed-off-by: Claudiu Manoil <claudiu.manoil at nxp.com>
>---
> drivers/net/Kconfig | 13 ++
> include/dm/uclass-id.h | 1 +
> include/net.h | 6 +
> include/net/dsa.h | 137 ++++++++++++++
> net/Makefile | 1 +
> net/dsa-uclass.c | 395 +++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 553 insertions(+)
> create mode 100644 include/net/dsa.h
> create mode 100644 net/dsa-uclass.c
>
>diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index
>4d1013c984..863314284b 100644
>--- a/drivers/net/Kconfig
>+++ b/drivers/net/Kconfig
>@@ -37,6 +37,19 @@ 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
>+ 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 "master" and "cpu" in DSA terminology.
>+ This is currently implemented in net/dsa-uclass.c
>+
> config MDIO_SANDBOX
> depends on DM_MDIO && SANDBOX
> default y
>diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index
>598f65ea7a..6fc52b72aa 100644
>--- a/include/dm/uclass-id.h
>+++ b/include/dm/uclass-id.h
>@@ -44,6 +44,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_FIRMWARE, /* Firmware */
>diff --git a/include/net.h b/include/net.h index 82500eeb30..155f49196e
>100644
>--- a/include/net.h
>+++ b/include/net.h
>@@ -489,7 +489,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 or tailroom) */
>+#define DSA_MAX_OVR 256
>+#define PKTSIZE_ALIGN (1536 + DSA_MAX_OVR)
>+#endif
>
> /*
> * Maximum receive ring size; that is, the number of packets diff --git
>a/include/net/dsa.h b/include/net/dsa.h new file mode 100644 index
>0000000000..45f1d42e7e
>--- /dev/null
>+++ b/include/net/dsa.h
>@@ -0,0 +1,137 @@
>+/* SPDX-License-Identifier: GPL-2.0+ */
>+/*
>+ * Copyright 2019-2020 NXP
>+ */
>+
>+#ifndef __DSA_H__
>+#define __DSA_H__
>+
>+#include <common.h>
>+#include <dm.h>
>+#include <phy.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:
>+ *
>+ * |---------------------------
>+ * | CPU network device (eth0)|
>+ * ----------------------------
>+ * | <tag added by switch |
>+ * | |
>+ * | |
>+ * | tag added by CPU> |
>+ * |--------------------------------------------|
>+ * | Switch driver |
>+ * |--------------------------------------------|
>+ * || || ||
>+ * |-------| |-------| |-------|
>+ * | sw0p0 | | sw0p1 | | sw0p2 |
>+ * |-------| |-------| |-------|
>+ *
>+ * In U-Boot the intent is to allow access to front panel ports (shown
>+at the
>+ * bottom of the picture) though the master Ethernet port (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 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 headroom and tailroom
>+ * @rcv: Process the DSA tag on reception
>+ * 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 *port, void *packet, int length);
>+};
>+
>+#define dsa_get_ops(dev) ((struct dsa_ops *)(dev)->driver->ops)
>+
>+/**
>+ * struct dsa_port_platdata - DSA port platform data
>+ *
>+ * @dev : Port u-device
>+ * Uclass code sets this field for all ports
>+ * @phy: PHY device associated with this port
>+ * Uclass code sets this field for all ports except CPU port, based on
>+ * DT information. It may be NULL.
>+ * @node: Port DT node, if any. Uclass code sets this field.
>+ * @index: Port index in the DSA switch, set by class code.
>+ * @name: Name of the port Eth device. If a label property is present in the
>+ * port DT node, it is used as name. Drivers can use custom names by
>+ * populating this field, otherwise class code generates a default.
>+ */
>+struct dsa_port_platdata {
>+ struct udevice *dev;
>+ struct phy_device *phy;
>+ ofnode node;
>+ int index;
>+ char name[DSA_PORT_NAME_LENGTH];
>+};
>+
>+/**
>+ * struct dsa_perdev_platdata - Per-device platform data for DSA DM
>+ *
>+ * @num_ports: Number of ports the device has, must be <=
>DSA_MAX_PORTS
>+ * All DSA drivers must set this at _bind
>+ * @headroom: Size, in bytes, of headroom needed for the DSA tag
>+ * All DSA drivers must set this at _bind or _probe
>+ * @tailroom: Size, in bytes, of tailroom needed for the DSA tag
>+ * DSA class code allocates headroom and tailroom on Tx before
>+ * calling DSA driver xmit function
>+ * All DSA drivers must set this at _bind or _probe
>+ * @master_node: DT node of the master Ethernet. DT is optional so this
>may be
>+ * null.
>+ * @master_dev: Ethernet device to be used as master. Uclass code sets this
>+ * based on DT information if present, otherwise drivers must set
>+ * this field in _probe.
>+ * @cpu_port: Index of switch port linked to master Ethernet.
>+ * Uclass code sets this based on DT information if present,
>+ * otherwise drivers must set this field in _bind.
>+ * @port: per-port data
>+ */
>+struct dsa_perdev_platdata {
>+ int num_ports;
>+ int headroom;
>+ int tailroom;
>+
>+ ofnode master_node;
>+ struct udevice *master_dev;
>+ int cpu_port;
>+ struct dsa_port_platdata port[DSA_MAX_PORTS]; };
>+
>+#endif /* __DSA_H__ */
>diff --git a/net/Makefile b/net/Makefile index fef71b940a..32f5a7824e 100644
>--- a/net/Makefile
>+++ b/net/Makefile
>@@ -27,6 +27,7 @@ obj-$(CONFIG_CMD_SNTP) += sntp.o
> obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
> obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fastboot.o
> obj-$(CONFIG_CMD_WOL) += wol.o
>+obj-$(CONFIG_DM_DSA) += dsa-uclass.o
>
> # Disable this warning as it is triggered by:
> # sprintf(buf, index ? "foo%d" : "foo", index) diff --git a/net/dsa-uclass.c
>b/net/dsa-uclass.c new file mode 100644 index 0000000000..32573fe45c
>--- /dev/null
>+++ b/net/dsa-uclass.c
>@@ -0,0 +1,395 @@
>+// SPDX-License-Identifier: GPL-2.0+
>+/*
>+ * Copyright 2019-2020 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 <miiphy.h>
>+
>+#define DSA_PORT_CHILD_DRV_NAME "dsa-port"
>+
>+/* helper that returns the DSA master Ethernet device */ static struct
>+udevice *dsa_port_get_master(struct udevice *pdev) {
>+ struct udevice *dev = dev_get_parent(pdev);
>+ struct dsa_perdev_platdata *platdata = dev_get_platdata(dev);
>+
>+ return platdata->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_perdev_platdata *platdata = dev_get_platdata(dev);
>+ struct udevice *master = dsa_port_get_master(pdev);
>+ struct dsa_port_platdata *ppriv = dev_get_priv(pdev);
>+ struct dsa_ops *ops = dsa_get_ops(dev);
>+ int err;
>+
>+ if (!ppriv || !platdata)
>+ return -EINVAL;
>+
>+ if (!master) {
>+ dev_err(pdev, "DSA master Ethernet device not found!\n");
>+ return -EINVAL;
>+ }
>+
>+ if (ops->port_enable) {
>+ err = ops->port_enable(dev, ppriv->index, ppriv->phy);
>+ if (err)
>+ return err;
>+ err = ops->port_enable(dev, platdata->cpu_port,
>+ platdata->port[platdata->cpu_port].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_perdev_platdata *platdata = dev_get_platdata(dev);
>+ struct udevice *master = dsa_port_get_master(pdev);
>+ struct dsa_port_platdata *ppriv = dev_get_priv(pdev);
>+ struct dsa_ops *ops = dsa_get_ops(dev);
>+
>+ if (!ppriv || !platdata)
>+ return;
>+
>+ if (ops->port_disable) {
>+ ops->port_disable(dev, ppriv->index, ppriv->phy);
>+ ops->port_disable(dev, platdata->cpu_port,
>+ platdata->port[platdata->cpu_port].phy);
>+ }
>+
>+ /*
>+ * 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_perdev_platdata *platdata = dev_get_platdata(dev);
>+ int head = platdata->headroom, tail = platdata->tailroom;
>+ struct udevice *master = dsa_port_get_master(pdev);
>+ struct dsa_port_platdata *ppriv = dev_get_priv(pdev);
>+ struct dsa_ops *ops = dsa_get_ops(dev);
>+ uchar dsa_packet_tmp[PKTSIZE_ALIGN];
>+ 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);
>+
>+ err = ops->xmit(dev, ppriv->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_perdev_platdata *platdata = dev_get_platdata(dev);
>+ struct udevice *master = dsa_port_get_master(pdev);
>+ struct dsa_port_platdata *ppriv = dev_get_priv(pdev);
>+ struct dsa_ops *ops = dsa_get_ops(dev);
>+ int head = platdata->headroom, tail = platdata->tailroom;
>+ 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
>+ * and ctrl-c
>+ */
>+ err = ops->rcv(dev, &port_index, *packetp, length);
>+ if (err || port_index != ppriv->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 dsa_perdev_platdata *platdata = dev_get_platdata(dev);
>+ struct udevice *master = dsa_port_get_master(pdev);
>+
>+ if (!master)
>+ return -EINVAL;
>+
>+ if (eth_get_ops(master)->free_pkt) {
>+ /* return the original pointer and length to master Eth */
>+ packet -= platdata->headroom;
>+ length += platdata->headroom - platdata->tailroom;
>+
>+ return eth_get_ops(master)->free_pkt(master, packet,
>length);
>+ }
>+
>+ return 0;
>+}
>+
>+static int dsa_port_probe(struct udevice *pdev) {
>+ struct udevice *master = dsa_port_get_master(pdev);
>+ unsigned char env_enetaddr[ARP_HLEN];
>+
>+ /* If there is no MAC address in the environment, inherit it
>+ * from the DSA master.
>+ */
>+ eth_env_get_enetaddr_by_index("eth", pdev->seq, env_enetaddr);
>+ if (!is_zero_ethaddr(env_enetaddr))
>+ return 0;
>+
>+ if (master) {
>+ struct eth_pdata *meth = dev_get_platdata(master);
>+ struct eth_pdata *peth = dev_get_platdata(pdev);
>+
>+ memcpy(peth->enetaddr, meth->enetaddr, ARP_HLEN);
>+ eth_env_set_enetaddr_by_index("eth", pdev->seq,
>+ meth->enetaddr);
>+ }
>+
>+ 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,
>+};
>+
>+U_BOOT_DRIVER(dsa_port) = {
>+ .name = DSA_PORT_CHILD_DRV_NAME,
>+ .id = UCLASS_ETH,
>+ .ops = &dsa_port_ops,
>+ .probe = dsa_port_probe,
>+ .platdata_auto_alloc_size = sizeof(struct eth_pdata), };
>+
>+/*
>+ * reads the DT properties of the given DSA port.
>+ * If the return value is != 0 then the port is skipped */ static int
>+dsa_port_parse_dt(struct udevice *dev, int port_index,
>+ ofnode ports_node, bool *is_cpu) {
>+ struct dsa_perdev_platdata *platdata = dev_get_platdata(dev);
>+ struct dsa_port_platdata *port = &platdata->port[port_index];
>+ ofnode temp_node;
>+ u32 ethernet;
>+
>+ /*
>+ * if we don't have a DT we don't do anything here but the port is
>+ * registered normally
>+ */
>+ if (!ofnode_valid(ports_node))
>+ return 0;
>+
>+ ofnode_for_each_subnode(temp_node, ports_node) {
>+ const char *port_label;
>+ u32 reg;
>+
>+ if (ofnode_read_u32(temp_node, "reg", ®) ||
>+ reg != port_index)
>+ continue;
>+
>+ port->node = temp_node;
>+ /* if the port is explicitly disabled in DT skip it */
>+ if (!ofnode_is_available(temp_node))
>+ return -ENODEV;
>+
>+ dev_dbg(dev, "port %d node %s\n", port->index,
>+ ofnode_get_name(port->node));
>+
>+ /* Use 'label' if present in DT */
>+ port_label = ofnode_read_string(port->node, "label");
>+ if (port_label)
>+ strncpy(port->name, port_label,
>DSA_PORT_NAME_LENGTH);
>+
>+ *is_cpu = !ofnode_read_u32(port->node, "ethernet",
>+ ðernet);
>+
>+ if (*is_cpu) {
>+ platdata->master_node =
>+ ofnode_get_by_phandle(ethernet);
>+ platdata->cpu_port = port_index;
>+
>+ dev_dbg(dev, "master node %s on port %d\n",
>+ ofnode_get_name(platdata->master_node),
>+ port_index);
>+ }
>+ break;
>+ }
>+
>+ return 0;
>+}
>+
>+/**
>+ * This function mostly deals with pulling information out of the
>+device tree
>+ * into the platdata 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 dm_dsa_post_bind(struct udevice *dev) {
>+ struct dsa_perdev_platdata *platdata = dev_get_platdata(dev);
>+ ofnode ports_node = ofnode_null();
>+ int first_err = 0, err = 0, i;
>+
>+ if (!platdata) {
>+ dev_err(dev, "missing plaform data\n");
>+ return -EINVAL;
>+ }
>+
>+ if (platdata->num_ports <= 0 || platdata->num_ports >
>DSA_MAX_PORTS) {
>+ dev_err(dev, "unexpected num_ports value (%d)\n",
>+ platdata->num_ports);
>+ return -EINVAL;
>+ }
>+
>+ platdata->master_node = ofnode_null();
>+
>+ if (!ofnode_valid(dev->node)) {
>+ dev_dbg(dev, "Device doesn't have a valid DT node!\n");
>+ } else {
>+ ports_node = ofnode_find_subnode(dev->node, "ports");
>+ if (!ofnode_valid(ports_node))
>+ dev_dbg(dev,
>+ "ports node is missing under DSA device!\n");
>+ }
>+
>+ for (i = 0; i < platdata->num_ports; i++) {
>+ struct dsa_port_platdata *port = &platdata->port[i];
>+ bool skip_port, is_cpu = false;
>+
>+ port->index = i;
>+
>+ /*
>+ * If the driver set up port names in _bind use those,
>otherwise
>+ * use default ones.
>+ * If present, DT label is used as name and overrides anything
>+ * we may have here.
>+ */
>+ if (!strlen(port->name))
>+ snprintf(port->name, DSA_PORT_NAME_LENGTH,
>"%s@%d",
>+ dev->name, i);
>+
>+ skip_port = !!dsa_port_parse_dt(dev, i, ports_node, &is_cpu);
>+
>+ /*
>+ * 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
>+ * TODO: cpu port may have a PHY and we don't handle that
>yet.
>+ */
>+ if (is_cpu || skip_port)
>+ continue;
>+
>+ err = device_bind_driver_to_node(dev,
>DSA_PORT_CHILD_DRV_NAME,
>+ port->name, port->node,
>+ &port->dev);
>+
>+ /* try to bind all ports but keep 1st error */
>+ if (err && !first_err)
>+ first_err = err;
>+ }
>+
>+ if (!ofnode_valid(platdata->master_node))
>+ dev_dbg(dev, "DSA master Eth device is missing!\n");
>+
>+ return first_err;
>+}
>+
>+/**
>+ * This function deals with additional devices around the switch as
>+these should
>+ * have been bound to drivers by now.
>+ * TODO: pick up references to other switch devices here, if we're cascaded.
>+ */
>+static int dm_dsa_pre_probe(struct udevice *dev) {
>+ struct dsa_perdev_platdata *platdata = dev_get_platdata(dev);
>+ int i;
>+
>+ if (!platdata)
>+ return -EINVAL;
>+
>+ if (ofnode_valid(platdata->master_node))
>+ uclass_find_device_by_ofnode(UCLASS_ETH, platdata-
>>master_node,
>+ &platdata->master_dev);
>+
>+ for (i = 0; i < platdata->num_ports; i++) {
>+ struct dsa_port_platdata *port = &platdata->port[i];
>+
>+ /* non-cpu ports only */
>+ if (!port->dev)
>+ continue;
>+
>+ port->dev->priv = port;
>+ port->phy = dm_eth_phy_connect(port->dev);
>+ }
>+
>+ return 0;
>+}
>+
>+UCLASS_DRIVER(dsa) = {
>+ .id = UCLASS_DSA,
>+ .name = "dsa",
>+ .post_bind = dm_dsa_post_bind,
>+ .pre_probe = dm_dsa_pre_probe,
>+ .per_device_platdata_auto_alloc_size =
>+ sizeof(struct dsa_perdev_platdata),
>+};
>--
>2.17.1
Please help to rebase the patch series on top of master branch.
Regards
Priyanka
More information about the U-Boot
mailing list