415 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			415 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
// 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);
 |