1139749Simp/*- 267314Sjon * Copyright (c) 2000,2001 Jonathan Chen. 367314Sjon * All rights reserved. 467314Sjon * 567314Sjon * Redistribution and use in source and binary forms, with or without 667314Sjon * modification, are permitted provided that the following conditions 767314Sjon * are met: 867314Sjon * 1. Redistributions of source code must retain the above copyright 967314Sjon * notice, this list of conditions, and the following disclaimer, 1067314Sjon * without modification, immediately at the beginning of the file. 1167314Sjon * 2. Redistributions in binary form must reproduce the above copyright 1267314Sjon * notice, this list of conditions and the following disclaimer in 1367314Sjon * the documentation and/or other materials provided with the 1467314Sjon * distribution. 1567314Sjon * 1667314Sjon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1767314Sjon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1867314Sjon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1967314Sjon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 2067314Sjon * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2167314Sjon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2267314Sjon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2367314Sjon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2467314Sjon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2567314Sjon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2667314Sjon * SUCH DAMAGE. 2767314Sjon */ 2867314Sjon 29119418Sobrien#include <sys/cdefs.h> 30119418Sobrien__FBSDID("$FreeBSD$"); 31119418Sobrien 3267314Sjon/* 3367314Sjon * Driver for the TDK 78Q2120 MII 3467314Sjon * 3567314Sjon * References: 3667314Sjon * Datasheet for the 78Q2120 - http://www.tsc.tdk.com/lan/78q2120.pdf 3767314Sjon * Most of this code stolen from ukphy.c 3867314Sjon */ 3967314Sjon 4067314Sjon/* 41201453Simp * The TDK 78Q2120 is found on some Xircom X3201 based CardBus cards, 42150762Simp * also spotted on some 3C575 cards. It's just like any other normal 43150762Simp * phy, except it does auto negotiation in a different way. 4467314Sjon */ 4567314Sjon 4667314Sjon#include <sys/param.h> 4767314Sjon#include <sys/systm.h> 4867314Sjon#include <sys/kernel.h> 4967314Sjon#include <sys/socket.h> 5067314Sjon#include <sys/errno.h> 5167314Sjon#include <sys/module.h> 5267314Sjon#include <sys/bus.h> 5367314Sjon 5467314Sjon#include <net/if.h> 5567314Sjon#include <net/if_media.h> 5667314Sjon 5767314Sjon#include <dev/mii/mii.h> 5867314Sjon#include <dev/mii/miivar.h> 59109514Sobrien#include "miidevs.h" 6067314Sjon 6167314Sjon#include <dev/mii/tdkphyreg.h> 6267314Sjon 6367314Sjon#include "miibus_if.h" 6467314Sjon 65105135Salfredstatic int tdkphy_probe(device_t); 66105135Salfredstatic int tdkphy_attach(device_t); 6767314Sjon 6867314Sjonstatic device_method_t tdkphy_methods[] = { 6967314Sjon /* device interface */ 7067314Sjon DEVMETHOD(device_probe, tdkphy_probe), 7167314Sjon DEVMETHOD(device_attach, tdkphy_attach), 7295722Sphk DEVMETHOD(device_detach, mii_phy_detach), 7367314Sjon DEVMETHOD(device_shutdown, bus_generic_shutdown), 74227908Smarius DEVMETHOD_END 7567314Sjon}; 7667314Sjon 7767314Sjonstatic devclass_t tdkphy_devclass; 7867314Sjon 7967314Sjonstatic driver_t tdkphy_driver = { 8067314Sjon "tdkphy", 8167314Sjon tdkphy_methods, 8267314Sjon sizeof(struct mii_softc) 8367314Sjon}; 8467314Sjon 8567314SjonDRIVER_MODULE(tdkphy, miibus, tdkphy_driver, tdkphy_devclass, 0, 0); 8667314Sjon 8792739Salfredstatic int tdkphy_service(struct mii_softc *, struct mii_data *, int); 8892739Salfredstatic void tdkphy_status(struct mii_softc *); 8967314Sjon 90164827Smariusstatic const struct mii_phydesc tdkphys[] = { 91221407Smarius MII_PHY_DESC(xxTSC, 78Q2120), 92164827Smarius MII_PHY_END 93164827Smarius}; 94164827Smarius 95221407Smariusstatic const struct mii_phy_funcs tdkphy_funcs = { 96221407Smarius tdkphy_service, 97221407Smarius tdkphy_status, 98221407Smarius mii_phy_reset 99221407Smarius}; 100221407Smarius 10167314Sjonstatic int 10267314Sjontdkphy_probe(device_t dev) 10367314Sjon{ 10467314Sjon 105164827Smarius return (mii_phy_dev_probe(dev, tdkphys, BUS_PROBE_DEFAULT)); 10667314Sjon} 10767314Sjon 10867314Sjonstatic int 10967314Sjontdkphy_attach(device_t dev) 11067314Sjon{ 11167314Sjon 112221407Smarius mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &tdkphy_funcs, 1); 113164830Smarius return (0); 11467314Sjon} 11567314Sjon 11684145Sjlemonstatic int 11767314Sjontdkphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 11867314Sjon{ 11967314Sjon 12067314Sjon switch (cmd) { 12167314Sjon case MII_POLLSTAT: 12267314Sjon break; 12367314Sjon 12467314Sjon case MII_MEDIACHG: 12567314Sjon /* 12667314Sjon * If the interface is not up, don't do anything. 12767314Sjon */ 12867314Sjon if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 12967314Sjon break; 13067314Sjon 131164832Smarius mii_phy_setmedia(sc); 13267314Sjon break; 13367314Sjon 13467314Sjon case MII_TICK: 13584145Sjlemon if (mii_phy_tick(sc) == EJUSTRETURN) 13667314Sjon return (0); 13767314Sjon break; 13867314Sjon } 13967314Sjon 14067314Sjon /* Update the media status. */ 141221407Smarius PHY_STATUS(sc); 14267314Sjon if (sc->mii_pdata->mii_media_active & IFM_FDX) 14367314Sjon PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) | BMCR_FDX); 14467314Sjon else 14567314Sjon PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) & ~BMCR_FDX); 14684145Sjlemon 14767314Sjon /* Callback if something changed. */ 14884145Sjlemon mii_phy_update(sc, cmd); 14967314Sjon return (0); 15067314Sjon} 15167314Sjon 15284145Sjlemonstatic void 15367314Sjontdkphy_status(struct mii_softc *phy) 15467314Sjon{ 15567314Sjon struct mii_data *mii = phy->mii_pdata; 156164832Smarius struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 15767314Sjon int bmsr, bmcr, anlpar, diag; 15867314Sjon 15967314Sjon mii->mii_media_status = IFM_AVALID; 16067314Sjon mii->mii_media_active = IFM_ETHER; 16167314Sjon 16267314Sjon bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); 16367314Sjon if (bmsr & BMSR_LINK) 16467314Sjon mii->mii_media_status |= IFM_ACTIVE; 16567314Sjon 16667314Sjon bmcr = PHY_READ(phy, MII_BMCR); 16767314Sjon if (bmcr & BMCR_ISO) { 16867314Sjon mii->mii_media_active |= IFM_NONE; 16967314Sjon mii->mii_media_status = 0; 17067314Sjon return; 17167314Sjon } 17267314Sjon 17367314Sjon if (bmcr & BMCR_LOOP) 17467314Sjon mii->mii_media_active |= IFM_LOOP; 17567314Sjon 17667314Sjon if (bmcr & BMCR_AUTOEN) { 17767314Sjon /* 17867314Sjon * NWay autonegotiation takes the highest-order common 17967314Sjon * bit of the ANAR and ANLPAR (i.e. best media advertised 18067314Sjon * both by us and our link partner). 18167314Sjon */ 18267314Sjon if ((bmsr & BMSR_ACOMP) == 0) { 18367314Sjon /* Erg, still trying, I guess... */ 18467314Sjon mii->mii_media_active |= IFM_NONE; 18567314Sjon return; 18667314Sjon } 18767314Sjon 18867314Sjon anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR); 18967314Sjon /* 19067314Sjon * ANLPAR doesn't get set on my card, but we check it anyway, 19167314Sjon * since it is mentioned in the 78Q2120 specs. 19267314Sjon */ 193173665Syongari if (anlpar & ANLPAR_TX_FD) 194173665Syongari mii->mii_media_active |= IFM_100_TX|IFM_FDX; 195173665Syongari else if (anlpar & ANLPAR_T4) 196213384Smarius mii->mii_media_active |= IFM_100_T4|IFM_HDX; 19767314Sjon else if (anlpar & ANLPAR_TX) 198213384Smarius mii->mii_media_active |= IFM_100_TX|IFM_HDX; 19967314Sjon else if (anlpar & ANLPAR_10_FD) 20067314Sjon mii->mii_media_active |= IFM_10_T|IFM_FDX; 20167314Sjon else if (anlpar & ANLPAR_10) 202213384Smarius mii->mii_media_active |= IFM_10_T|IFM_HDX; 20367314Sjon else { 20467314Sjon /* 20567314Sjon * ANLPAR isn't set, which leaves two possibilities: 20667314Sjon * 1) Auto negotiation failed 20767314Sjon * 2) Auto negotiation completed, but the card forgot 20867314Sjon * to set ANLPAR. 20967314Sjon * So we check the MII_DIAG(18) register... 21067314Sjon */ 21167314Sjon diag = PHY_READ(phy, MII_DIAG); 21267314Sjon if (diag & DIAG_NEGFAIL) /* assume 10baseT if no neg */ 213213384Smarius mii->mii_media_active |= IFM_10_T|IFM_HDX; 21467314Sjon else { 21567314Sjon if (diag & DIAG_DUPLEX) 21667314Sjon mii->mii_media_active |= IFM_FDX; 217213384Smarius else 218213384Smarius mii->mii_media_active |= IFM_HDX; 21967314Sjon if (diag & DIAG_RATE_100) 22067314Sjon mii->mii_media_active |= IFM_100_TX; 22167314Sjon else 22267314Sjon mii->mii_media_active |= IFM_10_T; 22367314Sjon } 22467314Sjon } 225221407Smarius if ((mii->mii_media_active & IFM_FDX) != 0) 226221407Smarius mii->mii_media_active |= mii_phy_flowstatus(phy); 227164832Smarius } else 228164832Smarius mii->mii_media_active = ife->ifm_media; 22967314Sjon} 230