mii_physubr.c revision 1.8
1/* $OpenBSD: mii_physubr.c,v 1.8 2001/05/03 12:31:43 aaron Exp $ */ 2/* $NetBSD: mii_physubr.c,v 1.16 2000/03/15 20:34:43 thorpej Exp $ */ 3 4/*- 5 * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 10 * NASA Ames Research Center. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the NetBSD 23 * Foundation, Inc. and its contributors. 24 * 4. Neither the name of The NetBSD Foundation nor the names of its 25 * contributors may be used to endorse or promote products derived 26 * from this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 */ 40 41/* 42 * Subroutines common to all PHYs. 43 */ 44 45#include <sys/param.h> 46#include <sys/device.h> 47#include <sys/systm.h> 48#include <sys/kernel.h> 49#include <sys/socket.h> 50#include <sys/errno.h> 51#include <sys/proc.h> 52 53#include <net/if.h> 54#include <net/if_media.h> 55#include <net/route.h> 56 57#include <dev/mii/mii.h> 58#include <dev/mii/miivar.h> 59 60/* 61 * Media to register setting conversion table. Order matters. 62 */ 63const struct mii_media mii_media_table[] = { 64 { BMCR_ISO, ANAR_CSMA }, /* None */ 65 { 0, ANAR_CSMA|ANAR_10 }, /* 10baseT */ 66 { BMCR_FDX, ANAR_CSMA|ANAR_10_FD }, /* 10baseT-FDX */ 67 { BMCR_S100, ANAR_CSMA|ANAR_T4 }, /* 100baseT4 */ 68 { BMCR_S100, ANAR_CSMA|ANAR_TX }, /* 100baseTX */ 69 { BMCR_S100|BMCR_FDX, ANAR_CSMA|ANAR_TX_FD }, /* 100baseTX-FDX */ 70}; 71 72void mii_phy_auto_timeout __P((void *)); 73 74void 75mii_phy_setmedia(sc) 76 struct mii_softc *sc; 77{ 78 struct mii_data *mii = sc->mii_pdata; 79 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 80 int bmcr, anar; 81 82 if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { 83 if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0) 84 (void) mii_phy_auto(sc, 1); 85 return; 86 } 87 88 /* 89 * Table index is stored in the media entry. 90 */ 91 92#ifdef DIAGNOSTIC 93 if (ife->ifm_data < 0 || ife->ifm_data >= MII_NMEDIA) 94 panic("mii_phy_setmedia"); 95#endif 96 97 anar = mii_media_table[ife->ifm_data].mm_anar; 98 bmcr = mii_media_table[ife->ifm_data].mm_bmcr; 99 100 if (ife->ifm_media & IFM_LOOP) 101 bmcr |= BMCR_LOOP; 102 103 PHY_WRITE(sc, MII_ANAR, anar); 104 PHY_WRITE(sc, MII_BMCR, bmcr); 105} 106 107int 108mii_phy_auto(sc, waitfor) 109 struct mii_softc *sc; 110 int waitfor; 111{ 112 int bmsr, i; 113 114 if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) { 115 PHY_WRITE(sc, MII_ANAR, 116 BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA); 117 PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 118 } 119 120 if (waitfor) { 121 /* Wait 500ms for it to complete. */ 122 for (i = 0; i < 500; i++) { 123 if ((bmsr = PHY_READ(sc, MII_BMSR)) & BMSR_ACOMP) 124 return (0); 125 delay(1000); 126 } 127 128 /* 129 * Don't need to worry about clearing MIIF_DOINGAUTO. 130 * If that's set, a timeout is pending, and it will 131 * clear the flag. 132 */ 133 return (EIO); 134 } 135 136 /* 137 * Just let it finish asynchronously. This is for the benefit of 138 * the tick handler driving autonegotiation. Don't want 500ms 139 * delays all the time while the system is running! 140 */ 141 if (sc->mii_flags & MIIF_AUTOTSLEEP) { 142 sc->mii_flags |= MIIF_DOINGAUTO; 143 tsleep(&sc->mii_flags, PZERO, "miiaut", hz >> 1); 144 mii_phy_auto_timeout(sc); 145 } else if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) { 146 sc->mii_flags |= MIIF_DOINGAUTO; 147 timeout_set(&sc->mii_phy_timo, mii_phy_auto_timeout, sc); 148 timeout_add(&sc->mii_phy_timo, hz / 2); 149 } 150 return (EJUSTRETURN); 151} 152 153void 154mii_phy_auto_timeout(arg) 155 void *arg; 156{ 157 struct mii_softc *sc = arg; 158 int s, bmsr; 159 160 if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0) 161 return; 162 163 s = splnet(); 164 sc->mii_flags &= ~MIIF_DOINGAUTO; 165 bmsr = PHY_READ(sc, MII_BMSR); 166 167 /* Update the media status. */ 168 (void) (*sc->mii_service)(sc, sc->mii_pdata, MII_POLLSTAT); 169 splx(s); 170} 171 172int 173mii_phy_tick(sc) 174 struct mii_softc *sc; 175{ 176 struct mii_data *mii = sc->mii_pdata; 177 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 178 int reg; 179 180 /* Just bail now if the interface is down. */ 181 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 182 return (EJUSTRETURN); 183 184 /* 185 * If we're not doing autonegotiation, we don't need to do 186 * any extra work here. However, we need to check the link 187 * status so we can generate an announcement if the status 188 * changes. 189 */ 190 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 191 return (0); 192 193 /* Read the status register twice; BMSR_LINK is latch-low. */ 194 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 195 if (reg & BMSR_LINK) { 196 /* 197 * See above. 198 */ 199 return (0); 200 } 201 202 /* 203 * Only retry autonegotiation every 5 seconds. 204 */ 205 if (++sc->mii_ticks != 5) 206 return (EJUSTRETURN); 207 208 sc->mii_ticks = 0; 209 mii_phy_reset(sc); 210 211 if (mii_phy_auto(sc, 0) == EJUSTRETURN) 212 return (EJUSTRETURN); 213 214 /* 215 * Might need to generate a status message if autonegotiation 216 * failed. 217 */ 218 return (0); 219} 220 221void 222mii_phy_reset(sc) 223 struct mii_softc *sc; 224{ 225 int reg, i; 226 227 if (sc->mii_flags & MIIF_NOISOLATE) 228 reg = BMCR_RESET; 229 else 230 reg = BMCR_RESET | BMCR_ISO; 231 PHY_WRITE(sc, MII_BMCR, reg); 232 233 /* Wait 100ms for it to complete. */ 234 for (i = 0; i < 100; i++) { 235 reg = PHY_READ(sc, MII_BMCR); 236 if ((reg & BMCR_RESET) == 0) 237 break; 238 delay(1000); 239 } 240 241 if (sc->mii_inst != 0 && ((sc->mii_flags & MIIF_NOISOLATE) == 0)) 242 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 243} 244 245void 246mii_phy_down(sc) 247 struct mii_softc *sc; 248{ 249 if (sc->mii_flags & MIIF_DOINGAUTO) { 250 sc->mii_flags &= ~MIIF_DOINGAUTO; 251 timeout_del(&sc->mii_phy_timo); 252 } 253} 254 255 256void 257mii_phy_status(sc) 258 struct mii_softc *sc; 259{ 260 261 (*sc->mii_status)(sc); 262} 263 264void 265mii_phy_update(sc, cmd) 266 struct mii_softc *sc; 267 int cmd; 268{ 269 struct mii_data *mii = sc->mii_pdata; 270 271 if (sc->mii_media_active != mii->mii_media_active || 272 sc->mii_media_status != mii->mii_media_status || 273 cmd == MII_MEDIACHG) { 274 (*mii->mii_statchg)(sc->mii_dev.dv_parent); 275 mii_phy_statusmsg(sc); 276 sc->mii_media_active = mii->mii_media_active; 277 sc->mii_media_status = mii->mii_media_status; 278 } 279} 280 281void 282mii_phy_statusmsg(sc) 283 struct mii_softc *sc; 284{ 285 struct mii_data *mii = sc->mii_pdata; 286 struct ifnet *ifp = mii->mii_ifp; 287 int s, baudrate, link_state, announce = 0; 288 289 if (mii->mii_media_status & IFM_AVALID) { 290 if (mii->mii_media_status & IFM_ACTIVE) 291 link_state = LINK_STATE_UP; 292 else 293 link_state = LINK_STATE_DOWN; 294 } else 295 link_state = LINK_STATE_UNKNOWN; 296 297 baudrate = ifmedia_baudrate(mii->mii_media_active); 298 299 if (link_state != ifp->if_link_state) { 300 ifp->if_link_state = link_state; 301 /* 302 * XXX Right here we'd like to notify protocols 303 * XXX that the link status has changed, so that 304 * XXX e.g. Duplicate Address Detection can restart. 305 */ 306 announce = 1; 307 } 308 309 if (baudrate != ifp->if_baudrate) { 310 ifp->if_baudrate = baudrate; 311 announce = 1; 312 } 313 314 if (announce) { 315 s = splimp(); /* XXX Should be splnet() */ 316 rt_ifmsg(ifp); 317 splx(s); 318 } 319} 320 321/* 322 * Initialize generic PHY media based on BMSR, called when a PHY is 323 * attached. We expect to be set up to print a comma-separated list 324 * of media names. Does not print a newline. 325 */ 326void 327mii_phy_add_media(sc) 328 struct mii_softc *sc; 329{ 330 struct mii_data *mii = sc->mii_pdata; 331 332#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 333 334 if ((sc->mii_flags & MIIF_NOISOLATE) == 0) 335 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 336 MII_MEDIA_NONE); 337 338 if (sc->mii_capabilities & BMSR_10THDX) { 339 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), 340 MII_MEDIA_10_T); 341#if 0 342 if ((sc->mii_flags & MIIF_NOLOOP) == 0) 343 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_LOOP, 344 sc->mii_inst), MII_MEDIA_10_T); 345#endif 346 } 347 if (sc->mii_capabilities & BMSR_10TFDX) { 348 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), 349 MII_MEDIA_10_T_FDX); 350 } 351 if (sc->mii_capabilities & BMSR_100TXHDX) { 352 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), 353 MII_MEDIA_100_TX); 354#if 0 355 if ((sc->mii_flags & MIIF_NOLOOP) == 0) 356 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, 357 sc->mii_inst), MII_MEDIA_100_TX); 358#endif 359 } 360 if (sc->mii_capabilities & BMSR_100TXFDX) { 361 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), 362 MII_MEDIA_100_TX_FDX); 363 } 364 if (sc->mii_capabilities & BMSR_100T4) { 365 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst), 366 MII_MEDIA_100_T4); 367#if 0 368 if ((sc->mii_flags & MIIF_NOLOOP) == 0) 369 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, IFM_LOOP, 370 sc->mii_inst), MII_MEDIA_100_T4); 371#endif 372 } 373 if (sc->mii_capabilities & BMSR_ANEG) { 374 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 375 MII_NMEDIA); /* intentionally invalid index */ 376 } 377#undef ADD 378} 379 380void 381mii_phy_delete_media(sc) 382 struct mii_softc *sc; 383{ 384 struct mii_data *mii = sc->mii_pdata; 385 386 ifmedia_delete_instance(&mii->mii_media, sc->mii_inst); 387} 388 389int 390mii_phy_activate(self, act) 391 struct device *self; 392 enum devact act; 393{ 394 int rv = 0; 395 396 switch (act) { 397 case DVACT_ACTIVATE: 398 rv = EOPNOTSUPP; 399 break; 400 401 case DVACT_DEACTIVATE: 402 /* Nothing special to do. */ 403 break; 404 } 405 406 return (rv); 407} 408 409int 410mii_phy_detach(self, flags) 411 struct device *self; 412 int flags; 413{ 414 struct mii_softc *sc = (void *) self; 415 416 if (sc->mii_flags & MIIF_DOINGAUTO) 417 timeout_del(&sc->mii_phy_timo); 418 419 mii_phy_delete_media(sc); 420 421 return (0); 422} 423