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] = '' + 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);
Cache object: d330c958d5b860a79c25a0470da288db
|