1 /*- 2 * Copyright (c) 2016 Hiroki Mori 3 * Copyright (c) 2013 Luiz Otavio O Souza. 4 * Copyright (c) 2011-2012 Stefan Bethke. 5 * Copyright (c) 2012 Adrian Chadd. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 /* 33 * This is Infineon ADM6996FC/M/MX driver code on etherswitch framework. 34 * Support PORT and DOT1Q VLAN. 35 * This code suppose ADM6996FC SDC/SDIO connect to SOC network interface 36 * MDC/MDIO. 37 * This code development on Netgear WGR614Cv7. 38 * etherswitchcfg command port option support addtag. 39 */ 40 41 #include <sys/param.h> 42 #include <sys/bus.h> 43 #include <sys/errno.h> 44 #include <sys/kernel.h> 45 #include <sys/lock.h> 46 #include <sys/malloc.h> 47 #include <sys/module.h> 48 #include <sys/mutex.h> 49 #include <sys/socket.h> 50 #include <sys/sockio.h> 51 #include <sys/sysctl.h> 52 #include <sys/systm.h> 53 54 #include <net/if.h> 55 #include <net/if_var.h> 56 #include <net/ethernet.h> 57 #include <net/if_media.h> 58 #include <net/if_types.h> 59 60 #include <machine/bus.h> 61 #include <dev/mii/mii.h> 62 #include <dev/mii/miivar.h> 63 #include <dev/mdio/mdio.h> 64 65 #include <dev/etherswitch/etherswitch.h> 66 67 #include "mdio_if.h" 68 #include "miibus_if.h" 69 #include "etherswitch_if.h" 70 71 #define ADM6996FC_PRODUCT_CODE 0x7102 72 73 #define ADM6996FC_SC3 0x11 74 #define ADM6996FC_VF0L 0x40 75 #define ADM6996FC_VF0H 0x41 76 #define ADM6996FC_CI0 0xa0 77 #define ADM6996FC_CI1 0xa1 78 #define ADM6996FC_PHY_C0 0x200 79 80 #define ADM6996FC_PC_SHIFT 4 81 #define ADM6996FC_TBV_SHIFT 5 82 #define ADM6996FC_PVID_SHIFT 10 83 #define ADM6996FC_OPTE_SHIFT 4 84 #define ADM6996FC_VV_SHIFT 15 85 86 #define ADM6996FC_PHY_SIZE 0x20 87 88 MALLOC_DECLARE(M_ADM6996FC); 89 MALLOC_DEFINE(M_ADM6996FC, "adm6996fc", "adm6996fc data structures"); 90 91 struct adm6996fc_softc { 92 struct mtx sc_mtx; /* serialize access to softc */ 93 device_t sc_dev; 94 int vlan_mode; 95 int media; /* cpu port media */ 96 int cpuport; /* which PHY is connected to the CPU */ 97 int phymask; /* PHYs we manage */ 98 int numports; /* number of ports */ 99 int ifpport[MII_NPHY]; 100 int *portphy; 101 char **ifname; 102 device_t **miibus; 103 struct ifnet **ifp; 104 struct callout callout_tick; 105 etherswitch_info_t info; 106 }; 107 108 #define ADM6996FC_LOCK(_sc) \ 109 mtx_lock(&(_sc)->sc_mtx) 110 #define ADM6996FC_UNLOCK(_sc) \ 111 mtx_unlock(&(_sc)->sc_mtx) 112 #define ADM6996FC_LOCK_ASSERT(_sc, _what) \ 113 mtx_assert(&(_sc)->sc_mtx, (_what)) 114 #define ADM6996FC_TRYLOCK(_sc) \ 115 mtx_trylock(&(_sc)->sc_mtx) 116 117 #if defined(DEBUG) 118 #define DPRINTF(dev, args...) device_printf(dev, args) 119 #else 120 #define DPRINTF(dev, args...) 121 #endif 122 123 static inline int adm6996fc_portforphy(struct adm6996fc_softc *, int); 124 static void adm6996fc_tick(void *); 125 static int adm6996fc_ifmedia_upd(struct ifnet *); 126 static void adm6996fc_ifmedia_sts(struct ifnet *, struct ifmediareq *); 127 128 #define ADM6996FC_READREG(dev, x) \ 129 MDIO_READREG(dev, ((x) >> 5), ((x) & 0x1f)); 130 #define ADM6996FC_WRITEREG(dev, x, v) \ 131 MDIO_WRITEREG(dev, ((x) >> 5), ((x) & 0x1f), v); 132 133 #define ADM6996FC_PVIDBYDATA(data1, data2) \ 134 ((((data1) >> ADM6996FC_PVID_SHIFT) & 0x0f) | ((data2) << 4)) 135 136 static int 137 adm6996fc_probe(device_t dev) 138 { 139 int data1, data2; 140 int pc; 141 struct adm6996fc_softc *sc; 142 143 sc = device_get_softc(dev); 144 bzero(sc, sizeof(*sc)); 145 146 data1 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI0); 147 data2 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI1); 148 pc = ((data2 << 16) | data1) >> ADM6996FC_PC_SHIFT; 149 if (bootverbose) 150 device_printf(dev,"Chip Identifier Register %x %x\n", data1, 151 data2); 152 153 /* check Product Code */ 154 if (pc != ADM6996FC_PRODUCT_CODE) { 155 return (ENXIO); 156 } 157 158 device_set_desc_copy(dev, "Infineon ADM6996FC/M/MX MDIO switch driver"); 159 return (BUS_PROBE_DEFAULT); 160 } 161 162 static int 163 adm6996fc_attach_phys(struct adm6996fc_softc *sc) 164 { 165 int phy, port, err; 166 char name[IFNAMSIZ]; 167 168 port = 0; 169 err = 0; 170 /* PHYs need an interface, so we generate a dummy one */ 171 snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 172 for (phy = 0; phy < sc->numports; phy++) { 173 if (((1 << phy) & sc->phymask) == 0) 174 continue; 175 sc->ifpport[phy] = port; 176 sc->portphy[port] = phy; 177 sc->ifp[port] = if_alloc(IFT_ETHER); 178 if (sc->ifp[port] == NULL) { 179 device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n"); 180 err = ENOMEM; 181 break; 182 } 183 184 sc->ifp[port]->if_softc = sc; 185 sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | 186 IFF_DRV_RUNNING | IFF_SIMPLEX; 187 if_initname(sc->ifp[port], name, port); 188 sc->miibus[port] = malloc(sizeof(device_t), M_ADM6996FC, 189 M_WAITOK | M_ZERO); 190 if (sc->miibus[port] == NULL) { 191 err = ENOMEM; 192 goto failed; 193 } 194 err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port], 195 adm6996fc_ifmedia_upd, adm6996fc_ifmedia_sts, \ 196 BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 197 DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 198 device_get_nameunit(*sc->miibus[port]), 199 sc->ifp[port]->if_xname); 200 if (err != 0) { 201 device_printf(sc->sc_dev, 202 "attaching PHY %d failed\n", 203 phy); 204 goto failed; 205 } 206 ++port; 207 } 208 sc->info.es_nports = port; 209 if (sc->cpuport != -1) { 210 /* assume cpuport is last one */ 211 sc->ifpport[sc->cpuport] = port; 212 sc->portphy[port] = sc->cpuport; 213 ++sc->info.es_nports; 214 } 215 return (0); 216 217 failed: 218 for (phy = 0; phy < sc->numports; phy++) { 219 if (((1 << phy) & sc->phymask) == 0) 220 continue; 221 port = adm6996fc_portforphy(sc, phy); 222 if (sc->miibus[port] != NULL) 223 device_delete_child(sc->sc_dev, (*sc->miibus[port])); 224 if (sc->ifp[port] != NULL) 225 if_free(sc->ifp[port]); 226 if (sc->ifname[port] != NULL) 227 free(sc->ifname[port], M_ADM6996FC); 228 if (sc->miibus[port] != NULL) 229 free(sc->miibus[port], M_ADM6996FC); 230 } 231 return (err); 232 } 233 234 static int 235 adm6996fc_attach(device_t dev) 236 { 237 struct adm6996fc_softc *sc; 238 int err; 239 240 err = 0; 241 sc = device_get_softc(dev); 242 243 sc->sc_dev = dev; 244 mtx_init(&sc->sc_mtx, "adm6996fc", NULL, MTX_DEF); 245 strlcpy(sc->info.es_name, device_get_desc(dev), 246 sizeof(sc->info.es_name)); 247 248 /* ADM6996FC Defaults */ 249 sc->numports = 6; 250 sc->phymask = 0x1f; 251 sc->cpuport = 5; 252 sc->media = 100; 253 254 sc->info.es_nvlangroups = 16; 255 sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q; 256 257 sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_ADM6996FC, 258 M_WAITOK | M_ZERO); 259 sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC, 260 M_WAITOK | M_ZERO); 261 sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC, 262 M_WAITOK | M_ZERO); 263 sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC, 264 M_WAITOK | M_ZERO); 265 266 if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL || 267 sc->portphy == NULL) { 268 err = ENOMEM; 269 goto failed; 270 } 271 272 /* 273 * Attach the PHYs and complete the bus enumeration. 274 */ 275 err = adm6996fc_attach_phys(sc); 276 if (err != 0) 277 goto failed; 278 279 bus_generic_probe(dev); 280 bus_enumerate_hinted_children(dev); 281 err = bus_generic_attach(dev); 282 if (err != 0) 283 goto failed; 284 285 callout_init(&sc->callout_tick, 0); 286 287 adm6996fc_tick(sc); 288 289 return (0); 290 291 failed: 292 if (sc->portphy != NULL) 293 free(sc->portphy, M_ADM6996FC); 294 if (sc->miibus != NULL) 295 free(sc->miibus, M_ADM6996FC); 296 if (sc->ifname != NULL) 297 free(sc->ifname, M_ADM6996FC); 298 if (sc->ifp != NULL) 299 free(sc->ifp, M_ADM6996FC); 300 301 return (err); 302 } 303 304 static int 305 adm6996fc_detach(device_t dev) 306 { 307 struct adm6996fc_softc *sc; 308 int i, port; 309 310 sc = device_get_softc(dev); 311 312 callout_drain(&sc->callout_tick); 313 314 for (i = 0; i < MII_NPHY; i++) { 315 if (((1 << i) & sc->phymask) == 0) 316 continue; 317 port = adm6996fc_portforphy(sc, i); 318 if (sc->miibus[port] != NULL) 319 device_delete_child(dev, (*sc->miibus[port])); 320 if (sc->ifp[port] != NULL) 321 if_free(sc->ifp[port]); 322 free(sc->ifname[port], M_ADM6996FC); 323 free(sc->miibus[port], M_ADM6996FC); 324 } 325 326 free(sc->portphy, M_ADM6996FC); 327 free(sc->miibus, M_ADM6996FC); 328 free(sc->ifname, M_ADM6996FC); 329 free(sc->ifp, M_ADM6996FC); 330 331 bus_generic_detach(dev); 332 mtx_destroy(&sc->sc_mtx); 333 334 return (0); 335 } 336 337 /* 338 * Convert PHY number to port number. 339 */ 340 static inline int 341 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy) 342 { 343 344 return (sc->ifpport[phy]); 345 } 346 347 static inline struct mii_data * 348 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port) 349 { 350 351 if (port < 0 || port > sc->numports) 352 return (NULL); 353 if (port == sc->cpuport) 354 return (NULL); 355 return (device_get_softc(*sc->miibus[port])); 356 } 357 358 static inline struct ifnet * 359 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port) 360 { 361 362 if (port < 0 || port > sc->numports) 363 return (NULL); 364 return (sc->ifp[port]); 365 } 366 367 /* 368 * Poll the status for all PHYs. 369 */ 370 static void 371 adm6996fc_miipollstat(struct adm6996fc_softc *sc) 372 { 373 int i, port; 374 struct mii_data *mii; 375 struct mii_softc *miisc; 376 377 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED); 378 379 for (i = 0; i < MII_NPHY; i++) { 380 if (((1 << i) & sc->phymask) == 0) 381 continue; 382 port = adm6996fc_portforphy(sc, i); 383 if ((*sc->miibus[port]) == NULL) 384 continue; 385 mii = device_get_softc(*sc->miibus[port]); 386 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 387 if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 388 miisc->mii_inst) 389 continue; 390 ukphy_status(miisc); 391 mii_phy_update(miisc, MII_POLLSTAT); 392 } 393 } 394 } 395 396 static void 397 adm6996fc_tick(void *arg) 398 { 399 struct adm6996fc_softc *sc; 400 401 sc = arg; 402 403 adm6996fc_miipollstat(sc); 404 callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc); 405 } 406 407 static void 408 adm6996fc_lock(device_t dev) 409 { 410 struct adm6996fc_softc *sc; 411 412 sc = device_get_softc(dev); 413 414 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED); 415 ADM6996FC_LOCK(sc); 416 } 417 418 static void 419 adm6996fc_unlock(device_t dev) 420 { 421 struct adm6996fc_softc *sc; 422 423 sc = device_get_softc(dev); 424 425 ADM6996FC_LOCK_ASSERT(sc, MA_OWNED); 426 ADM6996FC_UNLOCK(sc); 427 } 428 429 static etherswitch_info_t * 430 adm6996fc_getinfo(device_t dev) 431 { 432 struct adm6996fc_softc *sc; 433 434 sc = device_get_softc(dev); 435 436 return (&sc->info); 437 } 438 439 static int 440 adm6996fc_getport(device_t dev, etherswitch_port_t *p) 441 { 442 struct adm6996fc_softc *sc; 443 struct mii_data *mii; 444 struct ifmediareq *ifmr; 445 device_t parent; 446 int err, phy; 447 int data1, data2; 448 449 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09}; 450 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c}; 451 452 sc = device_get_softc(dev); 453 ifmr = &p->es_ifmr; 454 455 if (p->es_port < 0 || p->es_port >= sc->numports) 456 return (ENXIO); 457 458 parent = device_get_parent(dev); 459 460 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 461 data1 = ADM6996FC_READREG(parent, bcaddr[p->es_port]); 462 data2 = ADM6996FC_READREG(parent, vidaddr[p->es_port]); 463 /* only port 4 is hi bit */ 464 if (p->es_port == 4) 465 data2 = (data2 >> 8) & 0xff; 466 else 467 data2 = data2 & 0xff; 468 469 p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2); 470 if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1) 471 p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 472 } else { 473 p->es_pvid = 0; 474 } 475 476 phy = sc->portphy[p->es_port]; 477 mii = adm6996fc_miiforport(sc, p->es_port); 478 if (sc->cpuport != -1 && phy == sc->cpuport) { 479 /* fill in fixed values for CPU port */ 480 p->es_flags |= ETHERSWITCH_PORT_CPU; 481 ifmr->ifm_count = 0; 482 if (sc->media == 100) 483 ifmr->ifm_current = ifmr->ifm_active = 484 IFM_ETHER | IFM_100_TX | IFM_FDX; 485 else 486 ifmr->ifm_current = ifmr->ifm_active = 487 IFM_ETHER | IFM_1000_T | IFM_FDX; 488 ifmr->ifm_mask = 0; 489 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 490 } else if (mii != NULL) { 491 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 492 &mii->mii_media, SIOCGIFMEDIA); 493 if (err) 494 return (err); 495 } else { 496 return (ENXIO); 497 } 498 return (0); 499 } 500 501 static int 502 adm6996fc_setport(device_t dev, etherswitch_port_t *p) 503 { 504 struct adm6996fc_softc *sc; 505 struct ifmedia *ifm; 506 struct mii_data *mii; 507 struct ifnet *ifp; 508 device_t parent; 509 int err; 510 int data; 511 512 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09}; 513 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c}; 514 515 sc = device_get_softc(dev); 516 parent = device_get_parent(dev); 517 518 if (p->es_port < 0 || p->es_port >= sc->numports) 519 return (ENXIO); 520 521 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 522 data = ADM6996FC_READREG(parent, bcaddr[p->es_port]); 523 data &= ~(0xf << 10); 524 data |= (p->es_pvid & 0xf) << ADM6996FC_PVID_SHIFT; 525 if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 526 data |= 1 << ADM6996FC_OPTE_SHIFT; 527 else 528 data &= ~(1 << ADM6996FC_OPTE_SHIFT); 529 ADM6996FC_WRITEREG(parent, bcaddr[p->es_port], data); 530 data = ADM6996FC_READREG(parent, vidaddr[p->es_port]); 531 /* only port 4 is hi bit */ 532 if (p->es_port == 4) { 533 data &= ~(0xff << 8); 534 data = data | (((p->es_pvid >> 4) & 0xff) << 8); 535 } else { 536 data &= ~0xff; 537 data = data | ((p->es_pvid >> 4) & 0xff); 538 } 539 ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data); 540 err = 0; 541 } else { 542 if (sc->portphy[p->es_port] == sc->cpuport) 543 return (ENXIO); 544 } 545 546 if (sc->portphy[p->es_port] != sc->cpuport) { 547 mii = adm6996fc_miiforport(sc, p->es_port); 548 if (mii == NULL) 549 return (ENXIO); 550 551 ifp = adm6996fc_ifpforport(sc, p->es_port); 552 553 ifm = &mii->mii_media; 554 err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA); 555 } 556 return (err); 557 } 558 559 static int 560 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 561 { 562 struct adm6996fc_softc *sc; 563 device_t parent; 564 int datahi, datalo; 565 566 sc = device_get_softc(dev); 567 parent = device_get_parent(dev); 568 569 if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { 570 if (vg->es_vlangroup <= 5) { 571 vg->es_vid = ETHERSWITCH_VID_VALID; 572 vg->es_vid |= vg->es_vlangroup; 573 datalo = ADM6996FC_READREG(parent, 574 ADM6996FC_VF0L + 2 * vg->es_vlangroup); 575 datahi = ADM6996FC_READREG(parent, 576 ADM6996FC_VF0H + 2 * vg->es_vlangroup); 577 578 vg->es_member_ports = datalo & 0x3f; 579 vg->es_untagged_ports = vg->es_member_ports; 580 vg->es_fid = 0; 581 } else { 582 vg->es_vid = 0; 583 } 584 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 585 datalo = ADM6996FC_READREG(parent, 586 ADM6996FC_VF0L + 2 * vg->es_vlangroup); 587 datahi = ADM6996FC_READREG(parent, 588 ADM6996FC_VF0H + 2 * vg->es_vlangroup); 589 590 if (datahi & (1 << ADM6996FC_VV_SHIFT)) { 591 vg->es_vid = ETHERSWITCH_VID_VALID; 592 vg->es_vid |= datahi & 0xfff; 593 vg->es_member_ports = datalo & 0x3f; 594 vg->es_untagged_ports = (~datalo >> 6) & 0x3f; 595 vg->es_fid = 0; 596 } else { 597 vg->es_fid = 0; 598 } 599 } else { 600 vg->es_fid = 0; 601 } 602 603 return (0); 604 } 605 606 static int 607 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 608 { 609 struct adm6996fc_softc *sc; 610 device_t parent; 611 612 sc = device_get_softc(dev); 613 parent = device_get_parent(dev); 614 615 if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { 616 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup, 617 vg->es_member_ports); 618 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 619 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup, 620 vg->es_member_ports | ((~vg->es_untagged_ports & 0x3f)<< 6)); 621 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * vg->es_vlangroup, 622 (1 << ADM6996FC_VV_SHIFT) | vg->es_vid); 623 } 624 625 return (0); 626 } 627 628 static int 629 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf) 630 { 631 struct adm6996fc_softc *sc; 632 633 sc = device_get_softc(dev); 634 635 /* Return the VLAN mode. */ 636 conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 637 conf->vlan_mode = sc->vlan_mode; 638 639 return (0); 640 } 641 642 static int 643 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf) 644 { 645 struct adm6996fc_softc *sc; 646 device_t parent; 647 int i; 648 int data; 649 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09}; 650 651 sc = device_get_softc(dev); 652 parent = device_get_parent(dev); 653 654 if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0) 655 return (0); 656 657 if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) { 658 sc->vlan_mode = ETHERSWITCH_VLAN_PORT; 659 data = ADM6996FC_READREG(parent, ADM6996FC_SC3); 660 data &= ~(1 << ADM6996FC_TBV_SHIFT); 661 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data); 662 for (i = 0;i <= 5; ++i) { 663 data = ADM6996FC_READREG(parent, bcaddr[i]); 664 data &= ~(0xf << 10); 665 data |= (i << 10); 666 ADM6996FC_WRITEREG(parent, bcaddr[i], data); 667 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i, 668 0x003f); 669 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i, 670 (1 << ADM6996FC_VV_SHIFT) | 1); 671 } 672 } else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 673 sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 674 data = ADM6996FC_READREG(parent, ADM6996FC_SC3); 675 data |= (1 << ADM6996FC_TBV_SHIFT); 676 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data); 677 for (i = 0;i <= 5; ++i) { 678 data = ADM6996FC_READREG(parent, bcaddr[i]); 679 /* Private VID set 1 */ 680 data &= ~(0xf << 10); 681 data |= (1 << 10); 682 ADM6996FC_WRITEREG(parent, bcaddr[i], data); 683 } 684 for (i = 2;i <= 15; ++i) { 685 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i, 686 0x0000); 687 } 688 } else { 689 /* 690 ADM6996FC have no VLAN off. Then set Port base and 691 add all port to member. Use VLAN Filter 1 is reset 692 default. 693 */ 694 sc->vlan_mode = 0; 695 data = ADM6996FC_READREG(parent, ADM6996FC_SC3); 696 data &= ~(1 << ADM6996FC_TBV_SHIFT); 697 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data); 698 for (i = 0;i <= 5; ++i) { 699 data = ADM6996FC_READREG(parent, bcaddr[i]); 700 data &= ~(0xf << 10); 701 data |= (1 << 10); 702 if (i == 5) 703 data &= ~(1 << 4); 704 ADM6996FC_WRITEREG(parent, bcaddr[i], data); 705 } 706 /* default setting */ 707 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2, 0x003f); 708 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2, 709 (1 << ADM6996FC_VV_SHIFT) | 1); 710 } 711 712 713 return (0); 714 } 715 716 static void 717 adm6996fc_statchg(device_t dev) 718 { 719 720 DPRINTF(dev, "%s\n", __func__); 721 } 722 723 static int 724 adm6996fc_ifmedia_upd(struct ifnet *ifp) 725 { 726 struct adm6996fc_softc *sc; 727 struct mii_data *mii; 728 729 sc = ifp->if_softc; 730 mii = adm6996fc_miiforport(sc, ifp->if_dunit); 731 732 DPRINTF(sc->sc_dev, "%s\n", __func__); 733 if (mii == NULL) 734 return (ENXIO); 735 mii_mediachg(mii); 736 return (0); 737 } 738 739 static void 740 adm6996fc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 741 { 742 struct adm6996fc_softc *sc; 743 struct mii_data *mii; 744 745 sc = ifp->if_softc; 746 mii = adm6996fc_miiforport(sc, ifp->if_dunit); 747 748 DPRINTF(sc->sc_dev, "%s\n", __func__); 749 750 if (mii == NULL) 751 return; 752 mii_pollstat(mii); 753 ifmr->ifm_active = mii->mii_media_active; 754 ifmr->ifm_status = mii->mii_media_status; 755 } 756 757 static int 758 adm6996fc_readphy(device_t dev, int phy, int reg) 759 { 760 struct adm6996fc_softc *sc; 761 int data; 762 763 sc = device_get_softc(dev); 764 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED); 765 766 if (phy < 0 || phy >= 32) 767 return (ENXIO); 768 if (reg < 0 || reg >= 32) 769 return (ENXIO); 770 771 ADM6996FC_LOCK(sc); 772 data = ADM6996FC_READREG(device_get_parent(dev), 773 (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg); 774 ADM6996FC_UNLOCK(sc); 775 776 return (data); 777 } 778 779 static int 780 adm6996fc_writephy(device_t dev, int phy, int reg, int data) 781 { 782 struct adm6996fc_softc *sc; 783 int err; 784 785 sc = device_get_softc(dev); 786 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED); 787 788 if (phy < 0 || phy >= 32) 789 return (ENXIO); 790 if (reg < 0 || reg >= 32) 791 return (ENXIO); 792 793 ADM6996FC_LOCK(sc); 794 err = ADM6996FC_WRITEREG(device_get_parent(dev), 795 (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg, data); 796 ADM6996FC_UNLOCK(sc); 797 798 return (err); 799 } 800 801 static int 802 adm6996fc_readreg(device_t dev, int addr) 803 { 804 805 return ADM6996FC_READREG(device_get_parent(dev), addr); 806 } 807 808 static int 809 adm6996fc_writereg(device_t dev, int addr, int value) 810 { 811 int err; 812 813 err = ADM6996FC_WRITEREG(device_get_parent(dev), addr, value); 814 return (err); 815 } 816 817 static device_method_t adm6996fc_methods[] = { 818 /* Device interface */ 819 DEVMETHOD(device_probe, adm6996fc_probe), 820 DEVMETHOD(device_attach, adm6996fc_attach), 821 DEVMETHOD(device_detach, adm6996fc_detach), 822 823 /* bus interface */ 824 DEVMETHOD(bus_add_child, device_add_child_ordered), 825 826 /* MII interface */ 827 DEVMETHOD(miibus_readreg, adm6996fc_readphy), 828 DEVMETHOD(miibus_writereg, adm6996fc_writephy), 829 DEVMETHOD(miibus_statchg, adm6996fc_statchg), 830 831 /* MDIO interface */ 832 DEVMETHOD(mdio_readreg, adm6996fc_readphy), 833 DEVMETHOD(mdio_writereg, adm6996fc_writephy), 834 835 /* etherswitch interface */ 836 DEVMETHOD(etherswitch_lock, adm6996fc_lock), 837 DEVMETHOD(etherswitch_unlock, adm6996fc_unlock), 838 DEVMETHOD(etherswitch_getinfo, adm6996fc_getinfo), 839 DEVMETHOD(etherswitch_readreg, adm6996fc_readreg), 840 DEVMETHOD(etherswitch_writereg, adm6996fc_writereg), 841 DEVMETHOD(etherswitch_readphyreg, adm6996fc_readphy), 842 DEVMETHOD(etherswitch_writephyreg, adm6996fc_writephy), 843 DEVMETHOD(etherswitch_getport, adm6996fc_getport), 844 DEVMETHOD(etherswitch_setport, adm6996fc_setport), 845 DEVMETHOD(etherswitch_getvgroup, adm6996fc_getvgroup), 846 DEVMETHOD(etherswitch_setvgroup, adm6996fc_setvgroup), 847 DEVMETHOD(etherswitch_setconf, adm6996fc_setconf), 848 DEVMETHOD(etherswitch_getconf, adm6996fc_getconf), 849 850 DEVMETHOD_END 851 }; 852 853 DEFINE_CLASS_0(adm6996fc, adm6996fc_driver, adm6996fc_methods, 854 sizeof(struct adm6996fc_softc)); 855 static devclass_t adm6996fc_devclass; 856 857 DRIVER_MODULE(adm6996fc, mdio, adm6996fc_driver, adm6996fc_devclass, 0, 0); 858 DRIVER_MODULE(miibus, adm6996fc, miibus_driver, miibus_devclass, 0, 0); 859 DRIVER_MODULE(mdio, adm6996fc, mdio_driver, mdio_devclass, 0, 0); 860 DRIVER_MODULE(etherswitch, adm6996fc, etherswitch_driver, etherswitch_devclass, 861 0, 0); 862 MODULE_VERSION(adm6996fc, 1); 863 MODULE_DEPEND(adm6996fc, miibus, 1, 1, 1); /* XXX which versions? */ 864 MODULE_DEPEND(adm6996fc, etherswitch, 1, 1, 1); /* XXX which versions? */ 865