e1000phy.c revision 120281
175353Smjacob/* 2119418Sobrien 3119418Sobrien#include <sys/cdefs.h> 4119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/mii/e1000phy.c 120281 2003-09-20 10:53:08Z wilko $"); 575353Smjacob * Principal Author: Parag Patel 675353Smjacob * Copyright (c) 2001 775353Smjacob * All rights reserved. 875353Smjacob * 975353Smjacob * Redistribution and use in source and binary forms, with or without 1075353Smjacob * modification, are permitted provided that the following conditions 1175353Smjacob * are met: 1275353Smjacob * 1. Redistributions of source code must retain the above copyright 1375353Smjacob * notice unmodified, this list of conditions, and the following 1475353Smjacob * disclaimer. 1575353Smjacob * 2. Redistributions in binary form must reproduce the above copyright 1675353Smjacob * notice, this list of conditions and the following disclaimer in the 1775353Smjacob * documentation and/or other materials provided with the distribution. 1875353Smjacob * 1975353Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2075353Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2175353Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2275353Smjacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2375353Smjacob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2475353Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2575353Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2675353Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2775353Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2875353Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2975353Smjacob * SUCH DAMAGE. 3075353Smjacob * 3175353Smjacob * Additonal Copyright (c) 2001 by Traakan Software under same licence. 3275353Smjacob * Secondary Author: Matthew Jacob 3375353Smjacob */ 3475353Smjacob 3575353Smjacob/* 3675353Smjacob * driver for the Marvell 88E1000 series external 1000/100/10-BT PHY. 3775353Smjacob */ 3875353Smjacob 39120281Swilko/* 40120281Swilko * Support added for the Marvell 88E1011 (Alaska) 1000/100/10baseTX and 41120281Swilko * 1000baseSX PHY. 42120281Swilko * Nathan Binkert <nate@openbsd.org> 43120281Swilko * Jung-uk Kim <jkim@niksun.com> 44120281Swilko */ 45120281Swilko 4675353Smjacob#include <sys/param.h> 4775353Smjacob#include <sys/systm.h> 4875353Smjacob#include <sys/kernel.h> 4975353Smjacob#include <sys/socket.h> 5075353Smjacob#include <sys/bus.h> 5175353Smjacob 5275353Smjacob#include <machine/clock.h> 5375353Smjacob 5475353Smjacob#include <net/if.h> 5575353Smjacob#include <net/if_media.h> 5675353Smjacob 5775353Smjacob#include <dev/mii/mii.h> 5875353Smjacob#include <dev/mii/miivar.h> 59109514Sobrien#include "miidevs.h" 6075353Smjacob 6175353Smjacob#include <dev/mii/e1000phyreg.h> 6275353Smjacob 6375353Smjacob#include "miibus_if.h" 6475353Smjacob 6575353Smjacobstatic int e1000phy_probe(device_t); 6675353Smjacobstatic int e1000phy_attach(device_t); 6775353Smjacob 6875353Smjacobstatic device_method_t e1000phy_methods[] = { 6975353Smjacob /* device interface */ 7075353Smjacob DEVMETHOD(device_probe, e1000phy_probe), 7175353Smjacob DEVMETHOD(device_attach, e1000phy_attach), 7295722Sphk DEVMETHOD(device_detach, mii_phy_detach), 7375353Smjacob DEVMETHOD(device_shutdown, bus_generic_shutdown), 7475353Smjacob { 0, 0 } 7575353Smjacob}; 7675353Smjacob 7775353Smjacobstatic devclass_t e1000phy_devclass; 7875353Smjacobstatic driver_t e1000phy_driver = { 7975353Smjacob "e1000phy", e1000phy_methods, sizeof (struct mii_softc) 8075353Smjacob}; 8175353SmjacobDRIVER_MODULE(e1000phy, miibus, e1000phy_driver, e1000phy_devclass, 0, 0); 8275353Smjacob 8384145Sjlemonstatic int e1000phy_service(struct mii_softc *, struct mii_data *, int); 8484145Sjlemonstatic void e1000phy_status(struct mii_softc *); 8584145Sjlemonstatic void e1000phy_reset(struct mii_softc *); 8696026Sphkstatic int e1000phy_mii_phy_auto(struct mii_softc *); 8775353Smjacob 8875353Smjacobstatic int e1000phy_debug = 0; 8975353Smjacob 9075353Smjacobstatic int 9175353Smjacobe1000phy_probe(device_t dev) 9275353Smjacob{ 9375353Smjacob struct mii_attach_args *ma; 9475353Smjacob u_int32_t id; 9575353Smjacob 9675353Smjacob ma = device_get_ivars(dev); 9775353Smjacob id = ((ma->mii_id1 << 16) | ma->mii_id2) & E1000_ID_MASK; 98120281Swilko if (id != E1000_ID_88E1000 99120281Swilko && id != E1000_ID_88E1000S 100120281Swilko && id != E1000_ID_88E1011) { 10175353Smjacob return ENXIO; 10275353Smjacob } 10375353Smjacob 10475353Smjacob device_set_desc(dev, MII_STR_MARVELL_E1000); 10575353Smjacob return 0; 10675353Smjacob} 10775353Smjacob 10875353Smjacobstatic int 10975353Smjacobe1000phy_attach(device_t dev) 11075353Smjacob{ 11175353Smjacob struct mii_softc *sc; 11275353Smjacob struct mii_attach_args *ma; 11375353Smjacob struct mii_data *mii; 114120281Swilko u_int32_t id; 11575353Smjacob 11675353Smjacob getenv_int("e1000phy_debug", &e1000phy_debug); 11775353Smjacob 11875353Smjacob sc = device_get_softc(dev); 11975353Smjacob ma = device_get_ivars(dev); 12075353Smjacob sc->mii_dev = device_get_parent(dev); 12175353Smjacob mii = device_get_softc(sc->mii_dev); 12275353Smjacob LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 12375353Smjacob 12475353Smjacob sc->mii_inst = mii->mii_instance; 12575353Smjacob sc->mii_phy = ma->mii_phyno; 12675353Smjacob sc->mii_service = e1000phy_service; 12775353Smjacob sc->mii_pdata = mii; 128120281Swilko sc->mii_flags |= MIIF_NOISOLATE; 12975353Smjacob 130120281Swilko id = ((ma->mii_id1 << 16) | ma->mii_id2) & E1000_ID_MASK; 131120281Swilko if (id == E1000_ID_88E1011 132120281Swilko && (PHY_READ(sc, E1000_ESSR) & E1000_ESSR_FIBER_LINK)) 133120281Swilko sc->mii_flags |= MIIF_HAVEFIBER; 13475353Smjacob mii->mii_instance++; 13575353Smjacob e1000phy_reset(sc); 13675353Smjacob 13784144Sjlemon device_printf(dev, " "); 13884144Sjlemon 13975353Smjacob#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 140120281Swilko if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { 141120281Swilko#if 0 142120281Swilko ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 143120281Swilko E1000_CR_ISOLATE); 144120281Swilko#endif 145120281Swilko ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), 146120281Swilko E1000_CR_SPEED_10); 147120281Swilko printf("10baseT, "); 148120281Swilko ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), 149120281Swilko E1000_CR_SPEED_10 | E1000_CR_FULL_DUPLEX); 150120281Swilko printf("10baseT-FDX, "); 151120281Swilko ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), 152120281Swilko E1000_CR_SPEED_100); 153120281Swilko printf("100baseTX, "); 154120281Swilko ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), 155120281Swilko E1000_CR_SPEED_100 | E1000_CR_FULL_DUPLEX); 156120281Swilko printf("100baseTX-FDX, "); 157120281Swilko /* 158120281Swilko * 1000BT-simplex not supported; driver must ignore this entry, 159120281Swilko * but it must be present in order to manually set full-duplex. 160120281Swilko */ 161120281Swilko ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst), 162120281Swilko E1000_CR_SPEED_1000); 163120281Swilko ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst), 164120281Swilko E1000_CR_SPEED_1000 | E1000_CR_FULL_DUPLEX); 165120281Swilko printf("1000baseTX-FDX, "); 166120281Swilko } else { 167120281Swilko ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), 168120281Swilko E1000_CR_SPEED_1000 | E1000_CR_FULL_DUPLEX); 169120281Swilko printf("1000baseSX-FDX, "); 170120281Swilko } 17175353Smjacob ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0); 17284144Sjlemon printf("auto\n"); 17375353Smjacob#undef ADD 17475353Smjacob 17575353Smjacob MIIBUS_MEDIAINIT(sc->mii_dev); 17675353Smjacob return(0); 17775353Smjacob} 17875353Smjacob 17975353Smjacobstatic void 18075353Smjacobe1000phy_reset(struct mii_softc *sc) 18175353Smjacob{ 18275353Smjacob u_int32_t reg; 18375353Smjacob int i; 18475353Smjacob 18575353Smjacob /* initialize custom E1000 registers to magic values */ 18675353Smjacob reg = PHY_READ(sc, E1000_SCR); 18775353Smjacob reg &= ~E1000_SCR_AUTO_X_MODE; 18875353Smjacob PHY_WRITE(sc, E1000_SCR, reg); 18975353Smjacob 19075353Smjacob /* normal PHY reset */ 19175353Smjacob /*mii_phy_reset(sc);*/ 19275353Smjacob reg = PHY_READ(sc, E1000_CR); 19375353Smjacob reg |= E1000_CR_RESET; 19475353Smjacob PHY_WRITE(sc, E1000_CR, reg); 19575353Smjacob 19675353Smjacob for (i = 0; i < 500; i++) { 19775353Smjacob DELAY(1); 19875353Smjacob reg = PHY_READ(sc, E1000_CR); 19975353Smjacob if (!(reg & E1000_CR_RESET)) 20075353Smjacob break; 20175353Smjacob } 20275353Smjacob 20375353Smjacob /* set more custom E1000 registers to magic values */ 20475353Smjacob reg = PHY_READ(sc, E1000_SCR); 20575353Smjacob reg |= E1000_SCR_ASSERT_CRS_ON_TX; 20675353Smjacob PHY_WRITE(sc, E1000_SCR, reg); 20775353Smjacob 20875353Smjacob reg = PHY_READ(sc, E1000_ESCR); 20975353Smjacob reg |= E1000_ESCR_TX_CLK_25; 21075353Smjacob PHY_WRITE(sc, E1000_ESCR, reg); 21175353Smjacob 21275353Smjacob /* even more magic to reset DSP? */ 21375353Smjacob PHY_WRITE(sc, 29, 0x1d); 21475353Smjacob PHY_WRITE(sc, 30, 0xc1); 21575353Smjacob PHY_WRITE(sc, 30, 0x00); 21675353Smjacob} 21775353Smjacob 21884145Sjlemonstatic int 21975353Smjacobe1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 22075353Smjacob{ 22175353Smjacob struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 22275353Smjacob int reg; 22375353Smjacob 22475353Smjacob switch (cmd) { 22575353Smjacob case MII_POLLSTAT: 22675353Smjacob /* 22775353Smjacob * If we're not polling our PHY instance, just return. 22875353Smjacob */ 22975353Smjacob if (IFM_INST(ife->ifm_media) != sc->mii_inst) 23075353Smjacob return (0); 23175353Smjacob break; 23275353Smjacob 23375353Smjacob case MII_MEDIACHG: 23475353Smjacob /* 23575353Smjacob * If the media indicates a different PHY instance, 23675353Smjacob * isolate ourselves. 23775353Smjacob */ 23875353Smjacob if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 23975353Smjacob reg = PHY_READ(sc, E1000_CR); 24075353Smjacob PHY_WRITE(sc, E1000_CR, reg | E1000_CR_ISOLATE); 24175353Smjacob return (0); 24275353Smjacob } 24375353Smjacob 24475353Smjacob /* 24575353Smjacob * If the interface is not up, don't do anything. 24675353Smjacob */ 24775353Smjacob if ((mii->mii_ifp->if_flags & IFF_UP) == 0) { 24875353Smjacob break; 24975353Smjacob } 25075353Smjacob 25175353Smjacob switch (IFM_SUBTYPE(ife->ifm_media)) { 25275353Smjacob case IFM_AUTO: 25375353Smjacob e1000phy_reset(sc); 25496026Sphk (void)e1000phy_mii_phy_auto(sc); 25575353Smjacob break; 25675353Smjacob 25795673Sphk case IFM_1000_T: 25875353Smjacob e1000phy_reset(sc); 25975353Smjacob 26075353Smjacob /* TODO - any other way to force 1000BT? */ 26196026Sphk (void)e1000phy_mii_phy_auto(sc); 26275353Smjacob break; 26375353Smjacob 264120281Swilko case IFM_1000_SX: 265120281Swilko e1000phy_reset(sc); 266120281Swilko 267120281Swilko PHY_WRITE(sc, E1000_CR, 268120281Swilko E1000_CR_FULL_DUPLEX | E1000_CR_SPEED_1000); 269120281Swilko PHY_WRITE(sc, E1000_AR, E1000_FA_1000X_FD); 270120281Swilko break; 271120281Swilko 27275353Smjacob case IFM_100_TX: 27375353Smjacob e1000phy_reset(sc); 27475353Smjacob 27575353Smjacob if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { 27675353Smjacob PHY_WRITE(sc, E1000_CR, 27775353Smjacob E1000_CR_FULL_DUPLEX | E1000_CR_SPEED_100); 27875353Smjacob PHY_WRITE(sc, E1000_AR, E1000_AR_100TX_FD); 27975353Smjacob } else { 28075353Smjacob PHY_WRITE(sc, E1000_CR, E1000_CR_SPEED_100); 28175353Smjacob PHY_WRITE(sc, E1000_AR, E1000_AR_100TX); 28275353Smjacob } 28375353Smjacob break; 28475353Smjacob 28575353Smjacob case IFM_10_T: 28675353Smjacob e1000phy_reset(sc); 28775353Smjacob 28875353Smjacob if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { 28975353Smjacob PHY_WRITE(sc, E1000_CR, 29075353Smjacob E1000_CR_FULL_DUPLEX | E1000_CR_SPEED_10); 29175353Smjacob PHY_WRITE(sc, E1000_AR, E1000_AR_10T_FD); 29275353Smjacob } else { 29375353Smjacob PHY_WRITE(sc, E1000_CR, E1000_CR_SPEED_10); 29475353Smjacob PHY_WRITE(sc, E1000_AR, E1000_AR_10T); 29575353Smjacob } 29675353Smjacob 29775353Smjacob break; 29875353Smjacob 29975353Smjacob default: 30075353Smjacob return (EINVAL); 30175353Smjacob } 30275353Smjacob 30375353Smjacob break; 30475353Smjacob 30575353Smjacob case MII_TICK: 30675353Smjacob /* 30775353Smjacob * If we're not currently selected, just return. 30875353Smjacob */ 30975353Smjacob if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 31075353Smjacob return (0); 31175353Smjacob } 31275353Smjacob 31375353Smjacob /* 31484145Sjlemon * Is the interface even up? 31575353Smjacob */ 31684145Sjlemon if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 31775353Smjacob return (0); 31875353Smjacob 31975353Smjacob /* 32084145Sjlemon * Only used for autonegotiation. 32175353Smjacob */ 32284145Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 32384145Sjlemon break; 32475353Smjacob 32575353Smjacob /* 32684145Sjlemon * check for link. 32784145Sjlemon * Read the status register twice; BMSR_LINK is latch-low. 32875353Smjacob */ 32984145Sjlemon reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 33084145Sjlemon if (reg & BMSR_LINK) 33184145Sjlemon break; 33275353Smjacob 33375353Smjacob /* 33484145Sjlemon * Only retry autonegotiation every 5 seconds. 33575353Smjacob */ 33684145Sjlemon if (++sc->mii_ticks != 5) 33784145Sjlemon return (0); 33875353Smjacob 33984145Sjlemon sc->mii_ticks = 0; 34075353Smjacob e1000phy_reset(sc); 34196026Sphk e1000phy_mii_phy_auto(sc); 34296026Sphk return (0); 34375353Smjacob } 34475353Smjacob 34575353Smjacob /* Update the media status. */ 34675353Smjacob e1000phy_status(sc); 34775353Smjacob 34875353Smjacob /* Callback if something changed. */ 34984145Sjlemon mii_phy_update(sc, cmd); 35075353Smjacob return (0); 35175353Smjacob} 35275353Smjacob 35384145Sjlemonstatic void 35475353Smjacobe1000phy_status(struct mii_softc *sc) 35575353Smjacob{ 35675353Smjacob struct mii_data *mii = sc->mii_pdata; 35775353Smjacob int bmsr, bmcr, esr, ssr, isr, ar, lpar; 35875353Smjacob 35975353Smjacob mii->mii_media_status = IFM_AVALID; 36075353Smjacob mii->mii_media_active = IFM_ETHER; 36175353Smjacob 36275353Smjacob bmsr = PHY_READ(sc, E1000_SR) | PHY_READ(sc, E1000_SR); 36375353Smjacob esr = PHY_READ(sc, E1000_ESR); 36475353Smjacob bmcr = PHY_READ(sc, E1000_CR); 36575353Smjacob ssr = PHY_READ(sc, E1000_SSR); 36675353Smjacob isr = PHY_READ(sc, E1000_ISR); 36775353Smjacob ar = PHY_READ(sc, E1000_AR); 36875353Smjacob lpar = PHY_READ(sc, E1000_LPAR); 36975353Smjacob 37075353Smjacob if (bmsr & E1000_SR_LINK_STATUS) 37175353Smjacob mii->mii_media_status |= IFM_ACTIVE; 37275353Smjacob 37375353Smjacob if (bmcr & E1000_CR_LOOPBACK) 37475353Smjacob mii->mii_media_active |= IFM_LOOP; 37575353Smjacob 37696026Sphk if ((!(bmsr & E1000_SR_AUTO_NEG_COMPLETE) || !(ssr & E1000_SSR_LINK) || 37775353Smjacob !(ssr & E1000_SSR_SPD_DPLX_RESOLVED))) { 37875353Smjacob /* Erg, still trying, I guess... */ 37975353Smjacob mii->mii_media_active |= IFM_NONE; 38075353Smjacob return; 38175353Smjacob } 38275353Smjacob 383120281Swilko if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { 384120281Swilko if (ssr & E1000_SSR_1000MBS) 385120281Swilko mii->mii_media_active |= IFM_1000_T; 386120281Swilko else if (ssr & E1000_SSR_100MBS) 387120281Swilko mii->mii_media_active |= IFM_100_TX; 388120281Swilko else 389120281Swilko mii->mii_media_active |= IFM_10_T; 390120281Swilko } else { 391120281Swilko if (ssr & E1000_SSR_1000MBS) 392120281Swilko mii->mii_media_active |= IFM_1000_SX; 393120281Swilko } 39475353Smjacob 39575353Smjacob if (ssr & E1000_SSR_DUPLEX) 39675353Smjacob mii->mii_media_active |= IFM_FDX; 39775353Smjacob else 39875353Smjacob mii->mii_media_active |= IFM_HDX; 39975353Smjacob 400120281Swilko if ((sc->mii_flags & MIIF_HAVEFIBER) == 0) { 401120281Swilko /* FLAG0==rx-flow-control FLAG1==tx-flow-control */ 402120281Swilko if ((ar & E1000_AR_PAUSE) && (lpar & E1000_LPAR_PAUSE)) { 403120281Swilko mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1; 404120281Swilko } else if (!(ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) && 405120281Swilko (lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) { 406120281Swilko mii->mii_media_active |= IFM_FLAG1; 407120281Swilko } else if ((ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) && 408120281Swilko !(lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) { 409120281Swilko mii->mii_media_active |= IFM_FLAG0; 410120281Swilko } 41175353Smjacob } 41275353Smjacob} 41375353Smjacob 41475353Smjacobstatic int 41596026Sphke1000phy_mii_phy_auto(struct mii_softc *mii) 41675353Smjacob{ 41775353Smjacob 418120281Swilko if ((mii->mii_flags & MIIF_HAVEFIBER) == 0) { 419120281Swilko PHY_WRITE(mii, E1000_AR, E1000_AR_10T | E1000_AR_10T_FD | 420120281Swilko E1000_AR_100TX | E1000_AR_100TX_FD | 421120281Swilko E1000_AR_PAUSE | E1000_AR_ASM_DIR); 422120281Swilko PHY_WRITE(mii, E1000_1GCR, E1000_1GCR_1000T_FD); 423120281Swilko PHY_WRITE(mii, E1000_CR, 424120281Swilko E1000_CR_AUTO_NEG_ENABLE | E1000_CR_RESTART_AUTO_NEG); 425120281Swilko } 42675353Smjacob 42775353Smjacob return (EJUSTRETURN); 42875353Smjacob} 429