inphy.c revision 111110
1317019Sdim/*- 2317019Sdim * Copyright (c) 2001 Jonathan Lemon 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms, with or without 6317019Sdim * modification, are permitted provided that the following conditions 7317019Sdim * are met: 8317019Sdim * 1. Redistributions of source code must retain the above copyright 9317019Sdim * notice, this list of conditions and the following disclaimer. 10317019Sdim * 2. Redistributions in binary form must reproduce the above copyright 11317019Sdim * notice, this list of conditions and the following disclaimer in the 12317019Sdim * documentation and/or other materials provided with the distribution. 13317019Sdim * 3. Neither the name of the author nor the names of any co-contributors 14317019Sdim * may be used to endorse or promote products derived from this software 15317019Sdim * without specific prior written permission. 16317019Sdim * 17317019Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18317019Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19317019Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20317019Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21317019Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22317019Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23317019Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24317019Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25317019Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26317019Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27341825Sdim * SUCH DAMAGE. 28317019Sdim * 29317019Sdim * $FreeBSD: head/sys/dev/mii/inphy.c 111110 2003-02-19 00:32:01Z peter $ 30317019Sdim */ 31317019Sdim 32317019Sdim/* 33317019Sdim * driver for Intel 82553 and 82555 PHYs 34317019Sdim */ 35317019Sdim 36317019Sdim#include <sys/param.h> 37317019Sdim#include <sys/systm.h> 38317019Sdim#include <sys/kernel.h> 39317019Sdim#include <sys/socket.h> 40317019Sdim#include <sys/bus.h> 41317019Sdim 42317019Sdim#include <net/if.h> 43317019Sdim#include <net/if_media.h> 44317019Sdim 45317019Sdim#include <dev/mii/mii.h> 46317019Sdim#include <dev/mii/miivar.h> 47317019Sdim#include "miidevs.h" 48317019Sdim 49317019Sdim#include <dev/mii/inphyreg.h> 50317019Sdim 51341825Sdim#include "miibus_if.h" 52317019Sdim 53317019Sdimstatic int inphy_probe(device_t dev); 54317019Sdimstatic int inphy_attach(device_t dev); 55317019Sdim 56317019Sdimstatic device_method_t inphy_methods[] = { 57317019Sdim /* device interface */ 58317019Sdim DEVMETHOD(device_probe, inphy_probe), 59317019Sdim DEVMETHOD(device_attach, inphy_attach), 60317019Sdim DEVMETHOD(device_detach, mii_phy_detach), 61317019Sdim DEVMETHOD(device_shutdown, bus_generic_shutdown), 62317019Sdim { 0, 0 } 63317019Sdim}; 64317019Sdim 65317019Sdimstatic devclass_t inphy_devclass; 66317019Sdim 67317019Sdimstatic driver_t inphy_driver = { 68317019Sdim "inphy", 69317019Sdim inphy_methods, 70317019Sdim sizeof(struct mii_softc) 71317019Sdim}; 72317019Sdim 73317019SdimDRIVER_MODULE(inphy, miibus, inphy_driver, inphy_devclass, 0, 0); 74317019Sdim 75317019Sdimstatic int inphy_service(struct mii_softc *, struct mii_data *, int); 76317019Sdimstatic void inphy_status(struct mii_softc *); 77317019Sdim 78317019Sdimstatic int 79317019Sdiminphy_probe(device_t dev) 80317019Sdim{ 81317019Sdim struct mii_attach_args *ma; 82317019Sdim 83317019Sdim ma = device_get_ivars(dev); 84317019Sdim 85317019Sdim /* Intel 82553 A/B steppings */ 86341825Sdim if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxINTEL && 87317019Sdim MII_MODEL(ma->mii_id2) == MII_MODEL_xxINTEL_I82553AB) { 88317019Sdim device_set_desc(dev, MII_STR_xxINTEL_I82553AB); 89317019Sdim return (0); 90317019Sdim } 91317019Sdim 92317019Sdim if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_INTEL) { 93317019Sdim switch (MII_MODEL(ma->mii_id2)) { 94317019Sdim case MII_MODEL_INTEL_I82555: 95317019Sdim device_set_desc(dev, MII_STR_INTEL_I82555); 96317019Sdim return (0); 97317019Sdim case MII_MODEL_INTEL_I82553C: 98317019Sdim device_set_desc(dev, MII_STR_INTEL_I82553C); 99317019Sdim return (0); 100317019Sdim case MII_MODEL_INTEL_I82562EM: 101317019Sdim device_set_desc(dev, MII_STR_INTEL_I82562EM); 102317019Sdim return (0); 103317019Sdim case MII_MODEL_INTEL_I82562ET: 104317019Sdim device_set_desc(dev, MII_STR_INTEL_I82562ET); 105317019Sdim return (0); 106317019Sdim } 107317019Sdim } 108317019Sdim 109317019Sdim return (ENXIO); 110317019Sdim} 111317019Sdim 112317019Sdimstatic int 113317019Sdiminphy_attach(device_t dev) 114317019Sdim{ 115317019Sdim struct mii_softc *sc; 116317019Sdim struct mii_attach_args *ma; 117317019Sdim struct mii_data *mii; 118317019Sdim 119317019Sdim sc = device_get_softc(dev); 120317019Sdim ma = device_get_ivars(dev); 121317019Sdim sc->mii_dev = device_get_parent(dev); 122317019Sdim mii = device_get_softc(sc->mii_dev); 123317019Sdim LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 124317019Sdim 125317019Sdim sc->mii_inst = mii->mii_instance; 126317019Sdim sc->mii_phy = ma->mii_phyno; 127317019Sdim sc->mii_service = inphy_service; 128317019Sdim sc->mii_pdata = mii; 129317019Sdim mii->mii_instance++; 130317019Sdim 131317019Sdim#if 0 132317019Sdim sc->mii_flags |= MIIF_NOISOLATE; 133317019Sdim#endif 134317019Sdim 135317019Sdim ifmedia_add(&mii->mii_media, 136317019Sdim IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 137317019Sdim BMCR_LOOP|BMCR_S100, NULL); 138317019Sdim 139317019Sdim mii_phy_reset(sc); 140317019Sdim 141317019Sdim sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 142317019Sdim device_printf(dev, " "); 143317019Sdim mii_phy_add_media(sc); 144317019Sdim printf("\n"); 145317019Sdim 146317019Sdim MIIBUS_MEDIAINIT(sc->mii_dev); 147317019Sdim 148317019Sdim return (0); 149317019Sdim} 150317019Sdim 151317019Sdimstatic int 152317019Sdiminphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 153317019Sdim{ 154317019Sdim struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 155317019Sdim int reg; 156317019Sdim 157317019Sdim switch (cmd) { 158317019Sdim case MII_POLLSTAT: 159317019Sdim if (IFM_INST(ife->ifm_media) != sc->mii_inst) 160317019Sdim return (0); 161317019Sdim break; 162317019Sdim 163317019Sdim case MII_MEDIACHG: 164317019Sdim if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 165317019Sdim reg = PHY_READ(sc, MII_BMCR); 166317019Sdim PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 167317019Sdim return (0); 168317019Sdim } 169317019Sdim 170317019Sdim /* 171317019Sdim * If the interface is not up, don't do anything. 172317019Sdim */ 173317019Sdim if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 174317019Sdim break; 175317019Sdim 176317019Sdim mii_phy_setmedia(sc); 177317019Sdim break; 178317019Sdim 179317019Sdim case MII_TICK: 180317019Sdim if (IFM_INST(ife->ifm_media) != sc->mii_inst) 181317019Sdim return (0); 182317019Sdim if (mii_phy_tick(sc) == EJUSTRETURN) 183317019Sdim return (0); 184317019Sdim break; 185317019Sdim } 186317019Sdim 187317019Sdim /* Update the media status. */ 188317019Sdim inphy_status(sc); 189317019Sdim 190317019Sdim /* Callback if something changed. */ 191317019Sdim mii_phy_update(sc, cmd); 192317019Sdim return (0); 193317019Sdim} 194317019Sdim 195317019Sdimstatic void 196317019Sdiminphy_status(struct mii_softc *sc) 197317019Sdim{ 198317019Sdim struct mii_data *mii = sc->mii_pdata; 199317019Sdim struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 200317019Sdim int bmsr, bmcr, scr; 201317019Sdim 202317019Sdim mii->mii_media_status = IFM_AVALID; 203317019Sdim mii->mii_media_active = IFM_ETHER; 204327952Sdim 205317019Sdim bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 206317019Sdim if (bmsr & BMSR_LINK) 207317019Sdim mii->mii_media_status |= IFM_ACTIVE; 208317019Sdim 209317019Sdim bmcr = PHY_READ(sc, MII_BMCR); 210317019Sdim if (bmcr & BMCR_ISO) { 211317019Sdim mii->mii_media_active |= IFM_NONE; 212317019Sdim mii->mii_media_status = 0; 213317019Sdim return; 214317019Sdim } 215317019Sdim 216317019Sdim if (bmcr & BMCR_LOOP) 217317019Sdim mii->mii_media_active |= IFM_LOOP; 218317019Sdim 219317019Sdim if (bmcr & BMCR_AUTOEN) { 220317019Sdim if ((bmsr & BMSR_ACOMP) == 0) { 221317019Sdim mii->mii_media_active |= IFM_NONE; 222317019Sdim return; 223317019Sdim } 224317019Sdim 225317019Sdim scr = PHY_READ(sc, MII_INPHY_SCR); 226317019Sdim if (scr & SCR_S100) 227317019Sdim mii->mii_media_active |= IFM_100_TX; 228317019Sdim else 229317019Sdim mii->mii_media_active |= IFM_10_T; 230317019Sdim if (scr & SCR_FDX) 231317019Sdim mii->mii_media_active |= IFM_FDX; 232317019Sdim } else 233317019Sdim mii->mii_media_active = ife->ifm_media; 234317019Sdim} 235317019Sdim