amphy.c revision 221407
1218885Sdim/*- 2218885Sdim * Copyright (c) 1997, 1998, 1999 3218885Sdim * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. 4218885Sdim * 5218885Sdim * Redistribution and use in source and binary forms, with or without 6218885Sdim * modification, are permitted provided that the following conditions 7218885Sdim * are met: 8218885Sdim * 1. Redistributions of source code must retain the above copyright 9218885Sdim * notice, this list of conditions and the following disclaimer. 10218885Sdim * 2. Redistributions in binary form must reproduce the above copyright 11218885Sdim * notice, this list of conditions and the following disclaimer in the 12218885Sdim * documentation and/or other materials provided with the distribution. 13218885Sdim * 3. All advertising materials mentioning features or use of this software 14218885Sdim * must display the following acknowledgement: 15218885Sdim * This product includes software developed by Bill Paul. 16218885Sdim * 4. Neither the name of the author nor the names of any co-contributors 17218885Sdim * may be used to endorse or promote products derived from this software 18218885Sdim * without specific prior written permission. 19218885Sdim * 20218885Sdim * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21218885Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22218885Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23218885Sdim * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24218885Sdim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25218885Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26218885Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27218885Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28218885Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29218885Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30218885Sdim * THE POSSIBILITY OF SUCH DAMAGE. 31218885Sdim */ 32218885Sdim 33218885Sdim#include <sys/cdefs.h> 34218885Sdim__FBSDID("$FreeBSD: head/sys/dev/mii/amphy.c 221407 2011-05-03 19:51:29Z marius $"); 35218885Sdim 36218885Sdim/* 37218885Sdim * driver for AMD AM79c873 PHYs 38218885Sdim * This driver also works for Davicom DM910{1,2} PHYs, which appear 39218885Sdim * to be AM79c873 workalikes. 40218885Sdim */ 41218885Sdim 42218885Sdim#include <sys/param.h> 43218885Sdim#include <sys/systm.h> 44218885Sdim#include <sys/kernel.h> 45218885Sdim#include <sys/module.h> 46218885Sdim#include <sys/socket.h> 47218885Sdim#include <sys/bus.h> 48218885Sdim 49218885Sdim#include <net/if.h> 50218885Sdim#include <net/if_media.h> 51218885Sdim 52218885Sdim#include <dev/mii/mii.h> 53218885Sdim#include <dev/mii/miivar.h> 54218885Sdim#include "miidevs.h" 55218885Sdim 56218885Sdim#include <dev/mii/amphyreg.h> 57218885Sdim 58218885Sdim#include "miibus_if.h" 59218885Sdim 60218885Sdimstatic int amphy_probe(device_t); 61218885Sdimstatic int amphy_attach(device_t); 62218885Sdim 63218885Sdimstatic device_method_t amphy_methods[] = { 64218885Sdim /* device interface */ 65218885Sdim DEVMETHOD(device_probe, amphy_probe), 66218885Sdim DEVMETHOD(device_attach, amphy_attach), 67218885Sdim DEVMETHOD(device_detach, mii_phy_detach), 68218885Sdim DEVMETHOD(device_shutdown, bus_generic_shutdown), 69218885Sdim { 0, 0 } 70221345Sdim}; 71218885Sdim 72218885Sdimstatic devclass_t amphy_devclass; 73218885Sdim 74221345Sdimstatic driver_t amphy_driver = { 75218885Sdim "amphy", 76218885Sdim amphy_methods, 77218885Sdim sizeof(struct mii_softc) 78218885Sdim}; 79218885Sdim 80218885SdimDRIVER_MODULE(amphy, miibus, amphy_driver, amphy_devclass, 0, 0); 81218885Sdim 82218885Sdimstatic int amphy_service(struct mii_softc *, struct mii_data *, int); 83218885Sdimstatic void amphy_status(struct mii_softc *); 84218885Sdim 85218885Sdimstatic const struct mii_phydesc amphys[] = { 86218885Sdim MII_PHY_DESC(xxDAVICOM, DM9102), 87218885Sdim MII_PHY_DESC(xxDAVICOM, DM9101), 88218885Sdim MII_PHY_DESC(yyDAVICOM, DM9101), 89218885Sdim MII_PHY_END 90218885Sdim}; 91218885Sdim 92218885Sdimstatic const struct mii_phy_funcs amphy_funcs = { 93218885Sdim amphy_service, 94218885Sdim amphy_status, 95218885Sdim mii_phy_reset 96218885Sdim}; 97218885Sdim 98218885Sdimstatic int 99218885Sdimamphy_probe(device_t dev) 100218885Sdim{ 101218885Sdim 102218885Sdim return (mii_phy_dev_probe(dev, amphys, BUS_PROBE_DEFAULT)); 103218885Sdim} 104218885Sdim 105218885Sdimstatic int 106218885Sdimamphy_attach(device_t dev) 107218885Sdim{ 108218885Sdim 109218885Sdim mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &hy_funcs, 1); 110221345Sdim return (0); 111218885Sdim} 112218885Sdim 113218885Sdimstatic int 114218885Sdimamphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 115218885Sdim{ 116218885Sdim 117218885Sdim switch (cmd) { 118218885Sdim case MII_POLLSTAT: 119218885Sdim break; 120218885Sdim 121218885Sdim case MII_MEDIACHG: 122218885Sdim /* 123218885Sdim * If the interface is not up, don't do anything. 124221345Sdim */ 125218885Sdim if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 126218885Sdim break; 127218885Sdim 128218885Sdim mii_phy_setmedia(sc); 129218885Sdim break; 130218885Sdim 131218885Sdim case MII_TICK: 132218885Sdim if (mii_phy_tick(sc) == EJUSTRETURN) 133218885Sdim return (0); 134218885Sdim break; 135218885Sdim } 136218885Sdim 137221345Sdim /* Update the media status. */ 138218885Sdim PHY_STATUS(sc); 139218885Sdim 140221345Sdim /* Callback if something changed. */ 141218885Sdim mii_phy_update(sc, cmd); 142218885Sdim return (0); 143218885Sdim} 144218885Sdim 145218885Sdimstatic void 146218885Sdimamphy_status(struct mii_softc *sc) 147218885Sdim{ 148218885Sdim struct mii_data *mii = sc->mii_pdata; 149218885Sdim struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 150218885Sdim int bmsr, bmcr, par, anlpar; 151218885Sdim 152218885Sdim mii->mii_media_status = IFM_AVALID; 153218885Sdim mii->mii_media_active = IFM_ETHER; 154218885Sdim 155218885Sdim bmsr = PHY_READ(sc, MII_BMSR) | 156218885Sdim PHY_READ(sc, MII_BMSR); 157218885Sdim if (bmsr & BMSR_LINK) 158218885Sdim mii->mii_media_status |= IFM_ACTIVE; 159218885Sdim 160218885Sdim bmcr = PHY_READ(sc, MII_BMCR); 161218885Sdim if (bmcr & BMCR_ISO) { 162218885Sdim mii->mii_media_active |= IFM_NONE; 163218885Sdim mii->mii_media_status = 0; 164218885Sdim return; 165218885Sdim } 166218885Sdim 167218885Sdim if (bmcr & BMCR_LOOP) 168218885Sdim mii->mii_media_active |= IFM_LOOP; 169218885Sdim 170218885Sdim if (bmcr & BMCR_AUTOEN) { 171218885Sdim /* 172218885Sdim * The PAR status bits are only valid if autonegotiation 173218885Sdim * has completed (or it's disabled). 174218885Sdim */ 175218885Sdim if ((bmsr & BMSR_ACOMP) == 0) { 176218885Sdim /* Erg, still trying, I guess... */ 177218885Sdim mii->mii_media_active |= IFM_NONE; 178218885Sdim return; 179221345Sdim } 180218885Sdim 181221345Sdim if (PHY_READ(sc, MII_ANER) & ANER_LPAN) { 182221345Sdim anlpar = PHY_READ(sc, MII_ANAR) & 183218885Sdim PHY_READ(sc, MII_ANLPAR); 184221345Sdim if (anlpar & ANLPAR_TX_FD) 185221345Sdim mii->mii_media_active |= IFM_100_TX|IFM_FDX; 186221345Sdim else if (anlpar & ANLPAR_T4) 187218885Sdim mii->mii_media_active |= IFM_100_T4|IFM_HDX; 188218885Sdim else if (anlpar & ANLPAR_TX) 189221345Sdim mii->mii_media_active |= IFM_100_TX|IFM_HDX; 190221345Sdim else if (anlpar & ANLPAR_10_FD) 191221345Sdim mii->mii_media_active |= IFM_10_T|IFM_FDX; 192221345Sdim else if (anlpar & ANLPAR_10) 193221345Sdim mii->mii_media_active |= IFM_10_T|IFM_HDX; 194221345Sdim else 195218885Sdim mii->mii_media_active |= IFM_NONE; 196218885Sdim return; 197218885Sdim } 198218885Sdim 199218885Sdim /* 200218885Sdim * Link partner is not capable of autonegotiation. 201218885Sdim */ 202218885Sdim par = PHY_READ(sc, MII_AMPHY_DSCSR); 203218885Sdim if (par & DSCSR_100FDX) 204218885Sdim mii->mii_media_active |= IFM_100_TX|IFM_FDX; 205218885Sdim else if (par & DSCSR_100HDX) 206218885Sdim mii->mii_media_active |= IFM_100_TX|IFM_HDX; 207218885Sdim else if (par & DSCSR_10FDX) 208218885Sdim mii->mii_media_active |= IFM_10_T|IFM_HDX; 209218885Sdim else if (par & DSCSR_10HDX) 210218885Sdim mii->mii_media_active |= IFM_10_T|IFM_HDX; 211218885Sdim if ((mii->mii_media_active & IFM_FDX) != 0) 212218885Sdim mii->mii_media_active |= mii_phy_flowstatus(sc); 213221345Sdim } else 214218885Sdim mii->mii_media_active = ife->ifm_media; 215221345Sdim} 216221345Sdim