inphy.c revision 76483
1251881Speter/*- 2251881Speter * Copyright (c) 2001 Jonathan Lemon 3251881Speter * All rights reserved. 4251881Speter * 5251881Speter * Redistribution and use in source and binary forms, with or without 6251881Speter * modification, are permitted provided that the following conditions 7251881Speter * are met: 8251881Speter * 1. Redistributions of source code must retain the above copyright 9251881Speter * notice, this list of conditions and the following disclaimer. 10251881Speter * 2. Redistributions in binary form must reproduce the above copyright 11251881Speter * notice, this list of conditions and the following disclaimer in the 12251881Speter * documentation and/or other materials provided with the distribution. 13251881Speter * 3. Neither the name of the author nor the names of any co-contributors 14251881Speter * may be used to endorse or promote products derived from this software 15251881Speter * without specific prior written permission. 16251881Speter * 17251881Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20251881Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27251881Speter * SUCH DAMAGE. 28251881Speter * 29251881Speter * $FreeBSD: head/sys/dev/mii/inphy.c 76483 2001-05-11 20:34:38Z jlemon $ 30251881Speter */ 31251881Speter 32251881Speter/* 33251881Speter * driver for Intel 82553 and 82555 PHYs 34251881Speter */ 35251881Speter 36251881Speter#include <sys/param.h> 37251881Speter#include <sys/systm.h> 38251881Speter#include <sys/kernel.h> 39251881Speter#include <sys/malloc.h> 40251881Speter#include <sys/socket.h> 41251881Speter#include <sys/bus.h> 42251881Speter 43251881Speter#include <net/if.h> 44251881Speter#include <net/if_media.h> 45251881Speter 46251881Speter#include <dev/mii/mii.h> 47251881Speter#include <dev/mii/miivar.h> 48251881Speter#include <dev/mii/miidevs.h> 49251881Speter 50251881Speter#include <dev/mii/inphyreg.h> 51251881Speter 52251881Speter#include "miibus_if.h" 53251881Speter 54251881Speterstatic int inphy_probe(device_t dev); 55251881Speterstatic int inphy_attach(device_t dev); 56251881Speterstatic int inphy_detach(device_t dev); 57251881Speter 58251881Speterstatic device_method_t inphy_methods[] = { 59251881Speter /* device interface */ 60251881Speter DEVMETHOD(device_probe, inphy_probe), 61251881Speter DEVMETHOD(device_attach, inphy_attach), 62251881Speter DEVMETHOD(device_detach, inphy_detach), 63251881Speter DEVMETHOD(device_shutdown, bus_generic_shutdown), 64251881Speter { 0, 0 } 65251881Speter}; 66251881Speter 67251881Speterstatic devclass_t inphy_devclass; 68251881Speter 69251881Speterstatic driver_t inphy_driver = { 70251881Speter "inphy", 71251881Speter inphy_methods, 72251881Speter sizeof(struct mii_softc) 73251881Speter}; 74251881Speter 75251881SpeterDRIVER_MODULE(inphy, miibus, inphy_driver, inphy_devclass, 0, 0); 76251881Speter 77251881Speterint inphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd); 78251881Spetervoid inphy_status(struct mii_softc *sc); 79251881Speter 80251881Speter 81251881Speterstatic int 82251881Speterinphy_probe(device_t dev) 83251881Speter{ 84251881Speter struct mii_attach_args *ma; 85251881Speter device_t parent; 86251881Speter 87251881Speter ma = device_get_ivars(dev); 88251881Speter parent = device_get_parent(device_get_parent(dev)); 89251881Speter 90251881Speter /* Intel 82553 A/B steppings */ 91251881Speter if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxINTEL && 92251881Speter MII_MODEL(ma->mii_id2) == MII_MODEL_xxINTEL_I82553AB) { 93251881Speter device_set_desc(dev, MII_STR_xxINTEL_I82553AB); 94251881Speter return (0); 95251881Speter } 96251881Speter 97251881Speter if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_INTEL) { 98251881Speter switch (MII_MODEL(ma->mii_id2)) { 99251881Speter case MII_MODEL_INTEL_I82555: 100251881Speter device_set_desc(dev, MII_STR_INTEL_I82555); 101251881Speter return (0); 102251881Speter case MII_MODEL_INTEL_I82553C: 103251881Speter device_set_desc(dev, MII_STR_INTEL_I82553C); 104251881Speter return (0); 105251881Speter case MII_MODEL_INTEL_I82562EM: 106251881Speter device_set_desc(dev, MII_STR_INTEL_I82562EM); 107251881Speter return (0); 108251881Speter case MII_MODEL_INTEL_I82562ET: 109251881Speter device_set_desc(dev, MII_STR_INTEL_I82562ET); 110251881Speter return (0); 111251881Speter } 112251881Speter } 113251881Speter 114251881Speter return (ENXIO); 115251881Speter} 116251881Speter 117251881Speterstatic int 118251881Speterinphy_attach(device_t dev) 119251881Speter{ 120251881Speter struct mii_softc *sc; 121251881Speter struct mii_attach_args *ma; 122251881Speter struct mii_data *mii; 123251881Speter 124251881Speter sc = device_get_softc(dev); 125251881Speter ma = device_get_ivars(dev); 126251881Speter sc->mii_dev = device_get_parent(dev); 127251881Speter mii = device_get_softc(sc->mii_dev); 128251881Speter LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 129251881Speter 130251881Speter sc->mii_inst = mii->mii_instance; 131251881Speter sc->mii_phy = ma->mii_phyno; 132251881Speter sc->mii_service = inphy_service; 133251881Speter sc->mii_pdata = mii; 134251881Speter mii->mii_instance++; 135251881Speter 136251881Speter#if 0 137251881Speter sc->mii_flags |= MIIF_NOISOLATE; 138251881Speter#endif 139251881Speter 140251881Speter ifmedia_add(&mii->mii_media, 141251881Speter IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 142251881Speter BMCR_LOOP|BMCR_S100, NULL); 143251881Speter 144251881Speter mii_phy_reset(sc); 145251881Speter 146251881Speter sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 147251881Speter device_printf(dev, " "); 148251881Speter if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) 149251881Speter printf("no media present"); 150251881Speter else 151251881Speter mii_add_media(mii, sc->mii_capabilities, sc->mii_inst); 152251881Speter printf("\n"); 153251881Speter 154251881Speter MIIBUS_MEDIAINIT(sc->mii_dev); 155251881Speter 156251881Speter return (0); 157251881Speter} 158251881Speter 159251881Speterstatic int 160251881Speterinphy_detach(device_t dev) 161251881Speter{ 162251881Speter struct mii_softc *sc; 163251881Speter struct mii_data *mii; 164251881Speter 165251881Speter sc = device_get_softc(dev); 166251881Speter mii = device_get_softc(device_get_softc(dev)); 167251881Speter mii_phy_auto_stop(sc); 168251881Speter sc->mii_dev = NULL; 169251881Speter LIST_REMOVE(sc, mii_list); 170251881Speter 171251881Speter return (0); 172251881Speter} 173251881Speter 174251881Speterint 175251881Speterinphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 176251881Speter{ 177251881Speter struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 178251881Speter int reg; 179251881Speter 180251881Speter switch (cmd) { 181251881Speter case MII_POLLSTAT: 182251881Speter if (IFM_INST(ife->ifm_media) != sc->mii_inst) 183251881Speter return (0); 184251881Speter break; 185251881Speter 186251881Speter case MII_MEDIACHG: 187251881Speter if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 188251881Speter reg = PHY_READ(sc, MII_BMCR); 189251881Speter PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 190251881Speter return (0); 191251881Speter } 192251881Speter 193251881Speter /* 194251881Speter * If the interface is not up, don't do anything. 195251881Speter */ 196251881Speter if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 197251881Speter break; 198251881Speter 199251881Speter switch (IFM_SUBTYPE(ife->ifm_media)) { 200251881Speter case IFM_AUTO: 201251881Speter /* 202251881Speter * If we're already in auto mode, just return. 203251881Speter */ 204251881Speter if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) 205251881Speter return (0); 206251881Speter (void) mii_phy_auto(sc, 0); 207251881Speter break; 208251881Speter case IFM_100_T4: 209251881Speter /* 210251881Speter * XXX Not supported as a manual setting right now. 211251881Speter */ 212251881Speter return (EINVAL); 213251881Speter default: 214251881Speter /* 215251881Speter * BMCR data is stored in the ifmedia entry. 216251881Speter */ 217251881Speter PHY_WRITE(sc, MII_ANAR, mii_anar(ife->ifm_media)); 218251881Speter PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 219251881Speter } 220251881Speter break; 221251881Speter 222251881Speter case MII_TICK: 223251881Speter if (IFM_INST(ife->ifm_media) != sc->mii_inst) 224251881Speter return (0); 225251881Speter 226251881Speter /* 227251881Speter * Is the interface even up? 228251881Speter */ 229251881Speter if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 230251881Speter return (0); 231251881Speter 232251881Speter /* 233251881Speter * Only used for autonegotiation. 234251881Speter */ 235251881Speter if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 236251881Speter return (0); 237251881Speter 238251881Speter /* 239251881Speter * check for link. 240251881Speter * Read the status register twice; BMSR_LINK is latch-low. 241251881Speter */ 242251881Speter reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 243251881Speter if (reg & BMSR_LINK) 244251881Speter return (0); 245251881Speter 246251881Speter /* 247251881Speter * Only retry autonegotiation every 5 seconds. 248251881Speter */ 249251881Speter if (++sc->mii_ticks != 5) 250251881Speter return (0); 251251881Speter 252251881Speter sc->mii_ticks = 0; 253251881Speter mii_phy_reset(sc); 254251881Speter if (mii_phy_auto(sc, 0) == EJUSTRETURN) 255251881Speter return (0); 256251881Speter break; 257251881Speter } 258251881Speter 259251881Speter /* Update the media status. */ 260251881Speter inphy_status(sc); 261251881Speter 262251881Speter /* Callback if something changed. */ 263251881Speter if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { 264251881Speter MIIBUS_STATCHG(sc->mii_dev); 265251881Speter sc->mii_active = mii->mii_media_active; 266251881Speter } 267251881Speter return (0); 268251881Speter} 269251881Speter 270251881Spetervoid 271251881Speterinphy_status(struct mii_softc *sc) 272251881Speter{ 273251881Speter struct mii_data *mii = sc->mii_pdata; 274251881Speter int bmsr, bmcr, scr; 275251881Speter 276251881Speter mii->mii_media_status = IFM_AVALID; 277251881Speter mii->mii_media_active = IFM_ETHER; 278251881Speter 279251881Speter bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 280251881Speter if (bmsr & BMSR_LINK) 281251881Speter mii->mii_media_status |= IFM_ACTIVE; 282251881Speter 283251881Speter bmcr = PHY_READ(sc, MII_BMCR); 284251881Speter if (bmcr & BMCR_ISO) { 285251881Speter mii->mii_media_active |= IFM_NONE; 286251881Speter mii->mii_media_status = 0; 287251881Speter return; 288251881Speter } 289251881Speter 290251881Speter if (bmcr & BMCR_LOOP) 291251881Speter mii->mii_media_active |= IFM_LOOP; 292251881Speter 293251881Speter if (bmcr & BMCR_AUTOEN) { 294251881Speter if ((bmsr & BMSR_ACOMP) == 0) { 295251881Speter mii->mii_media_active |= IFM_NONE; 296251881Speter return; 297251881Speter } 298251881Speter 299251881Speter scr = PHY_READ(sc, MII_INPHY_SCR); 300251881Speter if (scr & SCR_S100) 301251881Speter mii->mii_media_active |= IFM_100_TX; 302251881Speter else 303251881Speter mii->mii_media_active |= IFM_10_T; 304251881Speter if (scr & SCR_FDX) 305251881Speter mii->mii_media_active |= IFM_FDX; 306251881Speter } else 307251881Speter mii->mii_media_active |= mii_media_from_bmcr(bmcr); 308251881Speter} 309251881Speter