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 if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) { 381 printf("%s: phyloc and offloc specified\n", __func__); 382 return (EINVAL); 383 } 384 385 if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) { 386 printf("%s: invalid offloc %d\n", __func__, offloc); 387 return (EINVAL); 388 } 389 390 if (phyloc == MII_PHY_ANY) { 391 phymin = 0; 392 phymax = MII_NPHY - 1; 393 } else { 394 if (phyloc < 0 || phyloc >= MII_NPHY) { 395 printf("%s: invalid phyloc %d\n", __func__, phyloc); 396 return (EINVAL); 397 } 398 phymin = phymax = phyloc; 399 } 400 401 first = 0; 402 if (*miibus == NULL) { 403 first = 1; 404 ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT); 405 if (ivars == NULL) 406 return (ENOMEM); 407 ivars->ifp = ifp; 408 ivars->ifmedia_upd = ifmedia_upd; 409 ivars->ifmedia_sts = ifmedia_sts; 410 ivars->mii_flags = flags; 411 *miibus = device_add_child(dev, "miibus", -1); 412 if (*miibus == NULL) { 413 rv = ENXIO; 414 goto fail; 415 } 416 device_set_ivars(*miibus, ivars); 417 } else { 418 ivars = device_get_ivars(*miibus); 419 if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd || 420 ivars->ifmedia_sts != ifmedia_sts || 421 ivars->mii_flags != flags) { 422 printf("%s: non-matching invariant\n", __func__); 423 return (EINVAL); 424 } 425 /* 426 * Assignment of the attach arguments mii_data for the first 427 * pass is done in miibus_attach(), i.e. once the miibus softc 428 * has been allocated. 429 */ 430 ma.mii_data = device_get_softc(*miibus); 431 } 432 433 ma.mii_capmask = capmask; 434 435 if (resource_int_value(device_get_name(*miibus), 436 device_get_unit(*miibus), "phymask", &phymask) != 0) 437 phymask = 0xffffffff; 438 439 if (device_get_children(*miibus, &children, &nchildren) != 0) { 440 children = NULL; 441 nchildren = 0; 442 } 443 ivars->mii_offset = 0; 444 for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) { 445 /* 446 * Make sure we haven't already configured a PHY at this 447 * address. This allows mii_attach() to be called 448 * multiple times. 449 */ 450 for (i = 0; i < nchildren; i++) { 451 args = device_get_ivars(children[i]); 452 if (args->mii_phyno == ma.mii_phyno) { 453 /* 454 * Yes, there is already something 455 * configured at this address. 456 */ 457 goto skip; 458 } 459 } 460 461 /* 462 * Check to see if there is a PHY at this address. Note, 463 * many braindead PHYs report 0/0 in their ID registers, 464 * so we test for media in the BMSR. 465 */ 466 bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR); 467 if (bmsr == 0 || bmsr == 0xffff || 468 (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) { 469 /* Assume no PHY at this address. */ 470 continue; 471 } 472 473 /* 474 * There is a PHY at this address. If we were given an 475 * `offset' locator, skip this PHY if it doesn't match. 476 */ 477 if (offloc != MII_OFFSET_ANY && offloc != ivars->mii_offset) 478 goto skip; 479 480 /* 481 * Skip this PHY if it's not included in the phymask hint. 482 */ 483 if ((phymask & (1 << ma.mii_phyno)) == 0) 484 goto skip; 485 486 /* 487 * Extract the IDs. Braindead PHYs will be handled by 488 * the `ukphy' driver, as we have no ID information to 489 * match on. 490 */ 491 ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1); 492 ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2); 493 494 ma.mii_offset = ivars->mii_offset; 495 args = malloc(sizeof(struct mii_attach_args), M_DEVBUF, 496 M_NOWAIT); 497 if (args == NULL) 498 goto skip; 499 bcopy((char *)&ma, (char *)args, sizeof(ma)); 500 phy = device_add_child(*miibus, NULL, -1); 501 if (phy == NULL) { 502 free(args, M_DEVBUF); 503 goto skip; 504 } 505 device_set_ivars(phy, args); 506 skip: 507 ivars->mii_offset++; 508 } 509 free(children, M_TEMP); 510 511 if (first != 0) { 512 rv = device_set_driver(*miibus, &miibus_driver); 513 if (rv != 0) 514 goto fail; 515 bus_enumerate_hinted_children(*miibus); 516 rv = device_get_children(*miibus, &children, &nchildren); 517 if (rv != 0) 518 goto fail; 519 free(children, M_TEMP); 520 if (nchildren == 0) { 521 rv = ENXIO; 522 goto fail; 523 } 524 rv = bus_generic_attach(dev); 525 if (rv != 0) 526 goto fail; 527 528 /* Attaching of the PHY drivers is done in miibus_attach(). */ 529 return (0); 530 } 531 rv = bus_generic_attach(*miibus); 532 if (rv != 0) 533 goto fail; 534 535 return (0); 536 537 fail: 538 if (*miibus != NULL) 539 device_delete_child(dev, *miibus); 540 free(ivars, M_DEVBUF); 541 if (first != 0) 542 *miibus = NULL; 543 return (rv); 544 } 545 546 /* 547 * Media changed; notify all PHYs. 548 */ 549 int 550 mii_mediachg(struct mii_data *mii) 551 { 552 struct mii_softc *child; 553 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 554 int rv; 555 556 mii->mii_media_status = 0; 557 mii->mii_media_active = IFM_NONE; 558 559 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 560 /* 561 * If the media indicates a different PHY instance, 562 * isolate this one. 563 */ 564 if (IFM_INST(ife->ifm_media) != child->mii_inst) { 565 if ((child->mii_flags & MIIF_NOISOLATE) != 0) { 566 device_printf(child->mii_dev, "%s: " 567 "can't handle non-zero PHY instance %d\n", 568 __func__, child->mii_inst); 569 continue; 570 } 571 PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) | 572 BMCR_ISO); 573 continue; 574 } 575 rv = PHY_SERVICE(child, mii, MII_MEDIACHG); 576 if (rv) 577 return (rv); 578 } 579 return (0); 580 } 581 582 /* 583 * Call the PHY tick routines, used during autonegotiation. 584 */ 585 void 586 mii_tick(struct mii_data *mii) 587 { 588 struct mii_softc *child; 589 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 590 591 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 592 /* 593 * If this PHY instance isn't currently selected, just skip 594 * it. 595 */ 596 if (IFM_INST(ife->ifm_media) != child->mii_inst) 597 continue; 598 (void)PHY_SERVICE(child, mii, MII_TICK); 599 } 600 } 601 602 /* 603 * Get media status from PHYs. 604 */ 605 void 606 mii_pollstat(struct mii_data *mii) 607 { 608 struct mii_softc *child; 609 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 610 611 mii->mii_media_status = 0; 612 mii->mii_media_active = IFM_NONE; 613 614 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 615 /* 616 * If we're not polling this PHY instance, just skip it. 617 */ 618 if (IFM_INST(ife->ifm_media) != child->mii_inst) 619 continue; 620 (void)PHY_SERVICE(child, mii, MII_POLLSTAT); 621 } 622 } 623 624 static unsigned char 625 mii_bitreverse(unsigned char x) 626 { 627 static unsigned const char nibbletab[16] = { 628 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 629 }; 630 631 return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]); 632 } 633 634 u_int 635 mii_oui(u_int id1, u_int id2) 636 { 637 u_int h; 638 639 h = (id1 << 6) | (id2 >> 10); 640 641 return ((mii_bitreverse(h >> 16) << 16) | 642 (mii_bitreverse((h >> 8) & 0xff) << 8) | 643 mii_bitreverse(h & 0xff)); 644 } 645 646 int 647 mii_phy_mac_match(struct mii_softc *mii, const char *name) 648 { 649 650 return (strcmp(device_get_name(device_get_parent(mii->mii_dev)), 651 name) == 0); 652 } 653 654 int 655 mii_dev_mac_match(device_t parent, const char *name) 656 { 657 658 return (strcmp(device_get_name(device_get_parent( 659 device_get_parent(parent))), name) == 0); 660 } 661 662 void * 663 mii_phy_mac_softc(struct mii_softc *mii) 664 { 665 666 return (device_get_softc(device_get_parent(mii->mii_dev))); 667 } 668 669 void * 670 mii_dev_mac_softc(device_t parent) 671 { 672 673 return (device_get_softc(device_get_parent(device_get_parent(parent)))); 674 } 675