150120Swpaul/* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */ 250120Swpaul 350120Swpaul/*- 450120Swpaul * Copyright (c) 1998 The NetBSD Foundation, Inc. 550120Swpaul * All rights reserved. 650120Swpaul * 750120Swpaul * This code is derived from software contributed to The NetBSD Foundation 850120Swpaul * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 950120Swpaul * NASA Ames Research Center. 1050120Swpaul * 1150120Swpaul * Redistribution and use in source and binary forms, with or without 1250120Swpaul * modification, are permitted provided that the following conditions 1350120Swpaul * are met: 1450120Swpaul * 1. Redistributions of source code must retain the above copyright 1550120Swpaul * notice, this list of conditions and the following disclaimer. 1650120Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1750120Swpaul * notice, this list of conditions and the following disclaimer in the 1850120Swpaul * documentation and/or other materials provided with the distribution. 1950120Swpaul * 2050120Swpaul * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2150120Swpaul * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2250120Swpaul * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2350120Swpaul * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2450120Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2550120Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2650120Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2750120Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2850120Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2950120Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3050120Swpaul * POSSIBILITY OF SUCH DAMAGE. 3150120Swpaul */ 3250120Swpaul 33119418Sobrien#include <sys/cdefs.h> 34119418Sobrien__FBSDID("$FreeBSD$"); 35119418Sobrien 3650120Swpaul/* 3750120Swpaul * MII bus layer, glues MII-capable network interface drivers to sharable 3850120Swpaul * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, 3950120Swpaul * plus some NetBSD extensions. 4050120Swpaul */ 4150120Swpaul 4250120Swpaul#include <sys/param.h> 4350120Swpaul#include <sys/systm.h> 4450120Swpaul#include <sys/socket.h> 4550120Swpaul#include <sys/malloc.h> 4650120Swpaul#include <sys/module.h> 47221407Smarius#include <sys/bus.h> 4850120Swpaul 4950120Swpaul#include <net/if.h> 5050120Swpaul#include <net/if_media.h> 5150120Swpaul 5250120Swpaul#include <dev/mii/mii.h> 5350120Swpaul#include <dev/mii/miivar.h> 5450120Swpaul 5559757SpeterMODULE_VERSION(miibus, 1); 5659757Speter 5750120Swpaul#include "miibus_if.h" 5850120Swpaul 59230711Smariusstatic device_attach_t miibus_attach; 60230709Smariusstatic bus_child_location_str_t miibus_child_location_str; 61230709Smariusstatic bus_child_pnpinfo_str_t miibus_child_pnpinfo_str; 62230711Smariusstatic device_detach_t miibus_detach; 63230709Smariusstatic bus_hinted_child_t miibus_hinted_child; 64230709Smariusstatic bus_print_child_t miibus_print_child; 65230711Smariusstatic device_probe_t miibus_probe; 66230709Smariusstatic bus_read_ivar_t miibus_read_ivar; 67230709Smariusstatic miibus_readreg_t miibus_readreg; 68230709Smariusstatic miibus_statchg_t miibus_statchg; 69230709Smariusstatic miibus_writereg_t miibus_writereg; 70230709Smariusstatic miibus_linkchg_t miibus_linkchg; 71230709Smariusstatic miibus_mediainit_t miibus_mediainit; 72230709Smarius 73221407Smariusstatic unsigned char mii_bitreverse(unsigned char x); 7450120Swpaul 7550120Swpaulstatic device_method_t miibus_methods[] = { 7650120Swpaul /* device interface */ 7750120Swpaul DEVMETHOD(device_probe, miibus_probe), 7850120Swpaul DEVMETHOD(device_attach, miibus_attach), 7950120Swpaul DEVMETHOD(device_detach, miibus_detach), 8050120Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 8150120Swpaul 8250120Swpaul /* bus interface */ 83166112Smarius DEVMETHOD(bus_print_child, miibus_print_child), 84213878Smarius DEVMETHOD(bus_read_ivar, miibus_read_ivar), 85141960Simp DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str), 86141960Simp DEVMETHOD(bus_child_location_str, miibus_child_location_str), 87230709Smarius DEVMETHOD(bus_hinted_child, miibus_hinted_child), 8850120Swpaul 8950120Swpaul /* MII interface */ 9050120Swpaul DEVMETHOD(miibus_readreg, miibus_readreg), 9150120Swpaul DEVMETHOD(miibus_writereg, miibus_writereg), 92221407Smarius DEVMETHOD(miibus_statchg, miibus_statchg), 93221407Smarius DEVMETHOD(miibus_linkchg, miibus_linkchg), 94221407Smarius DEVMETHOD(miibus_mediainit, miibus_mediainit), 9550120Swpaul 96229093Shselasky DEVMETHOD_END 9750120Swpaul}; 9850120Swpaul 9950120Swpauldevclass_t miibus_devclass; 10050120Swpaul 10150120Swpauldriver_t miibus_driver = { 10250120Swpaul "miibus", 10350120Swpaul miibus_methods, 10450120Swpaul sizeof(struct mii_data) 10550120Swpaul}; 10650120Swpaul 107169184Smarcelstruct miibus_ivars { 108213878Smarius struct ifnet *ifp; 109169184Smarcel ifm_change_cb_t ifmedia_upd; 110169184Smarcel ifm_stat_cb_t ifmedia_sts; 111230709Smarius u_int mii_flags; 112230709Smarius u_int mii_offset; 113169184Smarcel}; 114169184Smarcel 115230711Smariusstatic int 116141937Simpmiibus_probe(device_t dev) 11750120Swpaul{ 11850120Swpaul 11950120Swpaul device_set_desc(dev, "MII bus"); 12050120Swpaul 121213878Smarius return (BUS_PROBE_SPECIFIC); 12250120Swpaul} 12350120Swpaul 124230711Smariusstatic int 125141937Simpmiibus_attach(device_t dev) 12650120Swpaul{ 127169184Smarcel struct miibus_ivars *ivars; 128213878Smarius struct mii_attach_args *ma; 12950120Swpaul struct mii_data *mii; 130213878Smarius device_t *children; 131213878Smarius int i, nchildren; 13250120Swpaul 13350120Swpaul mii = device_get_softc(dev); 134213878Smarius if (device_get_children(dev, &children, &nchildren) == 0) { 135213878Smarius for (i = 0; i < nchildren; i++) { 136213878Smarius ma = device_get_ivars(children[i]); 137213878Smarius ma->mii_data = mii; 138213878Smarius } 139213878Smarius free(children, M_TEMP); 140213878Smarius } 141213878Smarius if (nchildren == 0) { 142215711Smarius device_printf(dev, "cannot get children\n"); 143213878Smarius return (ENXIO); 144213878Smarius } 145169184Smarcel ivars = device_get_ivars(dev); 146169184Smarcel ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd, 147169184Smarcel ivars->ifmedia_sts); 148213878Smarius mii->mii_ifp = ivars->ifp; 149213878Smarius mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE; 150213878Smarius mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE; 151213878Smarius LIST_INIT(&mii->mii_phys); 15250120Swpaul 153213878Smarius return (bus_generic_attach(dev)); 15450120Swpaul} 15550120Swpaul 156230711Smariusstatic int 157141937Simpmiibus_detach(device_t dev) 15850120Swpaul{ 15950120Swpaul struct mii_data *mii; 16050120Swpaul 16150120Swpaul bus_generic_detach(dev); 16250120Swpaul mii = device_get_softc(dev); 16350120Swpaul ifmedia_removeall(&mii->mii_media); 16450120Swpaul mii->mii_ifp = NULL; 16550120Swpaul 166213361Smarius return (0); 16750120Swpaul} 16850120Swpaul 169105135Salfredstatic int 170166112Smariusmiibus_print_child(device_t dev, device_t child) 171166112Smarius{ 172166112Smarius struct mii_attach_args *ma; 173166112Smarius int retval; 174166112Smarius 175166112Smarius ma = device_get_ivars(child); 176166112Smarius retval = bus_print_child_header(dev, child); 177166112Smarius retval += printf(" PHY %d", ma->mii_phyno); 178166112Smarius retval += bus_print_child_footer(dev, child); 179166112Smarius 180166112Smarius return (retval); 181166112Smarius} 182166112Smarius 183166112Smariusstatic int 184213878Smariusmiibus_read_ivar(device_t dev, device_t child __unused, int which, 185213878Smarius uintptr_t *result) 186213878Smarius{ 187213878Smarius struct miibus_ivars *ivars; 188213878Smarius 189213878Smarius /* 190213878Smarius * NB: this uses the instance variables of the miibus rather than 191213878Smarius * its PHY children. 192213878Smarius */ 193213878Smarius ivars = device_get_ivars(dev); 194213878Smarius switch (which) { 195213878Smarius case MIIBUS_IVAR_FLAGS: 196213878Smarius *result = ivars->mii_flags; 197213878Smarius break; 198213878Smarius default: 199213878Smarius return (ENOENT); 200213878Smarius } 201213878Smarius return (0); 202213878Smarius} 203213878Smarius 204213878Smariusstatic int 205230709Smariusmiibus_child_pnpinfo_str(device_t dev __unused, device_t child, char *buf, 206141960Simp size_t buflen) 207141960Simp{ 208213361Smarius struct mii_attach_args *ma; 209213361Smarius 210213361Smarius ma = device_get_ivars(child); 211141964Simp snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x", 212213361Smarius MII_OUI(ma->mii_id1, ma->mii_id2), 213213361Smarius MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); 214141960Simp return (0); 215141960Simp} 216141960Simp 217141960Simpstatic int 218230709Smariusmiibus_child_location_str(device_t dev __unused, device_t child, char *buf, 219141960Simp size_t buflen) 220141960Simp{ 221213361Smarius struct mii_attach_args *ma; 222213361Smarius 223213361Smarius ma = device_get_ivars(child); 224213361Smarius snprintf(buf, buflen, "phyno=%d", ma->mii_phyno); 225141960Simp return (0); 226141960Simp} 227141960Simp 228230709Smariusstatic void 229230709Smariusmiibus_hinted_child(device_t dev, const char *name, int unit) 230230709Smarius{ 231230709Smarius struct miibus_ivars *ivars; 232230709Smarius struct mii_attach_args *args, *ma; 233230709Smarius device_t *children, phy; 234230709Smarius int i, nchildren; 235230709Smarius u_int val; 236230709Smarius 237230709Smarius if (resource_int_value(name, unit, "phyno", &val) != 0) 238230709Smarius return; 239230709Smarius if (device_get_children(dev, &children, &nchildren) != 0) 240230709Smarius return; 241230709Smarius ma = NULL; 242230709Smarius for (i = 0; i < nchildren; i++) { 243230709Smarius args = device_get_ivars(children[i]); 244230709Smarius if (args->mii_phyno == val) { 245230709Smarius ma = args; 246230709Smarius break; 247230709Smarius } 248230709Smarius } 249230709Smarius free(children, M_TEMP); 250230709Smarius 251230709Smarius /* 252230709Smarius * Don't add a PHY that was automatically identified by having media 253230709Smarius * in its BMSR twice, only allow to alter its attach arguments. 254230709Smarius */ 255230709Smarius if (ma == NULL) { 256230709Smarius ma = malloc(sizeof(struct mii_attach_args), M_DEVBUF, 257230709Smarius M_NOWAIT); 258230709Smarius if (ma == NULL) 259230709Smarius return; 260230709Smarius phy = device_add_child(dev, name, unit); 261230709Smarius if (phy == NULL) { 262230709Smarius free(ma, M_DEVBUF); 263230709Smarius return; 264230709Smarius } 265230709Smarius ivars = device_get_ivars(dev); 266230709Smarius ma->mii_phyno = val; 267230709Smarius ma->mii_offset = ivars->mii_offset++; 268230709Smarius ma->mii_id1 = 0; 269230709Smarius ma->mii_id2 = 0; 270230709Smarius ma->mii_capmask = BMSR_DEFCAPMASK; 271230709Smarius device_set_ivars(phy, ma); 272230709Smarius } 273230709Smarius 274230709Smarius if (resource_int_value(name, unit, "id1", &val) == 0) 275230709Smarius ma->mii_id1 = val; 276230709Smarius if (resource_int_value(name, unit, "id2", &val) == 0) 277230709Smarius ma->mii_id2 = val; 278230709Smarius if (resource_int_value(name, unit, "capmask", &val) == 0) 279230709Smarius ma->mii_capmask = val; 280230709Smarius} 281230709Smarius 282141960Simpstatic int 283141937Simpmiibus_readreg(device_t dev, int phy, int reg) 28450120Swpaul{ 28550120Swpaul device_t parent; 28650120Swpaul 28750120Swpaul parent = device_get_parent(dev); 288213361Smarius return (MIIBUS_READREG(parent, phy, reg)); 28950120Swpaul} 29050120Swpaul 291105135Salfredstatic int 292141937Simpmiibus_writereg(device_t dev, int phy, int reg, int data) 29350120Swpaul{ 29450120Swpaul device_t parent; 29550120Swpaul 29650120Swpaul parent = device_get_parent(dev); 297213361Smarius return (MIIBUS_WRITEREG(parent, phy, reg, data)); 29850120Swpaul} 29950120Swpaul 300105135Salfredstatic void 301141937Simpmiibus_statchg(device_t dev) 30250120Swpaul{ 30350120Swpaul device_t parent; 304155669Sglebius struct mii_data *mii; 30550120Swpaul 30650120Swpaul parent = device_get_parent(dev); 30750120Swpaul MIIBUS_STATCHG(parent); 308155669Sglebius 309155669Sglebius mii = device_get_softc(dev); 310205270Simp mii->mii_ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active); 31150120Swpaul} 31250120Swpaul 31384140Sjlemonstatic void 314141937Simpmiibus_linkchg(device_t dev) 31584140Sjlemon{ 316138542Ssam struct mii_data *mii; 317138542Ssam device_t parent; 318138542Ssam int link_state; 31984140Sjlemon 32084140Sjlemon parent = device_get_parent(dev); 32184140Sjlemon MIIBUS_LINKCHG(parent); 32284140Sjlemon 32384140Sjlemon mii = device_get_softc(dev); 324221407Smarius 32584140Sjlemon if (mii->mii_media_status & IFM_AVALID) { 326138542Ssam if (mii->mii_media_status & IFM_ACTIVE) 327128871Sandre link_state = LINK_STATE_UP; 328138542Ssam else 329128871Sandre link_state = LINK_STATE_DOWN; 330138542Ssam } else 331128871Sandre link_state = LINK_STATE_UNKNOWN; 332205270Simp if_link_state_change(mii->mii_ifp, link_state); 33384140Sjlemon} 33484140Sjlemon 335105135Salfredstatic void 336141937Simpmiibus_mediainit(device_t dev) 33750120Swpaul{ 33850120Swpaul struct mii_data *mii; 33950120Swpaul struct ifmedia_entry *m; 34050120Swpaul int media = 0; 34150120Swpaul 34250577Swpaul /* Poke the parent in case it has any media of its own to add. */ 34350577Swpaul MIIBUS_MEDIAINIT(device_get_parent(dev)); 34450577Swpaul 34550120Swpaul mii = device_get_softc(dev); 34672012Sphk LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) { 34750120Swpaul media = m->ifm_media; 348213361Smarius if (media == (IFM_ETHER | IFM_AUTO)) 34950120Swpaul break; 35050120Swpaul } 35150120Swpaul 35250120Swpaul ifmedia_set(&mii->mii_media, media); 35350120Swpaul} 35450120Swpaul 355213878Smarius/* 356213878Smarius * Helper function used by network interface drivers, attaches the miibus and 357213878Smarius * the PHYs to the network interface driver parent. 358213878Smarius */ 359105135Salfredint 360213878Smariusmii_attach(device_t dev, device_t *miibus, struct ifnet *ifp, 361213878Smarius ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask, 362213878Smarius int phyloc, int offloc, int flags) 36350120Swpaul{ 364213878Smarius struct miibus_ivars *ivars; 365230709Smarius struct mii_attach_args *args, ma; 366213878Smarius device_t *children, phy; 367230709Smarius int bmsr, first, i, nchildren, phymax, phymin, rv; 368230709Smarius uint32_t phymask; 36950120Swpaul 370213878Smarius if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) { 371215711Smarius printf("%s: phyloc and offloc specified\n", __func__); 372213878Smarius return (EINVAL); 373213878Smarius } 37450120Swpaul 375213878Smarius if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) { 376215711Smarius printf("%s: ivalid offloc %d\n", __func__, offloc); 377213878Smarius return (EINVAL); 37850120Swpaul } 37950120Swpaul 380213878Smarius if (phyloc == MII_PHY_ANY) { 381213878Smarius phymin = 0; 382213878Smarius phymax = MII_NPHY - 1; 383213878Smarius } else { 384213878Smarius if (phyloc < 0 || phyloc >= MII_NPHY) { 385215711Smarius printf("%s: ivalid phyloc %d\n", __func__, phyloc); 386213878Smarius return (EINVAL); 387213878Smarius } 388213878Smarius phymin = phymax = phyloc; 38950120Swpaul } 39050120Swpaul 391213878Smarius first = 0; 392213878Smarius if (*miibus == NULL) { 393213878Smarius first = 1; 394213878Smarius ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT); 395213878Smarius if (ivars == NULL) 396213878Smarius return (ENOMEM); 397213878Smarius ivars->ifp = ifp; 398213878Smarius ivars->ifmedia_upd = ifmedia_upd; 399213878Smarius ivars->ifmedia_sts = ifmedia_sts; 400213878Smarius ivars->mii_flags = flags; 401213878Smarius *miibus = device_add_child(dev, "miibus", -1); 402213878Smarius if (*miibus == NULL) { 403213878Smarius rv = ENXIO; 404213878Smarius goto fail; 405213878Smarius } 406213878Smarius device_set_ivars(*miibus, ivars); 407213878Smarius } else { 408213878Smarius ivars = device_get_ivars(*miibus); 409213878Smarius if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd || 410213878Smarius ivars->ifmedia_sts != ifmedia_sts || 411213878Smarius ivars->mii_flags != flags) { 412215711Smarius printf("%s: non-matching invariant\n", __func__); 413213878Smarius return (EINVAL); 414213878Smarius } 415213878Smarius /* 416213878Smarius * Assignment of the attach arguments mii_data for the first 417213878Smarius * pass is done in miibus_attach(), i.e. once the miibus softc 418213878Smarius * has been allocated. 419213878Smarius */ 420213878Smarius ma.mii_data = device_get_softc(*miibus); 421221407Smarius } 42250120Swpaul 423213878Smarius ma.mii_capmask = capmask; 424213878Smarius 425230709Smarius if (resource_int_value(device_get_name(*miibus), 426230709Smarius device_get_unit(*miibus), "phymask", &phymask) != 0) 427230709Smarius phymask = 0xffffffff; 428230709Smarius 429230709Smarius if (device_get_children(*miibus, &children, &nchildren) != 0) { 430230709Smarius children = NULL; 431230709Smarius nchildren = 0; 432230709Smarius } 433230709Smarius ivars->mii_offset = 0; 434213878Smarius for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) { 435213878Smarius /* 436213878Smarius * Make sure we haven't already configured a PHY at this 437213878Smarius * address. This allows mii_attach() to be called 438213878Smarius * multiple times. 439213878Smarius */ 440230709Smarius for (i = 0; i < nchildren; i++) { 441230709Smarius args = device_get_ivars(children[i]); 442230709Smarius if (args->mii_phyno == ma.mii_phyno) { 443230709Smarius /* 444230709Smarius * Yes, there is already something 445230709Smarius * configured at this address. 446230709Smarius */ 447230709Smarius goto skip; 448213878Smarius } 449213878Smarius } 450213878Smarius 451213878Smarius /* 452213878Smarius * Check to see if there is a PHY at this address. Note, 453213878Smarius * many braindead PHYs report 0/0 in their ID registers, 454213878Smarius * so we test for media in the BMSR. 455221407Smarius */ 456213878Smarius bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR); 457213878Smarius if (bmsr == 0 || bmsr == 0xffff || 458213878Smarius (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) { 459213878Smarius /* Assume no PHY at this address. */ 460213878Smarius continue; 461213878Smarius } 462213878Smarius 463213878Smarius /* 464213878Smarius * There is a PHY at this address. If we were given an 465213878Smarius * `offset' locator, skip this PHY if it doesn't match. 466213878Smarius */ 467230709Smarius if (offloc != MII_OFFSET_ANY && offloc != ivars->mii_offset) 468213878Smarius goto skip; 469213878Smarius 470213878Smarius /* 471230709Smarius * Skip this PHY if it's not included in the phymask hint. 472230709Smarius */ 473230709Smarius if ((phymask & (1 << ma.mii_phyno)) == 0) 474230709Smarius goto skip; 475230709Smarius 476230709Smarius /* 477230709Smarius * Extract the IDs. Braindead PHYs will be handled by 478213878Smarius * the `ukphy' driver, as we have no ID information to 479213878Smarius * match on. 480221407Smarius */ 481213878Smarius ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1); 482213878Smarius ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2); 483213878Smarius 484230709Smarius ma.mii_offset = ivars->mii_offset; 485213878Smarius args = malloc(sizeof(struct mii_attach_args), M_DEVBUF, 486213878Smarius M_NOWAIT); 487213878Smarius if (args == NULL) 488213878Smarius goto skip; 489213878Smarius bcopy((char *)&ma, (char *)args, sizeof(ma)); 490213878Smarius phy = device_add_child(*miibus, NULL, -1); 491213878Smarius if (phy == NULL) { 492213878Smarius free(args, M_DEVBUF); 493213878Smarius goto skip; 494213878Smarius } 495213878Smarius device_set_ivars(phy, args); 496213878Smarius skip: 497230709Smarius ivars->mii_offset++; 498213878Smarius } 499230709Smarius free(children, M_TEMP); 500213878Smarius 501213878Smarius if (first != 0) { 502230709Smarius rv = device_set_driver(*miibus, &miibus_driver); 503230709Smarius if (rv != 0) 504230709Smarius goto fail; 505230709Smarius bus_enumerate_hinted_children(*miibus); 506230709Smarius rv = device_get_children(*miibus, &children, &nchildren); 507230709Smarius if (rv != 0) 508230709Smarius goto fail; 509230709Smarius free(children, M_TEMP); 510230709Smarius if (nchildren == 0) { 511213878Smarius rv = ENXIO; 512213878Smarius goto fail; 513213878Smarius } 514213878Smarius rv = bus_generic_attach(dev); 515213878Smarius if (rv != 0) 516213878Smarius goto fail; 517215348Smarius 518215348Smarius /* Attaching of the PHY drivers is done in miibus_attach(). */ 519215348Smarius return (0); 520213878Smarius } 521213878Smarius rv = bus_generic_attach(*miibus); 522213878Smarius if (rv != 0) 523213878Smarius goto fail; 524213878Smarius 525213361Smarius return (0); 526213878Smarius 527213878Smarius fail: 528213878Smarius if (*miibus != NULL) 529213878Smarius device_delete_child(dev, *miibus); 530213878Smarius free(ivars, M_DEVBUF); 531213878Smarius if (first != 0) 532213878Smarius *miibus = NULL; 533213878Smarius return (rv); 53450120Swpaul} 53550120Swpaul 53650120Swpaul/* 53750120Swpaul * Media changed; notify all PHYs. 53850120Swpaul */ 53950120Swpaulint 540141937Simpmii_mediachg(struct mii_data *mii) 54150120Swpaul{ 54250120Swpaul struct mii_softc *child; 543213364Smarius struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 54450120Swpaul int rv; 54550120Swpaul 54650120Swpaul mii->mii_media_status = 0; 54750120Swpaul mii->mii_media_active = IFM_NONE; 54850120Swpaul 54972012Sphk LIST_FOREACH(child, &mii->mii_phys, mii_list) { 550213364Smarius /* 551213364Smarius * If the media indicates a different PHY instance, 552213364Smarius * isolate this one. 553213364Smarius */ 554213364Smarius if (IFM_INST(ife->ifm_media) != child->mii_inst) { 555213364Smarius if ((child->mii_flags & MIIF_NOISOLATE) != 0) { 556213364Smarius device_printf(child->mii_dev, "%s: " 557213364Smarius "can't handle non-zero PHY instance %d\n", 558213364Smarius __func__, child->mii_inst); 559213364Smarius continue; 560213364Smarius } 561213364Smarius PHY_WRITE(child, MII_BMCR, PHY_READ(child, MII_BMCR) | 562213364Smarius BMCR_ISO); 563213364Smarius continue; 564213364Smarius } 565221407Smarius rv = PHY_SERVICE(child, mii, MII_MEDIACHG); 56650120Swpaul if (rv) 56750120Swpaul return (rv); 56850120Swpaul } 56950120Swpaul return (0); 57050120Swpaul} 57150120Swpaul 57250120Swpaul/* 57350120Swpaul * Call the PHY tick routines, used during autonegotiation. 57450120Swpaul */ 57550120Swpaulvoid 576141937Simpmii_tick(struct mii_data *mii) 57750120Swpaul{ 57850120Swpaul struct mii_softc *child; 579213364Smarius struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 58050120Swpaul 581213364Smarius LIST_FOREACH(child, &mii->mii_phys, mii_list) { 582213364Smarius /* 583213364Smarius * If this PHY instance isn't currently selected, just skip 584213364Smarius * it. 585213364Smarius */ 586213364Smarius if (IFM_INST(ife->ifm_media) != child->mii_inst) 587213364Smarius continue; 588221407Smarius (void)PHY_SERVICE(child, mii, MII_TICK); 589213364Smarius } 59050120Swpaul} 59150120Swpaul 59250120Swpaul/* 59350120Swpaul * Get media status from PHYs. 59450120Swpaul */ 59550120Swpaulvoid 596141937Simpmii_pollstat(struct mii_data *mii) 59750120Swpaul{ 59850120Swpaul struct mii_softc *child; 599213364Smarius struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 60050120Swpaul 60150120Swpaul mii->mii_media_status = 0; 60250120Swpaul mii->mii_media_active = IFM_NONE; 60350120Swpaul 604213364Smarius LIST_FOREACH(child, &mii->mii_phys, mii_list) { 605213364Smarius /* 606213364Smarius * If we're not polling this PHY instance, just skip it. 607213364Smarius */ 608213364Smarius if (IFM_INST(ife->ifm_media) != child->mii_inst) 609213364Smarius continue; 610221407Smarius (void)PHY_SERVICE(child, mii, MII_POLLSTAT); 611213364Smarius } 61250120Swpaul} 61395722Sphk 61495722Sphk/* 61595722Sphk * Inform the PHYs that the interface is down. 61695722Sphk */ 61795722Sphkvoid 61895722Sphkmii_down(struct mii_data *mii) 61995722Sphk{ 62095722Sphk struct mii_softc *child; 62195722Sphk 62295722Sphk LIST_FOREACH(child, &mii->mii_phys, mii_list) 62395722Sphk mii_phy_down(child); 62495722Sphk} 625221407Smarius 626221407Smariusstatic unsigned char 627221407Smariusmii_bitreverse(unsigned char x) 628221407Smarius{ 629242908Sdim static unsigned const char nibbletab[16] = { 630221407Smarius 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 631221407Smarius }; 632221407Smarius 633221407Smarius return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]); 634221407Smarius} 635221407Smarius 636221407Smariusu_int 637221407Smariusmii_oui(u_int id1, u_int id2) 638221407Smarius{ 639221407Smarius u_int h; 640221407Smarius 641221407Smarius h = (id1 << 6) | (id2 >> 10); 642221407Smarius 643221407Smarius return ((mii_bitreverse(h >> 16) << 16) | 644221913Smarius (mii_bitreverse((h >> 8) & 0xff) << 8) | 645221913Smarius mii_bitreverse(h & 0xff)); 646221407Smarius} 647