1406a4362SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
23b158859SBen Hutchings /*
33b158859SBen Hutchings * net/dsa/mv88e6060.c - Driver for Marvell 88e6060 switch chips
43b158859SBen Hutchings * Copyright (c) 2008-2009 Marvell Semiconductor
53b158859SBen Hutchings */
63b158859SBen Hutchings
719b2f97eSBarry Grussling #include <linux/delay.h>
856c3ff9bSVivien Didelot #include <linux/etherdevice.h>
919b2f97eSBarry Grussling #include <linux/jiffies.h>
103b158859SBen Hutchings #include <linux/list.h>
112bbba277SPaul Gortmaker #include <linux/module.h>
123b158859SBen Hutchings #include <linux/netdevice.h>
133b158859SBen Hutchings #include <linux/phy.h>
143b158859SBen Hutchings #include <net/dsa.h>
156a4b2980SNeil Armstrong #include "mv88e6060.h"
163b158859SBen Hutchings
reg_read(struct mv88e6060_priv * priv,int addr,int reg)173e8bc1b8SAndrew Lunn static int reg_read(struct mv88e6060_priv *priv, int addr, int reg)
183b158859SBen Hutchings {
19a77d43f1SAndrew Lunn return mdiobus_read_nested(priv->bus, priv->sw_addr + addr, reg);
203b158859SBen Hutchings }
213b158859SBen Hutchings
reg_write(struct mv88e6060_priv * priv,int addr,int reg,u16 val)223e8bc1b8SAndrew Lunn static int reg_write(struct mv88e6060_priv *priv, int addr, int reg, u16 val)
233b158859SBen Hutchings {
24a77d43f1SAndrew Lunn return mdiobus_write_nested(priv->bus, priv->sw_addr + addr, reg, val);
253b158859SBen Hutchings }
263b158859SBen Hutchings
mv88e6060_get_name(struct mii_bus * bus,int sw_addr)270209d144SVivien Didelot static const char *mv88e6060_get_name(struct mii_bus *bus, int sw_addr)
283b158859SBen Hutchings {
293b158859SBen Hutchings int ret;
303b158859SBen Hutchings
316a4b2980SNeil Armstrong ret = mdiobus_read(bus, sw_addr + REG_PORT(0), PORT_SWITCH_ID);
323b158859SBen Hutchings if (ret >= 0) {
336a4b2980SNeil Armstrong if (ret == PORT_SWITCH_ID_6060)
343de6aa4cSGuenter Roeck return "Marvell 88E6060 (A0)";
356a4b2980SNeil Armstrong if (ret == PORT_SWITCH_ID_6060_R1 ||
366a4b2980SNeil Armstrong ret == PORT_SWITCH_ID_6060_R2)
373de6aa4cSGuenter Roeck return "Marvell 88E6060 (B0)";
386a4b2980SNeil Armstrong if ((ret & PORT_SWITCH_ID_6060_MASK) == PORT_SWITCH_ID_6060)
393b158859SBen Hutchings return "Marvell 88E6060";
403b158859SBen Hutchings }
413b158859SBen Hutchings
423b158859SBen Hutchings return NULL;
433b158859SBen Hutchings }
443b158859SBen Hutchings
mv88e6060_get_tag_protocol(struct dsa_switch * ds,int port,enum dsa_tag_protocol m)455ed4e3ebSFlorian Fainelli static enum dsa_tag_protocol mv88e6060_get_tag_protocol(struct dsa_switch *ds,
464d776482SFlorian Fainelli int port,
474d776482SFlorian Fainelli enum dsa_tag_protocol m)
487b314362SAndrew Lunn {
497b314362SAndrew Lunn return DSA_TAG_PROTO_TRAILER;
507b314362SAndrew Lunn }
517b314362SAndrew Lunn
mv88e6060_switch_reset(struct mv88e6060_priv * priv)523e8bc1b8SAndrew Lunn static int mv88e6060_switch_reset(struct mv88e6060_priv *priv)
533b158859SBen Hutchings {
543b158859SBen Hutchings int i;
553b158859SBen Hutchings int ret;
5619b2f97eSBarry Grussling unsigned long timeout;
573b158859SBen Hutchings
583675c8d7SBarry Grussling /* Set all ports to the disabled state. */
596a4b2980SNeil Armstrong for (i = 0; i < MV88E6060_PORTS; i++) {
601ba22bf5SAndrew Lunn ret = reg_read(priv, REG_PORT(i), PORT_CONTROL);
611ba22bf5SAndrew Lunn if (ret < 0)
621ba22bf5SAndrew Lunn return ret;
63c4362c37SAndrew Lunn ret = reg_write(priv, REG_PORT(i), PORT_CONTROL,
646a4b2980SNeil Armstrong ret & ~PORT_CONTROL_STATE_MASK);
65c4362c37SAndrew Lunn if (ret)
66c4362c37SAndrew Lunn return ret;
673b158859SBen Hutchings }
683b158859SBen Hutchings
693675c8d7SBarry Grussling /* Wait for transmit queues to drain. */
7019b2f97eSBarry Grussling usleep_range(2000, 4000);
713b158859SBen Hutchings
723675c8d7SBarry Grussling /* Reset the switch. */
73c4362c37SAndrew Lunn ret = reg_write(priv, REG_GLOBAL, GLOBAL_ATU_CONTROL,
746a4b2980SNeil Armstrong GLOBAL_ATU_CONTROL_SWRESET |
75a7451560SAnderson Luiz Alves GLOBAL_ATU_CONTROL_LEARNDIS);
76c4362c37SAndrew Lunn if (ret)
77c4362c37SAndrew Lunn return ret;
783b158859SBen Hutchings
793675c8d7SBarry Grussling /* Wait up to one second for reset to complete. */
8019b2f97eSBarry Grussling timeout = jiffies + 1 * HZ;
8119b2f97eSBarry Grussling while (time_before(jiffies, timeout)) {
821ba22bf5SAndrew Lunn ret = reg_read(priv, REG_GLOBAL, GLOBAL_STATUS);
831ba22bf5SAndrew Lunn if (ret < 0)
841ba22bf5SAndrew Lunn return ret;
851ba22bf5SAndrew Lunn
866a4b2980SNeil Armstrong if (ret & GLOBAL_STATUS_INIT_READY)
873b158859SBen Hutchings break;
883b158859SBen Hutchings
8919b2f97eSBarry Grussling usleep_range(1000, 2000);
903b158859SBen Hutchings }
9119b2f97eSBarry Grussling if (time_after(jiffies, timeout))
923b158859SBen Hutchings return -ETIMEDOUT;
933b158859SBen Hutchings
943b158859SBen Hutchings return 0;
953b158859SBen Hutchings }
963b158859SBen Hutchings
mv88e6060_setup_global(struct mv88e6060_priv * priv)973e8bc1b8SAndrew Lunn static int mv88e6060_setup_global(struct mv88e6060_priv *priv)
983b158859SBen Hutchings {
99c4362c37SAndrew Lunn int ret;
100c4362c37SAndrew Lunn
1013675c8d7SBarry Grussling /* Disable discarding of frames with excessive collisions,
1023b158859SBen Hutchings * set the maximum frame size to 1536 bytes, and mask all
1033b158859SBen Hutchings * interrupt sources.
1043b158859SBen Hutchings */
105c4362c37SAndrew Lunn ret = reg_write(priv, REG_GLOBAL, GLOBAL_CONTROL,
106c4362c37SAndrew Lunn GLOBAL_CONTROL_MAX_FRAME_1536);
107c4362c37SAndrew Lunn if (ret)
108c4362c37SAndrew Lunn return ret;
1093b158859SBen Hutchings
110a7451560SAnderson Luiz Alves /* Disable automatic address learning.
1113b158859SBen Hutchings */
112c4362c37SAndrew Lunn return reg_write(priv, REG_GLOBAL, GLOBAL_ATU_CONTROL,
113a7451560SAnderson Luiz Alves GLOBAL_ATU_CONTROL_LEARNDIS);
1143b158859SBen Hutchings }
1153b158859SBen Hutchings
mv88e6060_setup_port(struct mv88e6060_priv * priv,int p)1163e8bc1b8SAndrew Lunn static int mv88e6060_setup_port(struct mv88e6060_priv *priv, int p)
1173b158859SBen Hutchings {
1183b158859SBen Hutchings int addr = REG_PORT(p);
119c4362c37SAndrew Lunn int ret;
1203b158859SBen Hutchings
121246bbf2fSSergei Antonov if (dsa_is_unused_port(priv->ds, p))
122246bbf2fSSergei Antonov return 0;
123246bbf2fSSergei Antonov
1243675c8d7SBarry Grussling /* Do not force flow control, disable Ingress and Egress
1253b158859SBen Hutchings * Header tagging, disable VLAN tunneling, and set the port
1263b158859SBen Hutchings * state to Forwarding. Additionally, if this is the CPU
1273b158859SBen Hutchings * port, enable Ingress and Egress Trailer tagging mode.
1283b158859SBen Hutchings */
129c4362c37SAndrew Lunn ret = reg_write(priv, addr, PORT_CONTROL,
1303e8bc1b8SAndrew Lunn dsa_is_cpu_port(priv->ds, p) ?
1316a4b2980SNeil Armstrong PORT_CONTROL_TRAILER |
1326a4b2980SNeil Armstrong PORT_CONTROL_INGRESS_MODE |
1336a4b2980SNeil Armstrong PORT_CONTROL_STATE_FORWARDING :
1346a4b2980SNeil Armstrong PORT_CONTROL_STATE_FORWARDING);
135c4362c37SAndrew Lunn if (ret)
136c4362c37SAndrew Lunn return ret;
1373b158859SBen Hutchings
1383675c8d7SBarry Grussling /* Port based VLAN map: give each port its own address
1393b158859SBen Hutchings * database, allow the CPU port to talk to each of the 'real'
1403b158859SBen Hutchings * ports, and allow each of the 'real' ports to only talk to
1413b158859SBen Hutchings * the CPU port.
1423b158859SBen Hutchings */
143c4362c37SAndrew Lunn ret = reg_write(priv, addr, PORT_VLAN_MAP,
1446a4b2980SNeil Armstrong ((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) |
145c4362c37SAndrew Lunn (dsa_is_cpu_port(priv->ds, p) ?
146c4362c37SAndrew Lunn dsa_user_ports(priv->ds) :
1473e8bc1b8SAndrew Lunn BIT(dsa_to_port(priv->ds, p)->cpu_dp->index)));
148c4362c37SAndrew Lunn if (ret)
149c4362c37SAndrew Lunn return ret;
1503b158859SBen Hutchings
1513675c8d7SBarry Grussling /* Port Association Vector: when learning source addresses
1523b158859SBen Hutchings * of packets, add the address to the address database using
1533b158859SBen Hutchings * a port bitmap that has only the bit for this port set and
1543b158859SBen Hutchings * the other bits clear.
1553b158859SBen Hutchings */
156c4362c37SAndrew Lunn return reg_write(priv, addr, PORT_ASSOC_VECTOR, BIT(p));
1573b158859SBen Hutchings }
1583b158859SBen Hutchings
mv88e6060_setup_addr(struct mv88e6060_priv * priv)1593e8bc1b8SAndrew Lunn static int mv88e6060_setup_addr(struct mv88e6060_priv *priv)
16056c3ff9bSVivien Didelot {
16156c3ff9bSVivien Didelot u8 addr[ETH_ALEN];
162c4362c37SAndrew Lunn int ret;
16356c3ff9bSVivien Didelot u16 val;
16456c3ff9bSVivien Didelot
16556c3ff9bSVivien Didelot eth_random_addr(addr);
16656c3ff9bSVivien Didelot
16756c3ff9bSVivien Didelot val = addr[0] << 8 | addr[1];
16856c3ff9bSVivien Didelot
16956c3ff9bSVivien Didelot /* The multicast bit is always transmitted as a zero, so the switch uses
17056c3ff9bSVivien Didelot * bit 8 for "DiffAddr", where 0 means all ports transmit the same SA.
17156c3ff9bSVivien Didelot */
17256c3ff9bSVivien Didelot val &= 0xfeff;
17356c3ff9bSVivien Didelot
174c4362c37SAndrew Lunn ret = reg_write(priv, REG_GLOBAL, GLOBAL_MAC_01, val);
175c4362c37SAndrew Lunn if (ret)
176c4362c37SAndrew Lunn return ret;
17756c3ff9bSVivien Didelot
178c4362c37SAndrew Lunn ret = reg_write(priv, REG_GLOBAL, GLOBAL_MAC_23,
179c4362c37SAndrew Lunn (addr[2] << 8) | addr[3]);
180c4362c37SAndrew Lunn if (ret)
181c4362c37SAndrew Lunn return ret;
182c4362c37SAndrew Lunn
183c4362c37SAndrew Lunn return reg_write(priv, REG_GLOBAL, GLOBAL_MAC_45,
184c4362c37SAndrew Lunn (addr[4] << 8) | addr[5]);
18556c3ff9bSVivien Didelot }
18656c3ff9bSVivien Didelot
mv88e6060_setup(struct dsa_switch * ds)1873b158859SBen Hutchings static int mv88e6060_setup(struct dsa_switch *ds)
1883b158859SBen Hutchings {
1893e8bc1b8SAndrew Lunn struct mv88e6060_priv *priv = ds->priv;
1903b158859SBen Hutchings int ret;
191a77d43f1SAndrew Lunn int i;
1923b158859SBen Hutchings
1933e8bc1b8SAndrew Lunn priv->ds = ds;
1943e8bc1b8SAndrew Lunn
1953e8bc1b8SAndrew Lunn ret = mv88e6060_switch_reset(priv);
1963b158859SBen Hutchings if (ret < 0)
1973b158859SBen Hutchings return ret;
1983b158859SBen Hutchings
1993b158859SBen Hutchings /* @@@ initialise atu */
2003b158859SBen Hutchings
2013e8bc1b8SAndrew Lunn ret = mv88e6060_setup_global(priv);
2023b158859SBen Hutchings if (ret < 0)
2033b158859SBen Hutchings return ret;
2043b158859SBen Hutchings
2053e8bc1b8SAndrew Lunn ret = mv88e6060_setup_addr(priv);
20656c3ff9bSVivien Didelot if (ret < 0)
20756c3ff9bSVivien Didelot return ret;
20856c3ff9bSVivien Didelot
2096a4b2980SNeil Armstrong for (i = 0; i < MV88E6060_PORTS; i++) {
2103e8bc1b8SAndrew Lunn ret = mv88e6060_setup_port(priv, i);
2113b158859SBen Hutchings if (ret < 0)
2123b158859SBen Hutchings return ret;
2133b158859SBen Hutchings }
2143b158859SBen Hutchings
2153b158859SBen Hutchings return 0;
2163b158859SBen Hutchings }
2173b158859SBen Hutchings
mv88e6060_port_to_phy_addr(int port)2183b158859SBen Hutchings static int mv88e6060_port_to_phy_addr(int port)
2193b158859SBen Hutchings {
2206a4b2980SNeil Armstrong if (port >= 0 && port < MV88E6060_PORTS)
2213b158859SBen Hutchings return port;
2223b158859SBen Hutchings return -1;
2233b158859SBen Hutchings }
2243b158859SBen Hutchings
mv88e6060_phy_read(struct dsa_switch * ds,int port,int regnum)2253b158859SBen Hutchings static int mv88e6060_phy_read(struct dsa_switch *ds, int port, int regnum)
2263b158859SBen Hutchings {
2273e8bc1b8SAndrew Lunn struct mv88e6060_priv *priv = ds->priv;
2283b158859SBen Hutchings int addr;
2293b158859SBen Hutchings
2303b158859SBen Hutchings addr = mv88e6060_port_to_phy_addr(port);
2313b158859SBen Hutchings if (addr == -1)
2323b158859SBen Hutchings return 0xffff;
2333b158859SBen Hutchings
2343e8bc1b8SAndrew Lunn return reg_read(priv, addr, regnum);
2353b158859SBen Hutchings }
2363b158859SBen Hutchings
2373b158859SBen Hutchings static int
mv88e6060_phy_write(struct dsa_switch * ds,int port,int regnum,u16 val)2383b158859SBen Hutchings mv88e6060_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
2393b158859SBen Hutchings {
2403e8bc1b8SAndrew Lunn struct mv88e6060_priv *priv = ds->priv;
2413b158859SBen Hutchings int addr;
2423b158859SBen Hutchings
2433b158859SBen Hutchings addr = mv88e6060_port_to_phy_addr(port);
2443b158859SBen Hutchings if (addr == -1)
2453b158859SBen Hutchings return 0xffff;
2463b158859SBen Hutchings
2473e8bc1b8SAndrew Lunn return reg_write(priv, addr, regnum, val);
2483b158859SBen Hutchings }
2493b158859SBen Hutchings
mv88e6060_phylink_get_caps(struct dsa_switch * ds,int port,struct phylink_config * config)250*479b322eSRussell King (Oracle) static void mv88e6060_phylink_get_caps(struct dsa_switch *ds, int port,
251*479b322eSRussell King (Oracle) struct phylink_config *config)
252*479b322eSRussell King (Oracle) {
253*479b322eSRussell King (Oracle) unsigned long *interfaces = config->supported_interfaces;
254*479b322eSRussell King (Oracle) struct mv88e6060_priv *priv = ds->priv;
255*479b322eSRussell King (Oracle) int addr = REG_PORT(port);
256*479b322eSRussell King (Oracle) int ret;
257*479b322eSRussell King (Oracle)
258*479b322eSRussell King (Oracle) ret = reg_read(priv, addr, PORT_STATUS);
259*479b322eSRussell King (Oracle) if (ret < 0) {
260*479b322eSRussell King (Oracle) dev_err(ds->dev,
261*479b322eSRussell King (Oracle) "port %d: unable to read status register: %pe\n",
262*479b322eSRussell King (Oracle) port, ERR_PTR(ret));
263*479b322eSRussell King (Oracle) return;
264*479b322eSRussell King (Oracle) }
265*479b322eSRussell King (Oracle)
266*479b322eSRussell King (Oracle) /* If the port is configured in SNI mode (acts as a 10Mbps PHY),
267*479b322eSRussell King (Oracle) * it should have phy-mode = "sni", but that doesn't yet exist, so
268*479b322eSRussell King (Oracle) * forcibly fail validation until the need arises to introduce it.
269*479b322eSRussell King (Oracle) */
270*479b322eSRussell King (Oracle) if (!(ret & PORT_STATUS_PORTMODE)) {
271*479b322eSRussell King (Oracle) dev_warn(ds->dev, "port %d: SNI mode not supported\n", port);
272*479b322eSRussell King (Oracle) return;
273*479b322eSRussell King (Oracle) }
274*479b322eSRussell King (Oracle)
275*479b322eSRussell King (Oracle) config->mac_capabilities = MAC_100 | MAC_10 | MAC_SYM_PAUSE;
276*479b322eSRussell King (Oracle)
277*479b322eSRussell King (Oracle) if (port >= 4) {
278*479b322eSRussell King (Oracle) /* Ports 4 and 5 can support MII, REVMII and REVRMII modes */
279*479b322eSRussell King (Oracle) __set_bit(PHY_INTERFACE_MODE_MII, interfaces);
280*479b322eSRussell King (Oracle) __set_bit(PHY_INTERFACE_MODE_REVMII, interfaces);
281*479b322eSRussell King (Oracle) __set_bit(PHY_INTERFACE_MODE_REVRMII, interfaces);
282*479b322eSRussell King (Oracle) }
283*479b322eSRussell King (Oracle) if (port <= 4) {
284*479b322eSRussell King (Oracle) /* Ports 0 to 3 have internal PHYs, and port 4 can optionally
285*479b322eSRussell King (Oracle) * use an internal PHY.
286*479b322eSRussell King (Oracle) */
287*479b322eSRussell King (Oracle) /* Internal PHY */
288*479b322eSRussell King (Oracle) __set_bit(PHY_INTERFACE_MODE_INTERNAL, interfaces);
289*479b322eSRussell King (Oracle) /* Default phylib interface mode */
290*479b322eSRussell King (Oracle) __set_bit(PHY_INTERFACE_MODE_GMII, interfaces);
291*479b322eSRussell King (Oracle) }
292*479b322eSRussell King (Oracle) }
293*479b322eSRussell King (Oracle)
294a82f67afSFlorian Fainelli static const struct dsa_switch_ops mv88e6060_switch_ops = {
2957b314362SAndrew Lunn .get_tag_protocol = mv88e6060_get_tag_protocol,
2963b158859SBen Hutchings .setup = mv88e6060_setup,
2973b158859SBen Hutchings .phy_read = mv88e6060_phy_read,
2983b158859SBen Hutchings .phy_write = mv88e6060_phy_write,
299*479b322eSRussell King (Oracle) .phylink_get_caps = mv88e6060_phylink_get_caps,
3003b158859SBen Hutchings };
3013b158859SBen Hutchings
mv88e6060_probe(struct mdio_device * mdiodev)30227761760SAndrew Lunn static int mv88e6060_probe(struct mdio_device *mdiodev)
30327761760SAndrew Lunn {
30427761760SAndrew Lunn struct device *dev = &mdiodev->dev;
30527761760SAndrew Lunn struct mv88e6060_priv *priv;
30627761760SAndrew Lunn struct dsa_switch *ds;
30727761760SAndrew Lunn const char *name;
30827761760SAndrew Lunn
30927761760SAndrew Lunn priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
31027761760SAndrew Lunn if (!priv)
31127761760SAndrew Lunn return -ENOMEM;
31227761760SAndrew Lunn
31327761760SAndrew Lunn priv->bus = mdiodev->bus;
31427761760SAndrew Lunn priv->sw_addr = mdiodev->addr;
31527761760SAndrew Lunn
31627761760SAndrew Lunn name = mv88e6060_get_name(priv->bus, priv->sw_addr);
31727761760SAndrew Lunn if (!name)
31827761760SAndrew Lunn return -ENODEV;
31927761760SAndrew Lunn
32027761760SAndrew Lunn dev_info(dev, "switch %s detected\n", name);
32127761760SAndrew Lunn
3227e99e347SVivien Didelot ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
32327761760SAndrew Lunn if (!ds)
32427761760SAndrew Lunn return -ENOMEM;
32527761760SAndrew Lunn
3267e99e347SVivien Didelot ds->dev = dev;
3277e99e347SVivien Didelot ds->num_ports = MV88E6060_PORTS;
32827761760SAndrew Lunn ds->priv = priv;
32927761760SAndrew Lunn ds->dev = dev;
33027761760SAndrew Lunn ds->ops = &mv88e6060_switch_ops;
33127761760SAndrew Lunn
33227761760SAndrew Lunn dev_set_drvdata(dev, ds);
33327761760SAndrew Lunn
33427761760SAndrew Lunn return dsa_register_switch(ds);
33527761760SAndrew Lunn }
33627761760SAndrew Lunn
mv88e6060_remove(struct mdio_device * mdiodev)33727761760SAndrew Lunn static void mv88e6060_remove(struct mdio_device *mdiodev)
33827761760SAndrew Lunn {
33927761760SAndrew Lunn struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
34027761760SAndrew Lunn
3410650bf52SVladimir Oltean if (!ds)
3420650bf52SVladimir Oltean return;
3430650bf52SVladimir Oltean
34427761760SAndrew Lunn dsa_unregister_switch(ds);
3450650bf52SVladimir Oltean }
3460650bf52SVladimir Oltean
mv88e6060_shutdown(struct mdio_device * mdiodev)3470650bf52SVladimir Oltean static void mv88e6060_shutdown(struct mdio_device *mdiodev)
3480650bf52SVladimir Oltean {
3490650bf52SVladimir Oltean struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
3500650bf52SVladimir Oltean
3510650bf52SVladimir Oltean if (!ds)
3520650bf52SVladimir Oltean return;
3530650bf52SVladimir Oltean
3540650bf52SVladimir Oltean dsa_switch_shutdown(ds);
3550650bf52SVladimir Oltean
3560650bf52SVladimir Oltean dev_set_drvdata(&mdiodev->dev, NULL);
35727761760SAndrew Lunn }
35827761760SAndrew Lunn
35927761760SAndrew Lunn static const struct of_device_id mv88e6060_of_match[] = {
36027761760SAndrew Lunn {
36127761760SAndrew Lunn .compatible = "marvell,mv88e6060",
36227761760SAndrew Lunn },
36327761760SAndrew Lunn { /* sentinel */ },
36427761760SAndrew Lunn };
36527761760SAndrew Lunn
36627761760SAndrew Lunn static struct mdio_driver mv88e6060_driver = {
36727761760SAndrew Lunn .probe = mv88e6060_probe,
36827761760SAndrew Lunn .remove = mv88e6060_remove,
3690650bf52SVladimir Oltean .shutdown = mv88e6060_shutdown,
37027761760SAndrew Lunn .mdiodrv.driver = {
37127761760SAndrew Lunn .name = "mv88e6060",
37227761760SAndrew Lunn .of_match_table = mv88e6060_of_match,
37327761760SAndrew Lunn },
37427761760SAndrew Lunn };
37527761760SAndrew Lunn
3762f8e7eceSAndrew Lunn mdio_module_driver(mv88e6060_driver);
3773b158859SBen Hutchings
3783b158859SBen Hutchings MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3793b158859SBen Hutchings MODULE_DESCRIPTION("Driver for Marvell 88E6060 ethernet switch chip");
3803b158859SBen Hutchings MODULE_LICENSE("GPL");
3813b158859SBen Hutchings MODULE_ALIAS("platform:mv88e6060");
382