tdkphy.c revision 84823
1244543Sbrooks/* 2244543Sbrooks * Copyright (c) 2000,2001 Jonathan Chen. 3244543Sbrooks * All rights reserved. 4244543Sbrooks * 5244543Sbrooks * Redistribution and use in source and binary forms, with or without 6244543Sbrooks * modification, are permitted provided that the following conditions 7244543Sbrooks * are met: 8244543Sbrooks * 1. Redistributions of source code must retain the above copyright 9244543Sbrooks * notice, this list of conditions, and the following disclaimer, 10244543Sbrooks * without modification, immediately at the beginning of the file. 11244543Sbrooks * 2. Redistributions in binary form must reproduce the above copyright 12244543Sbrooks * notice, this list of conditions and the following disclaimer in 13244543Sbrooks * the documentation and/or other materials provided with the 14244543Sbrooks * distribution. 15244543Sbrooks * 16244543Sbrooks * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17244543Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18244543Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19244543Sbrooks * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20244543Sbrooks * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21244543Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22244543Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23244543Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24244543Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25244543Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26244543Sbrooks * SUCH DAMAGE. 27244543Sbrooks * 28244543Sbrooks * $FreeBSD: head/sys/dev/mii/tdkphy.c 84823 2001-10-11 22:02:14Z mjacob $ 29244543Sbrooks */ 30244543Sbrooks 31244543Sbrooks/* 32244543Sbrooks * Driver for the TDK 78Q2120 MII 33244543Sbrooks * 34244543Sbrooks * References: 35244543Sbrooks * Datasheet for the 78Q2120 - http://www.tsc.tdk.com/lan/78q2120.pdf 36244543Sbrooks * Most of this code stolen from ukphy.c 37244543Sbrooks */ 38244543Sbrooks 39244543Sbrooks/* 40244543Sbrooks * The TDK 78Q2120 is found on some Xircom X3201 based cardbus cards. It's just 41244543Sbrooks * like any other normal phy, except it does auto negotiation in a different 42244543Sbrooks * way. 43244543Sbrooks */ 44244543Sbrooks 45244543Sbrooks#include <sys/param.h> 46244543Sbrooks#include <sys/systm.h> 47244543Sbrooks#include <sys/kernel.h> 48244543Sbrooks#include <sys/malloc.h> 49244543Sbrooks#include <sys/socket.h> 50244543Sbrooks#include <sys/errno.h> 51244543Sbrooks#include <sys/module.h> 52244543Sbrooks#include <sys/bus.h> 53244543Sbrooks 54244543Sbrooks#include <net/if.h> 55244543Sbrooks#include <net/if_media.h> 56244543Sbrooks 57244543Sbrooks#include <machine/clock.h> 58244543Sbrooks 59244543Sbrooks#include <dev/mii/mii.h> 60244543Sbrooks#include <dev/mii/miivar.h> 61244543Sbrooks#include <dev/mii/miidevs.h> 62244543Sbrooks 63244543Sbrooks#include <dev/mii/tdkphyreg.h> 64244543Sbrooks 65244543Sbrooks#include "miibus_if.h" 66244543Sbrooks 67244543Sbrooks#if !defined(lint) 68244543Sbrooksstatic const char rcsid[] = 69244543Sbrooks "$Id: tdkphy.c,v 1.3 2000/10/14 06:20:56 jon Exp $"; 70244543Sbrooks#endif 71244543Sbrooks 72244543Sbrooksstatic int tdkphy_probe __P((device_t)); 73244543Sbrooksstatic int tdkphy_attach __P((device_t)); 74244543Sbrooksstatic int tdkphy_detach __P((device_t)); 75244543Sbrooks 76244543Sbrooksstatic device_method_t tdkphy_methods[] = { 77244543Sbrooks /* device interface */ 78244543Sbrooks DEVMETHOD(device_probe, tdkphy_probe), 79244543Sbrooks DEVMETHOD(device_attach, tdkphy_attach), 80244543Sbrooks DEVMETHOD(device_detach, tdkphy_detach), 81244543Sbrooks DEVMETHOD(device_shutdown, bus_generic_shutdown), 82244543Sbrooks { 0, 0 } 83244543Sbrooks}; 84244543Sbrooks 85244543Sbrooksstatic devclass_t tdkphy_devclass; 86244543Sbrooks 87244543Sbrooksstatic driver_t tdkphy_driver = { 88244543Sbrooks "tdkphy", 89244543Sbrooks tdkphy_methods, 90244543Sbrooks sizeof(struct mii_softc) 91244543Sbrooks}; 92244543Sbrooks 93244543SbrooksDRIVER_MODULE(tdkphy, miibus, tdkphy_driver, tdkphy_devclass, 0, 0); 94244543Sbrooks 95244543Sbrooksstatic int tdkphy_service __P((struct mii_softc *, struct mii_data *, int)); 96244543Sbrooksstatic void tdkphy_status __P((struct mii_softc *)); 97244543Sbrooks 98244543Sbrooksstatic int 99244543Sbrookstdkphy_probe(device_t dev) 100244543Sbrooks{ 101244543Sbrooks struct mii_attach_args *ma; 102244543Sbrooks ma = device_get_ivars(dev); 103244543Sbrooks if ((MII_OUI(ma->mii_id1, ma->mii_id2) != MII_OUI_TDK || 104244543Sbrooks MII_MODEL(ma->mii_id2) != MII_MODEL_TDK_78Q2120)) 105244543Sbrooks return (ENXIO); 106244543Sbrooks 107244543Sbrooks device_set_desc(dev, MII_STR_TDK_78Q2120); 108244543Sbrooks return (0); 109244543Sbrooks} 110244543Sbrooks 111244543Sbrooksstatic int 112244543Sbrookstdkphy_attach(device_t dev) 113244543Sbrooks{ 114244543Sbrooks struct mii_softc *sc; 115244543Sbrooks struct mii_attach_args *ma; 116244543Sbrooks struct mii_data *mii; 117244543Sbrooks sc = device_get_softc(dev); 118244543Sbrooks ma = device_get_ivars(dev); 119244543Sbrooks sc->mii_dev = device_get_parent(dev); 120244543Sbrooks mii = device_get_softc(sc->mii_dev); 121244543Sbrooks LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 122244543Sbrooks 123244543Sbrooks if (bootverbose) 124244543Sbrooks device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", 125244543Sbrooks MII_OUI(ma->mii_id1, ma->mii_id2), 126244543Sbrooks MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); 127244543Sbrooks 128244543Sbrooks sc->mii_inst = mii->mii_instance; 129244543Sbrooks sc->mii_phy = ma->mii_phyno; 130244543Sbrooks sc->mii_service = tdkphy_service; 131244543Sbrooks sc->mii_pdata = mii; 132244543Sbrooks 133244543Sbrooks mii->mii_instance++; 134244543Sbrooks 135244543Sbrooks sc->mii_flags |= MIIF_NOISOLATE; 136244543Sbrooks 137244543Sbrooks#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 138244543Sbrooks 139244543Sbrooks ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 140244543Sbrooks BMCR_ISO); 141244543Sbrooks#if 0 142244543Sbrooks ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 143244543Sbrooks BMCR_LOOP|BMCR_S100); 144244543Sbrooks#endif 145244543Sbrooks 146244543Sbrooks mii_phy_reset(sc); 147244543Sbrooks 148244543Sbrooks sc->mii_capabilities = 149244543Sbrooks PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 150244543Sbrooks device_printf(dev, " "); 151244543Sbrooks if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) 152244543Sbrooks printf("no media present"); 153244543Sbrooks else 154244543Sbrooks mii_add_media(mii, sc->mii_capabilities, 155244543Sbrooks sc->mii_inst); 156244543Sbrooks printf("\n"); 157244543Sbrooks#undef ADD 158244543Sbrooks 159244543Sbrooks MIIBUS_MEDIAINIT(sc->mii_dev); 160244543Sbrooks 161244543Sbrooks return(0); 162244543Sbrooks} 163244543Sbrooks 164244543Sbrooksstatic int tdkphy_detach(device_t dev) 165244543Sbrooks{ 166244543Sbrooks struct mii_softc *sc; 167244543Sbrooks struct mii_data *mii; 168244543Sbrooks 169244543Sbrooks sc = device_get_softc(dev); 170244543Sbrooks mii = device_get_softc(device_get_parent(dev)); 171244543Sbrooks mii_phy_auto_stop(sc); 172244543Sbrooks sc->mii_dev = NULL; 173244543Sbrooks LIST_REMOVE(sc, mii_list); 174244543Sbrooks 175244543Sbrooks return(0); 176244543Sbrooks} 177244543Sbrooks 178244543Sbrooksstatic int 179244543Sbrookstdkphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 180244543Sbrooks{ 181244543Sbrooks struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 182244543Sbrooks int reg; 183244543Sbrooks 184244543Sbrooks switch (cmd) { 185244543Sbrooks case MII_POLLSTAT: 186244543Sbrooks /* 187244543Sbrooks * If we're not polling our PHY instance, just return. 188244543Sbrooks */ 189244543Sbrooks if (IFM_INST(ife->ifm_media) != sc->mii_inst) 190244543Sbrooks return (0); 191244543Sbrooks break; 192244543Sbrooks 193244543Sbrooks case MII_MEDIACHG: 194244543Sbrooks /* 195244543Sbrooks * If the media indicates a different PHY instance, 196244543Sbrooks * isolate ourselves. 197244543Sbrooks */ 198244543Sbrooks if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 199244543Sbrooks reg = PHY_READ(sc, MII_BMCR); 200244543Sbrooks PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 201244543Sbrooks return (0); 202244543Sbrooks } 203244543Sbrooks 204244543Sbrooks /* 205244543Sbrooks * If the interface is not up, don't do anything. 206244543Sbrooks */ 207244543Sbrooks if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 208244543Sbrooks break; 209244543Sbrooks 210244543Sbrooks switch (IFM_SUBTYPE(ife->ifm_media)) { 211244543Sbrooks case IFM_AUTO: 212244543Sbrooks /* 213244543Sbrooks * If we're already in auto mode, just return. 214244543Sbrooks */ 215244543Sbrooks if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) 216244543Sbrooks return (0); 217244543Sbrooks (void) mii_phy_auto(sc, 1); 218244543Sbrooks break; 219244543Sbrooks case IFM_100_T4: 220244543Sbrooks /* 221244543Sbrooks * Not supported on MII 222244543Sbrooks */ 223244543Sbrooks return (EINVAL); 224244543Sbrooks default: 225244543Sbrooks /* 226244543Sbrooks * BMCR data is stored in the ifmedia entry. 227244543Sbrooks */ 228244543Sbrooks PHY_WRITE(sc, MII_ANAR, 229244543Sbrooks mii_anar(ife->ifm_media)); 230244543Sbrooks PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 231244543Sbrooks } 232244543Sbrooks break; 233244543Sbrooks 234244543Sbrooks case MII_TICK: 235244543Sbrooks /* 236244543Sbrooks * If we're not currently selected, just return. 237244543Sbrooks */ 238244543Sbrooks if (IFM_INST(ife->ifm_media) != sc->mii_inst) 239244543Sbrooks return (0); 240244543Sbrooks if (mii_phy_tick(sc) == EJUSTRETURN) 241244543Sbrooks return (0); 242244543Sbrooks break; 243244543Sbrooks } 244244543Sbrooks 245244543Sbrooks /* Update the media status. */ 246244543Sbrooks tdkphy_status(sc); 247244543Sbrooks if (sc->mii_pdata->mii_media_active & IFM_FDX) 248244543Sbrooks PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) | BMCR_FDX); 249244543Sbrooks else 250244543Sbrooks PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) & ~BMCR_FDX); 251244543Sbrooks 252244543Sbrooks /* Callback if something changed. */ 253244543Sbrooks mii_phy_update(sc, cmd); 254244543Sbrooks return (0); 255244543Sbrooks} 256244543Sbrooks 257244543Sbrooksstatic void 258244543Sbrookstdkphy_status(struct mii_softc *phy) 259244543Sbrooks{ 260244543Sbrooks struct mii_data *mii = phy->mii_pdata; 261244543Sbrooks int bmsr, bmcr, anlpar, diag; 262244543Sbrooks 263244543Sbrooks mii->mii_media_status = IFM_AVALID; 264244543Sbrooks mii->mii_media_active = IFM_ETHER; 265244543Sbrooks 266244543Sbrooks bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); 267244543Sbrooks if (bmsr & BMSR_LINK) 268244543Sbrooks mii->mii_media_status |= IFM_ACTIVE; 269244543Sbrooks 270244543Sbrooks bmcr = PHY_READ(phy, MII_BMCR); 271244543Sbrooks if (bmcr & BMCR_ISO) { 272244543Sbrooks mii->mii_media_active |= IFM_NONE; 273244543Sbrooks mii->mii_media_status = 0; 274244543Sbrooks return; 275244543Sbrooks } 276244543Sbrooks 277244543Sbrooks if (bmcr & BMCR_LOOP) 278244543Sbrooks mii->mii_media_active |= IFM_LOOP; 279244543Sbrooks 280244543Sbrooks if (bmcr & BMCR_AUTOEN) { 281244543Sbrooks /* 282244543Sbrooks * NWay autonegotiation takes the highest-order common 283244543Sbrooks * bit of the ANAR and ANLPAR (i.e. best media advertised 284244543Sbrooks * both by us and our link partner). 285244543Sbrooks */ 286244543Sbrooks if ((bmsr & BMSR_ACOMP) == 0) { 287244543Sbrooks /* Erg, still trying, I guess... */ 288244543Sbrooks mii->mii_media_active |= IFM_NONE; 289244543Sbrooks return; 290244543Sbrooks } 291 292 anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR); 293 /* 294 * ANLPAR doesn't get set on my card, but we check it anyway, 295 * since it is mentioned in the 78Q2120 specs. 296 */ 297 if (anlpar & ANLPAR_T4) 298 mii->mii_media_active |= IFM_100_T4; 299 else if (anlpar & ANLPAR_TX_FD) 300 mii->mii_media_active |= IFM_100_TX|IFM_FDX; 301 else if (anlpar & ANLPAR_TX) 302 mii->mii_media_active |= IFM_100_TX; 303 else if (anlpar & ANLPAR_10_FD) 304 mii->mii_media_active |= IFM_10_T|IFM_FDX; 305 else if (anlpar & ANLPAR_10) 306 mii->mii_media_active |= IFM_10_T; 307 else { 308 /* 309 * ANLPAR isn't set, which leaves two possibilities: 310 * 1) Auto negotiation failed 311 * 2) Auto negotiation completed, but the card forgot 312 * to set ANLPAR. 313 * So we check the MII_DIAG(18) register... 314 */ 315 diag = PHY_READ(phy, MII_DIAG); 316 if (diag & DIAG_NEGFAIL) /* assume 10baseT if no neg */ 317 mii->mii_media_active |= IFM_10_T; 318 else { 319 if (diag & DIAG_DUPLEX) 320 mii->mii_media_active |= IFM_FDX; 321 if (diag & DIAG_RATE_100) 322 mii->mii_media_active |= IFM_100_TX; 323 else 324 mii->mii_media_active |= IFM_10_T; 325 } 326 } 327 } else { 328 mii->mii_media_active = mii_media_from_bmcr(bmcr); 329 } 330} 331