mii_physubr.c revision 84140
1285163Sdim/* $NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner Exp $ */ 2285163Sdim 3285163Sdim/*- 4285163Sdim * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. 5285163Sdim * All rights reserved. 6285163Sdim * 7285163Sdim * This code is derived from software contributed to The NetBSD Foundation 8285163Sdim * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9286684Sdim * NASA Ames Research Center. 10286684Sdim * 11341825Sdim * Redistribution and use in source and binary forms, with or without 12286684Sdim * modification, are permitted provided that the following conditions 13285163Sdim * are met: 14285163Sdim * 1. Redistributions of source code must retain the above copyright 15285163Sdim * notice, this list of conditions and the following disclaimer. 16285163Sdim * 2. Redistributions in binary form must reproduce the above copyright 17285163Sdim * notice, this list of conditions and the following disclaimer in the 18285163Sdim * documentation and/or other materials provided with the distribution. 19285163Sdim * 3. All advertising materials mentioning features or use of this software 20285163Sdim * must display the following acknowledgement: 21285163Sdim * This product includes software developed by the NetBSD 22285163Sdim * Foundation, Inc. and its contributors. 23327952Sdim * 4. Neither the name of The NetBSD Foundation nor the names of its 24327952Sdim * contributors may be used to endorse or promote products derived 25327952Sdim * from this software without specific prior written permission. 26327952Sdim * 27327952Sdim * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28327952Sdim * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29327952Sdim * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30327952Sdim * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31327952Sdim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32327952Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33341825Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34341825Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35341825Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36341825Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37341825Sdim * POSSIBILITY OF SUCH DAMAGE. 38341825Sdim */ 39341825Sdim 40341825Sdim/* 41285163Sdim * Subroutines common to all PHYs. 42341825Sdim */ 43341825Sdim 44341825Sdim#include <sys/param.h> 45341825Sdim#include <sys/systm.h> 46341825Sdim#include <sys/kernel.h> 47341825Sdim#include <sys/socket.h> 48341825Sdim#include <sys/errno.h> 49341825Sdim#include <sys/module.h> 50341825Sdim#include <sys/bus.h> 51341825Sdim 52285163Sdim 53285163Sdim#include <net/if.h> 54285163Sdim#include <net/if_media.h> 55285163Sdim 56321369Sdim#include <dev/mii/mii.h> 57321369Sdim#include <dev/mii/miivar.h> 58296417Sdim 59296417Sdim#include "miibus_if.h" 60296417Sdim 61296417Sdim#if !defined(lint) 62309124Sdimstatic const char rcsid[] = 63296417Sdim "$FreeBSD: head/sys/dev/mii/mii_physubr.c 84140 2001-09-29 18:40:06Z jlemon $"; 64296417Sdim#endif 65296417Sdim 66296417Sdimvoid mii_phy_auto_timeout __P((void *)); 67296417Sdim 68285163Sdimint 69285163Sdimmii_phy_auto(mii, waitfor) 70285163Sdim struct mii_softc *mii; 71285163Sdim int waitfor; 72296417Sdim{ 73296417Sdim int bmsr, i; 74296417Sdim 75296417Sdim if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 76296417Sdim PHY_WRITE(mii, MII_ANAR, 77296417Sdim BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA); 78296417Sdim PHY_WRITE(mii, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 79296417Sdim } 80296417Sdim 81296417Sdim if (waitfor) { 82296417Sdim /* Wait 500ms for it to complete. */ 83296417Sdim for (i = 0; i < 500; i++) { 84309124Sdim if ((bmsr = PHY_READ(mii, MII_BMSR)) & BMSR_ACOMP) 85309124Sdim return (0); 86309124Sdim DELAY(1000); 87296417Sdim#if 0 88296417Sdim if ((bmsr & BMSR_ACOMP) == 0) 89296417Sdim printf("%s: autonegotiation failed to complete\n", 90296417Sdim mii->mii_dev.dv_xname); 91296417Sdim#endif 92296417Sdim } 93296417Sdim 94285163Sdim /* 95285163Sdim * Don't need to worry about clearing MIIF_DOINGAUTO. 96285163Sdim * If that's set, a timeout is pending, and it will 97285163Sdim * clear the flag. 98296417Sdim */ 99286684Sdim return (EIO); 100296417Sdim } 101296417Sdim 102296417Sdim /* 103314564Sdim * Just let it finish asynchronously. This is for the benefit of 104314564Sdim * the tick handler driving autonegotiation. Don't want 500ms 105314564Sdim * delays all the time while the system is running! 106321369Sdim */ 107321369Sdim if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 108321369Sdim mii->mii_flags |= MIIF_DOINGAUTO; 109314564Sdim mii->mii_auto_ch = timeout(mii_phy_auto_timeout, mii, hz >> 1); 110314564Sdim } 111314564Sdim return (EJUSTRETURN); 112314564Sdim} 113314564Sdim 114314564Sdimvoid 115314564Sdimmii_phy_auto_stop(sc) 116296417Sdim struct mii_softc *sc; 117309124Sdim{ 118314564Sdim if (sc->mii_flags & MIIF_DOINGAUTO) { 119296417Sdim sc->mii_flags &= ~MIIF_DOINGAUTO; 120296417Sdim untimeout(mii_phy_auto_timeout, sc, sc->mii_auto_ch); 121314564Sdim } 122314564Sdim} 123314564Sdim 124314564Sdimvoid 125314564Sdimmii_phy_auto_timeout(arg) 126314564Sdim void *arg; 127309124Sdim{ 128309124Sdim struct mii_softc *mii = arg; 129309124Sdim int s, bmsr; 130309124Sdim 131309124Sdim s = splnet(); 132309124Sdim mii->mii_flags &= ~MIIF_DOINGAUTO; 133314564Sdim bmsr = PHY_READ(mii, MII_BMSR); 134314564Sdim#if 0 135314564Sdim if ((bmsr & BMSR_ACOMP) == 0) 136314564Sdim printf("%s: autonegotiation failed to complete\n", 137314564Sdim sc->sc_dev.dv_xname); 138314564Sdim#endif 139321369Sdim 140321369Sdim /* Update the media status. */ 141321369Sdim (void) (*mii->mii_service)(mii, mii->mii_pdata, MII_POLLSTAT); 142296417Sdim splx(s); 143296417Sdim} 144285163Sdim 145285163Sdimint 146285163Sdimmii_phy_tick(sc) 147285163Sdim struct mii_softc *sc; 148285163Sdim{ 149285163Sdim struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 150285163Sdim struct ifnet *ifp = sc->mii_pdata->mii_ifp; 151296417Sdim int reg; 152296417Sdim 153296417Sdim /* 154296417Sdim * Is the interface even up? 155296417Sdim */ 156341825Sdim if ((ifp->if_flags & IFF_UP) == 0) 157341825Sdim return (EJUSTRETURN); 158341825Sdim 159296417Sdim /* 160314564Sdim * If we're not doing autonegotiation, we don't need to do 161314564Sdim * any extra work here. However, we need to check the link 162341825Sdim * status so we can generate an announcement if the status 163341825Sdim * changes. 164341825Sdim */ 165314564Sdim if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 166314564Sdim return (0); 167341825Sdim 168341825Sdim /* 169341825Sdim * check for link. 170341825Sdim * Read the status register twice; BMSR_LINK is latch-low. 171341825Sdim */ 172341825Sdim reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 173341825Sdim if (reg & BMSR_LINK) 174341825Sdim return (0); 175341825Sdim 176296417Sdim /* 177296417Sdim * Only retry autonegotiation every 5 seconds. 178296417Sdim */ 179296417Sdim if (++sc->mii_ticks != 5) 180309124Sdim return (EJUSTRETURN); 181296417Sdim 182309124Sdim sc->mii_ticks = 0; 183314564Sdim mii_phy_reset(sc); 184314564Sdim if (mii_phy_auto(sc, 0) == EJUSTRETURN) 185314564Sdim return (EJUSTRETURN); 186314564Sdim 187314564Sdim /* 188314564Sdim * Might need to generate a status message if autonegotiation 189341825Sdim * failed. 190341825Sdim */ 191309124Sdim return (0); 192314564Sdim} 193314564Sdim 194314564Sdimvoid 195314564Sdimmii_phy_reset(mii) 196341825Sdim struct mii_softc *mii; 197341825Sdim{ 198314564Sdim int reg, i; 199314564Sdim 200314564Sdim if (mii->mii_flags & MIIF_NOISOLATE) 201314564Sdim reg = BMCR_RESET; 202314564Sdim else 203341825Sdim reg = BMCR_RESET | BMCR_ISO; 204341825Sdim PHY_WRITE(mii, MII_BMCR, reg); 205341825Sdim 206314564Sdim /* Wait 100ms for it to complete. */ 207314564Sdim for (i = 0; i < 100; i++) { 208314564Sdim reg = PHY_READ(mii, MII_BMCR); 209314564Sdim if ((reg & BMCR_RESET) == 0) 210314564Sdim break; 211341825Sdim DELAY(1000); 212341825Sdim } 213341825Sdim 214314564Sdim if (mii->mii_inst != 0 && ((mii->mii_flags & MIIF_NOISOLATE) == 0)) 215314564Sdim PHY_WRITE(mii, MII_BMCR, reg | BMCR_ISO); 216314564Sdim} 217314564Sdim 218314564Sdimvoid 219341825Sdimmii_phy_update(sc, cmd) 220341825Sdim struct mii_softc *sc; 221341825Sdim int cmd; 222341825Sdim{ 223314564Sdim struct mii_data *mii = sc->mii_pdata; 224321369Sdim 225341825Sdim if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { 226341825Sdim MIIBUS_STATCHG(sc->mii_dev); 227321369Sdim sc->mii_active = mii->mii_media_active; 228321369Sdim } 229341825Sdim if (sc->mii_status != mii->mii_media_status) { 230341825Sdim MIIBUS_LINKCHG(sc->mii_dev); 231341825Sdim sc->mii_status = mii->mii_media_status; 232341825Sdim } 233321369Sdim} 234321369Sdim 235341825Sdim/* 236341825Sdim * Given an ifmedia word, return the corresponding ANAR value. 237341825Sdim */ 238341825Sdimint 239321369Sdimmii_anar(media) 240309124Sdim int media; 241296417Sdim{ 242341825Sdim int rv; 243341825Sdim 244341825Sdim switch (media & (IFM_TMASK|IFM_NMASK|IFM_FDX)) { 245341825Sdim case IFM_ETHER|IFM_10_T: 246341825Sdim rv = ANAR_10|ANAR_CSMA; 247341825Sdim break; 248296417Sdim case IFM_ETHER|IFM_10_T|IFM_FDX: 249309124Sdim rv = ANAR_10_FD|ANAR_CSMA; 250341825Sdim break; 251341825Sdim case IFM_ETHER|IFM_100_TX: 252341825Sdim rv = ANAR_TX|ANAR_CSMA; 253341825Sdim break; 254341825Sdim case IFM_ETHER|IFM_100_TX|IFM_FDX: 255341825Sdim rv = ANAR_TX_FD|ANAR_CSMA; 256341825Sdim break; 257341825Sdim case IFM_ETHER|IFM_100_T4: 258341825Sdim rv = ANAR_T4|ANAR_CSMA; 259341825Sdim break; 260341825Sdim default: 261341825Sdim rv = 0; 262341825Sdim break; 263341825Sdim } 264341825Sdim 265341825Sdim return (rv); 266309124Sdim} 267296417Sdim 268296417Sdim/* 269296417Sdim * Given a BMCR value, return the corresponding ifmedia word. 270296417Sdim */ 271296417Sdimint 272296417Sdimmii_media_from_bmcr(bmcr) 273296417Sdim int bmcr; 274296417Sdim{ 275296417Sdim int rv = IFM_ETHER; 276285163Sdim 277285163Sdim if (bmcr & BMCR_S100) 278285163Sdim rv |= IFM_100_TX; 279286684Sdim else 280286684Sdim rv |= IFM_10_T; 281296417Sdim if (bmcr & BMCR_FDX) 282286684Sdim rv |= IFM_FDX; 283296417Sdim 284286684Sdim return (rv); 285285163Sdim} 286285163Sdim 287341825Sdim/* 288 * Initialize generic PHY media based on BMSR, called when a PHY is 289 * attached. We expect to be set up to print a comma-separated list 290 * of media names. Does not print a newline. 291 */ 292void 293mii_add_media(mii, bmsr, instance) 294 struct mii_data *mii; 295 int bmsr, instance; 296{ 297 const char *sep = ""; 298 299#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 300#define PRINT(s) printf("%s%s", sep, s); sep = ", " 301 302 if (bmsr & BMSR_10THDX) { 303 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, instance), 0); 304 PRINT("10baseT"); 305 } 306 if (bmsr & BMSR_10TFDX) { 307 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, instance), 308 BMCR_FDX); 309 PRINT("10baseT-FDX"); 310 } 311 if (bmsr & BMSR_100TXHDX) { 312 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, instance), 313 BMCR_S100); 314 PRINT("100baseTX"); 315 } 316 if (bmsr & BMSR_100TXFDX) { 317 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, instance), 318 BMCR_S100|BMCR_FDX); 319 PRINT("100baseTX-FDX"); 320 } 321 if (bmsr & BMSR_100T4) { 322 /* 323 * XXX How do you enable 100baseT4? I assume we set 324 * XXX BMCR_S100 and then assume the PHYs will take 325 * XXX watever action is necessary to switch themselves 326 * XXX into T4 mode. 327 */ 328 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, instance), 329 BMCR_S100); 330 PRINT("100baseT4"); 331 } 332 if (bmsr & BMSR_ANEG) { 333 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, instance), 334 BMCR_AUTOEN); 335 PRINT("auto"); 336 } 337#undef ADD 338#undef PRINT 339} 340