1 /* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-2-Clause-NetBSD 5 * 6 * Copyright (c) 1998 The NetBSD Foundation, Inc. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to The NetBSD Foundation 10 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 11 * NASA Ames Research Center. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 /* 39 * MII bus layer, glues MII-capable network interface drivers to sharable 40 * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, 41 * plus some NetBSD extensions. 42 */ 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/socket.h> 47 #include <sys/malloc.h> 48 #include <sys/module.h> 49 #include <sys/bus.h> 50 #include <sys/sbuf.h> 51 52 #include <net/if.h> 53 #include <net/if_var.h> 54 #include <net/if_media.h> 55 56 #include <dev/mii/mii.h> 57 #include <dev/mii/miivar.h> 58 59 MODULE_VERSION(miibus, 1); 60 61 #include "miibus_if.h" 62 63 static bus_child_detached_t miibus_child_detached; 64 static bus_child_location_t miibus_child_location; 65 static bus_child_pnpinfo_t miibus_child_pnpinfo; 66 static device_detach_t miibus_detach; 67 static bus_hinted_child_t miibus_hinted_child; 68 static bus_print_child_t miibus_print_child; 69 static device_probe_t miibus_probe; 70 static bus_read_ivar_t miibus_read_ivar; 71 static miibus_readreg_t miibus_readreg; 72 static miibus_statchg_t miibus_statchg; 73 static miibus_writereg_t miibus_writereg; 74 static miibus_linkchg_t miibus_linkchg; 75 static miibus_mediainit_t miibus_mediainit; 76 77 static unsigned char mii_bitreverse(unsigned char x); 78 79 static device_method_t miibus_methods[] = { 80 /* device interface */ 81 DEVMETHOD(device_probe, miibus_probe), 82 DEVMETHOD(device_attach, miibus_attach), 83 DEVMETHOD(device_detach, miibus_detach), 84 DEVMETHOD(device_shutdown, bus_generic_shutdown), 85 86 /* bus interface */ 87 DEVMETHOD(bus_print_child, miibus_print_child), 88 DEVMETHOD(bus_read_ivar, miibus_read_ivar), 89 DEVMETHOD(bus_child_detached, miibus_child_detached), 90 DEVMETHOD(bus_child_pnpinfo, miibus_child_pnpinfo), 91 DEVMETHOD(bus_child_location, miibus_child_location), 92 DEVMETHOD(bus_hinted_child, miibus_hinted_child), 93 94 /* MII interface */ 95 DEVMETHOD(miibus_readreg, miibus_readreg), 96 DEVMETHOD(miibus_writereg, miibus_writereg), 97 DEVMETHOD(miibus_statchg, miibus_statchg), 98 DEVMETHOD(miibus_linkchg, miibus_linkchg), 99 DEVMETHOD(miibus_mediainit, miibus_mediainit), 100 101 DEVMETHOD_END 102 }; 103 104 devclass_t miibus_devclass; 105 DEFINE_CLASS_0(miibus, miibus_driver, miibus_methods, sizeof(struct mii_data)); 106 107 struct miibus_ivars { 108 if_t ifp; 109 ifm_change_cb_t ifmedia_upd; 110 ifm_stat_cb_t ifmedia_sts; 111 u_int mii_flags; 112 u_int mii_offset; 113 }; 114 115 static int 116 miibus_probe(device_t dev) 117 { 118 119 device_set_desc(dev, "MII bus"); 120 121 return (BUS_PROBE_SPECIFIC); 122 } 123 124 int 125 miibus_attach(device_t dev) 126 { 127 struct miibus_ivars *ivars; 128 struct mii_attach_args *ma; 129 struct mii_data *mii; 130 device_t *children; 131 int i, nchildren; 132 133 mii = device_get_softc(dev); 134 if (device_get_children(dev, &children, &nchildren) == 0) { 135 for (i = 0; i < nchildren; i++) { 136 ma = device_get_ivars(children[i]); 137 ma->mii_data = mii; 138 } 139 free(children, M_TEMP); 140 } 141 if (nchildren == 0) { 142 device_printf(dev, "cannot get children\n"); 143 return (ENXIO); 144 } 145 ivars = device_get_ivars(dev); 146 ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd, 147 ivars->ifmedia_sts); 148 mii->mii_ifp = ivars->ifp; 149 if_setcapabilitiesbit(mii->mii_ifp, IFCAP_LINKSTATE, 0); 150 if_setcapenablebit(mii->mii_ifp, IFCAP_LINKSTATE, 0); 151 LIST_INIT(&mii->mii_phys); 152 153 return (bus_generic_attach(dev)); 154 } 155 156 static int 157 miibus_detach(device_t dev) 158 { 159 struct mii_data *mii; 160 struct miibus_ivars *ivars; 161 162 ivars = device_get_ivars(dev); 163 bus_generic_detach(dev); 164 mii = device_get_softc(dev); 165 ifmedia_removeall(&mii->mii_media); 166 free(ivars, M_DEVBUF); 167 mii->mii_ifp = NULL; 168 169 return (0); 170 } 171 172 static void 173 miibus_child_detached(device_t dev, device_t child) 174 { 175 struct mii_attach_args *args; 176 177 args = device_get_ivars(child); 178 free(args, M_DEVBUF); 179 } 180 181 static int 182 miibus_print_child(device_t dev, device_t child) 183 { 184 struct mii_attach_args *ma; 185 int retval; 186 187 ma = device_get_ivars(child); 188 retval = bus_print_child_header(dev, child); 189 retval += printf(" PHY %d", ma->mii_phyno); 190 retval += bus_print_child_footer(dev, child); 191 192 return (retval); 193 } 194 195 static int 196 miibus_read_ivar(device_t dev, device_t child __unused, int which, 197 uintptr_t *result) 198 { 199 struct miibus_ivars *ivars; 200 201 /* 202 * NB: this uses the instance variables of the miibus rather than 203 * its PHY children. 204 */ 205 ivars = device_get_ivars(dev); 206 switch (which) { 207 case MIIBUS_IVAR_FLAGS: 208 *result = ivars->mii_flags; 209 break; 210 default: 211 return (ENOENT); 212 } 213 return (0); 214 } 215 216 static int 217 miibus_child_pnpinfo(device_t dev __unused, device_t child, struct sbuf *sb) 218 { 219 struct mii_attach_args *ma; 220 221 ma = device_get_ivars(child); 222 sbuf_printf(sb, "oui=0x%x model=0x%x rev=0x%x", 223 MII_OUI(ma->mii_id1, ma->mii_id2), 224 MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); 225 return (0); 226 } 227 228 static int 229 miibus_child_location(device_t dev __unused, device_t child, struct sbuf *sb) 230 { 231 struct mii_attach_args *ma; 232 233 ma = device_get_ivars(child); 234 sbuf_printf(sb, "phyno=%d", ma->mii_phyno); 235 return (0); 236 } 237 238 static void 239 miibus_hinted_child(device_t dev, const char *name, int unit) 240 { 241 struct miibus_ivars *ivars; 242 struct mii_attach_args *args, *ma; 243 device_t *children, phy; 244 int i, nchildren; 245 u_int val; 246 247 if (resource_int_value(name, unit, "phyno", &val) != 0) 248 return; 249 if (device_get_children(dev, &children, &nchildren) != 0) 250 return; 251 ma = NULL; 252 for (i = 0; i < nchildren; i++) { 253 args = device_get_ivars(children[i]); 254 if (args->mii_phyno == val) { 255 ma = args; 256 break; 257 } 258 } 259 free(children, M_TEMP); 260 261 /* 262 * Don't add a PHY that was automatically identified by having media 263 * in its BMSR twice, only allow to alter its attach arguments. 264 */ 265 if (ma == NULL) { 266 ma = malloc(sizeof(struct mii_attach_args), M_DEVBUF, 267 M_NOWAIT); 268 if (ma == NULL) 269 return; 270 phy = device_add_child(dev, name, unit); 271 if (phy == NULL) { 272 free(ma, M_DEVBUF); 273 return; 274 } 275 ivars = device_get_ivars(dev); 276 ma->mii_phyno = val; 277 ma->mii_offset = ivars->mii_offset++; 278 ma->mii_id1 = 0; 279 ma->mii_id2 = 0; 280 ma->mii_capmask = BMSR_DEFCAPMASK; 281 device_set_ivars(phy, ma); 282 } 283 284 if (resource_int_value(name, unit, "id1", &val) == 0) 285 ma->mii_id1 = val; 286 if (resource_int_value(name, unit, "id2", &val) == 0) 287 ma->mii_id2 = val; 288 if (resource_int_value(name, unit, "capmask", &val) == 0) 289 ma->mii_capmask = val; 290 } 291 292 static int 293 miibus_readreg(device_t dev, int phy, int reg) 294 { 295 device_t parent; 296 297 parent = device_get_parent(dev); 298 return (MIIBUS_READREG(parent, phy, reg)); 299 } 300 301 static int 302 miibus_writereg(device_t dev, int phy, int reg, int data) 303 { 304 device_t parent; 305 306 parent = device_get_parent(dev); 307 return (MIIBUS_WRITEREG(parent, phy, reg, data)); 308 } 309 310 static void 311 miibus_statchg(device_t dev) 312 { 313 device_t parent; 314 struct mii_data *mii; 315 316 parent = device_get_parent(dev); 317 MIIBUS_STATCHG(parent); 318 319 mii = device_get_softc(dev); 320 if_setbaudrate(mii->mii_ifp, ifmedia_baudrate(mii->mii_media_active)); 321 } 322 323 static void 324 miibus_linkchg(device_t dev) 325 { 326 struct mii_data *mii; 327 device_t parent; 328 int link_state; 329 330 parent = device_get_parent(dev); 331 MIIBUS_LINKCHG(parent); 332 333 mii = device_get_softc(dev); 334 335 if (mii->mii_media_status & IFM_AVALID) { 336 if (mii->mii_media_status & IFM_ACTIVE) 337 link_state = LINK_STATE_UP; 338 else 339 link_state = LINK_STATE_DOWN; 340 } else 341 link_state = LINK_STATE_UNKNOWN; 342 if_link_state_change(mii->mii_ifp, link_state); 343 } 344 345 static void 346 miibus_mediainit(device_t dev) 347 { 348 struct mii_data *mii; 349 struct ifmedia_entry *m; 350 int media = 0; 351 352 /* Poke the parent in case it has any media of its own to add. */ 353 MIIBUS_MEDIAINIT(device_get_parent(dev)); 354 355 mii = device_get_softc(dev); 356 LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) { 357 media = m->ifm_media; 358 if (media == (IFM_ETHER | IFM_AUTO)) 359 break; 360 } 361 362 ifmedia_set(&mii->mii_media, media); 363 } 364 365 /* 366 * Helper function used by network interface drivers, attaches the miibus and 367 * the PHYs to the network interface driver parent. 368 */ 369 int 370 mii_attach(device_t dev, device_t *miibus, if_t ifp, 371 ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask, 372 int phyloc, int offloc, int flags) 373 { 374 struct miibus_ivars *ivars; 375 struct mii_attach_args *args, ma; 376 device_t *children, phy; 377 int bmsr, first, i, nchildren, phymax, phymin, rv; 378 uint32_t phymask; 379 380 bus_topo_assert(); 381 382 if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) { 383 printf("%s: phyloc and offloc specified\n", __func__); 384 return (EINVAL); 385 } 386 387 if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) { 388 printf("%s: invalid offloc %d\n", __func__, offloc); 389 return (EINVAL); 390 } 391 392 if (phyloc == MII_PHY_ANY) { 393 phymin = 0; 394 phymax = MII_NPHY - 1; 395 } else { 396 if (phyloc < 0 || phyloc >= MII_NPHY) { 397 printf("%s: invalid phyloc %d\n", __func__, phyloc); 398 return (EINVAL); 399 } 400 phymin = phymax = phyloc; 401 } 402 403 first = 0; 404 if (*miibus == NULL) { 405 first = 1; 406 ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT); 407 if (ivars == NULL) 408 return (ENOMEM); 409 ivars->ifp = ifp; 410 ivars->ifmedia_upd = ifmedia_upd; 411 ivars->ifmedia_sts = ifmedia_sts; 412 ivars->mii_flags = flags; 413 *miibus = device_add_child(dev, "miibus", -1); 414 if (*miibus == NULL) { 415 rv = ENXIO; 416 goto fail; 417 } 418 device_set_ivars(*miibus, ivars); 419 } else { 420 ivars = device_get_ivars(*miibus); 421 if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd || 422 ivars->ifmedia_sts != ifmedia_sts || 423 ivars->mii_flags != flags) { 424 printf("%s: non-matching invariant\n", __func__); 425 return (EINVAL); 426 } 427 /* 428 * Assignment of the attach arguments mii_data for the first 429 * pass is done in miibus_attach(), i.e. once the miibus softc 430 * has been allocated. 431 */ 432 ma.mii_data = device_get_softc(*miibus); 433 } 434 435 ma.mii_capmask = capmask; 436 437 if (resource_int_value(device_get_name(*miibus), 438 device_get_unit(*miibus), "phymask", &phymask) != 0) 439 phymask = 0xffffffff; 440 441 if (device_get_children(*miibus, &children, &nchildren) != 0) { 442 children = NULL; 443 nchildren = 0; 444 } 445 ivars->mii_offset = 0; 446 for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) { 447 /* 448 * Make sure we haven't already configured a PHY at this 449 * address. This allows mii_attach() to be called 450 * multiple times. 451 */ 452 for (i = 0; i < nchildren; i++) { 453 args = device_get_ivars(children[i]); 454 if (args->mii_phyno == ma.mii_phyno) { 455 /* 456 * Yes, there is already something 457 * configured at this address. 458 */ 459 goto skip; 460 } 461 } 462 463 /* 464 * Check to see if there is a PHY at this address. Note, 465 * many braindead PHYs report 0/0 in their ID registers, 466 * so we test for media in the BMSR. 467 */ 468 bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR); 469 if (bmsr == 0 || bmsr == 0xffff || 470 (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) { 471 /* Assume no PHY at this address. */ 472 continue; 473 } 474 475 /* 476 * There is a PHY at this address. If we were given an 477 * `offset' locator, skip this PHY if it doesn't match. 478 */ 479 if (offloc != MII_OFFSET_ANY && offloc != ivars->mii_offset) 480 goto skip; 481 482 /* 483 * Skip this PHY if it's not included in the phymask hint. 484 */ 485 if ((phymask & (1 << ma.mii_phyno)) == 0) 486 goto skip; 487 488 /* 489 * Extract the IDs. Braindead PHYs will be handled by 490 * the `ukphy' driver, as we have no ID information to 491 * match on. 492 */ 493 ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1); 494 ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2); 495 496 ma.mii_offset = ivars->mii_offset; 497 args = malloc(sizeof(struct mii_attach_args), M_DEVBUF, 498 M_NOWAIT); 499 if (args == NULL) 500 goto skip; 501 bcopy((char *)&ma, (char *)args, sizeof(ma)); 502 phy = device_add_child(*miibus, NULL, -1); 503 if (phy == NULL) { 504 free(args, M_DEVBUF); 505 goto skip; 506 } 507 device_set_ivars(phy, args); 508 skip: 509 ivars->mii_offset++; 510 } 511 free(children, M_TEMP); 512 513 if (first != 0) { 514 rv = device_set_driver(*miibus, &miibus_driver); 515 if (rv != 0) 516 goto fail; 517 bus_enumerate_hinted_children(*miibus); 518 rv = device_get_children(*miibus, &children, &nchildren); 519 if (rv != 0) 520 goto fail; 521 free(children, M_TEMP); 522 if (nchildren == 0) { 523 rv = ENXIO; 524 goto fail; 525 } 526 rv = bus_generic_attach(dev); 527 if (rv != 0) 528 goto fail; 529 530 /* Attaching of the PHY drivers is done in miibus_attach(). */ 531 return (0); 532 } 533 rv = bus_generic_attach(*miibus); 534 if (rv != 0) 535 goto fail; 536 537 return (0); 538 539 fail: 540 if (*miibus != NULL) 541 device_delete_child(dev, *miibus); 542 free(ivars, M_DEVBUF); 543 if (first != 0) 544 *miibus = NULL; 545 return (rv); 546 } 547 548 /* 549 * Media changed; notify all PHYs. 550 */ 551 int 552 mii_mediachg(struct mii_data *mii) 553 { 554 struct mii_softc *child; 555 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 556 int rv; 557 558 mii->mii_media_status = 0; 559 mii->mii_media_active = IFM_NONE; 560 561 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 562 /* 563 * If the media indicates a different PHY instance, 564 * isolate this one. 565 */ 566 if (IFM_INST(ife->ifm_media) != child->mii_inst) { 567 if ((child->mii_flags & MIIF_NOISOLATE) != 0) { 568 device_printf(child->mii_dev, "%s: " 569 "can't handle non-zero PHY instance %d\n", 570 __func__, child->mii_inst); 571 continue; 572 } 573 PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) | 574 BMCR_ISO); 575 continue; 576 } 577 rv = PHY_SERVICE(child, mii, MII_MEDIACHG); 578 if (rv) 579 return (rv); 580 } 581 return (0); 582 } 583 584 /* 585 * Call the PHY tick routines, used during autonegotiation. 586 */ 587 void 588 mii_tick(struct mii_data *mii) 589 { 590 struct mii_softc *child; 591 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 592 593 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 594 /* 595 * If this PHY instance isn't currently selected, just skip 596 * it. 597 */ 598 if (IFM_INST(ife->ifm_media) != child->mii_inst) 599 continue; 600 (void)PHY_SERVICE(child, mii, MII_TICK); 601 } 602 } 603 604 /* 605 * Get media status from PHYs. 606 */ 607 void 608 mii_pollstat(struct mii_data *mii) 609 { 610 struct mii_softc *child; 611 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 612 613 mii->mii_media_status = 0; 614 mii->mii_media_active = IFM_NONE; 615 616 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 617 /* 618 * If we're not polling this PHY instance, just skip it. 619 */ 620 if (IFM_INST(ife->ifm_media) != child->mii_inst) 621 continue; 622 (void)PHY_SERVICE(child, mii, MII_POLLSTAT); 623 } 624 } 625 626 static unsigned char 627 mii_bitreverse(unsigned char x) 628 { 629 static unsigned const char nibbletab[16] = { 630 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 631 }; 632 633 return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]); 634 } 635 636 u_int 637 mii_oui(u_int id1, u_int id2) 638 { 639 u_int h; 640 641 h = (id1 << 6) | (id2 >> 10); 642 643 return ((mii_bitreverse(h >> 16) << 16) | 644 (mii_bitreverse((h >> 8) & 0xff) << 8) | 645 mii_bitreverse(h & 0xff)); 646 } 647 648 int 649 mii_phy_mac_match(struct mii_softc *mii, const char *name) 650 { 651 652 return (strcmp(device_get_name(device_get_parent(mii->mii_dev)), 653 name) == 0); 654 } 655 656 int 657 mii_dev_mac_match(device_t parent, const char *name) 658 { 659 660 return (strcmp(device_get_name(device_get_parent( 661 device_get_parent(parent))), name) == 0); 662 } 663 664 void * 665 mii_phy_mac_softc(struct mii_softc *mii) 666 { 667 668 return (device_get_softc(device_get_parent(mii->mii_dev))); 669 } 670 671 void * 672 mii_dev_mac_softc(device_t parent) 673 { 674 675 return (device_get_softc(device_get_parent(device_get_parent(parent)))); 676 } 677