tdkphy.c revision 119418
143412Snewton/* 243412Snewton * Copyright (c) 2000,2001 Jonathan Chen. 343412Snewton * All rights reserved. 443412Snewton * 543412Snewton * Redistribution and use in source and binary forms, with or without 643412Snewton * modification, are permitted provided that the following conditions 743412Snewton * are met: 843412Snewton * 1. Redistributions of source code must retain the above copyright 943412Snewton * notice, this list of conditions, and the following disclaimer, 1043412Snewton * without modification, immediately at the beginning of the file. 1143412Snewton * 2. Redistributions in binary form must reproduce the above copyright 1243412Snewton * notice, this list of conditions and the following disclaimer in 1343412Snewton * the documentation and/or other materials provided with the 1443412Snewton * distribution. 1543412Snewton * 1643412Snewton * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1743412Snewton * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1843412Snewton * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1997748Sschweikh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 2043412Snewton * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2143412Snewton * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2243412Snewton * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2343412Snewton * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2443412Snewton * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2543412Snewton * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2643412Snewton * SUCH DAMAGE. 2743412Snewton */ 2843412Snewton 2943412Snewton#include <sys/cdefs.h> 3043412Snewton__FBSDID("$FreeBSD: head/sys/dev/mii/tdkphy.c 119418 2003-08-24 17:55:58Z obrien $"); 3143412Snewton 3243412Snewton/* 33116174Sobrien * Driver for the TDK 78Q2120 MII 34116174Sobrien * 35116174Sobrien * References: 3643412Snewton * Datasheet for the 78Q2120 - http://www.tsc.tdk.com/lan/78q2120.pdf 3743412Snewton * Most of this code stolen from ukphy.c 3843412Snewton */ 3943412Snewton 4043412Snewton/* 4143412Snewton * The TDK 78Q2120 is found on some Xircom X3201 based cardbus cards. It's just 4243412Snewton * like any other normal phy, except it does auto negotiation in a different 4376166Smarkm * way. 4476166Smarkm */ 4543412Snewton 4676166Smarkm#include <sys/cdefs.h> 4743412Snewton__FBSDID("$FreeBSD: head/sys/dev/mii/tdkphy.c 119418 2003-08-24 17:55:58Z obrien $"); 4843412Snewton 4943412Snewton#include <sys/param.h> 5043412Snewton#include <sys/systm.h> 5143412Snewton#include <sys/kernel.h> 5243412Snewton#include <sys/socket.h> 5343412Snewton#include <sys/errno.h> 5443412Snewton#include <sys/module.h> 5543412Snewton#include <sys/bus.h> 5665302Sobrien 5743412Snewton#include <net/if.h> 5892761Salfred#include <net/if_media.h> 5943412Snewton 6043412Snewton#include <machine/clock.h> 6143412Snewton 6243412Snewton#include <dev/mii/mii.h> 6343412Snewton#include <dev/mii/miivar.h> 6443412Snewton#include "miidevs.h" 6543412Snewton 6643412Snewton#include <dev/mii/tdkphyreg.h> 6743412Snewton 6843412Snewton#include "miibus_if.h" 6943412Snewton 7043412Snewton#if 0 71101771Sjeff#if !defined(lint) 7243412Snewtonstatic const char rcsid[] = 7343412Snewton "$Id: tdkphy.c,v 1.3 2000/10/14 06:20:56 jon Exp $"; 7443412Snewton#endif 7543412Snewton#endif 7643412Snewton 7743412Snewtonstatic int tdkphy_probe(device_t); 7843412Snewtonstatic int tdkphy_attach(device_t); 7943412Snewton 8043412Snewtonstatic device_method_t tdkphy_methods[] = { 8143412Snewton /* device interface */ 8243412Snewton DEVMETHOD(device_probe, tdkphy_probe), 8343412Snewton DEVMETHOD(device_attach, tdkphy_attach), 8443412Snewton DEVMETHOD(device_detach, mii_phy_detach), 8543412Snewton DEVMETHOD(device_shutdown, bus_generic_shutdown), 8643412Snewton { 0, 0 } 8743412Snewton}; 8843412Snewton 8943412Snewtonstatic devclass_t tdkphy_devclass; 9043412Snewton 9143412Snewtonstatic driver_t tdkphy_driver = { 9243412Snewton "tdkphy", 93131013Sobrien tdkphy_methods, 9443412Snewton sizeof(struct mii_softc) 9543412Snewton}; 9643412Snewton 9743412SnewtonDRIVER_MODULE(tdkphy, miibus, tdkphy_driver, tdkphy_devclass, 0, 0); 9843412Snewton 9943412Snewtonstatic int tdkphy_service(struct mii_softc *, struct mii_data *, int); 10043412Snewtonstatic void tdkphy_status(struct mii_softc *); 10143412Snewton 10243412Snewtonstatic int 10343412Snewtontdkphy_probe(device_t dev) 10443412Snewton{ 10543412Snewton struct mii_attach_args *ma; 10643412Snewton ma = device_get_ivars(dev); 10743412Snewton if ((MII_OUI(ma->mii_id1, ma->mii_id2) != MII_OUI_TDK || 10843412Snewton MII_MODEL(ma->mii_id2) != MII_MODEL_TDK_78Q2120)) 10943412Snewton return (ENXIO); 110125454Sjhb 11184783Sps device_set_desc(dev, MII_STR_TDK_78Q2120); 112125454Sjhb return (0); 113125454Sjhb} 11443412Snewton 115125454Sjhbstatic int 116125454Sjhbtdkphy_attach(device_t dev) 11743412Snewton{ 118101771Sjeff struct mii_softc *sc; 119101771Sjeff struct mii_attach_args *ma; 12043412Snewton struct mii_data *mii; 12143412Snewton sc = device_get_softc(dev); 12243412Snewton ma = device_get_ivars(dev); 123173361Skib sc->mii_dev = device_get_parent(dev); 124173361Skib mii = device_get_softc(sc->mii_dev); 125173361Skib LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 12643412Snewton 12743412Snewton if (bootverbose) 12843412Snewton device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", 12943412Snewton MII_OUI(ma->mii_id1, ma->mii_id2), 13043412Snewton MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); 13143412Snewton 13243412Snewton sc->mii_inst = mii->mii_instance; 13343412Snewton sc->mii_phy = ma->mii_phyno; 13443412Snewton sc->mii_service = tdkphy_service; 13580114Sassar sc->mii_pdata = mii; 13643412Snewton 13743412Snewton mii->mii_instance++; 13843412Snewton 13943412Snewton sc->mii_flags |= MIIF_NOISOLATE; 14043412Snewton 14143412Snewton#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 14243412Snewton 14343412Snewton ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 14443412Snewton BMCR_ISO); 145101771Sjeff#if 0 14643412Snewton ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 14743412Snewton BMCR_LOOP|BMCR_S100); 14843412Snewton#endif 14943412Snewton 150144501Sjhb mii_phy_reset(sc); 15143412Snewton 152101771Sjeff sc->mii_capabilities = 15343412Snewton PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 15443412Snewton device_printf(dev, " "); 15543412Snewton mii_add_media(sc); 15643412Snewton printf("\n"); 15743412Snewton#undef ADD 15843412Snewton 15943412Snewton MIIBUS_MEDIAINIT(sc->mii_dev); 16043412Snewton 161101771Sjeff return(0); 16243412Snewton} 16343412Snewton 16443412Snewtonstatic int 16543412Snewtontdkphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 16643412Snewton{ 16743412Snewton struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 16843412Snewton int reg; 16943412Snewton 17043412Snewton switch (cmd) { 17143412Snewton case MII_POLLSTAT: 172101771Sjeff /* 17343412Snewton * If we're not polling our PHY instance, just return. 17443412Snewton */ 17543412Snewton if (IFM_INST(ife->ifm_media) != sc->mii_inst) 17680114Sassar return (0); 17743412Snewton break; 17843412Snewton 17943412Snewton case MII_MEDIACHG: 18043412Snewton /* 18143412Snewton * If the media indicates a different PHY instance, 18243412Snewton * isolate ourselves. 18343412Snewton */ 18443412Snewton if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 18543412Snewton reg = PHY_READ(sc, MII_BMCR); 18643412Snewton PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 187144501Sjhb return (0); 18843412Snewton } 189101771Sjeff 19043412Snewton /* 19143412Snewton * If the interface is not up, don't do anything. 19280114Sassar */ 193131013Sobrien if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 19443412Snewton break; 19543412Snewton 19643412Snewton switch (IFM_SUBTYPE(ife->ifm_media)) { 19743412Snewton case IFM_AUTO: 19843412Snewton /* 19943412Snewton * If we're already in auto mode, just return. 20043412Snewton */ 20143412Snewton if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) 20243412Snewton return (0); 20343412Snewton (void) mii_phy_auto(sc); 204101771Sjeff break; 20543412Snewton case IFM_100_T4: 20643412Snewton /* 20743412Snewton * Not supported on MII 20843412Snewton */ 20943412Snewton return (EINVAL); 21043412Snewton default: 21143412Snewton /* 21243412Snewton * BMCR data is stored in the ifmedia entry. 21343412Snewton */ 214101771Sjeff PHY_WRITE(sc, MII_ANAR, 21543412Snewton mii_anar(ife->ifm_media)); 21680114Sassar PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 21780114Sassar } 21843412Snewton break; 21943412Snewton 22043412Snewton case MII_TICK: 22143412Snewton /* 22243412Snewton * If we're not currently selected, just return. 22343412Snewton */ 22443412Snewton if (IFM_INST(ife->ifm_media) != sc->mii_inst) 22543412Snewton return (0); 22643412Snewton if (mii_phy_tick(sc) == EJUSTRETURN) 22743412Snewton return (0); 22843412Snewton break; 22943412Snewton } 23043412Snewton 23143412Snewton /* Update the media status. */ 23243412Snewton tdkphy_status(sc); 233101771Sjeff if (sc->mii_pdata->mii_media_active & IFM_FDX) 234175202Sattilio PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) | BMCR_FDX); 235101771Sjeff else 23643412Snewton PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) & ~BMCR_FDX); 23743412Snewton 23843412Snewton /* Callback if something changed. */ 23943412Snewton mii_phy_update(sc, cmd); 24043412Snewton return (0); 24146889Speter} 24245738Speter 24343412Snewtonstatic void 244tdkphy_status(struct mii_softc *phy) 245{ 246 struct mii_data *mii = phy->mii_pdata; 247 int bmsr, bmcr, anlpar, diag; 248 249 mii->mii_media_status = IFM_AVALID; 250 mii->mii_media_active = IFM_ETHER; 251 252 bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); 253 if (bmsr & BMSR_LINK) 254 mii->mii_media_status |= IFM_ACTIVE; 255 256 bmcr = PHY_READ(phy, MII_BMCR); 257 if (bmcr & BMCR_ISO) { 258 mii->mii_media_active |= IFM_NONE; 259 mii->mii_media_status = 0; 260 return; 261 } 262 263 if (bmcr & BMCR_LOOP) 264 mii->mii_media_active |= IFM_LOOP; 265 266 if (bmcr & BMCR_AUTOEN) { 267 /* 268 * NWay autonegotiation takes the highest-order common 269 * bit of the ANAR and ANLPAR (i.e. best media advertised 270 * both by us and our link partner). 271 */ 272 if ((bmsr & BMSR_ACOMP) == 0) { 273 /* Erg, still trying, I guess... */ 274 mii->mii_media_active |= IFM_NONE; 275 return; 276 } 277 278 anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR); 279 /* 280 * ANLPAR doesn't get set on my card, but we check it anyway, 281 * since it is mentioned in the 78Q2120 specs. 282 */ 283 if (anlpar & ANLPAR_T4) 284 mii->mii_media_active |= IFM_100_T4; 285 else if (anlpar & ANLPAR_TX_FD) 286 mii->mii_media_active |= IFM_100_TX|IFM_FDX; 287 else if (anlpar & ANLPAR_TX) 288 mii->mii_media_active |= IFM_100_TX; 289 else if (anlpar & ANLPAR_10_FD) 290 mii->mii_media_active |= IFM_10_T|IFM_FDX; 291 else if (anlpar & ANLPAR_10) 292 mii->mii_media_active |= IFM_10_T; 293 else { 294 /* 295 * ANLPAR isn't set, which leaves two possibilities: 296 * 1) Auto negotiation failed 297 * 2) Auto negotiation completed, but the card forgot 298 * to set ANLPAR. 299 * So we check the MII_DIAG(18) register... 300 */ 301 diag = PHY_READ(phy, MII_DIAG); 302 if (diag & DIAG_NEGFAIL) /* assume 10baseT if no neg */ 303 mii->mii_media_active |= IFM_10_T; 304 else { 305 if (diag & DIAG_DUPLEX) 306 mii->mii_media_active |= IFM_FDX; 307 if (diag & DIAG_RATE_100) 308 mii->mii_media_active |= IFM_100_TX; 309 else 310 mii->mii_media_active |= IFM_10_T; 311 } 312 } 313 } else { 314 mii->mii_media_active = mii_media_from_bmcr(bmcr); 315 } 316} 317