1 /*- 2 * Copyright (c) 2013 Luiz Otavio O Souza. 3 * Copyright (c) 2011-2012 Stefan Bethke. 4 * Copyright (c) 2012 Adrian Chadd. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/bus.h> 33 #include <sys/errno.h> 34 #include <sys/kernel.h> 35 #include <sys/lock.h> 36 #include <sys/malloc.h> 37 #include <sys/module.h> 38 #include <sys/mutex.h> 39 #include <sys/socket.h> 40 #include <sys/sockio.h> 41 #include <sys/sysctl.h> 42 #include <sys/systm.h> 43 #include <sys/types.h> 44 45 #include <net/if.h> 46 #include <net/ethernet.h> 47 #include <net/if_media.h> 48 #include <net/if_types.h> 49 #include <net/if_var.h> 50 51 #include <machine/bus.h> 52 #include <dev/mii/mii.h> 53 #include <dev/mii/miivar.h> 54 #include <dev/etherswitch/mdio.h> 55 56 #include <dev/etherswitch/etherswitch.h> 57 #include <dev/etherswitch/ip17x/ip17x_phy.h> 58 #include <dev/etherswitch/ip17x/ip17x_reg.h> 59 #include <dev/etherswitch/ip17x/ip17x_var.h> 60 #include <dev/etherswitch/ip17x/ip17x_vlans.h> 61 #include <dev/etherswitch/ip17x/ip175c.h> 62 #include <dev/etherswitch/ip17x/ip175d.h> 63 64 #include "mdio_if.h" 65 #include "miibus_if.h" 66 #include "etherswitch_if.h" 67 68 MALLOC_DECLARE(M_IP17X); 69 MALLOC_DEFINE(M_IP17X, "ip17x", "ip17x data structures"); 70 71 static void ip17x_tick(void *); 72 static int ip17x_ifmedia_upd(struct ifnet *); 73 static void ip17x_ifmedia_sts(struct ifnet *, struct ifmediareq *); 74 75 static int 76 ip17x_probe(device_t dev) 77 { 78 struct ip17x_softc *sc; 79 uint32_t oui, model, phy_id1, phy_id2; 80 81 sc = device_get_softc(dev); 82 83 /* Read ID from PHY 0. */ 84 phy_id1 = MDIO_READREG(device_get_parent(dev), 0, MII_PHYIDR1); 85 phy_id2 = MDIO_READREG(device_get_parent(dev), 0, MII_PHYIDR2); 86 87 oui = MII_OUI(phy_id1, phy_id2), 88 model = MII_MODEL(phy_id2); 89 /* We only care about IC+ devices. */ 90 if (oui != IP17X_OUI) { 91 device_printf(dev, 92 "Unsupported IC+ switch. Unknown OUI: %#x\n", oui); 93 return (ENXIO); 94 } 95 96 switch (model) { 97 case IP17X_IP175A: 98 sc->sc_switchtype = IP17X_SWITCH_IP175A; 99 break; 100 case IP17X_IP175C: 101 sc->sc_switchtype = IP17X_SWITCH_IP175C; 102 break; 103 default: 104 device_printf(dev, "Unsupported IC+ switch model: %#x\n", 105 model); 106 return (ENXIO); 107 } 108 109 /* IP175D has a specific ID register. */ 110 model = MDIO_READREG(device_get_parent(dev), IP175D_ID_PHY, 111 IP175D_ID_REG); 112 if (model == 0x175d) 113 sc->sc_switchtype = IP17X_SWITCH_IP175D; 114 else { 115 /* IP178 has more PHYs. Try it. */ 116 model = MDIO_READREG(device_get_parent(dev), 5, MII_PHYIDR1); 117 if (phy_id1 == model) 118 sc->sc_switchtype = IP17X_SWITCH_IP178C; 119 } 120 121 device_set_desc_copy(dev, "IC+ IP17x switch driver"); 122 return (BUS_PROBE_DEFAULT); 123 } 124 125 static int 126 ip17x_attach_phys(struct ip17x_softc *sc) 127 { 128 int err, phy, port; 129 char name[IFNAMSIZ]; 130 131 port = err = 0; 132 133 /* PHYs need an interface, so we generate a dummy one */ 134 snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 135 for (phy = 0; phy < MII_NPHY; phy++) { 136 if (((1 << phy) & sc->phymask) == 0) 137 continue; 138 sc->phyport[phy] = port; 139 sc->portphy[port] = phy; 140 sc->ifp[port] = if_alloc(IFT_ETHER); 141 sc->ifp[port]->if_softc = sc; 142 sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | 143 IFF_DRV_RUNNING | IFF_SIMPLEX; 144 if_initname(sc->ifp[port], name, port); 145 sc->miibus[port] = malloc(sizeof(device_t), M_IP17X, 146 M_WAITOK | M_ZERO); 147 err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port], 148 ip17x_ifmedia_upd, ip17x_ifmedia_sts, \ 149 BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 150 DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 151 device_get_nameunit(*sc->miibus[port]), 152 sc->ifp[port]->if_xname); 153 if (err != 0) { 154 device_printf(sc->sc_dev, 155 "attaching PHY %d failed\n", 156 phy); 157 break; 158 } 159 sc->info.es_nports = port + 1; 160 if (++port >= sc->numports) 161 break; 162 } 163 return (err); 164 } 165 166 static int 167 ip17x_attach(device_t dev) 168 { 169 struct ip17x_softc *sc; 170 int err; 171 172 sc = device_get_softc(dev); 173 174 sc->sc_dev = dev; 175 mtx_init(&sc->sc_mtx, "ip17x", NULL, MTX_DEF); 176 strlcpy(sc->info.es_name, device_get_desc(dev), 177 sizeof(sc->info.es_name)); 178 179 /* XXX Defaults */ 180 sc->phymask = 0x0f; 181 sc->media = 100; 182 183 (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 184 "phymask", &sc->phymask); 185 186 /* Number of vlans supported by the switch. */ 187 sc->info.es_nvlangroups = IP17X_MAX_VLANS; 188 189 /* Attach the switch related functions. */ 190 if (IP17X_IS_SWITCH(sc, IP175C)) 191 ip175c_attach(sc); 192 else if (IP17X_IS_SWITCH(sc, IP175D)) 193 ip175d_attach(sc); 194 else 195 /* We don't have support to all the models yet :-/ */ 196 return (ENXIO); 197 198 /* Always attach the cpu port. */ 199 sc->phymask |= (1 << sc->cpuport); 200 201 sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_IP17X, 202 M_WAITOK | M_ZERO); 203 sc->pvid = malloc(sizeof(uint32_t) * sc->numports, M_IP17X, 204 M_WAITOK | M_ZERO); 205 sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_IP17X, 206 M_WAITOK | M_ZERO); 207 sc->portphy = malloc(sizeof(int) * sc->numports, M_IP17X, 208 M_WAITOK | M_ZERO); 209 210 /* Initialize the switch. */ 211 sc->hal.ip17x_reset(sc); 212 213 /* 214 * Attach the PHYs and complete the bus enumeration. 215 */ 216 err = ip17x_attach_phys(sc); 217 if (err != 0) 218 return (err); 219 220 /* 221 * Set the switch to port based vlans or disabled (if not supported 222 * on this model). 223 */ 224 sc->hal.ip17x_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT); 225 226 bus_generic_probe(dev); 227 bus_enumerate_hinted_children(dev); 228 err = bus_generic_attach(dev); 229 if (err != 0) 230 return (err); 231 232 callout_init(&sc->callout_tick, 0); 233 234 ip17x_tick(sc); 235 236 return (0); 237 } 238 239 static int 240 ip17x_detach(device_t dev) 241 { 242 struct ip17x_softc *sc; 243 int i, port; 244 245 sc = device_get_softc(dev); 246 callout_drain(&sc->callout_tick); 247 248 for (i=0; i < MII_NPHY; i++) { 249 if (((1 << i) & sc->phymask) == 0) 250 continue; 251 port = sc->phyport[i]; 252 if (sc->miibus[port] != NULL) 253 device_delete_child(dev, (*sc->miibus[port])); 254 if (sc->ifp[port] != NULL) 255 if_free(sc->ifp[port]); 256 free(sc->miibus[port], M_IP17X); 257 } 258 259 free(sc->portphy, M_IP17X); 260 free(sc->miibus, M_IP17X); 261 free(sc->pvid, M_IP17X); 262 free(sc->ifp, M_IP17X); 263 264 /* Reset the switch. */ 265 sc->hal.ip17x_reset(sc); 266 267 bus_generic_detach(dev); 268 mtx_destroy(&sc->sc_mtx); 269 270 return (0); 271 } 272 273 static inline struct mii_data * 274 ip17x_miiforport(struct ip17x_softc *sc, int port) 275 { 276 277 if (port < 0 || port > sc->numports) 278 return (NULL); 279 return (device_get_softc(*sc->miibus[port])); 280 } 281 282 static inline struct ifnet * 283 ip17x_ifpforport(struct ip17x_softc *sc, int port) 284 { 285 286 if (port < 0 || port > sc->numports) 287 return (NULL); 288 return (sc->ifp[port]); 289 } 290 291 /* 292 * Poll the status for all PHYs. 293 */ 294 static void 295 ip17x_miipollstat(struct ip17x_softc *sc) 296 { 297 struct mii_softc *miisc; 298 struct mii_data *mii; 299 int i, port; 300 301 IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); 302 303 for (i = 0; i < MII_NPHY; i++) { 304 if (((1 << i) & sc->phymask) == 0) 305 continue; 306 port = sc->phyport[i]; 307 if ((*sc->miibus[port]) == NULL) 308 continue; 309 mii = device_get_softc(*sc->miibus[port]); 310 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 311 if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 312 miisc->mii_inst) 313 continue; 314 ukphy_status(miisc); 315 mii_phy_update(miisc, MII_POLLSTAT); 316 } 317 } 318 } 319 320 static void 321 ip17x_tick(void *arg) 322 { 323 struct ip17x_softc *sc; 324 325 sc = arg; 326 ip17x_miipollstat(sc); 327 callout_reset(&sc->callout_tick, hz, ip17x_tick, sc); 328 } 329 330 static void 331 ip17x_lock(device_t dev) 332 { 333 struct ip17x_softc *sc; 334 335 sc = device_get_softc(dev); 336 IP17X_LOCK_ASSERT(sc, MA_NOTOWNED); 337 IP17X_LOCK(sc); 338 } 339 340 static void 341 ip17x_unlock(device_t dev) 342 { 343 struct ip17x_softc *sc; 344 345 sc = device_get_softc(dev); 346 IP17X_LOCK_ASSERT(sc, MA_OWNED); 347 IP17X_UNLOCK(sc); 348 } 349 350 static etherswitch_info_t * 351 ip17x_getinfo(device_t dev) 352 { 353 struct ip17x_softc *sc; 354 355 sc = device_get_softc(dev); 356 return (&sc->info); 357 } 358 359 static int 360 ip17x_getport(device_t dev, etherswitch_port_t *p) 361 { 362 struct ip17x_softc *sc; 363 struct ifmediareq *ifmr; 364 struct mii_data *mii; 365 int err, phy; 366 367 sc = device_get_softc(dev); 368 if (p->es_port < 0 || p->es_port >= sc->numports) 369 return (ENXIO); 370 371 phy = sc->portphy[p->es_port]; 372 373 /* Retrieve the PVID. */ 374 p->es_pvid = sc->pvid[phy]; 375 376 /* Port flags. */ 377 if (sc->addtag & (1 << phy)) 378 p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 379 if (sc->striptag & (1 << phy)) 380 p->es_flags |= ETHERSWITCH_PORT_STRIPTAG; 381 382 ifmr = &p->es_ifmr; 383 384 /* No media settings ? */ 385 if (p->es_ifmr.ifm_count == 0) 386 return (0); 387 388 mii = ip17x_miiforport(sc, p->es_port); 389 if (mii == NULL) 390 return (ENXIO); 391 if (phy == sc->cpuport) { 392 /* fill in fixed values for CPU port */ 393 p->es_flags |= ETHERSWITCH_PORT_CPU; 394 ifmr->ifm_count = 0; 395 if (sc->media == 100) 396 ifmr->ifm_current = ifmr->ifm_active = 397 IFM_ETHER | IFM_100_TX | IFM_FDX; 398 else 399 ifmr->ifm_current = ifmr->ifm_active = 400 IFM_ETHER | IFM_1000_T | IFM_FDX; 401 ifmr->ifm_mask = 0; 402 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 403 } else { 404 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 405 &mii->mii_media, SIOCGIFMEDIA); 406 if (err) 407 return (err); 408 } 409 return (0); 410 } 411 412 static int 413 ip17x_setport(device_t dev, etherswitch_port_t *p) 414 { 415 struct ip17x_softc *sc; 416 struct ifmedia *ifm; 417 struct ifnet *ifp; 418 struct mii_data *mii; 419 int phy; 420 421 sc = device_get_softc(dev); 422 if (p->es_port < 0 || p->es_port >= sc->numports) 423 return (ENXIO); 424 425 phy = sc->portphy[p->es_port]; 426 ifp = ip17x_ifpforport(sc, p->es_port); 427 mii = ip17x_miiforport(sc, p->es_port); 428 if (ifp == NULL || mii == NULL) 429 return (ENXIO); 430 431 /* Port flags. */ 432 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 433 434 /* Set the PVID. */ 435 if (p->es_pvid != 0) { 436 if (IP17X_IS_SWITCH(sc, IP175C) && 437 p->es_pvid > IP175C_LAST_VLAN) 438 return (ENXIO); 439 sc->pvid[phy] = p->es_pvid; 440 } 441 442 /* Mutually exclusive. */ 443 if (p->es_flags & ETHERSWITCH_PORT_ADDTAG && 444 p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 445 return (EINVAL); 446 447 /* Reset the settings for this port. */ 448 sc->addtag &= ~(1 << phy); 449 sc->striptag &= ~(1 << phy); 450 451 /* And then set it to the new value. */ 452 if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 453 sc->addtag |= (1 << phy); 454 if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 455 sc->striptag |= (1 << phy); 456 } 457 458 /* Update the switch configuration. */ 459 if (sc->hal.ip17x_hw_setup(sc)) 460 return (ENXIO); 461 462 /* Do not allow media changes on CPU port. */ 463 if (phy == sc->cpuport) 464 return (0); 465 466 /* No media settings ? */ 467 if (p->es_ifmr.ifm_count == 0) 468 return (0); 469 470 ifm = &mii->mii_media; 471 return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); 472 } 473 474 static void 475 ip17x_statchg(device_t dev) 476 { 477 478 DPRINTF(dev, "%s\n", __func__); 479 } 480 481 static int 482 ip17x_ifmedia_upd(struct ifnet *ifp) 483 { 484 struct ip17x_softc *sc; 485 struct mii_data *mii; 486 487 sc = ifp->if_softc; 488 DPRINTF(sc->sc_dev, "%s\n", __func__); 489 mii = ip17x_miiforport(sc, ifp->if_dunit); 490 if (mii == NULL) 491 return (ENXIO); 492 mii_mediachg(mii); 493 494 return (0); 495 } 496 497 static void 498 ip17x_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 499 { 500 struct ip17x_softc *sc; 501 struct mii_data *mii; 502 503 sc = ifp->if_softc; 504 DPRINTF(sc->sc_dev, "%s\n", __func__); 505 mii = ip17x_miiforport(sc, ifp->if_dunit); 506 if (mii == NULL) 507 return; 508 mii_pollstat(mii); 509 ifmr->ifm_active = mii->mii_media_active; 510 ifmr->ifm_status = mii->mii_media_status; 511 } 512 513 static int 514 ip17x_readreg(device_t dev, int addr) 515 { 516 struct ip17x_softc *sc; 517 518 sc = device_get_softc(dev); 519 IP17X_LOCK_ASSERT(sc, MA_OWNED); 520 521 /* Not supported. */ 522 return (0); 523 } 524 525 static int 526 ip17x_writereg(device_t dev, int addr, int value) 527 { 528 struct ip17x_softc *sc; 529 530 sc = device_get_softc(dev); 531 IP17X_LOCK_ASSERT(sc, MA_OWNED); 532 533 /* Not supported. */ 534 return (0); 535 } 536 537 static int 538 ip17x_getconf(device_t dev, etherswitch_conf_t *conf) 539 { 540 struct ip17x_softc *sc; 541 542 sc = device_get_softc(dev); 543 544 /* Return the VLAN mode. */ 545 conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 546 conf->vlan_mode = sc->hal.ip17x_get_vlan_mode(sc); 547 548 return (0); 549 } 550 551 static int 552 ip17x_setconf(device_t dev, etherswitch_conf_t *conf) 553 { 554 struct ip17x_softc *sc; 555 556 sc = device_get_softc(dev); 557 558 /* Set the VLAN mode. */ 559 if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) 560 sc->hal.ip17x_set_vlan_mode(sc, conf->vlan_mode); 561 562 return (0); 563 } 564 565 static device_method_t ip17x_methods[] = { 566 /* Device interface */ 567 DEVMETHOD(device_probe, ip17x_probe), 568 DEVMETHOD(device_attach, ip17x_attach), 569 DEVMETHOD(device_detach, ip17x_detach), 570 571 /* bus interface */ 572 DEVMETHOD(bus_add_child, device_add_child_ordered), 573 574 /* MII interface */ 575 DEVMETHOD(miibus_readreg, ip17x_readphy), 576 DEVMETHOD(miibus_writereg, ip17x_writephy), 577 DEVMETHOD(miibus_statchg, ip17x_statchg), 578 579 /* MDIO interface */ 580 DEVMETHOD(mdio_readreg, ip17x_readphy), 581 DEVMETHOD(mdio_writereg, ip17x_writephy), 582 583 /* etherswitch interface */ 584 DEVMETHOD(etherswitch_lock, ip17x_lock), 585 DEVMETHOD(etherswitch_unlock, ip17x_unlock), 586 DEVMETHOD(etherswitch_getinfo, ip17x_getinfo), 587 DEVMETHOD(etherswitch_readreg, ip17x_readreg), 588 DEVMETHOD(etherswitch_writereg, ip17x_writereg), 589 DEVMETHOD(etherswitch_readphyreg, ip17x_readphy), 590 DEVMETHOD(etherswitch_writephyreg, ip17x_writephy), 591 DEVMETHOD(etherswitch_getport, ip17x_getport), 592 DEVMETHOD(etherswitch_setport, ip17x_setport), 593 DEVMETHOD(etherswitch_getvgroup, ip17x_getvgroup), 594 DEVMETHOD(etherswitch_setvgroup, ip17x_setvgroup), 595 DEVMETHOD(etherswitch_getconf, ip17x_getconf), 596 DEVMETHOD(etherswitch_setconf, ip17x_setconf), 597 598 DEVMETHOD_END 599 }; 600 601 DEFINE_CLASS_0(ip17x, ip17x_driver, ip17x_methods, 602 sizeof(struct ip17x_softc)); 603 static devclass_t ip17x_devclass; 604 605 DRIVER_MODULE(ip17x, mdio, ip17x_driver, ip17x_devclass, 0, 0); 606 DRIVER_MODULE(miibus, ip17x, miibus_driver, miibus_devclass, 0, 0); 607 DRIVER_MODULE(mdio, ip17x, mdio_driver, mdio_devclass, 0, 0); 608 DRIVER_MODULE(etherswitch, ip17x, etherswitch_driver, etherswitch_devclass, 0, 0); 609 MODULE_VERSION(ip17x, 1); 610 MODULE_DEPEND(ip17x, miibus, 1, 1, 1); /* XXX which versions? */ 611 MODULE_DEPEND(ip17x, etherswitch, 1, 1, 1); /* XXX which versions? */ 612