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 sc->ifp[port]->if_softc = sc; 179 sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | 180 IFF_DRV_RUNNING | IFF_SIMPLEX; 181 if_initname(sc->ifp[port], name, port); 182 sc->miibus[port] = malloc(sizeof(device_t), M_ADM6996FC, 183 M_WAITOK | M_ZERO); 184 if (sc->miibus[port] == NULL) { 185 err = ENOMEM; 186 goto failed; 187 } 188 err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port], 189 adm6996fc_ifmedia_upd, adm6996fc_ifmedia_sts, \ 190 BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 191 DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 192 device_get_nameunit(*sc->miibus[port]), 193 sc->ifp[port]->if_xname); 194 if (err != 0) { 195 device_printf(sc->sc_dev, 196 "attaching PHY %d failed\n", 197 phy); 198 goto failed; 199 } 200 ++port; 201 } 202 sc->info.es_nports = port; 203 if (sc->cpuport != -1) { 204 /* assume cpuport is last one */ 205 sc->ifpport[sc->cpuport] = port; 206 sc->portphy[port] = sc->cpuport; 207 ++sc->info.es_nports; 208 } 209 return (0); 210 211 failed: 212 for (phy = 0; phy < sc->numports; phy++) { 213 if (((1 << phy) & sc->phymask) == 0) 214 continue; 215 port = adm6996fc_portforphy(sc, phy); 216 if (sc->miibus[port] != NULL) 217 device_delete_child(sc->sc_dev, (*sc->miibus[port])); 218 if (sc->ifp[port] != NULL) 219 if_free(sc->ifp[port]); 220 if (sc->ifname[port] != NULL) 221 free(sc->ifname[port], M_ADM6996FC); 222 if (sc->miibus[port] != NULL) 223 free(sc->miibus[port], M_ADM6996FC); 224 } 225 return (err); 226 } 227 228 static int 229 adm6996fc_attach(device_t dev) 230 { 231 struct adm6996fc_softc *sc; 232 int err; 233 234 err = 0; 235 sc = device_get_softc(dev); 236 237 sc->sc_dev = dev; 238 mtx_init(&sc->sc_mtx, "adm6996fc", NULL, MTX_DEF); 239 strlcpy(sc->info.es_name, device_get_desc(dev), 240 sizeof(sc->info.es_name)); 241 242 /* ADM6996FC Defaults */ 243 sc->numports = 6; 244 sc->phymask = 0x1f; 245 sc->cpuport = 5; 246 sc->media = 100; 247 248 sc->info.es_nvlangroups = 16; 249 sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q; 250 251 sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_ADM6996FC, 252 M_WAITOK | M_ZERO); 253 sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC, 254 M_WAITOK | M_ZERO); 255 sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC, 256 M_WAITOK | M_ZERO); 257 sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC, 258 M_WAITOK | M_ZERO); 259 260 if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL || 261 sc->portphy == NULL) { 262 err = ENOMEM; 263 goto failed; 264 } 265 266 /* 267 * Attach the PHYs and complete the bus enumeration. 268 */ 269 err = adm6996fc_attach_phys(sc); 270 if (err != 0) 271 goto failed; 272 273 bus_generic_probe(dev); 274 bus_enumerate_hinted_children(dev); 275 err = bus_generic_attach(dev); 276 if (err != 0) 277 goto failed; 278 279 callout_init(&sc->callout_tick, 0); 280 281 adm6996fc_tick(sc); 282 283 return (0); 284 285 failed: 286 if (sc->portphy != NULL) 287 free(sc->portphy, M_ADM6996FC); 288 if (sc->miibus != NULL) 289 free(sc->miibus, M_ADM6996FC); 290 if (sc->ifname != NULL) 291 free(sc->ifname, M_ADM6996FC); 292 if (sc->ifp != NULL) 293 free(sc->ifp, M_ADM6996FC); 294 295 return (err); 296 } 297 298 static int 299 adm6996fc_detach(device_t dev) 300 { 301 struct adm6996fc_softc *sc; 302 int i, port; 303 304 sc = device_get_softc(dev); 305 306 callout_drain(&sc->callout_tick); 307 308 for (i = 0; i < MII_NPHY; i++) { 309 if (((1 << i) & sc->phymask) == 0) 310 continue; 311 port = adm6996fc_portforphy(sc, i); 312 if (sc->miibus[port] != NULL) 313 device_delete_child(dev, (*sc->miibus[port])); 314 if (sc->ifp[port] != NULL) 315 if_free(sc->ifp[port]); 316 free(sc->ifname[port], M_ADM6996FC); 317 free(sc->miibus[port], M_ADM6996FC); 318 } 319 320 free(sc->portphy, M_ADM6996FC); 321 free(sc->miibus, M_ADM6996FC); 322 free(sc->ifname, M_ADM6996FC); 323 free(sc->ifp, M_ADM6996FC); 324 325 bus_generic_detach(dev); 326 mtx_destroy(&sc->sc_mtx); 327 328 return (0); 329 } 330 331 /* 332 * Convert PHY number to port number. 333 */ 334 static inline int 335 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy) 336 { 337 338 return (sc->ifpport[phy]); 339 } 340 341 static inline struct mii_data * 342 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port) 343 { 344 345 if (port < 0 || port > sc->numports) 346 return (NULL); 347 if (port == sc->cpuport) 348 return (NULL); 349 return (device_get_softc(*sc->miibus[port])); 350 } 351 352 static inline struct ifnet * 353 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port) 354 { 355 356 if (port < 0 || port > sc->numports) 357 return (NULL); 358 return (sc->ifp[port]); 359 } 360 361 /* 362 * Poll the status for all PHYs. 363 */ 364 static void 365 adm6996fc_miipollstat(struct adm6996fc_softc *sc) 366 { 367 int i, port; 368 struct mii_data *mii; 369 struct mii_softc *miisc; 370 371 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED); 372 373 for (i = 0; i < MII_NPHY; i++) { 374 if (((1 << i) & sc->phymask) == 0) 375 continue; 376 port = adm6996fc_portforphy(sc, i); 377 if ((*sc->miibus[port]) == NULL) 378 continue; 379 mii = device_get_softc(*sc->miibus[port]); 380 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 381 if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 382 miisc->mii_inst) 383 continue; 384 ukphy_status(miisc); 385 mii_phy_update(miisc, MII_POLLSTAT); 386 } 387 } 388 } 389 390 static void 391 adm6996fc_tick(void *arg) 392 { 393 struct adm6996fc_softc *sc; 394 395 sc = arg; 396 397 adm6996fc_miipollstat(sc); 398 callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc); 399 } 400 401 static void 402 adm6996fc_lock(device_t dev) 403 { 404 struct adm6996fc_softc *sc; 405 406 sc = device_get_softc(dev); 407 408 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED); 409 ADM6996FC_LOCK(sc); 410 } 411 412 static void 413 adm6996fc_unlock(device_t dev) 414 { 415 struct adm6996fc_softc *sc; 416 417 sc = device_get_softc(dev); 418 419 ADM6996FC_LOCK_ASSERT(sc, MA_OWNED); 420 ADM6996FC_UNLOCK(sc); 421 } 422 423 static etherswitch_info_t * 424 adm6996fc_getinfo(device_t dev) 425 { 426 struct adm6996fc_softc *sc; 427 428 sc = device_get_softc(dev); 429 430 return (&sc->info); 431 } 432 433 static int 434 adm6996fc_getport(device_t dev, etherswitch_port_t *p) 435 { 436 struct adm6996fc_softc *sc; 437 struct mii_data *mii; 438 struct ifmediareq *ifmr; 439 device_t parent; 440 int err, phy; 441 int data1, data2; 442 443 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09}; 444 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c}; 445 446 sc = device_get_softc(dev); 447 ifmr = &p->es_ifmr; 448 449 if (p->es_port < 0 || p->es_port >= sc->numports) 450 return (ENXIO); 451 452 parent = device_get_parent(dev); 453 454 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 455 data1 = ADM6996FC_READREG(parent, bcaddr[p->es_port]); 456 data2 = ADM6996FC_READREG(parent, vidaddr[p->es_port]); 457 /* only port 4 is hi bit */ 458 if (p->es_port == 4) 459 data2 = (data2 >> 8) & 0xff; 460 else 461 data2 = data2 & 0xff; 462 463 p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2); 464 if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1) 465 p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 466 } else { 467 p->es_pvid = 0; 468 } 469 470 phy = sc->portphy[p->es_port]; 471 mii = adm6996fc_miiforport(sc, p->es_port); 472 if (sc->cpuport != -1 && phy == sc->cpuport) { 473 /* fill in fixed values for CPU port */ 474 p->es_flags |= ETHERSWITCH_PORT_CPU; 475 ifmr->ifm_count = 0; 476 if (sc->media == 100) 477 ifmr->ifm_current = ifmr->ifm_active = 478 IFM_ETHER | IFM_100_TX | IFM_FDX; 479 else 480 ifmr->ifm_current = ifmr->ifm_active = 481 IFM_ETHER | IFM_1000_T | IFM_FDX; 482 ifmr->ifm_mask = 0; 483 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 484 } else if (mii != NULL) { 485 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 486 &mii->mii_media, SIOCGIFMEDIA); 487 if (err) 488 return (err); 489 } else { 490 return (ENXIO); 491 } 492 return (0); 493 } 494 495 static int 496 adm6996fc_setport(device_t dev, etherswitch_port_t *p) 497 { 498 struct adm6996fc_softc *sc; 499 struct ifmedia *ifm; 500 struct mii_data *mii; 501 struct ifnet *ifp; 502 device_t parent; 503 int err; 504 int data; 505 506 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09}; 507 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c}; 508 509 sc = device_get_softc(dev); 510 parent = device_get_parent(dev); 511 512 if (p->es_port < 0 || p->es_port >= sc->numports) 513 return (ENXIO); 514 515 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 516 data = ADM6996FC_READREG(parent, bcaddr[p->es_port]); 517 data &= ~(0xf << 10); 518 data |= (p->es_pvid & 0xf) << ADM6996FC_PVID_SHIFT; 519 if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 520 data |= 1 << ADM6996FC_OPTE_SHIFT; 521 else 522 data &= ~(1 << ADM6996FC_OPTE_SHIFT); 523 ADM6996FC_WRITEREG(parent, bcaddr[p->es_port], data); 524 data = ADM6996FC_READREG(parent, vidaddr[p->es_port]); 525 /* only port 4 is hi bit */ 526 if (p->es_port == 4) { 527 data &= ~(0xff << 8); 528 data = data | (((p->es_pvid >> 4) & 0xff) << 8); 529 } else { 530 data &= ~0xff; 531 data = data | ((p->es_pvid >> 4) & 0xff); 532 } 533 ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data); 534 err = 0; 535 } else { 536 if (sc->portphy[p->es_port] == sc->cpuport) 537 return (ENXIO); 538 } 539 540 if (sc->portphy[p->es_port] != sc->cpuport) { 541 mii = adm6996fc_miiforport(sc, p->es_port); 542 if (mii == NULL) 543 return (ENXIO); 544 545 ifp = adm6996fc_ifpforport(sc, p->es_port); 546 547 ifm = &mii->mii_media; 548 err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA); 549 } 550 return (err); 551 } 552 553 static int 554 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 555 { 556 struct adm6996fc_softc *sc; 557 device_t parent; 558 int datahi, datalo; 559 560 sc = device_get_softc(dev); 561 parent = device_get_parent(dev); 562 563 if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { 564 if (vg->es_vlangroup <= 5) { 565 vg->es_vid = ETHERSWITCH_VID_VALID; 566 vg->es_vid |= vg->es_vlangroup; 567 datalo = ADM6996FC_READREG(parent, 568 ADM6996FC_VF0L + 2 * vg->es_vlangroup); 569 datahi = ADM6996FC_READREG(parent, 570 ADM6996FC_VF0H + 2 * vg->es_vlangroup); 571 572 vg->es_member_ports = datalo & 0x3f; 573 vg->es_untagged_ports = vg->es_member_ports; 574 vg->es_fid = 0; 575 } else { 576 vg->es_vid = 0; 577 } 578 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 579 datalo = ADM6996FC_READREG(parent, 580 ADM6996FC_VF0L + 2 * vg->es_vlangroup); 581 datahi = ADM6996FC_READREG(parent, 582 ADM6996FC_VF0H + 2 * vg->es_vlangroup); 583 584 if (datahi & (1 << ADM6996FC_VV_SHIFT)) { 585 vg->es_vid = ETHERSWITCH_VID_VALID; 586 vg->es_vid |= datahi & 0xfff; 587 vg->es_member_ports = datalo & 0x3f; 588 vg->es_untagged_ports = (~datalo >> 6) & 0x3f; 589 vg->es_fid = 0; 590 } else { 591 vg->es_fid = 0; 592 } 593 } else { 594 vg->es_fid = 0; 595 } 596 597 return (0); 598 } 599 600 static int 601 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 602 { 603 struct adm6996fc_softc *sc; 604 device_t parent; 605 606 sc = device_get_softc(dev); 607 parent = device_get_parent(dev); 608 609 if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { 610 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup, 611 vg->es_member_ports); 612 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 613 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup, 614 vg->es_member_ports | ((~vg->es_untagged_ports & 0x3f)<< 6)); 615 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * vg->es_vlangroup, 616 (1 << ADM6996FC_VV_SHIFT) | vg->es_vid); 617 } 618 619 return (0); 620 } 621 622 static int 623 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf) 624 { 625 struct adm6996fc_softc *sc; 626 627 sc = device_get_softc(dev); 628 629 /* Return the VLAN mode. */ 630 conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 631 conf->vlan_mode = sc->vlan_mode; 632 633 return (0); 634 } 635 636 static int 637 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf) 638 { 639 struct adm6996fc_softc *sc; 640 device_t parent; 641 int i; 642 int data; 643 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09}; 644 645 sc = device_get_softc(dev); 646 parent = device_get_parent(dev); 647 648 if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0) 649 return (0); 650 651 if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) { 652 sc->vlan_mode = ETHERSWITCH_VLAN_PORT; 653 data = ADM6996FC_READREG(parent, ADM6996FC_SC3); 654 data &= ~(1 << ADM6996FC_TBV_SHIFT); 655 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data); 656 for (i = 0;i <= 5; ++i) { 657 data = ADM6996FC_READREG(parent, bcaddr[i]); 658 data &= ~(0xf << 10); 659 data |= (i << 10); 660 ADM6996FC_WRITEREG(parent, bcaddr[i], data); 661 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i, 662 0x003f); 663 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i, 664 (1 << ADM6996FC_VV_SHIFT) | 1); 665 } 666 } else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 667 sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 668 data = ADM6996FC_READREG(parent, ADM6996FC_SC3); 669 data |= (1 << ADM6996FC_TBV_SHIFT); 670 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data); 671 for (i = 0;i <= 5; ++i) { 672 data = ADM6996FC_READREG(parent, bcaddr[i]); 673 /* Private VID set 1 */ 674 data &= ~(0xf << 10); 675 data |= (1 << 10); 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