mii_physubr.c revision 95707
1/* $NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner Exp $ */ 2 3/*- 4 * Copyright (c) 1998, 1999 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/* 41 * Subroutines common to all PHYs. 42 */ 43 44#include <sys/param.h> 45#include <sys/systm.h> 46#include <sys/kernel.h> 47#include <sys/socket.h> 48#include <sys/errno.h> 49#include <sys/module.h> 50#include <sys/bus.h> 51 52 53#include <net/if.h> 54#include <net/if_media.h> 55 56#include <dev/mii/mii.h> 57#include <dev/mii/miivar.h> 58 59#include "miibus_if.h" 60 61#if !defined(lint) 62static const char rcsid[] = 63 "$FreeBSD: head/sys/dev/mii/mii_physubr.c 95707 2002-04-29 07:18:26Z phk $"; 64#endif 65 66/* 67 * Media to register setting conversion table. Order matters. 68 */ 69const struct mii_media mii_media_table[MII_NMEDIA] = { 70 /* None */ 71 { BMCR_ISO, ANAR_CSMA, 72 0, }, 73 74 /* 10baseT */ 75 { BMCR_S10, ANAR_CSMA|ANAR_10, 76 0, }, 77 78 /* 10baseT-FDX */ 79 { BMCR_S10|BMCR_FDX, ANAR_CSMA|ANAR_10_FD, 80 0, }, 81 82 /* 100baseT4 */ 83 { BMCR_S100, ANAR_CSMA|ANAR_T4, 84 0, }, 85 86 /* 100baseTX */ 87 { BMCR_S100, ANAR_CSMA|ANAR_TX, 88 0, }, 89 90 /* 100baseTX-FDX */ 91 { BMCR_S100|BMCR_FDX, ANAR_CSMA|ANAR_TX_FD, 92 0, }, 93 94 /* 1000baseX */ 95 { BMCR_S1000, ANAR_CSMA, 96 0, }, 97 98 /* 1000baseX-FDX */ 99 { BMCR_S1000|BMCR_FDX, ANAR_CSMA, 100 0, }, 101 102 /* 1000baseT */ 103 { BMCR_S1000, ANAR_CSMA, 104 GTCR_ADV_1000THDX }, 105 106 /* 1000baseT-FDX */ 107 { BMCR_S1000, ANAR_CSMA, 108 GTCR_ADV_1000TFDX }, 109}; 110 111void mii_phy_auto_timeout(void *); 112 113void 114mii_phy_setmedia(struct mii_softc *sc) 115{ 116 struct mii_data *mii = sc->mii_pdata; 117 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 118 int bmcr, anar, gtcr; 119 120 if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { 121 if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0) 122 (void) mii_phy_auto(sc, 1); 123 return; 124 } 125 126 /* 127 * Table index is stored in the media entry. 128 */ 129 130#ifdef DIAGNOSTIC 131 if (ife->ifm_data < 0 || ife->ifm_data >= MII_NMEDIA) 132 panic("mii_phy_setmedia"); 133#endif 134 135 anar = mii_media_table[ife->ifm_data].mm_anar; 136 bmcr = mii_media_table[ife->ifm_data].mm_bmcr; 137 gtcr = mii_media_table[ife->ifm_data].mm_gtcr; 138 139 if (mii->mii_media.ifm_media & IFM_ETH_MASTER) { 140 switch (IFM_SUBTYPE(ife->ifm_media)) { 141 case IFM_1000_T: 142 gtcr |= GTCR_MAN_MS|GTCR_ADV_MS; 143 break; 144 145 default: 146 panic("mii_phy_setmedia: MASTER on wrong media"); 147 } 148 } 149 150 if (ife->ifm_media & IFM_LOOP) 151 bmcr |= BMCR_LOOP; 152 153 PHY_WRITE(sc, MII_ANAR, anar); 154 PHY_WRITE(sc, MII_BMCR, bmcr); 155 if (sc->mii_flags & MIIF_HAVE_GTCR) 156 PHY_WRITE(sc, MII_100T2CR, gtcr); 157} 158 159int 160mii_phy_auto(mii, waitfor) 161 struct mii_softc *mii; 162 int waitfor; 163{ 164 int bmsr, i; 165 166 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 167 PHY_WRITE(mii, MII_ANAR, 168 BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA); 169 PHY_WRITE(mii, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 170 } 171 172 if (waitfor) { 173 /* Wait 500ms for it to complete. */ 174 for (i = 0; i < 500; i++) { 175 if ((bmsr = PHY_READ(mii, MII_BMSR)) & BMSR_ACOMP) 176 return (0); 177 DELAY(1000); 178#if 0 179 if ((bmsr & BMSR_ACOMP) == 0) 180 printf("%s: autonegotiation failed to complete\n", 181 mii->mii_dev.dv_xname); 182#endif 183 } 184 185 /* 186 * Don't need to worry about clearing MIIF_DOINGAUTO. 187 * If that's set, a timeout is pending, and it will 188 * clear the flag. 189 */ 190 return (EIO); 191 } 192 193 /* 194 * Just let it finish asynchronously. This is for the benefit of 195 * the tick handler driving autonegotiation. Don't want 500ms 196 * delays all the time while the system is running! 197 */ 198 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 199 mii->mii_flags |= MIIF_DOINGAUTO; 200 mii->mii_auto_ch = timeout(mii_phy_auto_timeout, mii, hz >> 1); 201 } 202 return (EJUSTRETURN); 203} 204 205void 206mii_phy_auto_stop(sc) 207 struct mii_softc *sc; 208{ 209 if (sc->mii_flags & MIIF_DOINGAUTO) { 210 sc->mii_flags &= ~MIIF_DOINGAUTO; 211 untimeout(mii_phy_auto_timeout, sc, sc->mii_auto_ch); 212 } 213} 214 215void 216mii_phy_auto_timeout(arg) 217 void *arg; 218{ 219 struct mii_softc *mii = arg; 220 int s, bmsr; 221 222 s = splnet(); 223 mii->mii_flags &= ~MIIF_DOINGAUTO; 224 bmsr = PHY_READ(mii, MII_BMSR); 225#if 0 226 if ((bmsr & BMSR_ACOMP) == 0) 227 printf("%s: autonegotiation failed to complete\n", 228 sc->sc_dev.dv_xname); 229#endif 230 231 /* Update the media status. */ 232 (void) (*mii->mii_service)(mii, mii->mii_pdata, MII_POLLSTAT); 233 splx(s); 234} 235 236int 237mii_phy_tick(sc) 238 struct mii_softc *sc; 239{ 240 struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 241 struct ifnet *ifp = sc->mii_pdata->mii_ifp; 242 int reg; 243 244 /* 245 * Is the interface even up? 246 */ 247 if ((ifp->if_flags & IFF_UP) == 0) 248 return (EJUSTRETURN); 249 250 /* 251 * If we're not doing autonegotiation, we don't need to do 252 * any extra work here. However, we need to check the link 253 * status so we can generate an announcement if the status 254 * changes. 255 */ 256 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 257 return (0); 258 259 /* 260 * check for link. 261 * Read the status register twice; BMSR_LINK is latch-low. 262 */ 263 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 264 if (reg & BMSR_LINK) 265 return (0); 266 267 /* 268 * Only retry autonegotiation every 5 seconds. 269 */ 270 if (++sc->mii_ticks != 5) 271 return (EJUSTRETURN); 272 273 sc->mii_ticks = 0; 274 mii_phy_reset(sc); 275 if (mii_phy_auto(sc, 0) == EJUSTRETURN) 276 return (EJUSTRETURN); 277 278 /* 279 * Might need to generate a status message if autonegotiation 280 * failed. 281 */ 282 return (0); 283} 284 285void 286mii_phy_reset(mii) 287 struct mii_softc *mii; 288{ 289 int reg, i; 290 291 if (mii->mii_flags & MIIF_NOISOLATE) 292 reg = BMCR_RESET; 293 else 294 reg = BMCR_RESET | BMCR_ISO; 295 PHY_WRITE(mii, MII_BMCR, reg); 296 297 /* Wait 100ms for it to complete. */ 298 for (i = 0; i < 100; i++) { 299 reg = PHY_READ(mii, MII_BMCR); 300 if ((reg & BMCR_RESET) == 0) 301 break; 302 DELAY(1000); 303 } 304 305 if (mii->mii_inst != 0 && ((mii->mii_flags & MIIF_NOISOLATE) == 0)) 306 PHY_WRITE(mii, MII_BMCR, reg | BMCR_ISO); 307} 308 309void 310mii_phy_update(sc, cmd) 311 struct mii_softc *sc; 312 int cmd; 313{ 314 struct mii_data *mii = sc->mii_pdata; 315 316 if (sc->mii_media_active != mii->mii_media_active || cmd == MII_MEDIACHG) { 317 MIIBUS_STATCHG(sc->mii_dev); 318 sc->mii_media_active = mii->mii_media_active; 319 } 320 if (sc->mii_media_status != mii->mii_media_status) { 321 MIIBUS_LINKCHG(sc->mii_dev); 322 sc->mii_media_status = mii->mii_media_status; 323 } 324} 325 326/* 327 * Given an ifmedia word, return the corresponding ANAR value. 328 */ 329int 330mii_anar(media) 331 int media; 332{ 333 int rv; 334 335 switch (media & (IFM_TMASK|IFM_NMASK|IFM_FDX)) { 336 case IFM_ETHER|IFM_10_T: 337 rv = ANAR_10|ANAR_CSMA; 338 break; 339 case IFM_ETHER|IFM_10_T|IFM_FDX: 340 rv = ANAR_10_FD|ANAR_CSMA; 341 break; 342 case IFM_ETHER|IFM_100_TX: 343 rv = ANAR_TX|ANAR_CSMA; 344 break; 345 case IFM_ETHER|IFM_100_TX|IFM_FDX: 346 rv = ANAR_TX_FD|ANAR_CSMA; 347 break; 348 case IFM_ETHER|IFM_100_T4: 349 rv = ANAR_T4|ANAR_CSMA; 350 break; 351 default: 352 rv = 0; 353 break; 354 } 355 356 return (rv); 357} 358 359/* 360 * Given a BMCR value, return the corresponding ifmedia word. 361 */ 362int 363mii_media_from_bmcr(bmcr) 364 int bmcr; 365{ 366 int rv = IFM_ETHER; 367 368 if (bmcr & BMCR_S100) 369 rv |= IFM_100_TX; 370 else 371 rv |= IFM_10_T; 372 if (bmcr & BMCR_FDX) 373 rv |= IFM_FDX; 374 375 return (rv); 376} 377 378/* 379 * Initialize generic PHY media based on BMSR, called when a PHY is 380 * attached. We expect to be set up to print a comma-separated list 381 * of media names. Does not print a newline. 382 */ 383void 384mii_add_media(struct mii_softc *sc) 385{ 386 const char *sep = ""; 387 struct mii_data *mii; 388 389 mii = device_get_softc(sc->mii_dev); 390 if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) { 391 printf("no media present"); 392 return; 393 } 394 395#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 396#define PRINT(s) printf("%s%s", sep, s); sep = ", " 397 398 if (sc->mii_capabilities & BMSR_10THDX) { 399 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), 0); 400 PRINT("10baseT"); 401 } 402 if (sc->mii_capabilities & BMSR_10TFDX) { 403 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), 404 BMCR_FDX); 405 PRINT("10baseT-FDX"); 406 } 407 if (sc->mii_capabilities & BMSR_100TXHDX) { 408 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), 409 BMCR_S100); 410 PRINT("100baseTX"); 411 } 412 if (sc->mii_capabilities & BMSR_100TXFDX) { 413 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), 414 BMCR_S100|BMCR_FDX); 415 PRINT("100baseTX-FDX"); 416 } 417 if (sc->mii_capabilities & BMSR_100T4) { 418 /* 419 * XXX How do you enable 100baseT4? I assume we set 420 * XXX BMCR_S100 and then assume the PHYs will take 421 * XXX watever action is necessary to switch themselves 422 * XXX into T4 mode. 423 */ 424 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst), 425 BMCR_S100); 426 PRINT("100baseT4"); 427 } 428 if (sc->mii_capabilities & BMSR_ANEG) { 429 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 430 BMCR_AUTOEN); 431 PRINT("auto"); 432 } 433 434 435 436#undef ADD 437#undef PRINT 438} 439