]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/cavium/octe/mv88e61xxphy.c
sys/mips: further adoption of SPDX licensing ID tags.
[FreeBSD/FreeBSD.git] / sys / mips / cavium / octe / mv88e61xxphy.c
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
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
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
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, 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, 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, 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, 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, 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, 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, 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, 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
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
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
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
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
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
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
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
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
621         { 0, 0 }
622 };
623
624 static devclass_t mv88e61xxphy_devclass;
625
626 static driver_t mv88e61xxphy_driver = {
627         "mv88e61xxphy",
628         mv88e61xxphy_methods,
629         sizeof(struct mv88e61xxphy_softc)
630 };
631
632 DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);