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