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