acphy.c revision 84145
172132Ssemenu/*- 272132Ssemenu * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. 372132Ssemenu * All rights reserved. 472132Ssemenu * 572132Ssemenu * This code is derived from software contributed to The NetBSD Foundation 672132Ssemenu * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 772132Ssemenu * NASA Ames Research Center. 872132Ssemenu * 972132Ssemenu * Redistribution and use in source and binary forms, with or without 1072132Ssemenu * modification, are permitted provided that the following conditions 1172132Ssemenu * are met: 1272132Ssemenu * 1. Redistributions of source code must retain the above copyright 1372132Ssemenu * notice, this list of conditions and the following disclaimer. 1472132Ssemenu * 2. Redistributions in binary form must reproduce the above copyright 1572132Ssemenu * notice, this list of conditions and the following disclaimer in the 1672132Ssemenu * documentation and/or other materials provided with the distribution. 1772132Ssemenu * 3. All advertising materials mentioning features or use of this software 1872132Ssemenu * must display the following acknowledgement: 1972132Ssemenu * This product includes software developed by the NetBSD 2072132Ssemenu * Foundation, Inc. and its contributors. 2172132Ssemenu * 4. Neither the name of The NetBSD Foundation nor the names of its 2272132Ssemenu * contributors may be used to endorse or promote products derived 2372132Ssemenu * from this software without specific prior written permission. 2472132Ssemenu * 2572132Ssemenu * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2672132Ssemenu * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2772132Ssemenu * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2872132Ssemenu * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2972132Ssemenu * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3072132Ssemenu * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3172132Ssemenu * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3272132Ssemenu * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3372132Ssemenu * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3472132Ssemenu * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3572132Ssemenu * POSSIBILITY OF SUCH DAMAGE. 3672132Ssemenu */ 3772132Ssemenu 3872132Ssemenu/* 3972132Ssemenu * Copyright (c) 1997 Manuel Bouyer. All rights reserved. 4072132Ssemenu * 4172132Ssemenu * Redistribution and use in source and binary forms, with or without 4272132Ssemenu * modification, are permitted provided that the following conditions 4372132Ssemenu * are met: 4472132Ssemenu * 1. Redistributions of source code must retain the above copyright 4572132Ssemenu * notice, this list of conditions and the following disclaimer. 4672132Ssemenu * 2. Redistributions in binary form must reproduce the above copyright 4772132Ssemenu * notice, this list of conditions and the following disclaimer in the 4872132Ssemenu * documentation and/or other materials provided with the distribution. 4972132Ssemenu * 3. All advertising materials mentioning features or use of this software 5072132Ssemenu * must display the following acknowledgement: 5172132Ssemenu * This product includes software developed by Manuel Bouyer. 5272132Ssemenu * 4. The name of the author may not be used to endorse or promote products 5372132Ssemenu * derived from this software without specific prior written permission. 5472132Ssemenu * 5572132Ssemenu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 5672132Ssemenu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 5772132Ssemenu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 5872132Ssemenu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 5972132Ssemenu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 6072132Ssemenu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 6172132Ssemenu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 6272132Ssemenu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 6372132Ssemenu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 6472132Ssemenu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 6572132Ssemenu */ 6672132Ssemenu 6772132Ssemenu/* 6872132Ssemenu * Driver for Altima AC101 10/100 PHY 6972132Ssemenu */ 7072132Ssemenu 7172132Ssemenu#include <sys/param.h> 7272132Ssemenu#include <sys/systm.h> 7372132Ssemenu#include <sys/kernel.h> 7472132Ssemenu#include <sys/socket.h> 7572132Ssemenu#include <sys/errno.h> 7672132Ssemenu#include <sys/module.h> 7772132Ssemenu#include <sys/bus.h> 7872132Ssemenu 7972132Ssemenu#include <net/if.h> 8072132Ssemenu#include <net/if_media.h> 8172132Ssemenu 8272132Ssemenu#include <dev/mii/mii.h> 8372132Ssemenu#include <dev/mii/miivar.h> 8472132Ssemenu#include <dev/mii/miidevs.h> 8572132Ssemenu 8672132Ssemenu#include <dev/mii/acphyreg.h> 8772132Ssemenu 8872132Ssemenu#include "miibus_if.h" 8972132Ssemenu 9072132Ssemenu#if !defined(lint) 9172132Ssemenustatic const char rcsid[] = 9272132Ssemenu "$FreeBSD: head/sys/dev/mii/acphy.c 84145 2001-09-29 19:18:52Z jlemon $"; 9372132Ssemenu#endif 9472132Ssemenu 9572132Ssemenustatic int acphy_probe __P((device_t)); 9672132Ssemenustatic int acphy_attach __P((device_t)); 9772132Ssemenustatic int acphy_detach __P((device_t)); 9872132Ssemenu 9972132Ssemenustatic device_method_t acphy_methods[] = { 10072132Ssemenu /* device interface */ 10172132Ssemenu DEVMETHOD(device_probe, acphy_probe), 10272132Ssemenu DEVMETHOD(device_attach, acphy_attach), 10372132Ssemenu DEVMETHOD(device_detach, acphy_detach), 10472132Ssemenu DEVMETHOD(device_shutdown, bus_generic_shutdown), 10572132Ssemenu { 0, 0 } 10672132Ssemenu}; 10772132Ssemenu 10872132Ssemenustatic devclass_t acphy_devclass; 10972132Ssemenu 11072132Ssemenustatic driver_t acphy_driver = { 11172132Ssemenu "acphy", 11272132Ssemenu acphy_methods, 11372132Ssemenu sizeof(struct mii_softc) 11472132Ssemenu}; 11572132Ssemenu 11672132SsemenuDRIVER_MODULE(acphy, miibus, acphy_driver, acphy_devclass, 0, 0); 11772132Ssemenu 11884145Sjlemonstatic int acphy_service __P((struct mii_softc *, struct mii_data *, int)); 11984145Sjlemonstatic void acphy_reset __P((struct mii_softc *)); 12084145Sjlemonstatic void acphy_status __P((struct mii_softc *)); 12172132Ssemenu 12272132Ssemenustatic int acphy_probe(dev) 12372132Ssemenu device_t dev; 12472132Ssemenu{ 12572132Ssemenu struct mii_attach_args *ma; 12672132Ssemenu 12772132Ssemenu ma = device_get_ivars(dev); 12872132Ssemenu 12972132Ssemenu if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxALTIMA && 13072132Ssemenu MII_MODEL(ma->mii_id2) == MII_MODEL_xxALTIMA_AC101) { 13172132Ssemenu device_set_desc(dev, MII_STR_xxALTIMA_AC101); 13272132Ssemenu } else 13372132Ssemenu return (ENXIO); 13472132Ssemenu 13572132Ssemenu return (0); 13672132Ssemenu} 13772132Ssemenu 13872132Ssemenustatic int acphy_attach(dev) 13972132Ssemenu device_t dev; 14072132Ssemenu{ 14172132Ssemenu struct mii_softc *sc; 14272132Ssemenu struct mii_attach_args *ma; 14372132Ssemenu struct mii_data *mii; 14472132Ssemenu 14572132Ssemenu sc = device_get_softc(dev); 14672132Ssemenu ma = device_get_ivars(dev); 14772132Ssemenu sc->mii_dev = device_get_parent(dev); 14872132Ssemenu mii = device_get_softc(sc->mii_dev); 14972132Ssemenu LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 15072132Ssemenu 15172132Ssemenu sc->mii_inst = mii->mii_instance; 15272132Ssemenu sc->mii_phy = ma->mii_phyno; 15372132Ssemenu sc->mii_service = acphy_service; 15472132Ssemenu sc->mii_pdata = mii; 15572132Ssemenu sc->mii_flags |= MIIF_NOISOLATE; 15672132Ssemenu 15772132Ssemenu acphy_reset(sc); 15872132Ssemenu 15972132Ssemenu mii->mii_instance++; 16072132Ssemenu 16172132Ssemenu sc->mii_capabilities = 16272132Ssemenu PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 16372132Ssemenu device_printf(dev, " "); 16472132Ssemenu if (sc->mii_capabilities & BMSR_MEDIAMASK) 16572132Ssemenu mii_add_media(mii, sc->mii_capabilities, 16672132Ssemenu sc->mii_inst); 16772132Ssemenu printf("\n"); 16872132Ssemenu 16972132Ssemenu MIIBUS_MEDIAINIT(sc->mii_dev); 17072132Ssemenu return (0); 17172132Ssemenu} 17272132Ssemenu 17372132Ssemenustatic int acphy_detach(dev) 17472132Ssemenu device_t dev; 17572132Ssemenu{ 17672132Ssemenu struct mii_softc *sc; 17772132Ssemenu struct mii_data *mii; 17872132Ssemenu 17972132Ssemenu sc = device_get_softc(dev); 18072132Ssemenu mii = device_get_softc(device_get_parent(dev)); 18172132Ssemenu sc->mii_dev = NULL; 18272132Ssemenu LIST_REMOVE(sc, mii_list); 18372132Ssemenu 18472132Ssemenu return(0); 18572132Ssemenu} 18672132Ssemenu 18784145Sjlemonstatic int 18872132Ssemenuacphy_service(sc, mii, cmd) 18972132Ssemenu struct mii_softc *sc; 19072132Ssemenu struct mii_data *mii; 19172132Ssemenu int cmd; 19272132Ssemenu{ 19372132Ssemenu struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 19472132Ssemenu int reg; 19572132Ssemenu 19672132Ssemenu switch (cmd) { 19772132Ssemenu case MII_POLLSTAT: 19872132Ssemenu /* 19972132Ssemenu * If we're not polling our PHY instance, just return. 20072132Ssemenu */ 20172132Ssemenu if (IFM_INST(ife->ifm_media) != sc->mii_inst) 20272132Ssemenu return (0); 20372132Ssemenu break; 20472132Ssemenu 20572132Ssemenu case MII_MEDIACHG: 20672132Ssemenu /* 20772132Ssemenu * If the media indicates a different PHY instance, 20872132Ssemenu * isolate ourselves. 20972132Ssemenu */ 21072132Ssemenu if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 21172132Ssemenu reg = PHY_READ(sc, MII_BMCR); 21272132Ssemenu PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO | BMCR_PDOWN); 21372132Ssemenu return (0); 21472132Ssemenu } 21572132Ssemenu 21672132Ssemenu /* 21772132Ssemenu * If the interface is not up, don't do anything. 21872132Ssemenu */ 21972132Ssemenu if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 22072132Ssemenu break; 22172132Ssemenu 22272132Ssemenu /* Wake & deisolate up is needed */ 22372132Ssemenu reg = PHY_READ(sc, MII_BMCR); 22472132Ssemenu if (reg & (BMCR_ISO | BMCR_PDOWN)) 22572132Ssemenu PHY_WRITE(sc, MII_BMCR, reg & ~(BMCR_ISO | BMCR_PDOWN)); 22672132Ssemenu 22772132Ssemenu switch (IFM_SUBTYPE(ife->ifm_media)) { 22872132Ssemenu case IFM_AUTO: 22972132Ssemenu /* 23072132Ssemenu * If we're already in auto mode, just return. 23172132Ssemenu */ 23272132Ssemenu if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) 23372132Ssemenu return (0); 23472132Ssemenu 23572132Ssemenu (void) mii_phy_auto(sc, 1); 23672132Ssemenu break; 23772132Ssemenu 23872132Ssemenu default: 23972132Ssemenu /* 24072132Ssemenu * BMCR data is stored in the ifmedia entry. 24172132Ssemenu */ 24272132Ssemenu PHY_WRITE(sc, MII_ANAR, 24372132Ssemenu mii_anar(ife->ifm_media)); 24472132Ssemenu PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 24572132Ssemenu } 24672132Ssemenu break; 24772132Ssemenu 24872132Ssemenu case MII_TICK: 24972132Ssemenu /* 25072132Ssemenu * If we're not currently selected, just return. 25172132Ssemenu */ 25272132Ssemenu if (IFM_INST(ife->ifm_media) != sc->mii_inst) 25372132Ssemenu return (0); 25472132Ssemenu 25572132Ssemenu /* 25684145Sjlemon * Is the interface even up? 25772132Ssemenu */ 25884145Sjlemon if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 25972132Ssemenu return (0); 26072132Ssemenu 26172132Ssemenu /* 26284145Sjlemon * Only used for autonegotiation. 26372132Ssemenu */ 26484145Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 26584145Sjlemon break; 26672132Ssemenu 26772132Ssemenu /* 26884145Sjlemon * check for link. 26984145Sjlemon * Read the status register twice; BMSR_LINK is latch-low. 27072132Ssemenu */ 27184145Sjlemon reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 27272132Ssemenu if (reg & BMSR_LINK) 27384145Sjlemon break; 27472132Ssemenu 27572132Ssemenu /* 27672132Ssemenu * Only retry autonegotiation every 5 seconds. 27772132Ssemenu */ 27872132Ssemenu if (++sc->mii_ticks != 5) 27972132Ssemenu return (0); 28072132Ssemenu 28172132Ssemenu sc->mii_ticks = 0; 28272132Ssemenu acphy_reset(sc); 28372132Ssemenu if (mii_phy_auto(sc, 0) == EJUSTRETURN) 28472132Ssemenu return (0); 28572132Ssemenu break; 28672132Ssemenu } 28772132Ssemenu 28872132Ssemenu /* Update the media status. */ 28972132Ssemenu acphy_status(sc); 29072132Ssemenu 29172132Ssemenu /* Callback if something changed. */ 29284145Sjlemon mii_phy_update(sc, cmd); 29372132Ssemenu return (0); 29472132Ssemenu} 29572132Ssemenu 29684145Sjlemonstatic void 29772132Ssemenuacphy_status(sc) 29872132Ssemenu struct mii_softc *sc; 29972132Ssemenu{ 30072132Ssemenu struct mii_data *mii = sc->mii_pdata; 30172132Ssemenu struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 30272132Ssemenu int bmsr, bmcr, diag; 30372132Ssemenu 30472132Ssemenu mii->mii_media_status = IFM_AVALID; 30572132Ssemenu mii->mii_media_active = IFM_ETHER; 30672132Ssemenu 30772132Ssemenu bmsr = PHY_READ(sc, MII_BMSR) | 30872132Ssemenu PHY_READ(sc, MII_BMSR); 30972132Ssemenu if (bmsr & BMSR_LINK) 31072132Ssemenu mii->mii_media_status |= IFM_ACTIVE; 31172132Ssemenu 31272132Ssemenu bmcr = PHY_READ(sc, MII_BMCR); 31372132Ssemenu if (bmcr & BMCR_ISO) { 31472132Ssemenu mii->mii_media_active |= IFM_NONE; 31572132Ssemenu mii->mii_media_status = 0; 31672132Ssemenu return; 31772132Ssemenu } 31872132Ssemenu 31972132Ssemenu if (bmcr & BMCR_LOOP) 32072132Ssemenu mii->mii_media_active |= IFM_LOOP; 32172132Ssemenu 32272132Ssemenu if (bmcr & BMCR_AUTOEN) { 32372132Ssemenu if ((bmsr & BMSR_ACOMP) == 0) { 32472132Ssemenu /* Erg, still trying, I guess... */ 32572132Ssemenu mii->mii_media_active |= IFM_NONE; 32672132Ssemenu return; 32772132Ssemenu } 32872132Ssemenu diag = PHY_READ(sc, MII_ACPHY_DIAG) | 32972132Ssemenu PHY_READ(sc, MII_ACPHY_DIAG); 33072132Ssemenu if (diag & AC_DIAG_SPEED) 33172132Ssemenu mii->mii_media_active |= IFM_100_TX; 33272132Ssemenu else 33372132Ssemenu mii->mii_media_active |= IFM_10_T; 33472132Ssemenu 33572132Ssemenu if (diag & AC_DIAG_DUPLEX) 33672132Ssemenu mii->mii_media_active |= IFM_FDX; 33772132Ssemenu } else 33872132Ssemenu mii->mii_media_active = ife->ifm_media; 33972132Ssemenu} 34072132Ssemenu 34184145Sjlemonstatic void 34272132Ssemenuacphy_reset(sc) 34372132Ssemenu struct mii_softc *sc; 34472132Ssemenu{ 34572132Ssemenu 34672132Ssemenu mii_phy_reset(sc); 34772132Ssemenu PHY_WRITE(sc, MII_ACPHY_INT, 0); 34872132Ssemenu} 349