[U-Boot] [PATCH 4/6] drivers: net: add Felix DSA switch driver

Michael Walle michael at walle.cc
Sun Nov 24 00:05:57 UTC 2019


Am 2019-11-22 02:36, schrieb Alex Marginean:
> This driver is used for the Ethernet switch integrated into LS1028A 
> NXP.
> Felix on LS1028A has 4 front panel ports and two internal ports, I/O
> to/from the switch is done through an ENETC Ethernet interface.
> The 4 front panel ports are available as Ethernet interfaces and can be
> used with the typical network commands like tftp.
> 
> Signed-off-by: Alex Marginean <alexandru.marginean at nxp.com>
> ---
>  drivers/net/fsl_enetc.h                 |   5 +
>  drivers/net/mscc_eswitch/Kconfig        |   8 +
>  drivers/net/mscc_eswitch/Makefile       |   1 +
>  drivers/net/mscc_eswitch/felix_switch.c | 453 ++++++++++++++++++++++++
>  4 files changed, 467 insertions(+)
>  create mode 100644 drivers/net/mscc_eswitch/felix_switch.c
> 
> diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h
> index 9a36cdad80..29e7781b5e 100644
> --- a/drivers/net/fsl_enetc.h
> +++ b/drivers/net/fsl_enetc.h
> @@ -200,6 +200,11 @@ struct enetc_priv {
>  /* PCS replicator block for USXGMII */
>  #define ENETC_PCS_DEVAD_REPL		0x1f
> 
> +#define ENETC_PCS_REPL_LINK_TIMER_1	0x12
> +#define  ENETC_PCS_REPL_LINK_TIMER_1_DEF	0x0003
> +#define ENETC_PCS_REPL_LINK_TIMER_2	0x13
> +#define  ENETC_PCS_REPL_LINK_TIMER_2_DEF	0x06a0
> +
>  /* ENETC external MDIO registers */
>  #define ENETC_MDIO_BASE		0x1c00
>  #define ENETC_MDIO_CFG		0x00
> diff --git a/drivers/net/mscc_eswitch/Kconfig 
> b/drivers/net/mscc_eswitch/Kconfig
> index 80dd22f98b..11fb08edaa 100644
> --- a/drivers/net/mscc_eswitch/Kconfig
> +++ b/drivers/net/mscc_eswitch/Kconfig
> @@ -36,3 +36,11 @@ config MSCC_SERVAL_SWITCH
>  	select PHYLIB
>  	help
>  	  This driver supports the Serval network switch device.
> +
> +config MSCC_FELIX_SWITCH
> +	bool "Felix switch driver"
> +	depends on DM_DSA && DM_PCI
> +	select FSL_ENETC
> +	help
> +	  This driver supports the Ethernet switch integrated in LS1028A NXP
> +	  SoC.
> diff --git a/drivers/net/mscc_eswitch/Makefile
> b/drivers/net/mscc_eswitch/Makefile
> index d583fe9fc4..22342ed114 100644
> --- a/drivers/net/mscc_eswitch/Makefile
> +++ b/drivers/net/mscc_eswitch/Makefile
> @@ -4,3 +4,4 @@ obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o
> mscc_xfer.o mscc_mac_table.o m
>  obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o mscc_miim.o
>  obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o 
> mscc_miim.o
>  obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o
> mscc_mac_table.o mscc_miim.o
> +obj-$(CONFIG_MSCC_FELIX_SWITCH) += felix_switch.o
> diff --git a/drivers/net/mscc_eswitch/felix_switch.c
> b/drivers/net/mscc_eswitch/felix_switch.c
> new file mode 100644
> index 0000000000..8df0e8e4b7
> --- /dev/null
> +++ b/drivers/net/mscc_eswitch/felix_switch.c
> @@ -0,0 +1,453 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Felix Ethernet switch driver
> + * Copyright 2018-2019 NXP
> + */
> +
> +/*
> + * This driver is used for the Ethernet switch integrated into LS1028A 
> NXP.
> + * 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 <common.h>
> +#include <dsa.h>
> +#include <asm/io.h>
> +#include <pci.h>
> +#include <miiphy.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;
> +	struct udevice *port[FELIX_PORT_COUNT];
> +};
> +
> +/* 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 int felix_init_sgmii(struct udevice *dev, int port, int 
> if_type)
> +{
> +	struct felix_priv *priv = dev_get_priv(dev);
> +	bool is2500 = false;
> +	u16 reg;
> +
> +	/* set up PCS lane address */
> +	out_le32(FELIX_SERDES_SGMIICR1(port), FELIX_SERDES_SGMIICR1_SGPCS |
> +		 FELIX_SERDES_SGMIICR1_MDEV(port));
> +
> +	if (!priv->imdio.priv)
> +		return 0;
> +
> +	if (if_type == PHY_INTERFACE_MODE_SGMII_2500)
> +		is2500 = true;
> +
> +	/*
> +	 * 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 |= is2500 ? ENETC_PCS_IF_MODE_SPEED_1G : 
> ENETC_PCS_IF_MODE_SGMII_AN;
> +	felix_mdio_write(&priv->imdio, port, MDIO_DEVAD_NONE,
> +			 ENETC_PCS_IF_MODE, reg);
> +
> +	/* Dev ability - SGMII */
> +	felix_mdio_write(&priv->imdio, port, MDIO_DEVAD_NONE,
> +			 ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SGMII);
> +
> +	/* Adjust link timer for SGMII */
> +	felix_mdio_write(&priv->imdio, port, MDIO_DEVAD_NONE,
> +			 ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL);
> +	felix_mdio_write(&priv->imdio, port, MDIO_DEVAD_NONE,
> +			 ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL);
> +
> +	reg = ENETC_PCS_CR_DEF_VAL;
> +	reg |= is2500 ? ENETC_PCS_CR_RST : ENETC_PCS_CR_RESET_AN;
> +	/* restart PCS AN */
> +	felix_mdio_write(&priv->imdio, port, MDIO_DEVAD_NONE,
> +			 ENETC_PCS_CR, reg);
> +
> +	return 0;
> +}
> +
> +/* set up MAC and serdes for (Q)SXGMII */
> +static int felix_init_sxgmii(struct udevice *dev, int port)
> +{
> +	struct felix_priv *priv = dev_get_priv(dev);
> +	int to = 1000;
> +
> +	/* set up transit equalization control on serdes lane */
> +	out_le32(FELIX_SERDES_LNATECR0(1), FELIX_SERDES_LNATECR0_ADPT_EQ);
> +
> +	if (!priv->imdio.priv)
> +		return 0;
> +
> +	/*reset lane */
> +	felix_mdio_write(&priv->imdio, port, MDIO_MMD_PCS, FELIX_PCS_CTRL,
> +			 FELIX_PCS_CTRL_RST);
> +	while (felix_mdio_read(&priv->imdio, port, MDIO_MMD_PCS,
> +			       FELIX_PCS_CTRL) & FELIX_PCS_CTRL_RST &&
> +			--to) {
> +		mdelay(10);
> +	}
> +	if (felix_mdio_read(&priv->imdio, port, MDIO_MMD_PCS,
> +			    FELIX_PCS_CTRL) & FELIX_PCS_CTRL_RST)
> +		dev_dbg(port, "PCS reset time-out\n");
> +
> +	/* Dev ability - SXGMII */
> +	felix_mdio_write(&priv->imdio, port, ENETC_PCS_DEVAD_REPL,
> +			 ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII);
> +
> +	/* Restart PCS AN */
> +	felix_mdio_write(&priv->imdio, port, ENETC_PCS_DEVAD_REPL,
> +			 ENETC_PCS_CR,
> +			 ENETC_PCS_CR_RST | ENETC_PCS_CR_RESET_AN);
> +	felix_mdio_write(&priv->imdio, port, ENETC_PCS_DEVAD_REPL,
> +			 ENETC_PCS_REPL_LINK_TIMER_1,
> +			 ENETC_PCS_REPL_LINK_TIMER_1_DEF);
> +	felix_mdio_write(&priv->imdio, port, 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 dsa_perdev_platdata *platdata = dev->platdata;
> +	struct felix_priv *priv = dev_get_priv(dev);
> +	const char *if_str;
> +	int if_type;
> +
> +	if_type = PHY_INTERFACE_MODE_NONE;
> +
> +	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);
> +
> +	if_str = ofnode_read_string(platdata->port[port].node, "phy-mode");
> +	if (if_str)
> +		if_type = phy_get_interface_by_name(if_str);
> +	else
> +		dev_dbg(port,
> +			"phy-mode property not found, defaulting to NONE\n");
> +	if (if_type < 0)
> +		if_type = PHY_INTERFACE_MODE_NONE;
> +
> +	switch (if_type) {
> +	case PHY_INTERFACE_MODE_SGMII:
> +	case PHY_INTERFACE_MODE_SGMII_2500:
> +	case PHY_INTERFACE_MODE_QSGMII:
> +		felix_init_sgmii(dev, port, if_type);
> +		break;
> +	case PHY_INTERFACE_MODE_XGMII:
> +	case PHY_INTERFACE_MODE_XFI:
> +	case PHY_INTERFACE_MODE_USXGMII:
> +		felix_init_sxgmii(dev, port);
> +		break;
> +	}
> +}
> +
> +void felix_init(struct udevice *dev)
> +{
> +	struct dsa_perdev_platdata *platdata = dev->platdata;
> +	struct felix_priv *priv = dev_get_priv(dev);
> +	int supported, to = 100, port;
> +	void *base = priv->regs_base;
> +	struct phy_device *phy;
> +
> +	dev_dbg(dev, "trying to set up L2 switch\n");
> +
> +	/* 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 
> &&
> +	       --to)
> +		udelay(10);
> +	if (in_le32(base + FELIX_SYS_RAM_CTRL) & FELIX_SYS_RAM_CTRL_INIT)
> +		dev_dbg(dev, "Time-out 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);
> +
> +	supported = PHY_GBIT_FEATURES | SUPPORTED_2500baseX_Full;
> +
> +	for (port = 0; port < FELIX_PORT_COUNT; port++) {
> +		/* 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);
> +
> +		felix_start_pcs(dev, port);
> +
> +		phy = platdata->port[port].phy;
> +		if (phy) {
> +			phy->supported &= supported;
> +			phy->advertising &= supported;
> +			phy_config(phy);
> +		}
> +	}
> +
> +	/* set up CPU port */
> +	out_le32(base + FELIX_QSYS_SYSTEM_EXT_CPU_CFG,
> +		 FELIX_QSYS_SYSTEM_EXT_CPU_PORT(platdata->cpu_port));
> +	out_le32(base + FELIX_SYS_SYSTEM_PORT_MODE(platdata->cpu_port),
> +		 FELIX_SYS_SYSTEM_PORT_MODE_CPU);
> +}
> +
> +static int felix_bind(struct udevice *dev)
> +{
> +	struct dsa_perdev_platdata *pdata = dev->platdata;
> +
> +	pdata->num_ports = FELIX_PORT_COUNT;
> +	pdata->headroom = FELIX_DSA_TAG_LEN;
> +
> +	return 0;
> +}
> +
> +/*
> + * 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->node) && !ofnode_is_available(dev->node)) {
> +		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_dbg(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_dbg(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);
> +
> +	/* set up registers */
> +	felix_init(dev);
> +
> +	return 0;
> +}
> +
> +static int felix_port_enable(struct udevice *dev, int port,
> +			     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(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));
> +

if (phy)
> +	phy_startup(phy);
> +	return 0;
> +}
> +
> +static void felix_port_disable(struct udevice *dev, int port,
> +			       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(port), 0);
> +
> +	out_le32(base + FELIX_QSYS_SYSTEM_SW_PORT_MODE(port),
> +		 FELIX_QSYS_SYSTEM_SW_PORT_LOSSY |
> +		 FELIX_QSYS_SYSTEM_SW_PORT_SCH(1));
> +
> +	/*
> +	 * we don't call phy_shutdown here to avoind 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 port, 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(port);
> +
> +	return 0;
> +}
> +
> +static int felix_rcv(struct udevice *dev, int *port, void *packet, int 
> length)
> +{
> +	struct felix_dsa_tag *tag = packet;
> +
> +	if (tag->magic != FELIX_DSA_TAG_MAGIC)
> +		return -EINVAL;
> +
> +	*port = 
> 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,
> +	.bind	= felix_bind,
> +	.probe	= felix_probe,
> +	.ops    = &felix_dsa_ops,
> +	.priv_auto_alloc_size = sizeof(struct felix_priv),
> +	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
> +};
> +
> +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);


More information about the U-Boot mailing list