1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2010 Juli Mallett <[email protected]>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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.
15  *
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
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 /*
35  * Driver for the Marvell 88E61xx family of switch PHYs
36  */
37 
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>
44 #include <sys/bus.h>
45 #include <sys/sysctl.h>
46 
47 #include <net/ethernet.h>
48 #include <net/if.h>
49 #include <net/if_media.h>
50 
51 #include "miibus_if.h"
52 
53 #include "mv88e61xxphyreg.h"
54 
55 struct mv88e61xxphy_softc;
56 
57 struct mv88e61xxphy_port_softc {
58 	struct mv88e61xxphy_softc *sc_switch;
59 	unsigned sc_port;
60 	unsigned sc_domain;
61 	unsigned sc_vlan;
62 	unsigned sc_priority;
63 	unsigned sc_flags;
64 };
65 
66 #define	MV88E61XXPHY_PORT_FLAG_VTU_UPDATE	(0x0001)
67 
68 struct mv88e61xxphy_softc {
69 	device_t sc_dev;
70 	struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS];
71 };
72 
73 enum mv88e61xxphy_vtu_membership_type {
74 	MV88E61XXPHY_VTU_UNMODIFIED,
75 	MV88E61XXPHY_VTU_UNTAGGED,
76 	MV88E61XXPHY_VTU_TAGGED,
77 	MV88E61XXPHY_VTU_DISCARDED,
78 };
79 
80 enum mv88e61xxphy_sysctl_link_type {
81 	MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
82 	MV88E61XXPHY_LINK_SYSCTL_LINK,
83 	MV88E61XXPHY_LINK_SYSCTL_MEDIA,
84 };
85 
86 enum mv88e61xxphy_sysctl_port_type {
87 	MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
88 	MV88E61XXPHY_PORT_SYSCTL_VLAN,
89 	MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
90 };
91 
92 /*
93  * Register access macros.
94  */
95 #define	MV88E61XX_READ(sc, phy, reg)					\
96 	MIIBUS_READREG(device_get_parent((sc)->sc_dev), (phy), (reg))
97 
98 #define	MV88E61XX_WRITE(sc, phy, reg, val)				\
99 	MIIBUS_WRITEREG(device_get_parent((sc)->sc_dev), (phy), (reg), (val))
100 
101 #define	MV88E61XX_READ_PORT(psc, reg)					\
102 	MV88E61XX_READ((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg))
103 
104 #define	MV88E61XX_WRITE_PORT(psc, reg, val)				\
105 	MV88E61XX_WRITE((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg), (val))
106 
107 static int mv88e61xxphy_probe(device_t);
108 static int mv88e61xxphy_attach(device_t);
109 
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 *);
118 
119 static int
mv88e61xxphy_probe(device_t dev)120 mv88e61xxphy_probe(device_t dev)
121 {
122 	uint16_t val;
123 
124 	val = MIIBUS_READREG(device_get_parent(dev), MV88E61XX_PORT(0),
125 	    MV88E61XX_PORT_REVISION);
126 	switch (val >> 4) {
127 	case 0x121:
128 		device_set_desc(dev, "Marvell Link Street 88E6123 3-Port Gigabit Switch");
129 		return (0);
130 	case 0x161:
131 		device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Gigabit Switch");
132 		return (0);
133 	case 0x165:
134 		device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Advanced Gigabit Switch");
135 		return (0);
136 	default:
137 		return (ENXIO);
138 	}
139 }
140 
141 static int
mv88e61xxphy_attach(device_t dev)142 mv88e61xxphy_attach(device_t dev)
143 {
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;
151 	unsigned port;
152 
153 	sc = device_get_softc(dev);
154 	sc->sc_dev = dev;
155 
156 	/*
157 	 * Initialize port softcs.
158 	 */
159 	for (port = 0; port < MV88E61XX_PORTS; port++) {
160 		struct mv88e61xxphy_port_softc *psc;
161 
162 		psc = &sc->sc_ports[port];
163 		psc->sc_switch = sc;
164 		psc->sc_port = 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.  */
168 		psc->sc_flags = 0;
169 	}
170 
171 	/*
172 	 * Add per-port sysctl tree/handlers.
173 	 */
174 	port_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "port",
175 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Switch Ports");
176 	port_tree = SYSCTL_CHILDREN(port_node);
177 	for (port = 0; port < MV88E61XX_PORTS; port++) {
178 		struct mv88e61xxphy_port_softc *psc;
179 
180 		psc = &sc->sc_ports[port];
181 
182 		portbuf[0] = '0' + port;
183 		portN_node = SYSCTL_ADD_NODE(ctx, port_tree, OID_AUTO, portbuf,
184 		    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Switch Port");
185 		portN_tree = SYSCTL_CHILDREN(portN_node);
186 
187 		SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "duplex",
188 		    CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_NEEDGIANT, psc,
189 		    MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
190 		    mv88e61xxphy_sysctl_link_proc, "IU",
191 		    "Media duplex status (0 = half duplex; 1 = full duplex)");
192 
193 		SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "link",
194 		    CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_NEEDGIANT, psc,
195 		    MV88E61XXPHY_LINK_SYSCTL_LINK,
196 		    mv88e61xxphy_sysctl_link_proc, "IU",
197 		    "Link status (0 = down; 1 = up)");
198 
199 		SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "media",
200 		    CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_NEEDGIANT, psc,
201 		    MV88E61XXPHY_LINK_SYSCTL_MEDIA,
202 		    mv88e61xxphy_sysctl_link_proc, "IU",
203 		    "Media speed (0 = unknown; 10 = 10Mbps; 100 = 100Mbps; 1000 = 1Gbps)");
204 
205 		SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "domain",
206 		    CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT, 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)");
210 
211 		SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "vlan",
212 		    CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT, psc,
213 		    MV88E61XXPHY_PORT_SYSCTL_VLAN,
214 		    mv88e61xxphy_sysctl_port_proc, "IU",
215 		    "Tag packets from/for this port with a given VLAN.");
216 
217 		SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "priority",
218 		    CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT, psc,
219 		    MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
220 		    mv88e61xxphy_sysctl_port_proc, "IU",
221 		    "Default packet priority for this port.");
222 	}
223 
224 	mv88e61xxphy_init(sc);
225 
226 	return (0);
227 }
228 
229 static void
mv88e61xxphy_init(struct mv88e61xxphy_softc * sc)230 mv88e61xxphy_init(struct mv88e61xxphy_softc *sc)
231 {
232 	unsigned port;
233 	uint16_t val;
234 	unsigned i;
235 
236 	/* Disable all ports.  */
237 	for (port = 0; port < MV88E61XX_PORTS; port++) {
238 		struct mv88e61xxphy_port_softc *psc;
239 
240 		psc = &sc->sc_ports[port];
241 
242 		val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
243 		val &= ~0x3;
244 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
245 	}
246 
247 	DELAY(2000);
248 
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)
254 			break;
255 		DELAY(10);
256 	}
257 	if (i == 100) {
258 		device_printf(sc->sc_dev, "%s: switch reset timed out.\n", __func__);
259 		return;
260 	}
261 
262 	/* Disable PPU.  */
263 	MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0x0000);
264 
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));
269 
270 	/* Disable remote management.  */
271 	MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_REMOTE_MGMT, 0x0000);
272 
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);
276 
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);
280 
281 	/* Configure all ports.  */
282 	for (port = 0; port < MV88E61XX_PORTS; port++) {
283 		struct mv88e61xxphy_port_softc *psc;
284 
285 		psc = &sc->sc_ports[port];
286 		mv88e61xxphy_init_port(psc);
287 	}
288 
289 	/* Reprogram VLAN table (VTU.)  */
290 	mv88e61xxphy_init_vtu(sc);
291 
292 	/* Enable all ports.  */
293 	for (port = 0; port < MV88E61XX_PORTS; port++) {
294 		struct mv88e61xxphy_port_softc *psc;
295 
296 		psc = &sc->sc_ports[port];
297 
298 		val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
299 		val |= 0x3;
300 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
301 	}
302 }
303 
304 static void
mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc * psc)305 mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *psc)
306 {
307 	struct mv88e61xxphy_softc *sc;
308 	unsigned allow_mask;
309 
310 	sc = psc->sc_switch;
311 
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);
316 	} else {
317 		/* Make CPU port 1G FDX.  */
318 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x003e);
319 	}
320 
321 	/* Don't limit flow control pauses.  */
322 	MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PAUSE_CONTROL, 0x0000);
323 
324 	/* Set various port functions per Linux.  */
325 	if (psc->sc_port != MV88E61XX_HOST_PORT) {
326 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x04bc);
327 	} else {
328 		/*
329 		 * Send frames for unknown unicast and multicast groups to
330 		 * host, too.
331 		 */
332 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x063f);
333 	}
334 
335 	if (psc->sc_port != MV88E61XX_HOST_PORT) {
336 		/* Disable trunking.  */
337 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x0000);
338 	} else {
339 		/* Disable trunking and send learn messages to host.  */
340 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x8000);
341 	}
342 
343 	/*
344 	 * Port-based VLAN map; isolates MAC tables and forces ports to talk
345 	 * only to the host.
346 	 *
347 	 * Always allow the host to send to all ports and allow all ports to
348 	 * send to the host.
349 	 */
350 	if (psc->sc_port != MV88E61XX_HOST_PORT) {
351 		allow_mask = 1 << MV88E61XX_HOST_PORT;
352 	} else {
353 		allow_mask = (1 << MV88E61XX_PORTS) - 1;
354 		allow_mask &= ~(1 << MV88E61XX_HOST_PORT);
355 	}
356 	MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN_MAP,
357 	    (psc->sc_domain << 12) | allow_mask);
358 
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);
362 
363 	if (psc->sc_port == MV88E61XX_HOST_PORT) {
364 		/* Set provider ingress tag.  */
365 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PROVIDER_PROTO,
366 		    ETHERTYPE_VLAN);
367 
368 		/* Set provider egress tag.  */
369 		MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_ETHER_PROTO,
370 		    ETHERTYPE_VLAN);
371 
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);
377 	} else {
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);
382 	}
383 }
384 
385 static void
mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc * sc)386 mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *sc)
387 {
388 	unsigned port;
389 
390 	/*
391 	 * Start flush of the VTU.
392 	 */
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);
396 
397 	/*
398 	 * Queue each port's VLAN to be programmed.
399 	 */
400 	for (port = 0; port < MV88E61XX_PORTS; port++) {
401 		struct mv88e61xxphy_port_softc *psc;
402 
403 		psc = &sc->sc_ports[port];
404 		psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
405 		if (psc->sc_vlan == 0)
406 			continue;
407 		psc->sc_flags |= MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
408 	}
409 
410 	/*
411 	 * Program each VLAN that is in use.
412 	 */
413 	for (port = 0; port < MV88E61XX_PORTS; port++) {
414 		struct mv88e61xxphy_port_softc *psc;
415 
416 		psc = &sc->sc_ports[port];
417 		if ((psc->sc_flags & MV88E61XXPHY_PORT_FLAG_VTU_UPDATE) == 0)
418 			continue;
419 		mv88e61xxphy_vtu_load(sc, psc->sc_vlan);
420 	}
421 
422 	/*
423 	 * Wait for last pending VTU operation to complete.
424 	 */
425 	mv88e61xxphy_vtu_wait(sc);
426 }
427 
428 static int
mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS)429 mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS)
430 {
431 	struct mv88e61xxphy_port_softc *psc = arg1;
432 	enum mv88e61xxphy_sysctl_link_type type = arg2;
433 	uint16_t val;
434 	unsigned out;
435 
436 	val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_STATUS);
437 	switch (type) {
438 	case MV88E61XXPHY_LINK_SYSCTL_DUPLEX:
439 		if ((val & MV88E61XX_PORT_STATUS_DUPLEX) != 0)
440 			out = 1;
441 		else
442 			out = 0;
443 		break;
444 	case MV88E61XXPHY_LINK_SYSCTL_LINK:
445 		if ((val & MV88E61XX_PORT_STATUS_LINK) != 0)
446 			out = 1;
447 		else
448 			out = 0;
449 		break;
450 	case MV88E61XXPHY_LINK_SYSCTL_MEDIA:
451 		switch (val & MV88E61XX_PORT_STATUS_MEDIA) {
452 		case MV88E61XX_PORT_STATUS_MEDIA_10M:
453 			out = 10;
454 			break;
455 		case MV88E61XX_PORT_STATUS_MEDIA_100M:
456 			out = 100;
457 			break;
458 		case MV88E61XX_PORT_STATUS_MEDIA_1G:
459 			out = 1000;
460 			break;
461 		default:
462 			out = 0;
463 			break;
464 		}
465 		break;
466 	default:
467 		return (EINVAL);
468 	}
469 	return (sysctl_handle_int(oidp, NULL, out, req));
470 }
471 
472 static int
mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS)473 mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS)
474 {
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;
479 	int error;
480 
481 	switch (type) {
482 	case MV88E61XXPHY_PORT_SYSCTL_DOMAIN:
483 		valp = &psc->sc_domain;
484 		max = 0xf;
485 		break;
486 	case MV88E61XXPHY_PORT_SYSCTL_VLAN:
487 		valp = &psc->sc_vlan;
488 		max = 0x1000;
489 		break;
490 	case MV88E61XXPHY_PORT_SYSCTL_PRIORITY:
491 		valp = &psc->sc_priority;
492 		max = 3;
493 		break;
494 	default:
495 		return (EINVAL);
496 	}
497 
498 	val = *valp;
499 	error = sysctl_handle_int(oidp, &val, 0, req);
500 	if (error != 0 || req->newptr == NULL)
501 		return (error);
502 
503 	/* Bounds check value.  */
504 	if (val >= max)
505 		return (EINVAL);
506 
507 	/* Reinitialize switch with new value.  */
508 	*valp = val;
509 	mv88e61xxphy_init(sc);
510 
511 	return (0);
512 }
513 
514 static void
mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc * sc,uint16_t vid)515 mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *sc, uint16_t vid)
516 {
517 	unsigned port;
518 
519 	/*
520 	 * Wait for previous operation to complete.
521 	 */
522 	mv88e61xxphy_vtu_wait(sc);
523 
524 	/*
525 	 * Set VID.
526 	 */
527 	MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_VID,
528 	    MV88E61XX_GLOBAL_VTU_VID_VALID | vid);
529 
530 	/*
531 	 * Add ports to this VTU.
532 	 */
533 	for (port = 0; port < MV88E61XX_PORTS; port++) {
534 		struct mv88e61xxphy_port_softc *psc;
535 
536 		psc = &sc->sc_ports[port];
537 		if (psc->sc_vlan == vid) {
538 			/*
539 			 * Send this port its VLAN traffic untagged.
540 			 */
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) {
544 			/*
545 			 * The host sees all VLANs tagged.
546 			 */
547 			mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_TAGGED);
548 		} else {
549 			/*
550 			 * This port isn't on this VLAN.
551 			 */
552 			mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_DISCARDED);
553 		}
554 	}
555 
556 	/*
557 	 * Start adding this entry.
558 	 */
559 	MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
560 	    MV88E61XX_GLOBAL_VTU_OP_BUSY |
561 	    MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD);
562 }
563 
564 static void
mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc * sc,unsigned port,enum mv88e61xxphy_vtu_membership_type type)565 mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *sc, unsigned port,
566     enum mv88e61xxphy_vtu_membership_type type)
567 {
568 	unsigned shift, reg;
569 	uint16_t bits;
570 	uint16_t val;
571 
572 	switch (type) {
573 	case MV88E61XXPHY_VTU_UNMODIFIED:
574 		bits = 0;
575 		break;
576 	case MV88E61XXPHY_VTU_UNTAGGED:
577 		bits = 1;
578 		break;
579 	case MV88E61XXPHY_VTU_TAGGED:
580 		bits = 2;
581 		break;
582 	case MV88E61XXPHY_VTU_DISCARDED:
583 		bits = 3;
584 		break;
585 	default:
586 		return;
587 	}
588 
589 	if (port < 4) {
590 		reg = MV88E61XX_GLOBAL_VTU_DATA_P0P3;
591 		shift = port * 4;
592 	} else {
593 		reg = MV88E61XX_GLOBAL_VTU_DATA_P4P5;
594 		shift = (port - 4) * 4;
595 	}
596 
597 	val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, reg);
598 	val |= bits << shift;
599 	MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, reg, val);
600 }
601 
602 static void
mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc * sc)603 mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *sc)
604 {
605 	uint16_t val;
606 
607 	for (;;) {
608 		val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP);
609 		if ((val & MV88E61XX_GLOBAL_VTU_OP_BUSY) == 0)
610 			return;
611 	}
612 }
613 
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),
620 	{ 0, 0 }
621 };
622 
623 static devclass_t mv88e61xxphy_devclass;
624 
625 static driver_t mv88e61xxphy_driver = {
626 	"mv88e61xxphy",
627 	mv88e61xxphy_methods,
628 	sizeof(struct mv88e61xxphy_softc)
629 };
630 
631 DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);
632