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