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 | 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] = '' + 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
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 { 0, 0 }
621 };
622
623 static devclass_t mv88e61xxphy_devclass;
624
625 static driver_t mv88e61xxphy_driver = {
626 "mv88e61xxphy",
627 mv88e61xxphy_methods,
628 sizeof(struct mv88e61xxphy_softc)
629 };
630
631 DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);
Cache object: 772d96eec684cf14b10dbcbeb12307cb
|