2 * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
33 * Driver for the Marvell 88E61xx family of switch PHYs
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/socket.h>
40 #include <sys/errno.h>
41 #include <sys/module.h>
43 #include <sys/sysctl.h>
45 #include <net/ethernet.h>
47 #include <net/if_media.h>
49 #include "miibus_if.h"
51 #include "mv88e61xxphyreg.h"
53 struct mv88e61xxphy_softc;
55 struct mv88e61xxphy_port_softc {
56 struct mv88e61xxphy_softc *sc_switch;
64 #define MV88E61XXPHY_PORT_FLAG_VTU_UPDATE (0x0001)
66 struct mv88e61xxphy_softc {
68 struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS];
71 enum mv88e61xxphy_vtu_membership_type {
72 MV88E61XXPHY_VTU_UNMODIFIED,
73 MV88E61XXPHY_VTU_UNTAGGED,
74 MV88E61XXPHY_VTU_TAGGED,
75 MV88E61XXPHY_VTU_DISCARDED,
78 enum mv88e61xxphy_sysctl_link_type {
79 MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
80 MV88E61XXPHY_LINK_SYSCTL_LINK,
81 MV88E61XXPHY_LINK_SYSCTL_MEDIA,
84 enum mv88e61xxphy_sysctl_port_type {
85 MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
86 MV88E61XXPHY_PORT_SYSCTL_VLAN,
87 MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
91 * Register access macros.
93 #define MV88E61XX_READ(sc, phy, reg) \
94 MIIBUS_READREG(device_get_parent((sc)->sc_dev), (phy), (reg))
96 #define MV88E61XX_WRITE(sc, phy, reg, val) \
97 MIIBUS_WRITEREG(device_get_parent((sc)->sc_dev), (phy), (reg), (val))
99 #define MV88E61XX_READ_PORT(psc, reg) \
100 MV88E61XX_READ((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg))
102 #define MV88E61XX_WRITE_PORT(psc, reg, val) \
103 MV88E61XX_WRITE((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg), (val))
105 static int mv88e61xxphy_probe(device_t);
106 static int mv88e61xxphy_attach(device_t);
108 static void mv88e61xxphy_init(struct mv88e61xxphy_softc *);
109 static void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *);
110 static void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *);
111 static int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS);
112 static int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS);
113 static void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t);
114 static void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type);
115 static void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *);
118 mv88e61xxphy_probe(device_t dev)
122 val = MIIBUS_READREG(device_get_parent(dev), MV88E61XX_PORT(0),
123 MV88E61XX_PORT_REVISION);
126 device_set_desc(dev, "Marvell Link Street 88E6123 3-Port Gigabit Switch");
129 device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Gigabit Switch");
132 device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Advanced Gigabit Switch");
140 mv88e61xxphy_attach(device_t dev)
142 char portbuf[] = "N";
143 struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
144 struct sysctl_oid *tree = device_get_sysctl_tree(dev);
145 struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
146 struct sysctl_oid *port_node, *portN_node;
147 struct sysctl_oid_list *port_tree, *portN_tree;
148 struct mv88e61xxphy_softc *sc;
151 sc = device_get_softc(dev);
155 * Initialize port softcs.
157 for (port = 0; port < MV88E61XX_PORTS; port++) {
158 struct mv88e61xxphy_port_softc *psc;
160 psc = &sc->sc_ports[port];
163 psc->sc_domain = 0; /* One broadcast domain by default. */
164 psc->sc_vlan = port + 1; /* Tag VLANs by default. */
165 psc->sc_priority = 0; /* No default special priority. */
170 * Add per-port sysctl tree/handlers.
172 port_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "port",
173 CTLFLAG_RD, NULL, "Switch Ports");
174 port_tree = SYSCTL_CHILDREN(port_node);
175 for (port = 0; port < MV88E61XX_PORTS; port++) {
176 struct mv88e61xxphy_port_softc *psc;
178 psc = &sc->sc_ports[port];
180 portbuf[0] = '0' + port;
181 portN_node = SYSCTL_ADD_NODE(ctx, port_tree, OID_AUTO, portbuf,
182 CTLFLAG_RD, NULL, "Switch Port");
183 portN_tree = SYSCTL_CHILDREN(portN_node);
185 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "duplex",
186 CTLFLAG_RD | CTLTYPE_INT, psc,
187 MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
188 mv88e61xxphy_sysctl_link_proc, "IU",
189 "Media duplex status (0 = half duplex; 1 = full duplex)");
191 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "link",
192 CTLFLAG_RD | CTLTYPE_INT, psc,
193 MV88E61XXPHY_LINK_SYSCTL_LINK,
194 mv88e61xxphy_sysctl_link_proc, "IU",
195 "Link status (0 = down; 1 = up)");
197 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "media",
198 CTLFLAG_RD | CTLTYPE_INT, psc,
199 MV88E61XXPHY_LINK_SYSCTL_MEDIA,
200 mv88e61xxphy_sysctl_link_proc, "IU",
201 "Media speed (0 = unknown; 10 = 10Mbps; 100 = 100Mbps; 1000 = 1Gbps)");
203 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "domain",
204 CTLFLAG_RW | CTLTYPE_INT, psc,
205 MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
206 mv88e61xxphy_sysctl_port_proc, "IU",
207 "Broadcast domain (ports can only talk to other ports in the same domain)");
209 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "vlan",
210 CTLFLAG_RW | CTLTYPE_INT, psc,
211 MV88E61XXPHY_PORT_SYSCTL_VLAN,
212 mv88e61xxphy_sysctl_port_proc, "IU",
213 "Tag packets from/for this port with a given VLAN.");
215 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "priority",
216 CTLFLAG_RW | CTLTYPE_INT, psc,
217 MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
218 mv88e61xxphy_sysctl_port_proc, "IU",
219 "Default packet priority for this port.");
222 mv88e61xxphy_init(sc);
228 mv88e61xxphy_init(struct mv88e61xxphy_softc *sc)
234 /* Disable all ports. */
235 for (port = 0; port < MV88E61XX_PORTS; port++) {
236 struct mv88e61xxphy_port_softc *psc;
238 psc = &sc->sc_ports[port];
240 val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
242 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
247 /* Reset the switch. */
248 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0xc400);
249 for (i = 0; i < 100; i++) {
250 val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_STATUS);
251 if ((val & 0xc800) == 0xc800)
256 device_printf(sc->sc_dev, "%s: switch reset timed out.\n", __func__);
261 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0x0000);
263 /* Configure host port and send monitor frames to it. */
264 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_MONITOR,
265 (MV88E61XX_HOST_PORT << 12) | (MV88E61XX_HOST_PORT << 8) |
266 (MV88E61XX_HOST_PORT << 4));
268 /* Disable remote management. */
269 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_REMOTE_MGMT, 0x0000);
271 /* Send all specifically-addressed frames to the host port. */
272 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_2X, 0xffff);
273 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_0X, 0xffff);
275 /* Remove provider-supplied tag and use it for switching. */
276 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_CONTROL2,
277 MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG);
279 /* Configure all ports. */
280 for (port = 0; port < MV88E61XX_PORTS; port++) {
281 struct mv88e61xxphy_port_softc *psc;
283 psc = &sc->sc_ports[port];
284 mv88e61xxphy_init_port(psc);
287 /* Reprogram VLAN table (VTU.) */
288 mv88e61xxphy_init_vtu(sc);
290 /* Enable all ports. */
291 for (port = 0; port < MV88E61XX_PORTS; port++) {
292 struct mv88e61xxphy_port_softc *psc;
294 psc = &sc->sc_ports[port];
296 val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
298 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
303 mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *psc)
305 struct mv88e61xxphy_softc *sc;
310 /* Set media type and flow control. */
311 if (psc->sc_port != MV88E61XX_HOST_PORT) {
312 /* Don't force any media type or flow control. */
313 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x0003);
315 /* Make CPU port 1G FDX. */
316 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x003e);
319 /* Don't limit flow control pauses. */
320 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PAUSE_CONTROL, 0x0000);
322 /* Set various port functions per Linux. */
323 if (psc->sc_port != MV88E61XX_HOST_PORT) {
324 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x04bc);
327 * Send frames for unknown unicast and multicast groups to
330 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x063f);
333 if (psc->sc_port != MV88E61XX_HOST_PORT) {
334 /* Disable trunking. */
335 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x0000);
337 /* Disable trunking and send learn messages to host. */
338 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x8000);
342 * Port-based VLAN map; isolates MAC tables and forces ports to talk
345 * Always allow the host to send to all ports and allow all ports to
348 if (psc->sc_port != MV88E61XX_HOST_PORT) {
349 allow_mask = 1 << MV88E61XX_HOST_PORT;
351 allow_mask = (1 << MV88E61XX_PORTS) - 1;
352 allow_mask &= ~(1 << MV88E61XX_HOST_PORT);
354 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN_MAP,
355 (psc->sc_domain << 12) | allow_mask);
357 /* VLAN tagging. Set default priority and VLAN tag (or none.) */
358 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN,
359 (psc->sc_priority << 14) | psc->sc_vlan);
361 if (psc->sc_port == MV88E61XX_HOST_PORT) {
362 /* Set provider ingress tag. */
363 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PROVIDER_PROTO,
366 /* Set provider egress tag. */
367 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_ETHER_PROTO,
370 /* Use secure 802.1q mode and accept only tagged frames. */
371 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
372 MV88E61XX_PORT_FILTER_MAP_DEST |
373 MV88E61XX_PORT_FILTER_8021Q_SECURE |
374 MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED);
376 /* Don't allow tagged frames. */
377 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
378 MV88E61XX_PORT_FILTER_MAP_DEST |
379 MV88E61XX_PORT_FILTER_DISCARD_TAGGED);
384 mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *sc)
389 * Start flush of the VTU.
391 mv88e61xxphy_vtu_wait(sc);
392 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
393 MV88E61XX_GLOBAL_VTU_OP_BUSY | MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH);
396 * Queue each port's VLAN to be programmed.
398 for (port = 0; port < MV88E61XX_PORTS; port++) {
399 struct mv88e61xxphy_port_softc *psc;
401 psc = &sc->sc_ports[port];
402 psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
403 if (psc->sc_vlan == 0)
405 psc->sc_flags |= MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
409 * Program each VLAN that is in use.
411 for (port = 0; port < MV88E61XX_PORTS; port++) {
412 struct mv88e61xxphy_port_softc *psc;
414 psc = &sc->sc_ports[port];
415 if ((psc->sc_flags & MV88E61XXPHY_PORT_FLAG_VTU_UPDATE) == 0)
417 mv88e61xxphy_vtu_load(sc, psc->sc_vlan);
421 * Wait for last pending VTU operation to complete.
423 mv88e61xxphy_vtu_wait(sc);
427 mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS)
429 struct mv88e61xxphy_port_softc *psc = arg1;
430 enum mv88e61xxphy_sysctl_link_type type = arg2;
434 val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_STATUS);
436 case MV88E61XXPHY_LINK_SYSCTL_DUPLEX:
437 if ((val & MV88E61XX_PORT_STATUS_DUPLEX) != 0)
442 case MV88E61XXPHY_LINK_SYSCTL_LINK:
443 if ((val & MV88E61XX_PORT_STATUS_LINK) != 0)
448 case MV88E61XXPHY_LINK_SYSCTL_MEDIA:
449 switch (val & MV88E61XX_PORT_STATUS_MEDIA) {
450 case MV88E61XX_PORT_STATUS_MEDIA_10M:
453 case MV88E61XX_PORT_STATUS_MEDIA_100M:
456 case MV88E61XX_PORT_STATUS_MEDIA_1G:
467 return (sysctl_handle_int(oidp, NULL, out, req));
471 mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS)
473 struct mv88e61xxphy_port_softc *psc = arg1;
474 enum mv88e61xxphy_sysctl_port_type type = arg2;
475 struct mv88e61xxphy_softc *sc = psc->sc_switch;
476 unsigned max, val, *valp;
480 case MV88E61XXPHY_PORT_SYSCTL_DOMAIN:
481 valp = &psc->sc_domain;
484 case MV88E61XXPHY_PORT_SYSCTL_VLAN:
485 valp = &psc->sc_vlan;
488 case MV88E61XXPHY_PORT_SYSCTL_PRIORITY:
489 valp = &psc->sc_priority;
497 error = sysctl_handle_int(oidp, &val, 0, req);
498 if (error != 0 || req->newptr == NULL)
501 /* Bounds check value. */
505 /* Reinitialize switch with new value. */
507 mv88e61xxphy_init(sc);
513 mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *sc, uint16_t vid)
518 * Wait for previous operation to complete.
520 mv88e61xxphy_vtu_wait(sc);
525 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_VID,
526 MV88E61XX_GLOBAL_VTU_VID_VALID | vid);
529 * Add ports to this VTU.
531 for (port = 0; port < MV88E61XX_PORTS; port++) {
532 struct mv88e61xxphy_port_softc *psc;
534 psc = &sc->sc_ports[port];
535 if (psc->sc_vlan == vid) {
537 * Send this port its VLAN traffic untagged.
539 psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
540 mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_UNTAGGED);
541 } else if (psc->sc_port == MV88E61XX_HOST_PORT) {
543 * The host sees all VLANs tagged.
545 mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_TAGGED);
548 * This port isn't on this VLAN.
550 mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_DISCARDED);
555 * Start adding this entry.
557 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
558 MV88E61XX_GLOBAL_VTU_OP_BUSY |
559 MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD);
563 mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *sc, unsigned port,
564 enum mv88e61xxphy_vtu_membership_type type)
571 case MV88E61XXPHY_VTU_UNMODIFIED:
574 case MV88E61XXPHY_VTU_UNTAGGED:
577 case MV88E61XXPHY_VTU_TAGGED:
580 case MV88E61XXPHY_VTU_DISCARDED:
588 reg = MV88E61XX_GLOBAL_VTU_DATA_P0P3;
591 reg = MV88E61XX_GLOBAL_VTU_DATA_P4P5;
592 shift = (port - 4) * 4;
595 val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, reg);
596 val |= bits << shift;
597 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, reg, val);
601 mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *sc)
606 val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP);
607 if ((val & MV88E61XX_GLOBAL_VTU_OP_BUSY) == 0)
612 static device_method_t mv88e61xxphy_methods[] = {
613 /* device interface */
614 DEVMETHOD(device_probe, mv88e61xxphy_probe),
615 DEVMETHOD(device_attach, mv88e61xxphy_attach),
616 DEVMETHOD(device_detach, bus_generic_detach),
617 DEVMETHOD(device_shutdown, bus_generic_shutdown),
622 static devclass_t mv88e61xxphy_devclass;
624 static driver_t mv88e61xxphy_driver = {
626 mv88e61xxphy_methods,
627 sizeof(struct mv88e61xxphy_softc)
630 DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);