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