mii_physubr.c revision 95665
150120Swpaul/* $NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner Exp $ */ 250120Swpaul 350120Swpaul/*- 450120Swpaul * Copyright (c) 1998, 1999 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 * 3. All advertising materials mentioning features or use of this software 2050120Swpaul * must display the following acknowledgement: 2150120Swpaul * This product includes software developed by the NetBSD 2250120Swpaul * Foundation, Inc. and its contributors. 2350120Swpaul * 4. Neither the name of The NetBSD Foundation nor the names of its 2450120Swpaul * contributors may be used to endorse or promote products derived 2550120Swpaul * from this software without specific prior written permission. 2650120Swpaul * 2750120Swpaul * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2850120Swpaul * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2950120Swpaul * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 3050120Swpaul * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 3150120Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3250120Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3350120Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3450120Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3550120Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3650120Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3750120Swpaul * POSSIBILITY OF SUCH DAMAGE. 3850120Swpaul */ 3950120Swpaul 4050120Swpaul/* 4150120Swpaul * Subroutines common to all PHYs. 4250120Swpaul */ 4350120Swpaul 4450120Swpaul#include <sys/param.h> 4550120Swpaul#include <sys/systm.h> 4650120Swpaul#include <sys/kernel.h> 4750120Swpaul#include <sys/socket.h> 4850120Swpaul#include <sys/errno.h> 4950120Swpaul#include <sys/module.h> 5050120Swpaul#include <sys/bus.h> 5150120Swpaul 5250120Swpaul 5350120Swpaul#include <net/if.h> 5450120Swpaul#include <net/if_media.h> 5550120Swpaul 5650120Swpaul#include <dev/mii/mii.h> 5750120Swpaul#include <dev/mii/miivar.h> 5850120Swpaul 5950120Swpaul#include "miibus_if.h" 6050120Swpaul 6150120Swpaul#if !defined(lint) 6250120Swpaulstatic const char rcsid[] = 6350477Speter "$FreeBSD: head/sys/dev/mii/mii_physubr.c 95665 2002-04-28 19:01:32Z phk $"; 6450120Swpaul#endif 6550120Swpaul 6692739Salfredvoid mii_phy_auto_timeout(void *); 6750120Swpaul 6850120Swpaulint 6950120Swpaulmii_phy_auto(mii, waitfor) 7050120Swpaul struct mii_softc *mii; 7150120Swpaul int waitfor; 7250120Swpaul{ 7350120Swpaul int bmsr, i; 7450120Swpaul 7550120Swpaul if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 7650120Swpaul PHY_WRITE(mii, MII_ANAR, 7750120Swpaul BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA); 7850120Swpaul PHY_WRITE(mii, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 7950120Swpaul } 8050120Swpaul 8150120Swpaul if (waitfor) { 8250120Swpaul /* Wait 500ms for it to complete. */ 8350120Swpaul for (i = 0; i < 500; i++) { 8450120Swpaul if ((bmsr = PHY_READ(mii, MII_BMSR)) & BMSR_ACOMP) 8550120Swpaul return (0); 8650120Swpaul DELAY(1000); 8750120Swpaul#if 0 8850120Swpaul if ((bmsr & BMSR_ACOMP) == 0) 8950120Swpaul printf("%s: autonegotiation failed to complete\n", 9050120Swpaul mii->mii_dev.dv_xname); 9150120Swpaul#endif 9250120Swpaul } 9350120Swpaul 9450120Swpaul /* 9550120Swpaul * Don't need to worry about clearing MIIF_DOINGAUTO. 9650120Swpaul * If that's set, a timeout is pending, and it will 9750120Swpaul * clear the flag. 9850120Swpaul */ 9950120Swpaul return (EIO); 10050120Swpaul } 10150120Swpaul 10250120Swpaul /* 10350120Swpaul * Just let it finish asynchronously. This is for the benefit of 10450120Swpaul * the tick handler driving autonegotiation. Don't want 500ms 10550120Swpaul * delays all the time while the system is running! 10650120Swpaul */ 10750120Swpaul if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 10850120Swpaul mii->mii_flags |= MIIF_DOINGAUTO; 10969925Swpaul mii->mii_auto_ch = timeout(mii_phy_auto_timeout, mii, hz >> 1); 11050120Swpaul } 11150120Swpaul return (EJUSTRETURN); 11250120Swpaul} 11350120Swpaul 11450120Swpaulvoid 11569925Swpaulmii_phy_auto_stop(sc) 11669925Swpaul struct mii_softc *sc; 11769925Swpaul{ 11869925Swpaul if (sc->mii_flags & MIIF_DOINGAUTO) { 11969925Swpaul sc->mii_flags &= ~MIIF_DOINGAUTO; 12069925Swpaul untimeout(mii_phy_auto_timeout, sc, sc->mii_auto_ch); 12169925Swpaul } 12269925Swpaul} 12369925Swpaul 12469925Swpaulvoid 12550120Swpaulmii_phy_auto_timeout(arg) 12650120Swpaul void *arg; 12750120Swpaul{ 12850120Swpaul struct mii_softc *mii = arg; 12950120Swpaul int s, bmsr; 13050120Swpaul 13150120Swpaul s = splnet(); 13250120Swpaul mii->mii_flags &= ~MIIF_DOINGAUTO; 13350120Swpaul bmsr = PHY_READ(mii, MII_BMSR); 13450120Swpaul#if 0 13550120Swpaul if ((bmsr & BMSR_ACOMP) == 0) 13650120Swpaul printf("%s: autonegotiation failed to complete\n", 13750120Swpaul sc->sc_dev.dv_xname); 13850120Swpaul#endif 13950120Swpaul 14050120Swpaul /* Update the media status. */ 14150120Swpaul (void) (*mii->mii_service)(mii, mii->mii_pdata, MII_POLLSTAT); 14250120Swpaul splx(s); 14350120Swpaul} 14450120Swpaul 14584140Sjlemonint 14684140Sjlemonmii_phy_tick(sc) 14784140Sjlemon struct mii_softc *sc; 14884140Sjlemon{ 14984140Sjlemon struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur; 15084140Sjlemon struct ifnet *ifp = sc->mii_pdata->mii_ifp; 15184140Sjlemon int reg; 15284140Sjlemon 15384140Sjlemon /* 15484140Sjlemon * Is the interface even up? 15584140Sjlemon */ 15684140Sjlemon if ((ifp->if_flags & IFF_UP) == 0) 15784140Sjlemon return (EJUSTRETURN); 15884140Sjlemon 15984140Sjlemon /* 16084140Sjlemon * If we're not doing autonegotiation, we don't need to do 16184140Sjlemon * any extra work here. However, we need to check the link 16284140Sjlemon * status so we can generate an announcement if the status 16384140Sjlemon * changes. 16484140Sjlemon */ 16584140Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 16684140Sjlemon return (0); 16784140Sjlemon 16884140Sjlemon /* 16984140Sjlemon * check for link. 17084140Sjlemon * Read the status register twice; BMSR_LINK is latch-low. 17184140Sjlemon */ 17284140Sjlemon reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 17384140Sjlemon if (reg & BMSR_LINK) 17484140Sjlemon return (0); 17584140Sjlemon 17684140Sjlemon /* 17784140Sjlemon * Only retry autonegotiation every 5 seconds. 17884140Sjlemon */ 17984140Sjlemon if (++sc->mii_ticks != 5) 18084140Sjlemon return (EJUSTRETURN); 18184140Sjlemon 18284140Sjlemon sc->mii_ticks = 0; 18384140Sjlemon mii_phy_reset(sc); 18484140Sjlemon if (mii_phy_auto(sc, 0) == EJUSTRETURN) 18584140Sjlemon return (EJUSTRETURN); 18684140Sjlemon 18784140Sjlemon /* 18884140Sjlemon * Might need to generate a status message if autonegotiation 18984140Sjlemon * failed. 19084140Sjlemon */ 19184140Sjlemon return (0); 19284140Sjlemon} 19384140Sjlemon 19450120Swpaulvoid 19550120Swpaulmii_phy_reset(mii) 19650120Swpaul struct mii_softc *mii; 19750120Swpaul{ 19850120Swpaul int reg, i; 19950120Swpaul 20050120Swpaul if (mii->mii_flags & MIIF_NOISOLATE) 20150120Swpaul reg = BMCR_RESET; 20250120Swpaul else 20350120Swpaul reg = BMCR_RESET | BMCR_ISO; 20450120Swpaul PHY_WRITE(mii, MII_BMCR, reg); 20550120Swpaul 20650120Swpaul /* Wait 100ms for it to complete. */ 20750120Swpaul for (i = 0; i < 100; i++) { 20850120Swpaul reg = PHY_READ(mii, MII_BMCR); 20950120Swpaul if ((reg & BMCR_RESET) == 0) 21050120Swpaul break; 21150120Swpaul DELAY(1000); 21250120Swpaul } 21350120Swpaul 21450120Swpaul if (mii->mii_inst != 0 && ((mii->mii_flags & MIIF_NOISOLATE) == 0)) 21550120Swpaul PHY_WRITE(mii, MII_BMCR, reg | BMCR_ISO); 21650120Swpaul} 21750120Swpaul 21884140Sjlemonvoid 21984140Sjlemonmii_phy_update(sc, cmd) 22084140Sjlemon struct mii_softc *sc; 22184140Sjlemon int cmd; 22284140Sjlemon{ 22384140Sjlemon struct mii_data *mii = sc->mii_pdata; 22484140Sjlemon 22584140Sjlemon if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { 22684140Sjlemon MIIBUS_STATCHG(sc->mii_dev); 22784140Sjlemon sc->mii_active = mii->mii_media_active; 22884140Sjlemon } 22984140Sjlemon if (sc->mii_status != mii->mii_media_status) { 23084140Sjlemon MIIBUS_LINKCHG(sc->mii_dev); 23184140Sjlemon sc->mii_status = mii->mii_media_status; 23284140Sjlemon } 23384140Sjlemon} 23484140Sjlemon 23550120Swpaul/* 23650120Swpaul * Given an ifmedia word, return the corresponding ANAR value. 23750120Swpaul */ 23850120Swpaulint 23950120Swpaulmii_anar(media) 24050120Swpaul int media; 24150120Swpaul{ 24250120Swpaul int rv; 24350120Swpaul 24450120Swpaul switch (media & (IFM_TMASK|IFM_NMASK|IFM_FDX)) { 24550120Swpaul case IFM_ETHER|IFM_10_T: 24650120Swpaul rv = ANAR_10|ANAR_CSMA; 24750120Swpaul break; 24850120Swpaul case IFM_ETHER|IFM_10_T|IFM_FDX: 24950120Swpaul rv = ANAR_10_FD|ANAR_CSMA; 25050120Swpaul break; 25150120Swpaul case IFM_ETHER|IFM_100_TX: 25250120Swpaul rv = ANAR_TX|ANAR_CSMA; 25350120Swpaul break; 25450120Swpaul case IFM_ETHER|IFM_100_TX|IFM_FDX: 25550120Swpaul rv = ANAR_TX_FD|ANAR_CSMA; 25650120Swpaul break; 25750120Swpaul case IFM_ETHER|IFM_100_T4: 25850120Swpaul rv = ANAR_T4|ANAR_CSMA; 25950120Swpaul break; 26050120Swpaul default: 26150120Swpaul rv = 0; 26250120Swpaul break; 26350120Swpaul } 26450120Swpaul 26550120Swpaul return (rv); 26650120Swpaul} 26750120Swpaul 26850120Swpaul/* 26950120Swpaul * Given a BMCR value, return the corresponding ifmedia word. 27050120Swpaul */ 27150120Swpaulint 27250120Swpaulmii_media_from_bmcr(bmcr) 27350120Swpaul int bmcr; 27450120Swpaul{ 27550120Swpaul int rv = IFM_ETHER; 27650120Swpaul 27750120Swpaul if (bmcr & BMCR_S100) 27850120Swpaul rv |= IFM_100_TX; 27950120Swpaul else 28050120Swpaul rv |= IFM_10_T; 28150120Swpaul if (bmcr & BMCR_FDX) 28250120Swpaul rv |= IFM_FDX; 28350120Swpaul 28450120Swpaul return (rv); 28550120Swpaul} 28650120Swpaul 28750120Swpaul/* 28850120Swpaul * Initialize generic PHY media based on BMSR, called when a PHY is 28950120Swpaul * attached. We expect to be set up to print a comma-separated list 29050120Swpaul * of media names. Does not print a newline. 29150120Swpaul */ 29250120Swpaulvoid 29350120Swpaulmii_add_media(mii, bmsr, instance) 29450120Swpaul struct mii_data *mii; 29550120Swpaul int bmsr, instance; 29650120Swpaul{ 29750120Swpaul const char *sep = ""; 29850120Swpaul 29995665Sphk if ((bmsr & BMSR_MEDIAMASK) == 0) { 30095665Sphk printf("no media present"); 30195665Sphk return; 30295665Sphk } 30395665Sphk 30450120Swpaul#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 30550120Swpaul#define PRINT(s) printf("%s%s", sep, s); sep = ", " 30650120Swpaul 30750120Swpaul if (bmsr & BMSR_10THDX) { 30850120Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, instance), 0); 30950120Swpaul PRINT("10baseT"); 31050120Swpaul } 31150120Swpaul if (bmsr & BMSR_10TFDX) { 31250120Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, instance), 31350120Swpaul BMCR_FDX); 31450120Swpaul PRINT("10baseT-FDX"); 31550120Swpaul } 31650120Swpaul if (bmsr & BMSR_100TXHDX) { 31750120Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, instance), 31850120Swpaul BMCR_S100); 31950120Swpaul PRINT("100baseTX"); 32050120Swpaul } 32150120Swpaul if (bmsr & BMSR_100TXFDX) { 32250120Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, instance), 32350120Swpaul BMCR_S100|BMCR_FDX); 32450120Swpaul PRINT("100baseTX-FDX"); 32550120Swpaul } 32650120Swpaul if (bmsr & BMSR_100T4) { 32750120Swpaul /* 32850120Swpaul * XXX How do you enable 100baseT4? I assume we set 32950120Swpaul * XXX BMCR_S100 and then assume the PHYs will take 33050120Swpaul * XXX watever action is necessary to switch themselves 33150120Swpaul * XXX into T4 mode. 33250120Swpaul */ 33350120Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, instance), 33450120Swpaul BMCR_S100); 33550120Swpaul PRINT("100baseT4"); 33650120Swpaul } 33750120Swpaul if (bmsr & BMSR_ANEG) { 33850120Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, instance), 33950120Swpaul BMCR_AUTOEN); 34050120Swpaul PRINT("auto"); 34150120Swpaul } 34250120Swpaul#undef ADD 34350120Swpaul#undef PRINT 34450120Swpaul} 345