1 /* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-2-Clause 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 /* 37 * MII bus layer, glues MII-capable network interface drivers to sharable 38 * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, 39 * plus some NetBSD extensions. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/socket.h> 45 #include <sys/malloc.h> 46 #include <sys/module.h> 47 #include <sys/bus.h> 48 #include <sys/sbuf.h> 49 50 #include <net/if.h> 51 #include <net/if_var.h> 52 #include <net/if_media.h> 53 54 #include <dev/mii/mii.h> 55 #include <dev/mii/miivar.h> 56 57 MODULE_VERSION(miibus, 1); 58 59 #include "miibus_if.h" 60 61 static bus_child_deleted_t miibus_child_deleted; 62 static bus_child_location_t miibus_child_location; 63 static bus_child_pnpinfo_t miibus_child_pnpinfo; 64 static device_detach_t miibus_detach; 65 static bus_hinted_child_t miibus_hinted_child; 66 static bus_print_child_t miibus_print_child; 67 static device_probe_t miibus_probe; 68 static bus_read_ivar_t miibus_read_ivar; 69 static miibus_readreg_t miibus_readreg; 70 static miibus_statchg_t miibus_statchg; 71 static miibus_writereg_t miibus_writereg; 72 static miibus_linkchg_t miibus_linkchg; 73 static miibus_mediainit_t miibus_mediainit; 74 75 static unsigned char mii_bitreverse(unsigned char x); 76 77 static device_method_t miibus_methods[] = { 78 /* device interface */ 79 DEVMETHOD(device_probe, miibus_probe), 80 DEVMETHOD(device_attach, miibus_attach), 81 DEVMETHOD(device_detach, miibus_detach), 82 DEVMETHOD(device_shutdown, bus_generic_shutdown), 83 84 /* bus interface */ 85 DEVMETHOD(bus_print_child, miibus_print_child), 86 DEVMETHOD(bus_read_ivar, miibus_read_ivar), 87 DEVMETHOD(bus_child_deleted, miibus_child_deleted), 88 DEVMETHOD(bus_child_pnpinfo, miibus_child_pnpinfo), 89 DEVMETHOD(bus_child_location, miibus_child_location), 90 DEVMETHOD(bus_hinted_child, miibus_hinted_child), 91 92 /* MII interface */ 93 DEVMETHOD(miibus_readreg, miibus_readreg), 94 DEVMETHOD(miibus_writereg, miibus_writereg), 95 DEVMETHOD(miibus_statchg, miibus_statchg), 96 DEVMETHOD(miibus_linkchg, miibus_linkchg), 97 DEVMETHOD(miibus_mediainit, miibus_mediainit), 98 99 DEVMETHOD_END 100 }; 101 102 DEFINE_CLASS_0(miibus, miibus_driver, miibus_methods, sizeof(struct mii_data)); 103 104 struct miibus_ivars { 105 if_t ifp; 106 ifm_change_cb_t ifmedia_upd; 107 ifm_stat_cb_t ifmedia_sts; 108 u_int mii_flags; 109 u_int mii_offset; 110 }; 111 112 static int 113 miibus_probe(device_t dev) 114 { 115 116 device_set_desc(dev, "MII bus"); 117 118 return (BUS_PROBE_SPECIFIC); 119 } 120 121 int 122 miibus_attach(device_t dev) 123 { 124 struct miibus_ivars *ivars; 125 struct mii_attach_args *ma; 126 struct mii_data *mii; 127 device_t *children; 128 int i, nchildren; 129 130 mii = device_get_softc(dev); 131 if (device_get_children(dev, &children, &nchildren) == 0) { 132 for (i = 0; i < nchildren; i++) { 133 ma = device_get_ivars(children[i]); 134 ma->mii_data = mii; 135 } 136 free(children, M_TEMP); 137 } 138 if (nchildren == 0) { 139 device_printf(dev, "cannot get children\n"); 140 return (ENXIO); 141 } 142 ivars = device_get_ivars(dev); 143 ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd, 144 ivars->ifmedia_sts); 145 mii->mii_ifp = ivars->ifp; 146 if_setcapabilitiesbit(mii->mii_ifp, IFCAP_LINKSTATE, 0); 147 if_setcapenablebit(mii->mii_ifp, IFCAP_LINKSTATE, 0); 148 LIST_INIT(&mii->mii_phys); 149 150 bus_attach_children(dev); 151 return (0); 152 } 153 154 static int 155 miibus_detach(device_t dev) 156 { 157 struct mii_data *mii; 158 struct miibus_ivars *ivars; 159 160 ivars = device_get_ivars(dev); 161 bus_generic_detach(dev); 162 mii = device_get_softc(dev); 163 ifmedia_removeall(&mii->mii_media); 164 free(ivars, M_DEVBUF); 165 mii->mii_ifp = NULL; 166 167 return (0); 168 } 169 170 static void 171 miibus_child_deleted(device_t dev, device_t child) 172 { 173 struct mii_attach_args *args; 174 175 args = device_get_ivars(child); 176 free(args, M_DEVBUF); 177 } 178 179 static int 180 miibus_print_child(device_t dev, device_t child) 181 { 182 struct mii_attach_args *ma; 183 int retval; 184 185 ma = device_get_ivars(child); 186 retval = bus_print_child_header(dev, child); 187 retval += printf(" PHY %d", ma->mii_phyno); 188 retval += bus_print_child_footer(dev, child); 189 190 return (retval); 191 } 192 193 static int 194 miibus_read_ivar(device_t dev, device_t child __unused, int which, 195 uintptr_t *result) 196 { 197 struct miibus_ivars *ivars; 198 199 /* 200 * NB: this uses the instance variables of the miibus rather than 201 * its PHY children. 202 */ 203 ivars = device_get_ivars(dev); 204 switch (which) { 205 case MIIBUS_IVAR_FLAGS: 206 *result = ivars->mii_flags; 207 break; 208 default: 209 return (ENOENT); 210 } 211 return (0); 212 } 213 214 static int 215 miibus_child_pnpinfo(device_t dev __unused, device_t child, struct sbuf *sb) 216 { 217 struct mii_attach_args *ma; 218 219 ma = device_get_ivars(child); 220 sbuf_printf(sb, "oui=0x%x model=0x%x rev=0x%x", 221 MII_OUI(ma->mii_id1, ma->mii_id2), 222 MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); 223 return (0); 224 } 225 226 static int 227 miibus_child_location(device_t dev __unused, device_t child, struct sbuf *sb) 228 { 229 struct mii_attach_args *ma; 230 231 ma = device_get_ivars(child); 232 sbuf_printf(sb, "phyno=%d", ma->mii_phyno); 233 return (0); 234 } 235 236 static void 237 miibus_hinted_child(device_t dev, const char *name, int unit) 238 { 239 struct miibus_ivars *ivars; 240 struct mii_attach_args *args, *ma; 241 device_t *children, phy; 242 int i, nchildren; 243 u_int val; 244 245 if (resource_int_value(name, unit, "phyno", &val) != 0) 246 return; 247 if (device_get_children(dev, &children, &nchildren) != 0) 248 return; 249 ma = NULL; 250 for (i = 0; i < nchildren; i++) { 251 args = device_get_ivars(children[i]); 252 if (args->mii_phyno == val) { 253 ma = args; 254 break; 255 } 256 } 257 free(children, M_TEMP); 258 259 /* 260 * Don't add a PHY that was automatically identified by having media 261 * in its BMSR twice, only allow to alter its attach arguments. 262 */ 263 if (ma == NULL) { 264 ma = malloc(sizeof(struct mii_attach_args), M_DEVBUF, 265 M_NOWAIT); 266 if (ma == NULL) 267 return; 268 phy = device_add_child(dev, name, unit); 269 if (phy == NULL) { 270 free(ma, M_DEVBUF); 271 return; 272 } 273 ivars = device_get_ivars(dev); 274 ma->mii_phyno = val; 275 ma->mii_offset = ivars->mii_offset++; 276 ma->mii_id1 = 0; 277 ma->mii_id2 = 0; 278 ma->mii_capmask = BMSR_DEFCAPMASK; 279 device_set_ivars(phy, ma); 280 } 281 282 if (resource_int_value(name, unit, "id1", &val) == 0) 283 ma->mii_id1 = val; 284 if (resource_int_value(name, unit, "id2", &val) == 0) 285 ma->mii_id2 = val; 286 if (resource_int_value(name, unit, "capmask", &val) == 0) 287 ma->mii_capmask = val; 288 } 289 290 static int 291 miibus_readreg(device_t dev, int phy, int reg) 292 { 293 device_t parent; 294 295 parent = device_get_parent(dev); 296 return (MIIBUS_READREG(parent, phy, reg)); 297 } 298 299 static int 300 miibus_writereg(device_t dev, int phy, int reg, int data) 301 { 302 device_t parent; 303 304 parent = device_get_parent(dev); 305 return (MIIBUS_WRITEREG(parent, phy, reg, data)); 306 } 307 308 static void 309 miibus_statchg(device_t dev) 310 { 311 device_t parent; 312 struct mii_data *mii; 313 314 parent = device_get_parent(dev); 315 MIIBUS_STATCHG(parent); 316 317 mii = device_get_softc(dev); 318 if_setbaudrate(mii->mii_ifp, ifmedia_baudrate(mii->mii_media_active)); 319 } 320 321 static void 322 miibus_linkchg(device_t dev) 323 { 324 struct mii_data *mii; 325 device_t parent; 326 int link_state; 327 328 parent = device_get_parent(dev); 329 MIIBUS_LINKCHG(parent); 330 331 mii = device_get_softc(dev); 332 333 if (mii->mii_media_status & IFM_AVALID) { 334 if (mii->mii_media_status & IFM_ACTIVE) 335 link_state = LINK_STATE_UP; 336 else 337 link_state = LINK_STATE_DOWN; 338 } else 339 link_state = LINK_STATE_UNKNOWN; 340 if_link_state_change(mii->mii_ifp, link_state); 341 } 342 343 static void 344 miibus_mediainit(device_t dev) 345 { 346 struct mii_data *mii; 347 struct ifmedia_entry *m; 348 int media = 0; 349 350 /* Poke the parent in case it has any media of its own to add. */ 351 MIIBUS_MEDIAINIT(device_get_parent(dev)); 352 353 mii = device_get_softc(dev); 354 LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) { 355 media = m->ifm_media; 356 if (media == (IFM_ETHER | IFM_AUTO)) 357 break; 358 } 359 360 ifmedia_set(&mii->mii_media, media); 361 } 362 363 /* 364 * Helper function used by network interface drivers, attaches the miibus and 365 * the PHYs to the network interface driver parent. 366 */ 367 int 368 mii_attach(device_t dev, device_t *miibus, if_t ifp, 369 ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask, 370 int phyloc, int offloc, int flags) 371 { 372 struct miibus_ivars *ivars; 373 struct mii_attach_args *args, ma; 374 device_t *children, phy; 375 int bmsr, first, i, nchildren, phymax, phymin, rv; 376 uint32_t phymask; 377 378 bus_topo_assert(); 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", DEVICE_UNIT_ANY); 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, DEVICE_UNIT_ANY); 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 bus_attach_children(dev); 525 526 /* Attaching of the PHY drivers is done in miibus_attach(). */ 527 return (0); 528 } 529 bus_attach_children(*miibus); 530 531 return (0); 532 533 fail: 534 if (*miibus != NULL) 535 device_delete_child(dev, *miibus); 536 free(ivars, M_DEVBUF); 537 if (first != 0) 538 *miibus = NULL; 539 return (rv); 540 } 541 542 /* 543 * Media changed; notify all PHYs. 544 */ 545 int 546 mii_mediachg(struct mii_data *mii) 547 { 548 struct mii_softc *child; 549 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 550 int rv; 551 552 mii->mii_media_status = 0; 553 mii->mii_media_active = IFM_NONE; 554 555 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 556 /* 557 * If the media indicates a different PHY instance, 558 * isolate this one. 559 */ 560 if (IFM_INST(ife->ifm_media) != child->mii_inst) { 561 if ((child->mii_flags & MIIF_NOISOLATE) != 0) { 562 device_printf(child->mii_dev, "%s: " 563 "can't handle non-zero PHY instance %d\n", 564 __func__, child->mii_inst); 565 continue; 566 } 567 PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) | 568 BMCR_ISO); 569 continue; 570 } 571 rv = PHY_SERVICE(child, mii, MII_MEDIACHG); 572 if (rv) 573 return (rv); 574 } 575 return (0); 576 } 577 578 /* 579 * Call the PHY tick routines, used during autonegotiation. 580 */ 581 void 582 mii_tick(struct mii_data *mii) 583 { 584 struct mii_softc *child; 585 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 586 587 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 588 /* 589 * If this PHY instance isn't currently selected, just skip 590 * it. 591 */ 592 if (IFM_INST(ife->ifm_media) != child->mii_inst) 593 continue; 594 (void)PHY_SERVICE(child, mii, MII_TICK); 595 } 596 } 597 598 /* 599 * Get media status from PHYs. 600 */ 601 void 602 mii_pollstat(struct mii_data *mii) 603 { 604 struct mii_softc *child; 605 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 606 607 mii->mii_media_status = 0; 608 mii->mii_media_active = IFM_NONE; 609 610 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 611 /* 612 * If we're not polling this PHY instance, just skip it. 613 */ 614 if (IFM_INST(ife->ifm_media) != child->mii_inst) 615 continue; 616 (void)PHY_SERVICE(child, mii, MII_POLLSTAT); 617 } 618 } 619 620 static unsigned char 621 mii_bitreverse(unsigned char x) 622 { 623 static unsigned const char nibbletab[16] = { 624 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 625 }; 626 627 return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]); 628 } 629 630 u_int 631 mii_oui(u_int id1, u_int id2) 632 { 633 u_int h; 634 635 h = (id1 << 6) | (id2 >> 10); 636 637 return ((mii_bitreverse(h >> 16) << 16) | 638 (mii_bitreverse((h >> 8) & 0xff) << 8) | 639 mii_bitreverse(h & 0xff)); 640 } 641 642 int 643 mii_phy_mac_match(struct mii_softc *mii, const char *name) 644 { 645 646 return (strcmp(device_get_name(device_get_parent(mii->mii_dev)), 647 name) == 0); 648 } 649 650 int 651 mii_dev_mac_match(device_t parent, const char *name) 652 { 653 654 return (strcmp(device_get_name(device_get_parent( 655 device_get_parent(parent))), name) == 0); 656 } 657 658 void * 659 mii_phy_mac_softc(struct mii_softc *mii) 660 { 661 662 return (device_get_softc(device_get_parent(mii->mii_dev))); 663 } 664 665 void * 666 mii_dev_mac_softc(device_t parent) 667 { 668 669 return (device_get_softc(device_get_parent(device_get_parent(parent)))); 670 } 671