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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include "device.h" 41 42 #include <sys/cdefs.h> 43 __FBSDID("$FreeBSD: src/sys/dev/mii/mii.c,v 1.26.2.1 2006/03/17 20:17:43 glebius Exp $"); 44 45 /* 46 * MII bus layer, glues MII-capable network interface drivers to sharable 47 * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, 48 * plus some NetBSD extensions. 49 */ 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/socket.h> 54 #include <sys/malloc.h> 55 #include <sys/module.h> 56 #include <sys/bus.h> 57 58 #include <net/if.h> 59 #include <net/if_media.h> 60 #include <net/route.h> 61 62 #include <dev/mii/mii.h> 63 #include <dev/mii/miivar.h> 64 65 MODULE_VERSION(miibus, 1); 66 67 #include "miibus_if.h" 68 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, bus_generic_print_child), 88 DEVMETHOD(bus_driver_added, bus_generic_driver_added), 89 DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str), 90 DEVMETHOD(bus_child_location_str, miibus_child_location_str), 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 { 0, 0 } 100 }; 101 102 devclass_t miibus_devclass; 103 104 driver_t miibus_driver = { 105 "miibus", 106 miibus_methods, 107 sizeof(struct mii_data) 108 }; 109 110 /* 111 * Helper function used by network interface drivers, attaches PHYs 112 * to the network interface driver parent. 113 */ 114 int 115 miibus_probe(device_t dev) 116 { 117 struct mii_attach_args ma, *args; 118 struct mii_data *mii; 119 device_t child = NULL, parent; 120 int bmsr, capmask = 0xFFFFFFFF; 121 122 mii = device_get_softc(dev); 123 parent = device_get_parent(dev); 124 LIST_INIT(&mii->mii_phys); 125 126 for (ma.mii_phyno = 0; ma.mii_phyno < MII_NPHY; ma.mii_phyno++) { 127 /* 128 * Check to see if there is a PHY at this address. Note, 129 * many braindead PHYs report 0/0 in their ID registers, 130 * so we test for media in the BMSR. 131 */ 132 bmsr = MIIBUS_READREG(parent, ma.mii_phyno, MII_BMSR); 133 if (bmsr == 0 || bmsr == 0xffff || 134 (bmsr & (BMSR_EXTSTAT|BMSR_MEDIAMASK)) == 0) { 135 /* Assume no PHY at this address. */ 136 continue; 137 } 138 139 /* 140 * Extract the IDs. Braindead PHYs will be handled by 141 * the `ukphy' driver, as we have no ID information to 142 * match on. 143 */ 144 ma.mii_id1 = MIIBUS_READREG(parent, ma.mii_phyno, 145 MII_PHYIDR1); 146 ma.mii_id2 = MIIBUS_READREG(parent, ma.mii_phyno, 147 MII_PHYIDR2); 148 149 ma.mii_data = mii; 150 ma.mii_capmask = capmask; 151 152 args = kernel_malloc(sizeof(struct mii_attach_args), 153 M_DEVBUF, M_NOWAIT); 154 bcopy((char *)&ma, (char *)args, sizeof(ma)); 155 child = device_add_child(dev, NULL, -1); 156 device_set_ivars(child, args); 157 } 158 159 if (child == NULL) 160 return(ENXIO); 161 162 device_set_desc(dev, "MII bus"); 163 164 return(0); 165 } 166 167 int 168 miibus_attach(device_t dev) 169 { 170 void **v; 171 ifm_change_cb_t ifmedia_upd; 172 ifm_stat_cb_t ifmedia_sts; 173 struct mii_data *mii; 174 175 mii = device_get_softc(dev); 176 /* 177 * Note that each NIC's softc must start with an ifnet pointer. 178 * XXX: EVIL HACK! 179 */ 180 mii->mii_ifp = *(struct ifnet**)device_get_softc(device_get_parent(dev)); 181 v = device_get_ivars(dev); 182 ifmedia_upd = v[0]; 183 ifmedia_sts = v[1]; 184 ifmedia_init(&mii->mii_media, IFM_IMASK, ifmedia_upd, ifmedia_sts); 185 bus_generic_attach(dev); 186 187 return(0); 188 } 189 190 int 191 miibus_detach(device_t dev) 192 { 193 struct mii_data *mii; 194 195 bus_generic_detach(dev); 196 mii = device_get_softc(dev); 197 ifmedia_removeall(&mii->mii_media); 198 mii->mii_ifp = NULL; 199 200 return(0); 201 } 202 203 static int 204 miibus_child_pnpinfo_str(device_t bus, device_t child, char *buf, 205 size_t buflen) 206 { 207 struct mii_attach_args *maa = device_get_ivars(child); 208 snprintf(buf, buflen, "oui=0x%x model=0x%x rev=0x%x", 209 MII_OUI(maa->mii_id1, maa->mii_id2), 210 MII_MODEL(maa->mii_id2), MII_REV(maa->mii_id2)); 211 return (0); 212 } 213 214 static int 215 miibus_child_location_str(device_t bus, device_t child, char *buf, 216 size_t buflen) 217 { 218 struct mii_attach_args *maa = device_get_ivars(child); 219 snprintf(buf, buflen, "phyno=%d", maa->mii_phyno); 220 return (0); 221 } 222 223 static int 224 miibus_readreg(device_t dev, int phy, int reg) 225 { 226 device_t parent; 227 228 parent = device_get_parent(dev); 229 return(MIIBUS_READREG(parent, phy, reg)); 230 } 231 232 static int 233 miibus_writereg(device_t dev, int phy, int reg, int data) 234 { 235 device_t parent; 236 237 parent = device_get_parent(dev); 238 return(MIIBUS_WRITEREG(parent, phy, reg, data)); 239 } 240 241 static void 242 miibus_statchg(device_t dev) 243 { 244 device_t parent; 245 struct mii_data *mii; 246 struct ifnet *ifp; 247 248 parent = device_get_parent(dev); 249 MIIBUS_STATCHG(parent); 250 251 mii = device_get_softc(dev); 252 253 /* 254 * Note that each NIC's softc must start with an ifnet pointer. 255 * XXX: EVIL HACK! 256 */ 257 ifp = *(struct ifnet **)device_get_softc(parent); 258 ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active); 259 return; 260 } 261 262 static void 263 miibus_linkchg(device_t dev) 264 { 265 struct mii_data *mii; 266 device_t parent; 267 int link_state; 268 269 parent = device_get_parent(dev); 270 MIIBUS_LINKCHG(parent); 271 272 mii = device_get_softc(dev); 273 274 if (mii->mii_media_status & IFM_AVALID) { 275 if (mii->mii_media_status & IFM_ACTIVE) 276 link_state = LINK_STATE_UP; 277 else 278 link_state = LINK_STATE_DOWN; 279 } else 280 link_state = LINK_STATE_UNKNOWN; 281 282 /* 283 * Note that each NIC's softc must start with an ifnet pointer. 284 * XXX: EVIL HACK! 285 */ 286 if_link_state_change(*(struct ifnet**)device_get_softc(parent), link_state); 287 } 288 289 static void 290 miibus_mediainit(device_t dev) 291 { 292 struct mii_data *mii; 293 struct ifmedia_entry *m; 294 int media = 0; 295 296 /* Poke the parent in case it has any media of its own to add. */ 297 MIIBUS_MEDIAINIT(device_get_parent(dev)); 298 299 mii = device_get_softc(dev); 300 LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) { 301 media = m->ifm_media; 302 if (media == (IFM_ETHER|IFM_AUTO)) 303 break; 304 } 305 306 ifmedia_set(&mii->mii_media, media); 307 308 return; 309 } 310 311 int 312 mii_phy_probe(device_t dev, device_t *child, ifm_change_cb_t ifmedia_upd, 313 ifm_stat_cb_t ifmedia_sts) 314 { 315 void **v; 316 int bmsr, i; 317 318 v = kernel_malloc(sizeof(vm_offset_t) * 2, M_DEVBUF, M_NOWAIT); 319 if (v == 0) { 320 return (ENOMEM); 321 } 322 v[0] = ifmedia_upd; 323 v[1] = ifmedia_sts; 324 *child = device_add_child(dev, "miibus", -1); 325 device_set_ivars(*child, v); 326 327 for (i = 0; i < MII_NPHY; i++) { 328 bmsr = MIIBUS_READREG(dev, i, MII_BMSR); 329 if (bmsr == 0 || bmsr == 0xffff || 330 (bmsr & (BMSR_EXTSTAT|BMSR_MEDIAMASK)) == 0) { 331 /* Assume no PHY at this address. */ 332 continue; 333 } else 334 break; 335 } 336 337 if (i == MII_NPHY) { 338 device_delete_child(dev, *child); 339 *child = NULL; 340 return(ENXIO); 341 } 342 343 bus_generic_attach(dev); 344 345 return(0); 346 } 347 348 /* 349 * Media changed; notify all PHYs. 350 */ 351 int 352 mii_mediachg(struct mii_data *mii) 353 { 354 struct mii_softc *child; 355 int rv; 356 357 mii->mii_media_status = 0; 358 mii->mii_media_active = IFM_NONE; 359 360 LIST_FOREACH(child, &mii->mii_phys, mii_list) { 361 rv = (*child->mii_service)(child, mii, MII_MEDIACHG); 362 if (rv) 363 return (rv); 364 } 365 return (0); 366 } 367 368 /* 369 * Call the PHY tick routines, used during autonegotiation. 370 */ 371 void 372 mii_tick(struct mii_data *mii) 373 { 374 struct mii_softc *child; 375 376 LIST_FOREACH(child, &mii->mii_phys, mii_list) 377 (void) (*child->mii_service)(child, mii, MII_TICK); 378 } 379 380 /* 381 * Get media status from PHYs. 382 */ 383 void 384 mii_pollstat(struct mii_data *mii) 385 { 386 struct mii_softc *child; 387 388 mii->mii_media_status = 0; 389 mii->mii_media_active = IFM_NONE; 390 391 LIST_FOREACH(child, &mii->mii_phys, mii_list) 392 (void) (*child->mii_service)(child, mii, MII_POLLSTAT); 393 } 394 395 /* 396 * Inform the PHYs that the interface is down. 397 */ 398 void 399 mii_down(struct mii_data *mii) 400 { 401 struct mii_softc *child; 402 403 LIST_FOREACH(child, &mii->mii_phys, mii_list) 404 mii_phy_down(child); 405 } 406