1 // SPDX-License-Identifier: GPL-2.0+
3 * net/dsa/mv88e6060.c - Driver for Marvell 88e6060 switch chips
4 * Copyright (c) 2008-2009 Marvell Semiconductor
7 #include <linux/delay.h>
8 #include <linux/etherdevice.h>
9 #include <linux/jiffies.h>
10 #include <linux/list.h>
11 #include <linux/module.h>
12 #include <linux/netdevice.h>
13 #include <linux/phy.h>
15 #include "mv88e6060.h"
17 static int reg_read(struct mv88e6060_priv
*priv
, int addr
, int reg
)
19 return mdiobus_read_nested(priv
->bus
, priv
->sw_addr
+ addr
, reg
);
22 static int reg_write(struct mv88e6060_priv
*priv
, int addr
, int reg
, u16 val
)
24 return mdiobus_write_nested(priv
->bus
, priv
->sw_addr
+ addr
, reg
, val
);
27 static const char *mv88e6060_get_name(struct mii_bus
*bus
, int sw_addr
)
31 ret
= mdiobus_read(bus
, sw_addr
+ REG_PORT(0), PORT_SWITCH_ID
);
33 if (ret
== PORT_SWITCH_ID_6060
)
34 return "Marvell 88E6060 (A0)";
35 if (ret
== PORT_SWITCH_ID_6060_R1
||
36 ret
== PORT_SWITCH_ID_6060_R2
)
37 return "Marvell 88E6060 (B0)";
38 if ((ret
& PORT_SWITCH_ID_6060_MASK
) == PORT_SWITCH_ID_6060
)
39 return "Marvell 88E6060";
45 static enum dsa_tag_protocol
mv88e6060_get_tag_protocol(struct dsa_switch
*ds
,
47 enum dsa_tag_protocol m
)
49 return DSA_TAG_PROTO_TRAILER
;
52 static int mv88e6060_switch_reset(struct mv88e6060_priv
*priv
)
56 unsigned long timeout
;
58 /* Set all ports to the disabled state. */
59 for (i
= 0; i
< MV88E6060_PORTS
; i
++) {
60 ret
= reg_read(priv
, REG_PORT(i
), PORT_CONTROL
);
63 ret
= reg_write(priv
, REG_PORT(i
), PORT_CONTROL
,
64 ret
& ~PORT_CONTROL_STATE_MASK
);
69 /* Wait for transmit queues to drain. */
70 usleep_range(2000, 4000);
72 /* Reset the switch. */
73 ret
= reg_write(priv
, REG_GLOBAL
, GLOBAL_ATU_CONTROL
,
74 GLOBAL_ATU_CONTROL_SWRESET
|
75 GLOBAL_ATU_CONTROL_LEARNDIS
);
79 /* Wait up to one second for reset to complete. */
80 timeout
= jiffies
+ 1 * HZ
;
81 while (time_before(jiffies
, timeout
)) {
82 ret
= reg_read(priv
, REG_GLOBAL
, GLOBAL_STATUS
);
86 if (ret
& GLOBAL_STATUS_INIT_READY
)
89 usleep_range(1000, 2000);
91 if (time_after(jiffies
, timeout
))
97 static int mv88e6060_setup_global(struct mv88e6060_priv
*priv
)
101 /* Disable discarding of frames with excessive collisions,
102 * set the maximum frame size to 1536 bytes, and mask all
105 ret
= reg_write(priv
, REG_GLOBAL
, GLOBAL_CONTROL
,
106 GLOBAL_CONTROL_MAX_FRAME_1536
);
110 /* Disable automatic address learning.
112 return reg_write(priv
, REG_GLOBAL
, GLOBAL_ATU_CONTROL
,
113 GLOBAL_ATU_CONTROL_LEARNDIS
);
116 static int mv88e6060_setup_port(struct mv88e6060_priv
*priv
, int p
)
118 int addr
= REG_PORT(p
);
121 if (dsa_is_unused_port(priv
->ds
, p
))
124 /* Do not force flow control, disable Ingress and Egress
125 * Header tagging, disable VLAN tunneling, and set the port
126 * state to Forwarding. Additionally, if this is the CPU
127 * port, enable Ingress and Egress Trailer tagging mode.
129 ret
= reg_write(priv
, addr
, PORT_CONTROL
,
130 dsa_is_cpu_port(priv
->ds
, p
) ?
131 PORT_CONTROL_TRAILER
|
132 PORT_CONTROL_INGRESS_MODE
|
133 PORT_CONTROL_STATE_FORWARDING
:
134 PORT_CONTROL_STATE_FORWARDING
);
138 /* Port based VLAN map: give each port its own address
139 * database, allow the CPU port to talk to each of the 'real'
140 * ports, and allow each of the 'real' ports to only talk to
143 ret
= reg_write(priv
, addr
, PORT_VLAN_MAP
,
144 ((p
& 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT
) |
145 (dsa_is_cpu_port(priv
->ds
, p
) ?
146 dsa_user_ports(priv
->ds
) :
147 BIT(dsa_to_port(priv
->ds
, p
)->cpu_dp
->index
)));
151 /* Port Association Vector: when learning source addresses
152 * of packets, add the address to the address database using
153 * a port bitmap that has only the bit for this port set and
154 * the other bits clear.
156 return reg_write(priv
, addr
, PORT_ASSOC_VECTOR
, BIT(p
));
159 static int mv88e6060_setup_addr(struct mv88e6060_priv
*priv
)
165 eth_random_addr(addr
);
167 val
= addr
[0] << 8 | addr
[1];
169 /* The multicast bit is always transmitted as a zero, so the switch uses
170 * bit 8 for "DiffAddr", where 0 means all ports transmit the same SA.
174 ret
= reg_write(priv
, REG_GLOBAL
, GLOBAL_MAC_01
, val
);
178 ret
= reg_write(priv
, REG_GLOBAL
, GLOBAL_MAC_23
,
179 (addr
[2] << 8) | addr
[3]);
183 return reg_write(priv
, REG_GLOBAL
, GLOBAL_MAC_45
,
184 (addr
[4] << 8) | addr
[5]);
187 static int mv88e6060_setup(struct dsa_switch
*ds
)
189 struct mv88e6060_priv
*priv
= ds
->priv
;
195 ret
= mv88e6060_switch_reset(priv
);
199 /* @@@ initialise atu */
201 ret
= mv88e6060_setup_global(priv
);
205 ret
= mv88e6060_setup_addr(priv
);
209 for (i
= 0; i
< MV88E6060_PORTS
; i
++) {
210 ret
= mv88e6060_setup_port(priv
, i
);
218 static int mv88e6060_port_to_phy_addr(int port
)
220 if (port
>= 0 && port
< MV88E6060_PORTS
)
225 static int mv88e6060_phy_read(struct dsa_switch
*ds
, int port
, int regnum
)
227 struct mv88e6060_priv
*priv
= ds
->priv
;
230 addr
= mv88e6060_port_to_phy_addr(port
);
234 return reg_read(priv
, addr
, regnum
);
238 mv88e6060_phy_write(struct dsa_switch
*ds
, int port
, int regnum
, u16 val
)
240 struct mv88e6060_priv
*priv
= ds
->priv
;
243 addr
= mv88e6060_port_to_phy_addr(port
);
247 return reg_write(priv
, addr
, regnum
, val
);
250 static void mv88e6060_phylink_get_caps(struct dsa_switch
*ds
, int port
,
251 struct phylink_config
*config
)
253 unsigned long *interfaces
= config
->supported_interfaces
;
254 struct mv88e6060_priv
*priv
= ds
->priv
;
255 int addr
= REG_PORT(port
);
258 ret
= reg_read(priv
, addr
, PORT_STATUS
);
261 "port %d: unable to read status register: %pe\n",
266 /* If the port is configured in SNI mode (acts as a 10Mbps PHY),
267 * it should have phy-mode = "sni", but that doesn't yet exist, so
268 * forcibly fail validation until the need arises to introduce it.
270 if (!(ret
& PORT_STATUS_PORTMODE
)) {
271 dev_warn(ds
->dev
, "port %d: SNI mode not supported\n", port
);
275 config
->mac_capabilities
= MAC_100
| MAC_10
| MAC_SYM_PAUSE
;
278 /* Ports 4 and 5 can support MII, REVMII and REVRMII modes */
279 __set_bit(PHY_INTERFACE_MODE_MII
, interfaces
);
280 __set_bit(PHY_INTERFACE_MODE_REVMII
, interfaces
);
281 __set_bit(PHY_INTERFACE_MODE_REVRMII
, interfaces
);
284 /* Ports 0 to 3 have internal PHYs, and port 4 can optionally
285 * use an internal PHY.
288 __set_bit(PHY_INTERFACE_MODE_INTERNAL
, interfaces
);
289 /* Default phylib interface mode */
290 __set_bit(PHY_INTERFACE_MODE_GMII
, interfaces
);
294 static const struct dsa_switch_ops mv88e6060_switch_ops
= {
295 .get_tag_protocol
= mv88e6060_get_tag_protocol
,
296 .setup
= mv88e6060_setup
,
297 .phy_read
= mv88e6060_phy_read
,
298 .phy_write
= mv88e6060_phy_write
,
299 .phylink_get_caps
= mv88e6060_phylink_get_caps
,
302 static int mv88e6060_probe(struct mdio_device
*mdiodev
)
304 struct device
*dev
= &mdiodev
->dev
;
305 struct mv88e6060_priv
*priv
;
306 struct dsa_switch
*ds
;
309 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
313 priv
->bus
= mdiodev
->bus
;
314 priv
->sw_addr
= mdiodev
->addr
;
316 name
= mv88e6060_get_name(priv
->bus
, priv
->sw_addr
);
320 dev_info(dev
, "switch %s detected\n", name
);
322 ds
= devm_kzalloc(dev
, sizeof(*ds
), GFP_KERNEL
);
327 ds
->num_ports
= MV88E6060_PORTS
;
330 ds
->ops
= &mv88e6060_switch_ops
;
332 dev_set_drvdata(dev
, ds
);
334 return dsa_register_switch(ds
);
337 static void mv88e6060_remove(struct mdio_device
*mdiodev
)
339 struct dsa_switch
*ds
= dev_get_drvdata(&mdiodev
->dev
);
344 dsa_unregister_switch(ds
);
347 static void mv88e6060_shutdown(struct mdio_device
*mdiodev
)
349 struct dsa_switch
*ds
= dev_get_drvdata(&mdiodev
->dev
);
354 dsa_switch_shutdown(ds
);
356 dev_set_drvdata(&mdiodev
->dev
, NULL
);
359 static const struct of_device_id mv88e6060_of_match
[] = {
361 .compatible
= "marvell,mv88e6060",
366 static struct mdio_driver mv88e6060_driver
= {
367 .probe
= mv88e6060_probe
,
368 .remove
= mv88e6060_remove
,
369 .shutdown
= mv88e6060_shutdown
,
372 .of_match_table
= mv88e6060_of_match
,
376 mdio_module_driver(mv88e6060_driver
);
378 MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
379 MODULE_DESCRIPTION("Driver for Marvell 88E6060 ethernet switch chip");
380 MODULE_LICENSE("GPL");
381 MODULE_ALIAS("platform:mv88e6060");