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 55struct mv88e61xxphy_softc; 56 57struct 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 68struct mv88e61xxphy_softc { 69 device_t sc_dev; 70 struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS]; 71}; 72 73enum mv88e61xxphy_vtu_membership_type { 74 MV88E61XXPHY_VTU_UNMODIFIED, 75 MV88E61XXPHY_VTU_UNTAGGED, 76 MV88E61XXPHY_VTU_TAGGED, 77 MV88E61XXPHY_VTU_DISCARDED, 78}; 79 80enum mv88e61xxphy_sysctl_link_type { 81 MV88E61XXPHY_LINK_SYSCTL_DUPLEX, 82 MV88E61XXPHY_LINK_SYSCTL_LINK, 83 MV88E61XXPHY_LINK_SYSCTL_MEDIA, 84}; 85 86enum 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 107static int mv88e61xxphy_probe(device_t); 108static int mv88e61xxphy_attach(device_t); 109 110static void mv88e61xxphy_init(struct mv88e61xxphy_softc *); 111static void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *); 112static void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *); 113static int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS); 114static int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS); 115static void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t); 116static void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type); 117static void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *); 118 119static int 120mv88e61xxphy_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 141static int 142mv88e61xxphy_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] = '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 229static void 230mv88e61xxphy_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 304static void 305mv88e61xxphy_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 385static void 386mv88e61xxphy_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 428static int 429mv88e61xxphy_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 472static int 473mv88e61xxphy_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 514static void 515mv88e61xxphy_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 564static void 565mv88e61xxphy_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 602static void 603mv88e61xxphy_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 614static 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 623static devclass_t mv88e61xxphy_devclass; 624 625static driver_t mv88e61xxphy_driver = { 626 "mv88e61xxphy", 627 mv88e61xxphy_methods, 628 sizeof(struct mv88e61xxphy_softc) 629}; 630 631DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0); 632