1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
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
55struct mv88e61xxphy_softc;
56
57struct 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
68struct mv88e61xxphy_softc {
69	device_t sc_dev;
70	struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS];
71};
72
73enum mv88e61xxphy_vtu_membership_type {
74	MV88E61XXPHY_VTU_UNMODIFIED,
75	MV88E61XXPHY_VTU_UNTAGGED,
76	MV88E61XXPHY_VTU_TAGGED,
77	MV88E61XXPHY_VTU_DISCARDED,
78};
79
80enum mv88e61xxphy_sysctl_link_type {
81	MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
82	MV88E61XXPHY_LINK_SYSCTL_LINK,
83	MV88E61XXPHY_LINK_SYSCTL_MEDIA,
84};
85
86enum 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
107static int mv88e61xxphy_probe(device_t);
108static int mv88e61xxphy_attach(device_t);
109
110static void mv88e61xxphy_init(struct mv88e61xxphy_softc *);
111static void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *);
112static void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *);
113static int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS);
114static int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS);
115static void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t);
116static void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type);
117static void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *);
118
119static int
120mv88e61xxphy_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
141static int
142mv88e61xxphy_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
229static void
230mv88e61xxphy_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
304static void
305mv88e61xxphy_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
385static void
386mv88e61xxphy_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
428static int
429mv88e61xxphy_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
472static int
473mv88e61xxphy_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
514static void
515mv88e61xxphy_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
564static void
565mv88e61xxphy_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
602static void
603mv88e61xxphy_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
614static 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
623static devclass_t mv88e61xxphy_devclass;
624
625static driver_t mv88e61xxphy_driver = {
626	"mv88e61xxphy",
627	mv88e61xxphy_methods,
628	sizeof(struct mv88e61xxphy_softc)
629};
630
631DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);
632