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 static unsigned char mii_bitreverse(unsigned char x); 79 80 static device_method_t miibus_methods[] = { 81 /* device interface */ 82 DEVMETHOD(device_probe, miibus_probe), 83 DEVMETHOD(device_attach, miibus_attach), 84 DEVMETHOD(device_detach, miibus_detach), 85 DEVMETHOD(device_shutdown, bus_generic_shutdown), 86 87 /* bus interface */ 88 DEVMETHOD(bus_print_child, miibus_print_child), 89 DEVMETHOD(bus_read_ivar, miibus_read_ivar), 90 DEVMETHOD(bus_driver_added, bus_generic_driver_added), 91 DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str), 92 DEVMETHOD(bus_child_location_str, miibus_child_location_str), 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 { 0, 0 } 102 }; 103 104 devclass_t miibus_devclass; 105 106 driver_t miibus_driver = { 107 "miibus", 108 miibus_methods, 109 sizeof(struct mii_data) 110 }; 111 112 struct miibus_ivars { 113 struct ifnet *ifp; 114 ifm_change_cb_t ifmedia_upd; 115 ifm_stat_cb_t ifmedia_sts; 116 int mii_flags; 117 }; 118 119 int 120 miibus_probe(device_t dev) 121 { 122 123 device_set_desc(dev, "MII bus"); 124 125 return (BUS_PROBE_SPECIFIC); 126 } 127 128 int 129 miibus_attach(device_t dev) 130 { 131 struct miibus_ivars *ivars; 132 struct mii_attach_args *ma; 133 struct mii_data *mii; 134 device_t *children; 135 int i, nchildren; 136 137 mii = device_get_softc(dev); 138 nchildren = 0; 139 if (device_get_children(dev, &children, &nchildren) == 0) { 140 for (i = 0; i < nchildren; i++) { 141 ma = device_get_ivars(children[i]); 142 ma->mii_data = mii; 143 } 144 free(children, M_TEMP); 145 } 146 if (nchildren == 0) { 147 device_printf(dev, "cannot get children\n"); 148 return (ENXIO); 149 } 150 ivars = device_get_ivars(dev); 151 ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd, 152 ivars->ifmedia_sts); 153 mii->mii_ifp = ivars->ifp; 154 mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE; 155 mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE; 156 LIST_INIT(&mii->mii_phys); 157 158 return (bus_generic_attach(dev)); 159 } 160 161 int 162 miibus_detach(device_t dev) 163 { 164 struct mii_data *mii; 165 166 bus_generic_detach(dev); 167 mii = device_get_softc(dev); 168 ifmedia_removeall(&mii->mii_media); 169 mii->mii_ifp = NULL; 170 171 return (0); 172 } 173 174 static int 175 miibus_print_child(device_t dev, device_t child) 176 { 177 struct mii_attach_args *ma; 178 int retval; 179 180 ma = device_get_ivars(child); 181 retval = bus_print_child_header(dev, child); 182 retval += printf(" PHY %d", ma->mii_phyno); 183 retval += bus_print_child_footer(dev, child); 184 185 return (retval); 186 } 187 188 static int 189 miibus_read_ivar(device_t dev, device_t child __unused, int which, 190 uintptr_t *result) 191 { 192 struct miibus_ivars *ivars; 193 194 /* 195 * NB: this uses the instance variables of the miibus rather than 196 * its PHY children. 197 */ 198 ivars = device_get_ivars(dev); 199 switch (which) { 200 case MIIBUS_IVAR_FLAGS: 201 *result = ivars->mii_flags; 202 break; 203 default: 204 return (ENOENT); 205 } 206 return (0); 207 } 208 209 static int 210 miibus_child_pnpinfo_str(device_t bus __unused, device_t child, char *buf, 211 size_t buflen) 212 { 213 struct mii_attach_args *ma; 214 215 ma = device_get_ivars(child); 216 snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x", 217 MII_OUI(ma->mii_id1, ma->mii_id2), 218 MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); 219 return (0); 220 } 221 222 static int 223 miibus_child_location_str(device_t bus __unused, device_t child, char *buf, 224 size_t buflen) 225 { 226 struct mii_attach_args *ma; 227 228 ma = device_get_ivars(child); 229 snprintf(buf, buflen, "phyno=%d", ma->mii_phyno); 230 return (0); 231 } 232 233 static int 234 miibus_readreg(device_t dev, int phy, int reg) 235 { 236 device_t parent; 237 238 parent = device_get_parent(dev); 239 return (MIIBUS_READREG(parent, phy, reg)); 240 } 241 242 static int 243 miibus_writereg(device_t dev, int phy, int reg, int data) 244 { 245 device_t parent; 246 247 parent = device_get_parent(dev); 248 return (MIIBUS_WRITEREG(parent, phy, reg, data)); 249 } 250 251 static void 252 miibus_statchg(device_t dev) 253 { 254 device_t parent; 255 struct mii_data *mii; 256 257 parent = device_get_parent(dev); 258 MIIBUS_STATCHG(parent); 259 260 mii = device_get_softc(dev); 261 mii->mii_ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active); 262 } 263 264 static void 265 miibus_linkchg(device_t dev) 266 { 267 struct mii_data *mii; 268 device_t parent; 269 int link_state; 270 271 parent = device_get_parent(dev); 272 MIIBUS_LINKCHG(parent); 273 274 mii = device_get_softc(dev); 275 276 if (mii->mii_media_status & IFM_AVALID) { 277 if (mii->mii_media_status & IFM_ACTIVE) 278 link_state = LINK_STATE_UP; 279 else 280 link_state = LINK_STATE_DOWN; 281 } else 282 link_state = LINK_STATE_UNKNOWN; 283 if_link_state_change(mii->mii_ifp, link_state); 284 } 285 286 static void 287 miibus_mediainit(device_t dev) 288 { 289 struct mii_data *mii; 290 struct ifmedia_entry *m; 291 int media = 0; 292 293 /* Poke the parent in case it has any media of its own to add. */ 294 MIIBUS_MEDIAINIT(device_get_parent(dev)); 295 296 mii = device_get_softc(dev); 297 LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) { 298 media = m->ifm_media; 299 if (media == (IFM_ETHER | IFM_AUTO)) 300 break; 301 } 302 303 ifmedia_set(&mii->mii_media, media); 304 } 305 306 /* 307 * Helper function used by network interface drivers, attaches the miibus and 308 * the PHYs to the network interface driver parent. 309 */ 310 int 311 mii_attach(device_t dev, device_t *miibus, struct ifnet *ifp, 312 ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask, 313 int phyloc, int offloc, int flags) 314 { 315 struct miibus_ivars *ivars; 316 struct mii_attach_args ma, *args; 317 device_t *children, phy; 318 int bmsr, first, i, nchildren, offset, phymax, phymin, rv; 319 320 if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) { 321 printf("%s: phyloc and offloc specified\n", __func__); 322 return (EINVAL); 323 } 324 325 if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) { 326 printf("%s: ivalid offloc %d\n", __func__, offloc); 327 return (EINVAL); 328 } 329 330 if (phyloc == MII_PHY_ANY) { 331 phymin = 0; 332 phymax = MII_NPHY - 1; 333 } else { 334 if (phyloc < 0 || phyloc >= MII_NPHY) { 335 printf("%s: ivalid phyloc %d\n", __func__, phyloc); 336 return (EINVAL); 337 } 338 phymin = phymax = phyloc; 339 } 340 341 first = 0; 342 if (*miibus == NULL) { 343 first = 1; 344 ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT); 345 if (ivars == NULL) 346 return (ENOMEM); 347 ivars->ifp = ifp; 348 ivars->ifmedia_upd = ifmedia_upd; 349 ivars->ifmedia_sts = ifmedia_sts; 350 ivars->mii_flags = flags; 351 *miibus = device_add_child(dev, "miibus", -1); 352 if (*miibus == NULL) { 353 rv = ENXIO; 354 goto fail; 355 } 356 device_set_ivars(*miibus, ivars); 357 } else { 358 ivars = device_get_ivars(*miibus); 359 if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd || 360 ivars->ifmedia_sts != ifmedia_sts || 361 ivars->mii_flags != flags) { 362 printf("%s: non-matching invariant\n", __func__); 363 return (EINVAL); 364 } 365 /* 366 * Assignment of the attach arguments mii_data for the first 367 * pass is done in miibus_attach(), i.e. once the miibus softc 368 * has been allocated. 369 */ 370 ma.mii_data = device_get_softc(*miibus); 371 } 372 373 ma.mii_capmask = capmask; 374 375 phy = NULL; 376 offset = 0; 377 for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) { 378 /* 379 * Make sure we haven't already configured a PHY at this 380 * address. This allows mii_attach() to be called 381 * multiple times. 382 */ 383 if (device_get_children(*miibus, &children, &nchildren) == 0) { 384 for (i = 0; i < nchildren; i++) { 385 args = device_get_ivars(children[i]); 386 if (args->mii_phyno == ma.mii_phyno) { 387 /* 388 * Yes, there is already something 389 * configured at this address. 390 */ 391 free(children, M_TEMP); 392 goto skip; 393 } 394 } 395 free(children, M_TEMP); 396 } 397 398 /* 399 * Check to see if there is a PHY at this address. Note, 400 * many braindead PHYs report 0/0 in their ID registers, 401 * so we test for media in the BMSR. 402 */ 403 bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR); 404 if (bmsr == 0 || bmsr == 0xffff || 405 (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) { 406 /* Assume no PHY at this address. */ 407 continue; 408 } 409 410 /* 411 * There is a PHY at this address. If we were given an 412 * `offset' locator, skip this PHY if it doesn't match. 413 */ 414 if (offloc != MII_OFFSET_ANY && offloc != offset) 415 goto skip; 416 417 /* 418 * Extract the IDs. Braindead PHYs will be handled by 419 * the `ukphy' driver, as we have no ID information to 420 * match on. 421 */ 422 ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1); 423 ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2); 424 425 ma.mii_offset = offset; 426 args = malloc(sizeof(struct mii_attach_args), M_DEVBUF, 427 M_NOWAIT); 428 if (args == NULL) 429 goto skip; 430 bcopy((char *)&ma, (char *)args, sizeof(ma)); 431 phy = device_add_child(*miibus, NULL, -1); 432 if (phy == NULL) { 433 free(args, M_DEVBUF); 434 goto skip; 435 } 436 device_set_ivars(phy, args); 437 skip: 438 offset++; 439 } 440 441 if (first != 0) { 442 if (phy == NULL) { 443 rv = ENXIO; 444 goto fail; 445 } 446 rv = bus_generic_attach(dev); 447 if (rv != 0) 448 goto fail; 449 450 /* Attaching of the PHY drivers is done in miibus_attach(). */ 451 return (0); 452 } 453 rv = bus_generic_attach(*miibus); 454 if (rv != 0) 455 goto fail; 456 457 return (0); 458 459 fail: 460 if (*miibus != NULL) 461 device_delete_child(dev, *miibus); 462 free(ivars, M_DEVBUF); 463 if (first != 0) 464 *miibus = NULL; 465 return (rv); 466 } 467 468 /* 469 * Media changed; notify all PHYs. 470 */ 471 int 472 mii_mediachg(struct mii_data *mii) 473 { 474 struct mii_softc *child; 475 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 476 int rv; 477 478 mii->mii_media_status = 0; 479 mii->mii_media_active = IFM_NONE; 480 481 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 482 /* 483 * If the media indicates a different PHY instance, 484 * isolate this one. 485 */ 486 if (IFM_INST(ife->ifm_media) != child->mii_inst) { 487 if ((child->mii_flags & MIIF_NOISOLATE) != 0) { 488 device_printf(child->mii_dev, "%s: " 489 "can't handle non-zero PHY instance %d\n", 490 __func__, child->mii_inst); 491 continue; 492 } 493 PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) | 494 BMCR_ISO); 495 continue; 496 } 497 rv = PHY_SERVICE(child, mii, MII_MEDIACHG); 498 if (rv) 499 return (rv); 500 } 501 return (0); 502 } 503 504 /* 505 * Call the PHY tick routines, used during autonegotiation. 506 */ 507 void 508 mii_tick(struct mii_data *mii) 509 { 510 struct mii_softc *child; 511 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 512 513 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 514 /* 515 * If this PHY instance isn't currently selected, just skip 516 * it. 517 */ 518 if (IFM_INST(ife->ifm_media) != child->mii_inst) 519 continue; 520 (void)PHY_SERVICE(child, mii, MII_TICK); 521 } 522 } 523 524 /* 525 * Get media status from PHYs. 526 */ 527 void 528 mii_pollstat(struct mii_data *mii) 529 { 530 struct mii_softc *child; 531 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 532 533 mii->mii_media_status = 0; 534 mii->mii_media_active = IFM_NONE; 535 536 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 537 /* 538 * If we're not polling this PHY instance, just skip it. 539 */ 540 if (IFM_INST(ife->ifm_media) != child->mii_inst) 541 continue; 542 (void)PHY_SERVICE(child, mii, MII_POLLSTAT); 543 } 544 } 545 546 /* 547 * Inform the PHYs that the interface is down. 548 */ 549 void 550 mii_down(struct mii_data *mii) 551 { 552 struct mii_softc *child; 553 554 LIST_FOREACH(child, &mii->mii_phys, mii_list) 555 mii_phy_down(child); 556 } 557 558 static unsigned char 559 mii_bitreverse(unsigned char x) 560 { 561 unsigned const char const nibbletab[16] = { 562 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 563 }; 564 565 return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]); 566 } 567 568 u_int 569 mii_oui(u_int id1, u_int id2) 570 { 571 u_int h; 572 573 h = (id1 << 6) | (id2 >> 10); 574 575 return ((mii_bitreverse(h >> 16) << 16) | 576 (mii_bitreverse((h >> 8) & 0xff) << 8) | 577 mii_bitreverse(h & 0xff)); 578 } 579