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 /* Do not force flow control, disable Ingress and Egress
122 * Header tagging, disable VLAN tunneling, and set the port
123 * state to Forwarding. Additionally, if this is the CPU
124 * port, enable Ingress and Egress Trailer tagging mode.
126 ret
= reg_write(priv
, addr
, PORT_CONTROL
,
127 dsa_is_cpu_port(priv
->ds
, p
) ?
128 PORT_CONTROL_TRAILER
|
129 PORT_CONTROL_INGRESS_MODE
|
130 PORT_CONTROL_STATE_FORWARDING
:
131 PORT_CONTROL_STATE_FORWARDING
);
135 /* Port based VLAN map: give each port its own address
136 * database, allow the CPU port to talk to each of the 'real'
137 * ports, and allow each of the 'real' ports to only talk to
140 ret
= reg_write(priv
, addr
, PORT_VLAN_MAP
,
141 ((p
& 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT
) |
142 (dsa_is_cpu_port(priv
->ds
, p
) ?
143 dsa_user_ports(priv
->ds
) :
144 BIT(dsa_to_port(priv
->ds
, p
)->cpu_dp
->index
)));
148 /* Port Association Vector: when learning source addresses
149 * of packets, add the address to the address database using
150 * a port bitmap that has only the bit for this port set and
151 * the other bits clear.
153 return reg_write(priv
, addr
, PORT_ASSOC_VECTOR
, BIT(p
));
156 static int mv88e6060_setup_addr(struct mv88e6060_priv
*priv
)
162 eth_random_addr(addr
);
164 val
= addr
[0] << 8 | addr
[1];
166 /* The multicast bit is always transmitted as a zero, so the switch uses
167 * bit 8 for "DiffAddr", where 0 means all ports transmit the same SA.
171 ret
= reg_write(priv
, REG_GLOBAL
, GLOBAL_MAC_01
, val
);
175 ret
= reg_write(priv
, REG_GLOBAL
, GLOBAL_MAC_23
,
176 (addr
[2] << 8) | addr
[3]);
180 return reg_write(priv
, REG_GLOBAL
, GLOBAL_MAC_45
,
181 (addr
[4] << 8) | addr
[5]);
184 static int mv88e6060_setup(struct dsa_switch
*ds
)
186 struct mv88e6060_priv
*priv
= ds
->priv
;
192 ret
= mv88e6060_switch_reset(priv
);
196 /* @@@ initialise atu */
198 ret
= mv88e6060_setup_global(priv
);
202 ret
= mv88e6060_setup_addr(priv
);
206 for (i
= 0; i
< MV88E6060_PORTS
; i
++) {
207 ret
= mv88e6060_setup_port(priv
, i
);
215 static int mv88e6060_port_to_phy_addr(int port
)
217 if (port
>= 0 && port
< MV88E6060_PORTS
)
222 static int mv88e6060_phy_read(struct dsa_switch
*ds
, int port
, int regnum
)
224 struct mv88e6060_priv
*priv
= ds
->priv
;
227 addr
= mv88e6060_port_to_phy_addr(port
);
231 return reg_read(priv
, addr
, regnum
);
235 mv88e6060_phy_write(struct dsa_switch
*ds
, int port
, int regnum
, u16 val
)
237 struct mv88e6060_priv
*priv
= ds
->priv
;
240 addr
= mv88e6060_port_to_phy_addr(port
);
244 return reg_write(priv
, addr
, regnum
, val
);
247 static const struct dsa_switch_ops mv88e6060_switch_ops
= {
248 .get_tag_protocol
= mv88e6060_get_tag_protocol
,
249 .setup
= mv88e6060_setup
,
250 .phy_read
= mv88e6060_phy_read
,
251 .phy_write
= mv88e6060_phy_write
,
254 static int mv88e6060_probe(struct mdio_device
*mdiodev
)
256 struct device
*dev
= &mdiodev
->dev
;
257 struct mv88e6060_priv
*priv
;
258 struct dsa_switch
*ds
;
261 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
265 priv
->bus
= mdiodev
->bus
;
266 priv
->sw_addr
= mdiodev
->addr
;
268 name
= mv88e6060_get_name(priv
->bus
, priv
->sw_addr
);
272 dev_info(dev
, "switch %s detected\n", name
);
274 ds
= devm_kzalloc(dev
, sizeof(*ds
), GFP_KERNEL
);
279 ds
->num_ports
= MV88E6060_PORTS
;
282 ds
->ops
= &mv88e6060_switch_ops
;
284 dev_set_drvdata(dev
, ds
);
286 return dsa_register_switch(ds
);
289 static void mv88e6060_remove(struct mdio_device
*mdiodev
)
291 struct dsa_switch
*ds
= dev_get_drvdata(&mdiodev
->dev
);
293 dsa_unregister_switch(ds
);
296 static const struct of_device_id mv88e6060_of_match
[] = {
298 .compatible
= "marvell,mv88e6060",
303 static struct mdio_driver mv88e6060_driver
= {
304 .probe
= mv88e6060_probe
,
305 .remove
= mv88e6060_remove
,
308 .of_match_table
= mv88e6060_of_match
,
312 mdio_module_driver(mv88e6060_driver
);
314 MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
315 MODULE_DESCRIPTION("Driver for Marvell 88E6060 ethernet switch chip");
316 MODULE_LICENSE("GPL");
317 MODULE_ALIAS("platform:mv88e6060");