]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/mips/cavium/octe/mv88e61xxphy.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / mips / cavium / octe / mv88e61xxphy.c
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$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
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
53 struct mv88e61xxphy_softc;
54
55 struct 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
66 struct mv88e61xxphy_softc {
67         device_t sc_dev;
68         struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS];
69 };
70
71 enum mv88e61xxphy_vtu_membership_type {
72         MV88E61XXPHY_VTU_UNMODIFIED,
73         MV88E61XXPHY_VTU_UNTAGGED,
74         MV88E61XXPHY_VTU_TAGGED,
75         MV88E61XXPHY_VTU_DISCARDED,
76 };
77
78 enum mv88e61xxphy_sysctl_link_type {
79         MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
80         MV88E61XXPHY_LINK_SYSCTL_LINK,
81         MV88E61XXPHY_LINK_SYSCTL_MEDIA,
82 };
83
84 enum 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
105 static int mv88e61xxphy_probe(device_t);
106 static int mv88e61xxphy_attach(device_t);
107
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 *);
116
117 static int
118 mv88e61xxphy_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
139 static int
140 mv88e61xxphy_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
227 static void
228 mv88e61xxphy_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
302 static void
303 mv88e61xxphy_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
383 static void
384 mv88e61xxphy_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
426 static int
427 mv88e61xxphy_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
470 static int
471 mv88e61xxphy_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
512 static void
513 mv88e61xxphy_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
562 static void
563 mv88e61xxphy_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
600 static void
601 mv88e61xxphy_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
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),
618
619         { 0, 0 }
620 };
621
622 static devclass_t mv88e61xxphy_devclass;
623
624 static driver_t mv88e61xxphy_driver = {
625         "mv88e61xxphy",
626         mv88e61xxphy_methods,
627         sizeof(struct mv88e61xxphy_softc)
628 };
629
630 DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);