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