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$"); 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); 56221407Smariusstatic void smcphy_reset(struct mii_softc *); 57215297Smariusstatic void smcphy_auto(struct mii_softc *, int); 58233485Syongaristatic void smcphy_status(struct mii_softc *); 59179592Sbenno 60179592Sbennostatic device_method_t smcphy_methods[] = { 61179592Sbenno /* device interface */ 62179592Sbenno DEVMETHOD(device_probe, smcphy_probe), 63179592Sbenno DEVMETHOD(device_attach, smcphy_attach), 64179592Sbenno DEVMETHOD(device_detach, mii_phy_detach), 65179592Sbenno DEVMETHOD(device_shutdown, bus_generic_shutdown), 66229093Shselasky DEVMETHOD_END 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[] = { 80233485Syongari MII_PHY_DESC(SEEQ, 80220), 81221407Smarius MII_PHY_DESC(SEEQ, 84220), 82214262Smarius MII_PHY_END 83214262Smarius}; 84214262Smarius 85233485Syongaristatic const struct mii_phy_funcs smcphy80220_funcs = { 86233485Syongari smcphy_service, 87233485Syongari smcphy_status, 88233485Syongari mii_phy_reset 89233485Syongari}; 90233485Syongari 91221407Smariusstatic const struct mii_phy_funcs smcphy_funcs = { 92221407Smarius smcphy_service, 93233485Syongari smcphy_status, 94221407Smarius smcphy_reset 95221407Smarius}; 96221407Smarius 97179592Sbennostatic int 98179592Sbennosmcphy_probe(device_t dev) 99179592Sbenno{ 100179592Sbenno 101214262Smarius return (mii_phy_dev_probe(dev, smcphys, BUS_PROBE_DEFAULT)); 102179592Sbenno} 103179592Sbenno 104179592Sbennostatic int 105179592Sbennosmcphy_attach(device_t dev) 106179592Sbenno{ 107221407Smarius struct mii_softc *sc; 108233485Syongari struct mii_attach_args *ma; 109233485Syongari const struct mii_phy_funcs *mpf; 110179592Sbenno 111179592Sbenno sc = device_get_softc(dev); 112233485Syongari ma = device_get_ivars(dev); 113233485Syongari if (MII_MODEL(ma->mii_id2) == MII_MODEL_SEEQ_80220) 114233485Syongari mpf = &smcphy80220_funcs; 115233485Syongari else 116233485Syongari mpf = &smcphy_funcs; 117233485Syongari mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, mpf, 1); 118179592Sbenno mii_phy_setmedia(sc); 119179592Sbenno 120179592Sbenno return (0); 121179592Sbenno} 122179592Sbenno 123179592Sbennostatic int 124179592Sbennosmcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 125179592Sbenno{ 126179592Sbenno struct ifmedia_entry *ife; 127179592Sbenno int reg; 128179592Sbenno 129179592Sbenno ife = mii->mii_media.ifm_cur; 130179592Sbenno 131179592Sbenno switch (cmd) { 132179592Sbenno case MII_POLLSTAT: 133179592Sbenno break; 134179592Sbenno 135179592Sbenno case MII_MEDIACHG: 136179592Sbenno /* 137179592Sbenno * If the interface is not up, don't do anything. 138179592Sbenno */ 139179592Sbenno if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 140179592Sbenno break; 141179592Sbenno 142179592Sbenno switch (IFM_SUBTYPE(ife->ifm_media)) { 143179592Sbenno case IFM_AUTO: 144215297Smarius smcphy_auto(sc, ife->ifm_media); 145179592Sbenno break; 146179592Sbenno 147179592Sbenno default: 148179592Sbenno mii_phy_setmedia(sc); 149179592Sbenno break; 150179592Sbenno } 151179592Sbenno 152179592Sbenno break; 153179592Sbenno 154179592Sbenno case MII_TICK: 155179592Sbenno if ((mii->mii_ifp->if_flags & IFF_UP) == 0) { 156179592Sbenno return (0); 157179592Sbenno } 158179592Sbenno 159179592Sbenno if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 160179592Sbenno break; 161179592Sbenno } 162179592Sbenno 163179592Sbenno /* I have no idea why BMCR_ISO gets set. */ 164179592Sbenno reg = PHY_READ(sc, MII_BMCR); 165179592Sbenno if (reg & BMCR_ISO) { 166179592Sbenno PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO); 167179592Sbenno } 168179592Sbenno 169179592Sbenno reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 170179592Sbenno if (reg & BMSR_LINK) { 171179592Sbenno sc->mii_ticks = 0; 172179592Sbenno break; 173179592Sbenno } 174179592Sbenno 175179592Sbenno if (++sc->mii_ticks <= MII_ANEGTICKS) { 176179592Sbenno break; 177179592Sbenno } 178179592Sbenno 179179592Sbenno sc->mii_ticks = 0; 180221407Smarius PHY_RESET(sc); 181215297Smarius smcphy_auto(sc, ife->ifm_media); 182179592Sbenno break; 183179592Sbenno } 184179592Sbenno 185179592Sbenno /* Update the media status. */ 186221407Smarius PHY_STATUS(sc); 187179592Sbenno 188179592Sbenno /* Callback if something changed. */ 189179592Sbenno mii_phy_update(sc, cmd); 190179592Sbenno return (0); 191179592Sbenno} 192179592Sbenno 193221407Smariusstatic void 194179592Sbennosmcphy_reset(struct mii_softc *sc) 195179592Sbenno{ 196179592Sbenno u_int bmcr; 197179592Sbenno int timeout; 198179592Sbenno 199179592Sbenno PHY_WRITE(sc, MII_BMCR, BMCR_RESET); 200179592Sbenno 201179592Sbenno for (timeout = 2; timeout > 0; timeout--) { 202179592Sbenno DELAY(50000); 203179592Sbenno bmcr = PHY_READ(sc, MII_BMCR); 204179592Sbenno if ((bmcr & BMCR_RESET) == 0) 205179592Sbenno break; 206179592Sbenno } 207179592Sbenno 208221407Smarius if (bmcr & BMCR_RESET) 209221407Smarius device_printf(sc->mii_dev, "reset failed\n"); 210179592Sbenno 211179592Sbenno PHY_WRITE(sc, MII_BMCR, 0x3000); 212221407Smarius 213221407Smarius /* Mask interrupts, we poll instead. */ 214221407Smarius PHY_WRITE(sc, 0x1e, 0xffc0); 215179592Sbenno} 216179592Sbenno 217179592Sbennostatic void 218215297Smariussmcphy_auto(struct mii_softc *sc, int media) 219179592Sbenno{ 220179592Sbenno uint16_t anar; 221179592Sbenno 222229093Shselasky anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; 223215297Smarius if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 224179592Sbenno anar |= ANAR_FC; 225179592Sbenno PHY_WRITE(sc, MII_ANAR, anar); 226179592Sbenno /* Apparently this helps. */ 227179592Sbenno anar = PHY_READ(sc, MII_ANAR); 228179592Sbenno PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 229179592Sbenno} 230233485Syongari 231233485Syongaristatic void 232233485Syongarismcphy_status(struct mii_softc *sc) 233233485Syongari{ 234233485Syongari struct mii_data *mii; 235233485Syongari uint32_t bmcr, bmsr, status; 236233485Syongari 237233485Syongari mii = sc->mii_pdata; 238233485Syongari mii->mii_media_status = IFM_AVALID; 239233485Syongari mii->mii_media_active = IFM_ETHER; 240233485Syongari 241233485Syongari bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 242233485Syongari if ((bmsr & BMSR_LINK) != 0) 243233485Syongari mii->mii_media_status |= IFM_ACTIVE; 244233485Syongari 245233485Syongari bmcr = PHY_READ(sc, MII_BMCR); 246233485Syongari if ((bmcr & BMCR_ISO) != 0) { 247233485Syongari mii->mii_media_active |= IFM_NONE; 248233485Syongari mii->mii_media_status = 0; 249233485Syongari return; 250233485Syongari } 251233485Syongari 252233485Syongari if ((bmcr & BMCR_LOOP) != 0) 253233485Syongari mii->mii_media_active |= IFM_LOOP; 254233485Syongari 255233485Syongari if ((bmcr & BMCR_AUTOEN) != 0) { 256233485Syongari if ((bmsr & BMSR_ACOMP) == 0) { 257233485Syongari /* Erg, still trying, I guess... */ 258233485Syongari mii->mii_media_active |= IFM_NONE; 259233485Syongari return; 260233485Syongari } 261233485Syongari } 262233485Syongari 263233485Syongari status = PHY_READ(sc, 0x12); 264233485Syongari if (status & 0x0080) 265233485Syongari mii->mii_media_active |= IFM_100_TX; 266233485Syongari else 267233485Syongari mii->mii_media_active |= IFM_10_T; 268233485Syongari if (status & 0x0040) 269233485Syongari mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 270233485Syongari else 271233485Syongari mii->mii_media_active |= IFM_HDX; 272233485Syongari} 273