
On Tue, Mar 29, 2022 at 03:52:39PM -0700, Tim Harvey wrote:
Add a DSA driver for the MV88E61xx compatible GbE Ethernet switches.
Signed-off-by: Tim Harvey tharvey@gateworks.com
drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/mv88e61xx.c | 982 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 990 insertions(+) create mode 100644 drivers/net/mv88e61xx.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index a6171a7c7ffd..fc018f5ba47f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -428,6 +428,13 @@ config LPC32XX_ETH depends on ARCH_LPC32XX default y
+config MV88E61XX
- bool "Marvell MV88E61xx GbE switch DSA driver"
- depends on DM_DSA && DM_MDIO
- help
This driver implements a DSA switch driver for the MV88E61xx family
of GbE switches using the MDIO interface
config MVGBE bool "Marvell Orion5x/Kirkwood network interface support" depends on ARCH_KIRKWOOD || ARCH_ORION5X diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a6d0c23f02d3..11ada73658e9 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o +obj-$(CONFIG_MV88E61XX) += mv88e61xx.o obj-$(CONFIG_MVGBE) += mvgbe.o obj-$(CONFIG_MVMDIO) += mvmdio.o obj-$(CONFIG_MVNETA) += mvneta.o diff --git a/drivers/net/mv88e61xx.c b/drivers/net/mv88e61xx.c new file mode 100644 index 000000000000..9dd7a0c7f42e --- /dev/null +++ b/drivers/net/mv88e61xx.c @@ -0,0 +1,982 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022
- Gateworks Corporation <www.gateworks.com>
- Tim Harvey tharvey@gateworks.com
- (C) Copyright 2015
- Elecsys Corporation <www.elecsyscorp.com>
- Kevin Smith kevin.smith@elecsyscorp.com
- Original driver:
- (C) Copyright 2009
- Marvell Semiconductor <www.marvell.com>
- Prafulla Wadaskar prafulla@marvell.com
- */
+/*
- DSA driver for mv88e61xx ethernet switches.
- This driver configures the mv88e61xx for basic use as a DSA switch.
- This driver was adapted from drivers/net/phy/mv88e61xx and tested
- on the mv88e6176 via an SGMII interface.
- */
+#include <bitfield.h> +#include <common.h> +#include <miiphy.h> +#include <dm/device.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/of_extra.h> +#include <linux/delay.h> +#include <net/dsa.h>
+#define PORT_MASK(port_count) ((1 << (port_count)) - 1)
Not used.
+/* Device addresses */ +#define DEVADDR_PHY(p) (p) +#define DEVADDR_SERDES 0x0F
+/* SMI indirection registers for multichip addressing mode */ +#define SMI_CMD_REG 0x00 +#define SMI_DATA_REG 0x01
+/* Global registers */ +#define GLOBAL1_STATUS 0x00 +#define GLOBAL1_CTRL 0x04 +#define GLOBAL1_MON_CTRL 0x1A
+/* Global 2 registers */ +#define GLOBAL2_REG_PHY_CMD 0x18 +#define GLOBAL2_REG_PHY_DATA 0x19 +#define GLOBAL2_REG_SCRATCH 0x1A
+/* Port registers */ +#define PORT_REG_STATUS 0x00 +#define PORT_REG_PHYS_CTRL 0x01 +#define PORT_REG_SWITCH_ID 0x03 +#define PORT_REG_CTRL 0x04 +#define PORT_REG_VLAN_MAP 0x06 +#define PORT_REG_VLAN_ID 0x07 +#define PORT_REG_LED_CTRL 0x16
+/* Phy registers */ +#define PHY_REG_CTRL1 0x10 +#define PHY_REG_STATUS1 0x11 +#define PHY_REG_PAGE 0x16
+/* Serdes registers */ +#define SERDES_REG_CTRL_1 0x10
+/* Phy page numbers */ +#define PHY_PAGE_COPPER 0 +#define PHY_PAGE_SERDES 1
+/* Register fields */ +#define GLOBAL1_CTRL_SWRESET BIT(15)
+#define GLOBAL1_MON_CTRL_CPUDEST_SHIFT 4 +#define GLOBAL1_MON_CTRL_CPUDEST_WIDTH 4
+#define PORT_REG_STATUS_SPEED_SHIFT 8 +#define PORT_REG_STATUS_SPEED_10 0 +#define PORT_REG_STATUS_SPEED_100 1 +#define PORT_REG_STATUS_SPEED_1000 2
+#define PORT_REG_STATUS_CMODE_MASK 0xF +#define PORT_REG_STATUS_CMODE_100BASE_X 0x8 +#define PORT_REG_STATUS_CMODE_1000BASE_X 0x9 +#define PORT_REG_STATUS_CMODE_SGMII 0xa
+#define PORT_REG_PHYS_CTRL_RGMII_DELAY_RXCLK BIT(15) +#define PORT_REG_PHYS_CTRL_RGMII_DELAY_TXCLK BIT(14) +#define PORT_REG_PHYS_CTRL_PCS_AN_EN BIT(10) +#define PORT_REG_PHYS_CTRL_PCS_AN_RST BIT(9) +#define PORT_REG_PHYS_CTRL_FC_VALUE BIT(7) +#define PORT_REG_PHYS_CTRL_FC_FORCE BIT(6) +#define PORT_REG_PHYS_CTRL_LINK_VALUE BIT(5) +#define PORT_REG_PHYS_CTRL_LINK_FORCE BIT(4) +#define PORT_REG_PHYS_CTRL_DUPLEX_VALUE BIT(3) +#define PORT_REG_PHYS_CTRL_DUPLEX_FORCE BIT(2) +#define PORT_REG_PHYS_CTRL_SPD1000 BIT(1) +#define PORT_REG_PHYS_CTRL_SPD100 BIT(0) +#define PORT_REG_PHYS_CTRL_SPD_MASK (BIT(1) | BIT(0))
+#define PORT_REG_CTRL_PSTATE_SHIFT 0 +#define PORT_REG_CTRL_PSTATE_WIDTH 2
+#define PORT_REG_VLAN_ID_DEF_VID_SHIFT 0 +#define PORT_REG_VLAN_ID_DEF_VID_WIDTH 12
+#define PORT_REG_VLAN_MAP_TABLE_SHIFT 0 +#define PORT_REG_VLAN_MAP_TABLE_WIDTH 11
+#define SERDES_REG_CTRL_1_FORCE_LINK BIT(10)
+/* Field values */ +#define PORT_REG_CTRL_PSTATE_DISABLED 0 +#define PORT_REG_CTRL_PSTATE_FORWARD 3
+#define PHY_REG_CTRL1_ENERGY_DET_OFF 0 +#define PHY_REG_CTRL1_ENERGY_DET_SENSE_PULSE 1 +#define PHY_REG_CTRL1_ENERGY_DET_SENSE_ONLY 2 +#define PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT 3
+/* PHY Status Register */ +#define PHY_REG_STATUS1_SPEED 0xc000 +#define PHY_REG_STATUS1_GBIT 0x8000 +#define PHY_REG_STATUS1_100 0x4000 +#define PHY_REG_STATUS1_DUPLEX 0x2000 +#define PHY_REG_STATUS1_SPDDONE 0x0800 +#define PHY_REG_STATUS1_LINK 0x0400 +#define PHY_REG_STATUS1_ENERGY 0x0010
+/*
- Macros for building commands for indirect addressing modes. These are valid
- for both the indirect multichip addressing mode and the PHY indirection
- required for the writes to any PHY register.
- */
+#define SMI_BUSY BIT(15) +#define SMI_CMD_CLAUSE_22 BIT(12) +#define SMI_CMD_CLAUSE_22_OP_READ (2 << 10) +#define SMI_CMD_CLAUSE_22_OP_WRITE (1 << 10)
+#define SMI_CMD_READ (SMI_BUSY | SMI_CMD_CLAUSE_22 | \
SMI_CMD_CLAUSE_22_OP_READ)
+#define SMI_CMD_WRITE (SMI_BUSY | SMI_CMD_CLAUSE_22 | \
SMI_CMD_CLAUSE_22_OP_WRITE)
+#define SMI_CMD_ADDR_SHIFT 5 +#define SMI_CMD_ADDR_WIDTH 5 +#define SMI_CMD_REG_SHIFT 0 +#define SMI_CMD_REG_WIDTH 5
+/* Defines for Scratch and Misc Registers */ +#define SCRATCH_UPDATE BIT(15) +#define SCRATCH_ADDR_SHIFT 8 +#define SCRATCH_ADDR_WIDTH 7
+/* Scratch registers */ +#define SCRATCH_ADDR_GPIO0DIR 0x62 /* GPIO[7:0] direction 1=input */ +#define SCRATCH_ADDR_GPIO1DIR 0x63 /* GPIO[14:8] direction 1=input */ +#define SCRATCH_ADDR_GPIO0DATA 0x64 /* GPIO[7:0] data */ +#define SCRATCH_ADDR_GPIO1DATA 0x65 /* GPIO[14:8] data */ +#define SCRATCH_ADDR_GPIO0CTRL 0x68 /* GPIO[1:0] control */ +#define SCRATCH_ADDR_GPIO1CTRL 0x69 /* GPIO[3:2] control */
+/* ID register values for different switch models */ +#define PORT_SWITCH_ID_6020 0x0200 +#define PORT_SWITCH_ID_6070 0x0700 +#define PORT_SWITCH_ID_6071 0x0710 +#define PORT_SWITCH_ID_6096 0x0980 +#define PORT_SWITCH_ID_6097 0x0990 +#define PORT_SWITCH_ID_6172 0x1720 +#define PORT_SWITCH_ID_6176 0x1760 +#define PORT_SWITCH_ID_6220 0x2200 +#define PORT_SWITCH_ID_6240 0x2400 +#define PORT_SWITCH_ID_6250 0x2500 +#define PORT_SWITCH_ID_6352 0x3520
+struct mv88e61xx_phy_priv {
Could we choose a better name than mv88e61xx_phy_priv? It is not a PHY, it is a switch.
+static int mv88e61xx_fixed_port_setup(struct udevice *dev, u8 port) +{
- struct dsa_pdata *dsa_pdata = dev_get_uclass_plat(dev);
- struct mv88e61xx_phy_priv *priv = dev_get_priv(dev);
- int val;
- val = mv88e61xx_port_read(dev, port, PORT_REG_PHYS_CTRL);
- if (val < 0)
return val;
- val &= ~(PORT_REG_PHYS_CTRL_SPD_MASK |
PORT_REG_PHYS_CTRL_FC_VALUE |
PORT_REG_PHYS_CTRL_FC_FORCE);
- val |= PORT_REG_PHYS_CTRL_FC_FORCE |
PORT_REG_PHYS_CTRL_DUPLEX_VALUE |
PORT_REG_PHYS_CTRL_DUPLEX_FORCE;
- if (priv->id == PORT_SWITCH_ID_6071) {
val |= PORT_REG_PHYS_CTRL_SPD100;
- } else {
val |= PORT_REG_PHYS_CTRL_PCS_AN_EN |
PORT_REG_PHYS_CTRL_PCS_AN_RST |
PORT_REG_PHYS_CTRL_SPD1000;
- }
- if (port == dsa_pdata->cpu_port)
val |= PORT_REG_PHYS_CTRL_LINK_VALUE |
PORT_REG_PHYS_CTRL_LINK_FORCE;
- return mv88e61xx_port_write(dev, port, PORT_REG_PHYS_CTRL, val);
+}
+static int mv88e61xx_set_cpu_port(struct udevice *dev) +{
- struct dsa_pdata *dsa_pdata = dev_get_uclass_plat(dev);
- struct mv88e61xx_phy_priv *priv = dev_get_priv(dev);
- int val;
- /* Set CPUDest */
- val = mv88e61xx_reg_read(dev, priv->global1, GLOBAL1_MON_CTRL);
- if (val < 0)
return val;
- val = bitfield_replace(val, GLOBAL1_MON_CTRL_CPUDEST_SHIFT,
GLOBAL1_MON_CTRL_CPUDEST_WIDTH,
dsa_pdata->cpu_port);
- val = mv88e61xx_reg_write(dev, priv->global1, GLOBAL1_MON_CTRL, val);
- if (val < 0)
return val;
- /* Enable CPU port */
- val = mv88e61xx_port_enable(dev, dsa_pdata->cpu_port);
- if (val < 0)
return val;
- /* If CPU is connected to serdes, initialize serdes */
- if (mv88e61xx_6352_family(dev)) {
val = mv88e61xx_get_cmode(dev, dsa_pdata->cpu_port);
if (val < 0)
return val;
if (val == PORT_REG_STATUS_CMODE_100BASE_X ||
val == PORT_REG_STATUS_CMODE_1000BASE_X ||
val == PORT_REG_STATUS_CMODE_SGMII) {
val = mv88e61xx_serdes_init(dev);
Allow me to suggest that the SERDES init function is improperly placed in a function called "set_cpu_port".
Don't you need to initialize the SERDES also if a front port uses the SERDES, not just the CPU port?
What happens if you initialize it unconditionally?
if (val < 0)
return val;
}
- } else {
val = mv88e61xx_fixed_port_setup(dev, dsa_pdata->cpu_port);
if (val < 0)
return val;
- }
- return 0;
+}
+static int mv88e61xx_switch_init(struct udevice *dev) +{
- static int init;
- int res;
- if (init)
return 0;
Odd, why the static int?
- res = mv88e61xx_switch_reset(dev);
- if (res < 0)
return res;
- res = mv88e61xx_set_cpu_port(dev);
- if (res < 0)
return res;
- init = 1;
- return 0;
+}
+static int mv88e61xx_phy_setup(struct udevice *dev, u8 phy) +{
- struct mv88e61xx_phy_priv *priv = dev_get_priv(dev);
- int val;
- /*
* Enable energy-detect sensing on PHY, used to determine when a PHY
* port is physically connected
*/
- val = mv88e61xx_phy_read(dev, phy, PHY_REG_CTRL1);
- if (val < 0)
return val;
- val = bitfield_replace(val, priv->phy_ctrl1_en_det_shift,
priv->phy_ctrl1_en_det_width,
priv->phy_ctrl1_en_det_ctrl);
- val = mv88e61xx_phy_write(dev, phy, PHY_REG_CTRL1, val);
- if (val < 0)
return val;
- return 0;
+}
+/*
- This function is used to pre-configure the required register
- offsets, so that the indirect register access to the PHY registers
- is possible. This is necessary to be able to read the PHY ID
- while driver probing or in get_phy_id(). The globalN register
- offsets must be initialized correctly for a detected switch,
- otherwise detection of the PHY ID won't work!
- */
+static int mv88e61xx_priv_reg_offs_pre_init(struct udevice *dev) +{
- struct mv88e61xx_phy_priv *priv = dev_get_priv(dev);
- /*
* Initial 'port_reg_base' value must be an offset of existing
* port register, then reading the ID should succeed. First, try
* to read via port registers with device address 0x10 (88E6096
* and compatible switches).
*/
- priv->port_reg_base = 0x10;
- priv->id = mv88e61xx_get_switch_id(dev);
- if (priv->id != 0xfff0) {
priv->global1 = 0x1B;
priv->global2 = 0x1C;
return 0;
- }
- /*
* Now try via port registers with device address 0x08
* (88E6020 and compatible switches).
*/
- priv->port_reg_base = 0x08;
- priv->id = mv88e61xx_get_switch_id(dev);
- if (priv->id != 0xfff0) {
priv->global1 = 0x0F;
priv->global2 = 0x07;
return 0;
- }
- debug("%s Unknown ID 0x%x\n", __func__, priv->id);
This is the kind of error that is worth more than a "debug" message, no?
- return -ENODEV;
+}
+static int mv88e61xx_dsa_setup_cpu_interface(struct udevice *dev) +{
- struct dsa_pdata *dsa_pdata = dev_get_uclass_plat(dev);
- int port = dsa_pdata->cpu_port;
- phy_interface_t phy_interface;
- bool duplex, fixed;
- ofnode node;
- int speed;
- int val;
- fixed = ofnode_phy_is_fixed_link(dsa_pdata->cpu_port_node, &node);
- if (fixed && ofnode_valid(node)) {
speed = ofnode_read_u32_default(node, "speed", 0);
duplex = ofnode_read_bool(node, "full-duplex");
- } else {
duplex = false;
speed = 0;
- }
- phy_interface = phy_get_interface_by_name(ofnode_read_string(dsa_pdata->cpu_port_node,
"phy-mode"));
Isn't this something all too overcomplicated for the driver when you can just wait until mv88e61xx_dsa_port_enable() is called on the CPU port, and it passes the fixed PHY as argument, with PHY mode and all?
- dev_dbg(dev, "%s configuring CPU port P%d for %s fixed=%d speed=%d duplex=%d\n",
__func__, port, phy_string_for_interface(phy_interface),
fixed, speed, duplex);
- /* Physical Control register: Table 62 */
- val = mv88e61xx_port_read(dev, port, PORT_REG_PHYS_CTRL);
- val &= ~(PORT_REG_PHYS_CTRL_RGMII_DELAY_RXCLK ||
PORT_REG_PHYS_CTRL_RGMII_DELAY_TXCLK);
- if (phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
phy_interface == PHY_INTERFACE_MODE_RGMII_RXID)
val |= PORT_REG_PHYS_CTRL_RGMII_DELAY_RXCLK;
- if (phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
phy_interface == PHY_INTERFACE_MODE_RGMII_TXID)
val |= PORT_REG_PHYS_CTRL_RGMII_DELAY_TXCLK;
- if (fixed)
val |= BIT(5) | BIT(4); /* Force link */
- switch (speed) {
- case SPEED_10:
break;
- case SPEED_100:
val |= 0x1;
break;
- case SPEED_1000:
val |= 0x2;
break;
- default:
val |= 0x3; /* detect link speed */
break;
- }
- if (duplex)
val |= BIT(3) | BIT(2);
- return mv88e61xx_port_write(dev, port, PORT_REG_PHYS_CTRL, val);
+}
+static int mv88e61xx_dsa_port_enable(struct udevice *dev, int port, struct phy_device *phy) +{
- struct dsa_pdata *dsa_pdata = dev_get_uclass_plat(dev);
- struct mv88e61xx_phy_priv *priv = dev_get_priv(dev);
- int ret;
- dev_dbg(dev, "%s P%d phy:0x%08x %s\n", __func__, port,
phy->phy_id, phy_string_for_interface(phy->interface));
- ret = mv88e61xx_phy_setup(dev, port);
- if (ret < 0) {
dev_err(dev, "Error setting up PHY %i\n", port);
return ret;
- }
- ret = mv88e61xx_port_enable(dev, port);
- if (ret < 0) {
dev_err(dev, "Error enabling PHY %i\n", port);
return ret;
- }
- if (port != dsa_pdata->cpu_port)
priv->active_port = port;
- else
mv88e61xx_set_cpu_port(dev);
Why is there a need to call this twice, first in mv88e61xx_switch_init() then here?
- return 0;
+}
+static void mv88e61xx_dsa_port_disable(struct udevice *dev, int port, struct phy_device *phy) +{
- struct dsa_pdata *dsa_pdata = dev_get_uclass_plat(dev);
- int val;
- dev_dbg(dev, "%s P%d phy:0x%x\n", __func__, port, phy->phy_id);
- /* can't disable CPU port without re-configuring/re-starting switch */
- if (port == dsa_pdata->cpu_port)
return;
Interesting, do you know more?
- val = mv88e61xx_port_read(dev, port, PORT_REG_CTRL);
- if (val < 0)
return;
- val = bitfield_replace(val, PORT_REG_CTRL_PSTATE_SHIFT,
PORT_REG_CTRL_PSTATE_WIDTH,
PORT_REG_CTRL_PSTATE_DISABLED);
- val = mv88e61xx_port_write(dev, port, PORT_REG_CTRL, val);
- if (val < 0)
return;
+}
+/* bind and probe the switch mdios */ +static int mv88e61xx_dsa_probe_mdio(struct udevice *dev) +{
- struct udevice *pdev;
- ofnode node, mdios;
- const char *name;
- int ret;
- /* bind phy ports of mdios child node to mv88e61xx_mdio device */
- mdios = dev_read_subnode(dev, "mdios");
The Linux driver doesn't parse the "mdios" subnode, do you intend to do some work in that direction?
- if (ofnode_valid(mdios)) {
ofnode_for_each_subnode(node, mdios) {
name = ofnode_get_name(node);
ret = device_bind_driver_to_node(dev,
"mv88e61xx_mdio",
name, node, &pdev);
if (ret) {
dev_err(dev, "failed to bind %s: %d\n", name, ret);
continue;
}
/* need to probe it as there is no compatible to do so */
ret = uclass_get_device_by_ofnode(UCLASS_MDIO, node, &pdev);
if (ret) {
dev_err(dev, "failed to probe %s: %d\n", name, ret);
continue;
}
}
- }
- /* bind the mdios node to the parent mdio driver */
- name = ofnode_get_name(mdios);
- ret = device_bind_driver_to_node(dev,
"mv88e61xx_mdio",
name, mdios, &pdev);
- if (ret)
dev_err(dev, "failed to bind %s: %d\n", name, ret);
- /* need to probe it as there is no compatible to do so */
- ret = uclass_get_device_by_ofnode(UCLASS_MDIO, mdios, &pdev);
- if (ret)
dev_err(dev, "failed to probe %s: %d\n", name, ret);
- return ret;
+}
+static int mv88e61xx_dsa_probe(struct udevice *dev) +{
- struct mv88e61xx_phy_priv *priv = dev_get_priv(dev);
- struct udevice *master = dsa_get_master(dev);
- int ret;
- if (!master)
return -ENODEV;
- dev_dbg(dev, "%s master:%s\n", __func__, master->name);
- dev_set_parent_priv(dev, priv);
Copy-pasta from the ksz9477 driver?
- /* probe internal mdio bus */
- ret = mv88e61xx_dsa_probe_mdio(dev);
- if (ret)
return ret;
- ret = mv88e61xx_priv_reg_offs_pre_init(dev);
- if (ret)
return ret;
- dev_dbg(dev, "ID=0x%x PORT_BASE=0x%02x GLOBAL1=0x%02x GLOBAL2=0x%02x\n",
priv->id, priv->port_reg_base, priv->global1, priv->global2);
- switch (priv->id) {
- case PORT_SWITCH_ID_6096:
- case PORT_SWITCH_ID_6097:
- case PORT_SWITCH_ID_6172:
- case PORT_SWITCH_ID_6176:
- case PORT_SWITCH_ID_6240:
- case PORT_SWITCH_ID_6352:
priv->port_count = 11;
+// priv->port_stat_link_mask = BIT(11); +// priv->port_stat_dup_mask = BIT(10); +// priv->port_stat_speed_width = 2;
No commented code.
priv->phy_ctrl1_en_det_shift = 8;
priv->phy_ctrl1_en_det_width = 2;
priv->phy_ctrl1_en_det_ctrl =
PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT;
break;
- case PORT_SWITCH_ID_6020:
- case PORT_SWITCH_ID_6070:
- case PORT_SWITCH_ID_6071:
- case PORT_SWITCH_ID_6220:
- case PORT_SWITCH_ID_6250:
priv->port_count = 7;
+// priv->port_stat_link_mask = BIT(12); +// priv->port_stat_dup_mask = BIT(9); +// priv->port_stat_speed_width = 1;
priv->phy_ctrl1_en_det_shift = 14;
priv->phy_ctrl1_en_det_width = 1;
priv->phy_ctrl1_en_det_ctrl =
PHY_REG_CTRL1_ENERGY_DET_SENSE_PULSE;
break;
- default:
return -ENODEV;
- }
- /* configure CPU port interface mode */
- ret = mv88e61xx_dsa_setup_cpu_interface(dev);
- if (ret < 0)
return ret;
- ret = mv88e61xx_switch_init(dev);
- if (ret < 0)
return ret;
- dsa_set_tagging(dev, 0, 0);
- return 0;
+}
+static const struct udevice_id mv88e61xx_ids[] = {
- { .compatible = "marvell,mv88e6085" },
- { }
+};
+U_BOOT_DRIVER(mv88e61xx) = {
- .name = "mv88e61xx",
- .id = UCLASS_DSA,
- .of_match = mv88e61xx_ids,
- .probe = mv88e61xx_dsa_probe,
- .ops = &mv88e61xx_dsa_ops,
- .priv_auto = sizeof(struct mv88e61xx_phy_priv),
+};
2.17.1