2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
35 * Driver for the Marvell 88E61xx family of switch PHYs
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/socket.h>
42 #include <sys/errno.h>
43 #include <sys/module.h>
45 #include <sys/sysctl.h>
47 #include <net/ethernet.h>
49 #include <net/if_media.h>
51 #include "miibus_if.h"
53 #include "mv88e61xxphyreg.h"
55 struct mv88e61xxphy_softc;
57 struct mv88e61xxphy_port_softc {
58 struct mv88e61xxphy_softc *sc_switch;
66 #define MV88E61XXPHY_PORT_FLAG_VTU_UPDATE (0x0001)
68 struct mv88e61xxphy_softc {
70 struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS];
73 enum mv88e61xxphy_vtu_membership_type {
74 MV88E61XXPHY_VTU_UNMODIFIED,
75 MV88E61XXPHY_VTU_UNTAGGED,
76 MV88E61XXPHY_VTU_TAGGED,
77 MV88E61XXPHY_VTU_DISCARDED,
80 enum mv88e61xxphy_sysctl_link_type {
81 MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
82 MV88E61XXPHY_LINK_SYSCTL_LINK,
83 MV88E61XXPHY_LINK_SYSCTL_MEDIA,
86 enum mv88e61xxphy_sysctl_port_type {
87 MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
88 MV88E61XXPHY_PORT_SYSCTL_VLAN,
89 MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
93 * Register access macros.
95 #define MV88E61XX_READ(sc, phy, reg) \
96 MIIBUS_READREG(device_get_parent((sc)->sc_dev), (phy), (reg))
98 #define MV88E61XX_WRITE(sc, phy, reg, val) \
99 MIIBUS_WRITEREG(device_get_parent((sc)->sc_dev), (phy), (reg), (val))
101 #define MV88E61XX_READ_PORT(psc, reg) \
102 MV88E61XX_READ((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg))
104 #define MV88E61XX_WRITE_PORT(psc, reg, val) \
105 MV88E61XX_WRITE((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg), (val))
107 static int mv88e61xxphy_probe(device_t);
108 static int mv88e61xxphy_attach(device_t);
110 static void mv88e61xxphy_init(struct mv88e61xxphy_softc *);
111 static void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *);
112 static void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *);
113 static int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS);
114 static int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS);
115 static void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t);
116 static void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type);
117 static void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *);
120 mv88e61xxphy_probe(device_t dev)
124 val = MIIBUS_READREG(device_get_parent(dev), MV88E61XX_PORT(0),
125 MV88E61XX_PORT_REVISION);
128 device_set_desc(dev, "Marvell Link Street 88E6123 3-Port Gigabit Switch");
131 device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Gigabit Switch");
134 device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Advanced Gigabit Switch");
142 mv88e61xxphy_attach(device_t dev)
144 char portbuf[] = "N";
145 struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
146 struct sysctl_oid *tree = device_get_sysctl_tree(dev);
147 struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
148 struct sysctl_oid *port_node, *portN_node;
149 struct sysctl_oid_list *port_tree, *portN_tree;
150 struct mv88e61xxphy_softc *sc;
153 sc = device_get_softc(dev);
157 * Initialize port softcs.
159 for (port = 0; port < MV88E61XX_PORTS; port++) {
160 struct mv88e61xxphy_port_softc *psc;
162 psc = &sc->sc_ports[port];
165 psc->sc_domain = 0; /* One broadcast domain by default. */
166 psc->sc_vlan = port + 1; /* Tag VLANs by default. */
167 psc->sc_priority = 0; /* No default special priority. */
172 * Add per-port sysctl tree/handlers.
174 port_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "port",
175 CTLFLAG_RD, NULL, "Switch Ports");
176 port_tree = SYSCTL_CHILDREN(port_node);
177 for (port = 0; port < MV88E61XX_PORTS; port++) {
178 struct mv88e61xxphy_port_softc *psc;
180 psc = &sc->sc_ports[port];
182 portbuf[0] = '0' + port;
183 portN_node = SYSCTL_ADD_NODE(ctx, port_tree, OID_AUTO, portbuf,
184 CTLFLAG_RD, NULL, "Switch Port");
185 portN_tree = SYSCTL_CHILDREN(portN_node);
187 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "duplex",
188 CTLFLAG_RD | CTLTYPE_INT, psc,
189 MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
190 mv88e61xxphy_sysctl_link_proc, "IU",
191 "Media duplex status (0 = half duplex; 1 = full duplex)");
193 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "link",
194 CTLFLAG_RD | CTLTYPE_INT, psc,
195 MV88E61XXPHY_LINK_SYSCTL_LINK,
196 mv88e61xxphy_sysctl_link_proc, "IU",
197 "Link status (0 = down; 1 = up)");
199 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "media",
200 CTLFLAG_RD | CTLTYPE_INT, psc,
201 MV88E61XXPHY_LINK_SYSCTL_MEDIA,
202 mv88e61xxphy_sysctl_link_proc, "IU",
203 "Media speed (0 = unknown; 10 = 10Mbps; 100 = 100Mbps; 1000 = 1Gbps)");
205 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "domain",
206 CTLFLAG_RW | CTLTYPE_INT, psc,
207 MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
208 mv88e61xxphy_sysctl_port_proc, "IU",
209 "Broadcast domain (ports can only talk to other ports in the same domain)");
211 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "vlan",
212 CTLFLAG_RW | CTLTYPE_INT, psc,
213 MV88E61XXPHY_PORT_SYSCTL_VLAN,
214 mv88e61xxphy_sysctl_port_proc, "IU",
215 "Tag packets from/for this port with a given VLAN.");
217 SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "priority",
218 CTLFLAG_RW | CTLTYPE_INT, psc,
219 MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
220 mv88e61xxphy_sysctl_port_proc, "IU",
221 "Default packet priority for this port.");
224 mv88e61xxphy_init(sc);
230 mv88e61xxphy_init(struct mv88e61xxphy_softc *sc)
236 /* Disable all ports. */
237 for (port = 0; port < MV88E61XX_PORTS; port++) {
238 struct mv88e61xxphy_port_softc *psc;
240 psc = &sc->sc_ports[port];
242 val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
244 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
249 /* Reset the switch. */
250 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0xc400);
251 for (i = 0; i < 100; i++) {
252 val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_STATUS);
253 if ((val & 0xc800) == 0xc800)
258 device_printf(sc->sc_dev, "%s: switch reset timed out.\n", __func__);
263 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0x0000);
265 /* Configure host port and send monitor frames to it. */
266 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_MONITOR,
267 (MV88E61XX_HOST_PORT << 12) | (MV88E61XX_HOST_PORT << 8) |
268 (MV88E61XX_HOST_PORT << 4));
270 /* Disable remote management. */
271 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_REMOTE_MGMT, 0x0000);
273 /* Send all specifically-addressed frames to the host port. */
274 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_2X, 0xffff);
275 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_0X, 0xffff);
277 /* Remove provider-supplied tag and use it for switching. */
278 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_CONTROL2,
279 MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG);
281 /* Configure all ports. */
282 for (port = 0; port < MV88E61XX_PORTS; port++) {
283 struct mv88e61xxphy_port_softc *psc;
285 psc = &sc->sc_ports[port];
286 mv88e61xxphy_init_port(psc);
289 /* Reprogram VLAN table (VTU.) */
290 mv88e61xxphy_init_vtu(sc);
292 /* Enable all ports. */
293 for (port = 0; port < MV88E61XX_PORTS; port++) {
294 struct mv88e61xxphy_port_softc *psc;
296 psc = &sc->sc_ports[port];
298 val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
300 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
305 mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *psc)
307 struct mv88e61xxphy_softc *sc;
312 /* Set media type and flow control. */
313 if (psc->sc_port != MV88E61XX_HOST_PORT) {
314 /* Don't force any media type or flow control. */
315 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x0003);
317 /* Make CPU port 1G FDX. */
318 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x003e);
321 /* Don't limit flow control pauses. */
322 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PAUSE_CONTROL, 0x0000);
324 /* Set various port functions per Linux. */
325 if (psc->sc_port != MV88E61XX_HOST_PORT) {
326 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x04bc);
329 * Send frames for unknown unicast and multicast groups to
332 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x063f);
335 if (psc->sc_port != MV88E61XX_HOST_PORT) {
336 /* Disable trunking. */
337 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x0000);
339 /* Disable trunking and send learn messages to host. */
340 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x8000);
344 * Port-based VLAN map; isolates MAC tables and forces ports to talk
347 * Always allow the host to send to all ports and allow all ports to
350 if (psc->sc_port != MV88E61XX_HOST_PORT) {
351 allow_mask = 1 << MV88E61XX_HOST_PORT;
353 allow_mask = (1 << MV88E61XX_PORTS) - 1;
354 allow_mask &= ~(1 << MV88E61XX_HOST_PORT);
356 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN_MAP,
357 (psc->sc_domain << 12) | allow_mask);
359 /* VLAN tagging. Set default priority and VLAN tag (or none.) */
360 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN,
361 (psc->sc_priority << 14) | psc->sc_vlan);
363 if (psc->sc_port == MV88E61XX_HOST_PORT) {
364 /* Set provider ingress tag. */
365 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PROVIDER_PROTO,
368 /* Set provider egress tag. */
369 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_ETHER_PROTO,
372 /* Use secure 802.1q mode and accept only tagged frames. */
373 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
374 MV88E61XX_PORT_FILTER_MAP_DEST |
375 MV88E61XX_PORT_FILTER_8021Q_SECURE |
376 MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED);
378 /* Don't allow tagged frames. */
379 MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
380 MV88E61XX_PORT_FILTER_MAP_DEST |
381 MV88E61XX_PORT_FILTER_DISCARD_TAGGED);
386 mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *sc)
391 * Start flush of the VTU.
393 mv88e61xxphy_vtu_wait(sc);
394 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
395 MV88E61XX_GLOBAL_VTU_OP_BUSY | MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH);
398 * Queue each port's VLAN to be programmed.
400 for (port = 0; port < MV88E61XX_PORTS; port++) {
401 struct mv88e61xxphy_port_softc *psc;
403 psc = &sc->sc_ports[port];
404 psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
405 if (psc->sc_vlan == 0)
407 psc->sc_flags |= MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
411 * Program each VLAN that is in use.
413 for (port = 0; port < MV88E61XX_PORTS; port++) {
414 struct mv88e61xxphy_port_softc *psc;
416 psc = &sc->sc_ports[port];
417 if ((psc->sc_flags & MV88E61XXPHY_PORT_FLAG_VTU_UPDATE) == 0)
419 mv88e61xxphy_vtu_load(sc, psc->sc_vlan);
423 * Wait for last pending VTU operation to complete.
425 mv88e61xxphy_vtu_wait(sc);
429 mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS)
431 struct mv88e61xxphy_port_softc *psc = arg1;
432 enum mv88e61xxphy_sysctl_link_type type = arg2;
436 val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_STATUS);
438 case MV88E61XXPHY_LINK_SYSCTL_DUPLEX:
439 if ((val & MV88E61XX_PORT_STATUS_DUPLEX) != 0)
444 case MV88E61XXPHY_LINK_SYSCTL_LINK:
445 if ((val & MV88E61XX_PORT_STATUS_LINK) != 0)
450 case MV88E61XXPHY_LINK_SYSCTL_MEDIA:
451 switch (val & MV88E61XX_PORT_STATUS_MEDIA) {
452 case MV88E61XX_PORT_STATUS_MEDIA_10M:
455 case MV88E61XX_PORT_STATUS_MEDIA_100M:
458 case MV88E61XX_PORT_STATUS_MEDIA_1G:
469 return (sysctl_handle_int(oidp, NULL, out, req));
473 mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS)
475 struct mv88e61xxphy_port_softc *psc = arg1;
476 enum mv88e61xxphy_sysctl_port_type type = arg2;
477 struct mv88e61xxphy_softc *sc = psc->sc_switch;
478 unsigned max, val, *valp;
482 case MV88E61XXPHY_PORT_SYSCTL_DOMAIN:
483 valp = &psc->sc_domain;
486 case MV88E61XXPHY_PORT_SYSCTL_VLAN:
487 valp = &psc->sc_vlan;
490 case MV88E61XXPHY_PORT_SYSCTL_PRIORITY:
491 valp = &psc->sc_priority;
499 error = sysctl_handle_int(oidp, &val, 0, req);
500 if (error != 0 || req->newptr == NULL)
503 /* Bounds check value. */
507 /* Reinitialize switch with new value. */
509 mv88e61xxphy_init(sc);
515 mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *sc, uint16_t vid)
520 * Wait for previous operation to complete.
522 mv88e61xxphy_vtu_wait(sc);
527 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_VID,
528 MV88E61XX_GLOBAL_VTU_VID_VALID | vid);
531 * Add ports to this VTU.
533 for (port = 0; port < MV88E61XX_PORTS; port++) {
534 struct mv88e61xxphy_port_softc *psc;
536 psc = &sc->sc_ports[port];
537 if (psc->sc_vlan == vid) {
539 * Send this port its VLAN traffic untagged.
541 psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
542 mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_UNTAGGED);
543 } else if (psc->sc_port == MV88E61XX_HOST_PORT) {
545 * The host sees all VLANs tagged.
547 mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_TAGGED);
550 * This port isn't on this VLAN.
552 mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_DISCARDED);
557 * Start adding this entry.
559 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
560 MV88E61XX_GLOBAL_VTU_OP_BUSY |
561 MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD);
565 mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *sc, unsigned port,
566 enum mv88e61xxphy_vtu_membership_type type)
573 case MV88E61XXPHY_VTU_UNMODIFIED:
576 case MV88E61XXPHY_VTU_UNTAGGED:
579 case MV88E61XXPHY_VTU_TAGGED:
582 case MV88E61XXPHY_VTU_DISCARDED:
590 reg = MV88E61XX_GLOBAL_VTU_DATA_P0P3;
593 reg = MV88E61XX_GLOBAL_VTU_DATA_P4P5;
594 shift = (port - 4) * 4;
597 val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, reg);
598 val |= bits << shift;
599 MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, reg, val);
603 mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *sc)
608 val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP);
609 if ((val & MV88E61XX_GLOBAL_VTU_OP_BUSY) == 0)
614 static device_method_t mv88e61xxphy_methods[] = {
615 /* device interface */
616 DEVMETHOD(device_probe, mv88e61xxphy_probe),
617 DEVMETHOD(device_attach, mv88e61xxphy_attach),
618 DEVMETHOD(device_detach, bus_generic_detach),
619 DEVMETHOD(device_shutdown, bus_generic_shutdown),
624 static devclass_t mv88e61xxphy_devclass;
626 static driver_t mv88e61xxphy_driver = {
628 mv88e61xxphy_methods,
629 sizeof(struct mv88e61xxphy_softc)
632 DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);