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