smcphy.c revision 214262
1179592Sbenno/*- 2179592Sbenno * Copyright (c) 2006 Benno Rice. All rights reserved. 3179592Sbenno * 4179592Sbenno * Redistribution and use in source and binary forms, with or without 5179592Sbenno * modification, are permitted provided that the following conditions 6179592Sbenno * are met: 7179592Sbenno * 1. Redistributions of source code must retain the above copyright 8179592Sbenno * notice, this list of conditions and the following disclaimer. 9179592Sbenno * 2. Redistributions in binary form must reproduce the above copyright 10179592Sbenno * notice, this list of conditions and the following disclaimer in the 11179592Sbenno * documentation and/or other materials provided with the distribution. 12179592Sbenno * 13179592Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14179592Sbenno * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15179592Sbenno * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16179592Sbenno * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17179592Sbenno * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18179592Sbenno * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19179592Sbenno * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20179592Sbenno * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21179592Sbenno * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22179592Sbenno * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23179592Sbenno */ 24179592Sbenno 25179592Sbenno#include <sys/cdefs.h> 26179592Sbenno__FBSDID("$FreeBSD: head/sys/dev/mii/smcphy.c 214262 2010-10-24 11:37:01Z marius $"); 27179592Sbenno 28179592Sbenno/* 29179592Sbenno * Driver for the internal PHY on the SMSC LAN91C111. 30179592Sbenno */ 31179592Sbenno 32179592Sbenno#include <sys/param.h> 33179592Sbenno#include <sys/systm.h> 34179592Sbenno#include <sys/kernel.h> 35179592Sbenno#include <sys/socket.h> 36179592Sbenno#include <sys/errno.h> 37179592Sbenno#include <sys/module.h> 38179592Sbenno#include <sys/bus.h> 39179592Sbenno#include <sys/malloc.h> 40179592Sbenno 41179592Sbenno#include <machine/bus.h> 42179592Sbenno 43179592Sbenno#include <net/if.h> 44179592Sbenno#include <net/if_media.h> 45179592Sbenno 46179592Sbenno#include <dev/mii/mii.h> 47179592Sbenno#include <dev/mii/miivar.h> 48179592Sbenno#include "miidevs.h" 49179592Sbenno 50179592Sbenno#include "miibus_if.h" 51179592Sbenno 52179592Sbennostatic int smcphy_probe(device_t); 53179592Sbennostatic int smcphy_attach(device_t); 54179592Sbenno 55179592Sbennostatic int smcphy_service(struct mii_softc *, struct mii_data *, int); 56179592Sbennostatic int smcphy_reset(struct mii_softc *); 57179592Sbennostatic void smcphy_auto(struct mii_softc *); 58179592Sbenno 59179592Sbennostatic device_method_t smcphy_methods[] = { 60179592Sbenno /* device interface */ 61179592Sbenno DEVMETHOD(device_probe, smcphy_probe), 62179592Sbenno DEVMETHOD(device_attach, smcphy_attach), 63179592Sbenno DEVMETHOD(device_detach, mii_phy_detach), 64179592Sbenno DEVMETHOD(device_shutdown, bus_generic_shutdown), 65179592Sbenno 66179592Sbenno { 0, 0 } 67179592Sbenno}; 68179592Sbenno 69179592Sbennostatic devclass_t smcphy_devclass; 70179592Sbenno 71179592Sbennostatic driver_t smcphy_driver = { 72179592Sbenno "smcphy", 73179592Sbenno smcphy_methods, 74179592Sbenno sizeof(struct mii_softc) 75179592Sbenno}; 76179592Sbenno 77179592SbennoDRIVER_MODULE(smcphy, miibus, smcphy_driver, smcphy_devclass, 0, 0); 78179592Sbenno 79214262Smariusstatic const struct mii_phydesc smcphys[] = { 80214262Smarius MII_PHY_DESC(SMSC, LAN83C183), 81214262Smarius MII_PHY_END 82214262Smarius}; 83214262Smarius 84179592Sbennostatic int 85179592Sbennosmcphy_probe(device_t dev) 86179592Sbenno{ 87179592Sbenno 88214262Smarius return (mii_phy_dev_probe(dev, smcphys, BUS_PROBE_DEFAULT)); 89179592Sbenno} 90179592Sbenno 91179592Sbennostatic int 92179592Sbennosmcphy_attach(device_t dev) 93179592Sbenno{ 94179592Sbenno struct mii_softc *sc; 95179592Sbenno struct mii_attach_args *ma; 96179592Sbenno struct mii_data *mii; 97179592Sbenno 98179592Sbenno sc = device_get_softc(dev); 99179592Sbenno ma = device_get_ivars(dev); 100179592Sbenno sc->mii_dev = device_get_parent(dev); 101213229Smarius mii = ma->mii_data; 102179592Sbenno LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 103179592Sbenno 104213893Smarius sc->mii_flags = miibus_get_flags(dev); 105213364Smarius sc->mii_inst = mii->mii_instance++; 106179592Sbenno sc->mii_phy = ma->mii_phyno; 107179592Sbenno sc->mii_service = smcphy_service; 108179592Sbenno sc->mii_pdata = mii; 109179592Sbenno 110214262Smarius sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP; 111179592Sbenno 112179592Sbenno if (smcphy_reset(sc) != 0) { 113179592Sbenno device_printf(dev, "reset failed\n"); 114179592Sbenno } 115179592Sbenno 116179592Sbenno /* Mask interrupts, we poll instead. */ 117179592Sbenno PHY_WRITE(sc, 0x1e, 0xffc0); 118179592Sbenno 119179592Sbenno sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 120179592Sbenno device_printf(dev, " "); 121214262Smarius mii_phy_add_media(sc); 122179592Sbenno printf("\n"); 123179592Sbenno 124179592Sbenno MIIBUS_MEDIAINIT(sc->mii_dev); 125179592Sbenno mii_phy_setmedia(sc); 126179592Sbenno 127179592Sbenno return (0); 128179592Sbenno} 129179592Sbenno 130179592Sbennostatic int 131179592Sbennosmcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 132179592Sbenno{ 133179592Sbenno struct ifmedia_entry *ife; 134179592Sbenno int reg; 135179592Sbenno 136179592Sbenno ife = mii->mii_media.ifm_cur; 137179592Sbenno 138179592Sbenno switch (cmd) { 139179592Sbenno case MII_POLLSTAT: 140179592Sbenno break; 141179592Sbenno 142179592Sbenno case MII_MEDIACHG: 143179592Sbenno /* 144179592Sbenno * If the interface is not up, don't do anything. 145179592Sbenno */ 146179592Sbenno if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 147179592Sbenno break; 148179592Sbenno 149179592Sbenno switch (IFM_SUBTYPE(ife->ifm_media)) { 150179592Sbenno case IFM_AUTO: 151179592Sbenno smcphy_auto(sc); 152179592Sbenno break; 153179592Sbenno 154179592Sbenno default: 155179592Sbenno mii_phy_setmedia(sc); 156179592Sbenno break; 157179592Sbenno } 158179592Sbenno 159179592Sbenno break; 160179592Sbenno 161179592Sbenno case MII_TICK: 162179592Sbenno if ((mii->mii_ifp->if_flags & IFF_UP) == 0) { 163179592Sbenno return (0); 164179592Sbenno } 165179592Sbenno 166179592Sbenno if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 167179592Sbenno break; 168179592Sbenno } 169179592Sbenno 170179592Sbenno /* I have no idea why BMCR_ISO gets set. */ 171179592Sbenno reg = PHY_READ(sc, MII_BMCR); 172179592Sbenno if (reg & BMCR_ISO) { 173179592Sbenno PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO); 174179592Sbenno } 175179592Sbenno 176179592Sbenno reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 177179592Sbenno if (reg & BMSR_LINK) { 178179592Sbenno sc->mii_ticks = 0; 179179592Sbenno break; 180179592Sbenno } 181179592Sbenno 182179592Sbenno if (++sc->mii_ticks <= MII_ANEGTICKS) { 183179592Sbenno break; 184179592Sbenno } 185179592Sbenno 186179592Sbenno sc->mii_ticks = 0; 187179592Sbenno if (smcphy_reset(sc) != 0) { 188179592Sbenno device_printf(sc->mii_dev, "reset failed\n"); 189179592Sbenno } 190179592Sbenno smcphy_auto(sc); 191179592Sbenno break; 192179592Sbenno } 193179592Sbenno 194179592Sbenno /* Update the media status. */ 195179592Sbenno ukphy_status(sc); 196179592Sbenno 197179592Sbenno /* Callback if something changed. */ 198179592Sbenno mii_phy_update(sc, cmd); 199179592Sbenno return (0); 200179592Sbenno} 201179592Sbenno 202179592Sbennostatic int 203179592Sbennosmcphy_reset(struct mii_softc *sc) 204179592Sbenno{ 205179592Sbenno u_int bmcr; 206179592Sbenno int timeout; 207179592Sbenno 208179592Sbenno PHY_WRITE(sc, MII_BMCR, BMCR_RESET); 209179592Sbenno 210179592Sbenno for (timeout = 2; timeout > 0; timeout--) { 211179592Sbenno DELAY(50000); 212179592Sbenno bmcr = PHY_READ(sc, MII_BMCR); 213179592Sbenno if ((bmcr & BMCR_RESET) == 0) 214179592Sbenno break; 215179592Sbenno } 216179592Sbenno 217179592Sbenno if (bmcr & BMCR_RESET) { 218179592Sbenno return (EIO); 219179592Sbenno } 220179592Sbenno 221179592Sbenno PHY_WRITE(sc, MII_BMCR, 0x3000); 222179592Sbenno return (0); 223179592Sbenno} 224179592Sbenno 225179592Sbennostatic void 226179592Sbennosmcphy_auto(struct mii_softc *sc) 227179592Sbenno{ 228179592Sbenno uint16_t anar; 229179592Sbenno 230179592Sbenno anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | 231179592Sbenno ANAR_CSMA; 232179592Sbenno if (sc->mii_flags & MIIF_DOPAUSE) 233179592Sbenno anar |= ANAR_FC; 234179592Sbenno PHY_WRITE(sc, MII_ANAR, anar); 235179592Sbenno /* Apparently this helps. */ 236179592Sbenno anar = PHY_READ(sc, MII_ANAR); 237179592Sbenno PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 238179592Sbenno} 239