mv88e61xxphy.c revision 213762
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: head/sys/mips/cavium/octe/mv88e61xxphy.c 213762 2010-10-13 09:17:44Z jmallett $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: head/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