smcphy.c revision 229093
1178354Ssam/*- 2186904Ssam * Copyright (c) 2006 Benno Rice. All rights reserved. 3178354Ssam * 4178354Ssam * Redistribution and use in source and binary forms, with or without 5178354Ssam * modification, are permitted provided that the following conditions 6178354Ssam * are met: 7178354Ssam * 1. Redistributions of source code must retain the above copyright 8178354Ssam * notice, this list of conditions and the following disclaimer. 9178354Ssam * 2. Redistributions in binary form must reproduce the above copyright 10178354Ssam * notice, this list of conditions and the following disclaimer in the 11178354Ssam * documentation and/or other materials provided with the distribution. 12178354Ssam * 13178354Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14178354Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15178354Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16178354Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17178354Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18178354Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19178354Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20178354Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21178354Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22178354Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23178354Ssam */ 24178354Ssam 25178354Ssam#include <sys/cdefs.h> 26178354Ssam__FBSDID("$FreeBSD: stable/9/sys/dev/mii/smcphy.c 229093 2011-12-31 14:12:12Z hselasky $"); 27178354Ssam 28178354Ssam/* 29178354Ssam * Driver for the internal PHY on the SMSC LAN91C111. 30189106Sbz */ 31178354Ssam 32178354Ssam#include <sys/param.h> 33178354Ssam#include <sys/systm.h> 34178354Ssam#include <sys/kernel.h> 35178354Ssam#include <sys/socket.h> 36178354Ssam#include <sys/errno.h> 37178354Ssam#include <sys/module.h> 38178354Ssam#include <sys/bus.h> 39178354Ssam#include <sys/malloc.h> 40178354Ssam 41182144Sjulian#include <machine/bus.h> 42178354Ssam 43178354Ssam#include <net/if.h> 44178354Ssam#include <net/if_media.h> 45178354Ssam 46178354Ssam#include <dev/mii/mii.h> 47178354Ssam#include <dev/mii/miivar.h> 48189106Sbz#include "miidevs.h" 49185571Sbz 50178354Ssam#include "miibus_if.h" 51178354Ssam 52191551Ssamstatic int smcphy_probe(device_t); 53191551Ssamstatic int smcphy_attach(device_t); 54191551Ssam 55178354Ssamstatic int smcphy_service(struct mii_softc *, struct mii_data *, int); 56178354Ssamstatic void smcphy_reset(struct mii_softc *); 57183355Sthompsastatic void smcphy_auto(struct mii_softc *, int); 58178354Ssam 59191551Ssamstatic device_method_t smcphy_methods[] = { 60191551Ssam /* device interface */ 61183355Sthompsa DEVMETHOD(device_probe, smcphy_probe), 62191551Ssam DEVMETHOD(device_attach, smcphy_attach), 63191551Ssam DEVMETHOD(device_detach, mii_phy_detach), 64183355Sthompsa DEVMETHOD(device_shutdown, bus_generic_shutdown), 65178354Ssam DEVMETHOD_END 66178354Ssam}; 67178354Ssam 68178354Ssamstatic devclass_t smcphy_devclass; 69178354Ssam 70188182Ssamstatic driver_t smcphy_driver = { 71188182Ssam "smcphy", 72178354Ssam smcphy_methods, 73178354Ssam sizeof(struct mii_softc) 74178354Ssam}; 75178354Ssam 76178354SsamDRIVER_MODULE(smcphy, miibus, smcphy_driver, smcphy_devclass, 0, 0); 77178354Ssam 78178354Ssamstatic const struct mii_phydesc smcphys[] = { 79178354Ssam MII_PHY_DESC(SEEQ, 84220), 80178354Ssam MII_PHY_END 81178354Ssam}; 82178354Ssam 83178354Ssamstatic const struct mii_phy_funcs smcphy_funcs = { 84178354Ssam smcphy_service, 85178354Ssam ukphy_status, 86178354Ssam smcphy_reset 87178354Ssam}; 88178354Ssam 89178354Ssamstatic int 90178354Ssamsmcphy_probe(device_t dev) 91188182Ssam{ 92188182Ssam 93188182Ssam return (mii_phy_dev_probe(dev, smcphys, BUS_PROBE_DEFAULT)); 94188182Ssam} 95188182Ssam 96188182Ssamstatic int 97188182Ssamsmcphy_attach(device_t dev) 98188182Ssam{ 99188182Ssam struct mii_softc *sc; 100178354Ssam 101178354Ssam sc = device_get_softc(dev); 102178354Ssam 103178354Ssam mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, 104178354Ssam &smcphy_funcs, 1); 105178354Ssam mii_phy_setmedia(sc); 106178354Ssam 107178354Ssam return (0); 108178354Ssam} 109178354Ssam 110178354Ssamstatic int 111178354Ssamsmcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 112178354Ssam{ 113178354Ssam struct ifmedia_entry *ife; 114178354Ssam int reg; 115178354Ssam 116178354Ssam ife = mii->mii_media.ifm_cur; 117178354Ssam 118178354Ssam switch (cmd) { 119178354Ssam case MII_POLLSTAT: 120178354Ssam break; 121178354Ssam 122178354Ssam case MII_MEDIACHG: 123178354Ssam /* 124178354Ssam * If the interface is not up, don't do anything. 125178354Ssam */ 126178354Ssam if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 127178354Ssam break; 128178354Ssam 129178354Ssam switch (IFM_SUBTYPE(ife->ifm_media)) { 130178354Ssam case IFM_AUTO: 131178354Ssam smcphy_auto(sc, ife->ifm_media); 132178354Ssam break; 133178354Ssam 134178354Ssam default: 135178354Ssam mii_phy_setmedia(sc); 136178354Ssam break; 137178354Ssam } 138178354Ssam 139178354Ssam break; 140178354Ssam 141178354Ssam case MII_TICK: 142178354Ssam if ((mii->mii_ifp->if_flags & IFF_UP) == 0) { 143178354Ssam return (0); 144178354Ssam } 145178354Ssam 146178354Ssam if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 147178354Ssam break; 148178354Ssam } 149178354Ssam 150178354Ssam /* I have no idea why BMCR_ISO gets set. */ 151183550Szec reg = PHY_READ(sc, MII_BMCR); 152178354Ssam if (reg & BMCR_ISO) { 153178354Ssam PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO); 154178354Ssam } 155178354Ssam 156178354Ssam reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 157178354Ssam if (reg & BMSR_LINK) { 158178354Ssam sc->mii_ticks = 0; 159178354Ssam break; 160178354Ssam } 161178354Ssam 162183550Szec if (++sc->mii_ticks <= MII_ANEGTICKS) { 163183550Szec break; 164183550Szec } 165183550Szec 166183550Szec sc->mii_ticks = 0; 167178354Ssam PHY_RESET(sc); 168183550Szec smcphy_auto(sc, ife->ifm_media); 169183550Szec break; 170183550Szec } 171183550Szec 172183550Szec /* Update the media status. */ 173183550Szec PHY_STATUS(sc); 174183550Szec 175183550Szec /* Callback if something changed. */ 176183550Szec mii_phy_update(sc, cmd); 177183550Szec return (0); 178183550Szec} 179183550Szec 180183550Szecstatic void 181178354Ssamsmcphy_reset(struct mii_softc *sc) 182178354Ssam{ 183178354Ssam u_int bmcr; 184178354Ssam int timeout; 185178354Ssam 186191551Ssam PHY_WRITE(sc, MII_BMCR, BMCR_RESET); 187191551Ssam 188191551Ssam for (timeout = 2; timeout > 0; timeout--) { 189191551Ssam DELAY(50000); 190191551Ssam bmcr = PHY_READ(sc, MII_BMCR); 191191551Ssam if ((bmcr & BMCR_RESET) == 0) 192191551Ssam break; 193191551Ssam } 194191551Ssam 195191551Ssam if (bmcr & BMCR_RESET) 196178354Ssam device_printf(sc->mii_dev, "reset failed\n"); 197178354Ssam 198178354Ssam PHY_WRITE(sc, MII_BMCR, 0x3000); 199178354Ssam 200178354Ssam /* Mask interrupts, we poll instead. */ 201191551Ssam PHY_WRITE(sc, 0x1e, 0xffc0); 202191551Ssam} 203178354Ssam 204178354Ssamstatic void 205178354Ssamsmcphy_auto(struct mii_softc *sc, int media) 206178354Ssam{ 207178354Ssam uint16_t anar; 208191551Ssam 209191551Ssam anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; 210191551Ssam if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 211191551Ssam anar |= ANAR_FC; 212191551Ssam PHY_WRITE(sc, MII_ANAR, anar); 213191551Ssam /* Apparently this helps. */ 214191551Ssam anar = PHY_READ(sc, MII_ANAR); 215178354Ssam PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 216178354Ssam} 217178354Ssam