mlphy.c revision 213364
1139749Simp/*- 250461Swpaul * Copyright (c) 1997, 1998, 1999 350461Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 450461Swpaul * 550461Swpaul * Redistribution and use in source and binary forms, with or without 650461Swpaul * modification, are permitted provided that the following conditions 750461Swpaul * are met: 850461Swpaul * 1. Redistributions of source code must retain the above copyright 950461Swpaul * notice, this list of conditions and the following disclaimer. 1050461Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1150461Swpaul * notice, this list of conditions and the following disclaimer in the 1250461Swpaul * documentation and/or other materials provided with the distribution. 1350461Swpaul * 3. All advertising materials mentioning features or use of this software 1450461Swpaul * must display the following acknowledgement: 1550461Swpaul * This product includes software developed by Bill Paul. 1650461Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1750461Swpaul * may be used to endorse or promote products derived from this software 1850461Swpaul * without specific prior written permission. 1950461Swpaul * 2050461Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2150461Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2250461Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2350461Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2450461Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2550461Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2650461Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2750461Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2850461Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2950461Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3050461Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3150461Swpaul * 3250461Swpaul */ 3350461Swpaul 34119418Sobrien#include <sys/cdefs.h> 35119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/mii/mlphy.c 213364 2010-10-02 18:53:12Z marius $"); 36119418Sobrien 3750461Swpaul/* 3850461Swpaul * driver for Micro Linear 6692 PHYs 3950461Swpaul * 4050461Swpaul * The Micro Linear 6692 is a strange beast, and dealing with it using 4150461Swpaul * this code framework is tricky. The 6692 is actually a 100Mbps-only 4250461Swpaul * device, which means that a second PHY is required to support 10Mbps 4350461Swpaul * modes. However, even though the 6692 does not support 10Mbps modes, 4450461Swpaul * it can still advertise them when performing autonegotiation. If a 4550461Swpaul * 10Mbps mode is negotiated, we must program the registers of the 4650461Swpaul * companion PHY accordingly in addition to programming the registers 4750461Swpaul * of the 6692. 4850461Swpaul * 4950461Swpaul * This device also does not have vendor/device ID registers. 5050461Swpaul */ 5150461Swpaul 5250461Swpaul#include <sys/param.h> 5350461Swpaul#include <sys/systm.h> 5450461Swpaul#include <sys/kernel.h> 5550461Swpaul#include <sys/socket.h> 5650461Swpaul#include <sys/module.h> 5750461Swpaul#include <sys/bus.h> 5872196Sjhb#include <sys/malloc.h> 5950461Swpaul 6050461Swpaul#include <net/if.h> 6150461Swpaul#include <net/if_media.h> 6250461Swpaul 6350461Swpaul#include <dev/mii/mii.h> 6450461Swpaul#include <dev/mii/miivar.h> 6550461Swpaul 6650461Swpaul#include "miibus_if.h" 6750461Swpaul 6850461Swpaul#define ML_STATE_AUTO_SELF 1 6950461Swpaul#define ML_STATE_AUTO_OTHER 2 7050461Swpaul 7150461Swpaulstruct mlphy_softc { 7250461Swpaul struct mii_softc ml_mii; 7350461Swpaul int ml_state; 7450461Swpaul int ml_linked; 7550461Swpaul}; 7650461Swpaul 77105135Salfredstatic int mlphy_probe(device_t); 78105135Salfredstatic int mlphy_attach(device_t); 7950461Swpaul 8050461Swpaulstatic device_method_t mlphy_methods[] = { 8150461Swpaul /* device interface */ 8250461Swpaul DEVMETHOD(device_probe, mlphy_probe), 8350461Swpaul DEVMETHOD(device_attach, mlphy_attach), 8495722Sphk DEVMETHOD(device_detach, mii_phy_detach), 8550461Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 8650461Swpaul { 0, 0 } 8750461Swpaul}; 8850461Swpaul 8950461Swpaulstatic devclass_t mlphy_devclass; 9050461Swpaul 9150461Swpaulstatic driver_t mlphy_driver = { 9250461Swpaul "mlphy", 9350461Swpaul mlphy_methods, 9450461Swpaul sizeof(struct mlphy_softc) 9550461Swpaul}; 9650461Swpaul 9750461SwpaulDRIVER_MODULE(mlphy, miibus, mlphy_driver, mlphy_devclass, 0, 0); 9850461Swpaul 9992739Salfredstatic int mlphy_service(struct mii_softc *, struct mii_data *, int); 10092739Salfredstatic void mlphy_reset(struct mii_softc *); 10192739Salfredstatic void mlphy_status(struct mii_softc *); 10250461Swpaul 103105135Salfredstatic int 104105135Salfredmlphy_probe(dev) 10550461Swpaul device_t dev; 10650461Swpaul{ 10750461Swpaul struct mii_attach_args *ma; 10850461Swpaul device_t parent; 10950461Swpaul 11050461Swpaul ma = device_get_ivars(dev); 11150461Swpaul parent = device_get_parent(device_get_parent(dev)); 11250461Swpaul 11350461Swpaul /* 11450461Swpaul * Micro Linear PHY reports oui == 0 model == 0 11550461Swpaul */ 11659235Swpaul if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 || 11750461Swpaul MII_MODEL(ma->mii_id2) != 0) 11850461Swpaul return (ENXIO); 11950461Swpaul 12050461Swpaul /* 12150461Swpaul * Make sure the parent is a `tl'. So far, I have only 12250461Swpaul * encountered the 6692 on an Olicom card with a ThunderLAN 12350461Swpaul * controller chip. 12450461Swpaul */ 12550461Swpaul if (strcmp(device_get_name(parent), "tl") != 0) 12650461Swpaul return (ENXIO); 12750461Swpaul 12850461Swpaul device_set_desc(dev, "Micro Linear 6692 media interface"); 12950461Swpaul 130160076Syongari return (BUS_PROBE_DEFAULT); 13150461Swpaul} 13250461Swpaul 133105135Salfredstatic int 134105135Salfredmlphy_attach(dev) 13550461Swpaul device_t dev; 13650461Swpaul{ 13750461Swpaul struct mlphy_softc *msc; 13850461Swpaul struct mii_softc *sc; 13950461Swpaul struct mii_attach_args *ma; 14050461Swpaul struct mii_data *mii; 14150461Swpaul 14250461Swpaul msc = device_get_softc(dev); 14350461Swpaul sc = &msc->ml_mii; 14450461Swpaul ma = device_get_ivars(dev); 14550461Swpaul sc->mii_dev = device_get_parent(dev); 146213229Smarius mii = ma->mii_data; 14750461Swpaul LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 14850461Swpaul 149213364Smarius sc->mii_inst = mii->mii_instance++; 15050461Swpaul sc->mii_phy = ma->mii_phyno; 15150461Swpaul sc->mii_service = mlphy_service; 15250461Swpaul sc->mii_pdata = mii; 15350461Swpaul 15450461Swpaul#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 15550461Swpaul 15650461Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 15750461Swpaul BMCR_LOOP|BMCR_S100); 15850461Swpaul 15950461Swpaul mii_phy_reset(sc); 16050461Swpaul 161213364Smarius sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 16250461Swpaul ma->mii_capmask = ~sc->mii_capabilities; 16350461Swpaul device_printf(dev, " "); 16495667Sphk mii_add_media(sc); 16550461Swpaul printf("\n"); 16650461Swpaul#undef ADD 16750461Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 168164830Smarius return (0); 16950461Swpaul} 17050461Swpaul 171182067Simpstatic struct mii_softc * 172182067Simpmlphy_find_other(device_t mii) 173182067Simp{ 174182067Simp device_t *devlist; 175182067Simp struct mii_softc *retval; 176182067Simp int i, devs; 177182067Simp 178182067Simp retval = NULL; 179182067Simp if (device_get_children(mii, &devlist, &devs)) 180182067Simp return (NULL); 181182067Simp for (i = 0; i < devs; i++) 182182067Simp if (strcmp(device_get_name(devlist[i]), "mlphy")) { 183182067Simp retval = device_get_softc(devlist[i]); 184182067Simp break; 185182067Simp } 186182067Simp free(devlist, M_TEMP); 187182067Simp return (retval); 188182067Simp} 189182067Simp 19050461Swpaulstatic int 19150461Swpaulmlphy_service(xsc, mii, cmd) 19250461Swpaul struct mii_softc *xsc; 19350461Swpaul struct mii_data *mii; 19450461Swpaul int cmd; 19550461Swpaul{ 19650461Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 19750461Swpaul struct mii_softc *other = NULL; 19850461Swpaul struct mlphy_softc *msc = (struct mlphy_softc *)xsc; 19950461Swpaul struct mii_softc *sc = (struct mii_softc *)&msc->ml_mii; 200182067Simp int other_inst, reg; 20150461Swpaul 20250461Swpaul /* 20350461Swpaul * See if there's another PHY on this bus with us. 20450461Swpaul * If so, we may need it for 10Mbps modes. 20550461Swpaul */ 206182067Simp other = mlphy_find_other(msc->ml_mii.mii_dev); 20750461Swpaul 20850461Swpaul switch (cmd) { 20950461Swpaul case MII_POLLSTAT: 21050461Swpaul break; 21150461Swpaul 21250461Swpaul case MII_MEDIACHG: 21350461Swpaul /* 21450461Swpaul * If the interface is not up, don't do anything. 21550461Swpaul */ 21650461Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 21750461Swpaul break; 21850461Swpaul 21950461Swpaul switch (IFM_SUBTYPE(ife->ifm_media)) { 22050461Swpaul case IFM_AUTO: 22150461Swpaul /* 22250461Swpaul * For autonegotiation, reset and isolate the 22350461Swpaul * companion PHY (if any) and then do NWAY 22450461Swpaul * autonegotiation ourselves. 22550461Swpaul */ 22650461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 22750461Swpaul if (other != NULL) { 22850461Swpaul mii_phy_reset(other); 22950461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 23050461Swpaul } 23196026Sphk (void) mii_phy_auto(sc); 23250461Swpaul msc->ml_linked = 0; 233164830Smarius return (0); 23450461Swpaul case IFM_10_T: 23550461Swpaul /* 23650461Swpaul * For 10baseT modes, reset and program the 23750461Swpaul * companion PHY (of any), then program ourselves 23850461Swpaul * to match. This will put us in pass-through 23950461Swpaul * mode and let the companion PHY do all the 24050461Swpaul * work. 24150461Swpaul * 24250461Swpaul * BMCR data is stored in the ifmedia entry. 24350461Swpaul */ 24450461Swpaul if (other != NULL) { 24550461Swpaul mii_phy_reset(other); 24650461Swpaul PHY_WRITE(other, MII_BMCR, ife->ifm_data); 24750461Swpaul } 24850461Swpaul PHY_WRITE(sc, MII_ANAR, mii_anar(ife->ifm_media)); 24950461Swpaul PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 25050461Swpaul msc->ml_state = 0; 25150461Swpaul break; 25250461Swpaul case IFM_100_TX: 25350461Swpaul /* 25450461Swpaul * For 100baseTX modes, reset and isolate the 25550461Swpaul * companion PHY (if any), then program ourselves 25650461Swpaul * accordingly. 25750461Swpaul * 25850461Swpaul * BMCR data is stored in the ifmedia entry. 25950461Swpaul */ 26050461Swpaul if (other != NULL) { 26150461Swpaul mii_phy_reset(other); 26250461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 26350461Swpaul } 26450461Swpaul PHY_WRITE(sc, MII_ANAR, mii_anar(ife->ifm_media)); 26550461Swpaul PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 26650461Swpaul msc->ml_state = 0; 26750461Swpaul break; 26850461Swpaul case IFM_100_T4: 26950461Swpaul /* 27050461Swpaul * XXX Not supported as a manual setting right now. 27150461Swpaul */ 27250461Swpaul return (EINVAL); 27350461Swpaul default: 27450461Swpaul break; 27550461Swpaul 27650461Swpaul } 27750461Swpaul break; 27850461Swpaul 27950461Swpaul case MII_TICK: 28050461Swpaul /* 28150461Swpaul * Is the interface even up? 28250461Swpaul */ 28350461Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 28450461Swpaul return (0); 28550461Swpaul 28650461Swpaul /* 28784145Sjlemon * Only used for autonegotiation. 28850461Swpaul */ 28984145Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 29084145Sjlemon break; 29150461Swpaul 29250461Swpaul /* 29350461Swpaul * Check to see if we have link. If we do, we don't 29450461Swpaul * need to restart the autonegotiation process. Read 29550461Swpaul * the BMSR twice in case it's latched. 29650461Swpaul * If we're in a 10Mbps mode, check the link of the 29750461Swpaul * 10Mbps PHY. Sometimes the Micro Linear PHY's 29850461Swpaul * linkstat bit will clear while the linkstat bit of 29950461Swpaul * the companion PHY will remain set. 30050461Swpaul */ 30150461Swpaul if (msc->ml_state == ML_STATE_AUTO_OTHER) { 30250461Swpaul reg = PHY_READ(other, MII_BMSR) | 30350461Swpaul PHY_READ(other, MII_BMSR); 30450461Swpaul } else { 30550461Swpaul reg = PHY_READ(sc, MII_BMSR) | 30650461Swpaul PHY_READ(sc, MII_BMSR); 30750461Swpaul } 30850461Swpaul 30950461Swpaul if (reg & BMSR_LINK) { 31050461Swpaul if (!msc->ml_linked) { 31150461Swpaul msc->ml_linked = 1; 31250461Swpaul mlphy_status(sc); 31350461Swpaul } 31484145Sjlemon break; 31550461Swpaul } 31650461Swpaul 31784145Sjlemon /* 31884145Sjlemon * Only retry autonegotiation every 5 seconds. 31984145Sjlemon */ 320164830Smarius if (++sc->mii_ticks <= MII_ANEGTICKS) 321128870Sandre break; 322164830Smarius 32384145Sjlemon sc->mii_ticks = 0; 32450461Swpaul msc->ml_linked = 0; 32550461Swpaul mii->mii_media_active = IFM_NONE; 32650461Swpaul mii_phy_reset(sc); 32750461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 32850461Swpaul if (other != NULL) { 32950461Swpaul mii_phy_reset(other); 33050461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 33150461Swpaul } 33296026Sphk mii_phy_auto(sc); 333164830Smarius return (0); 33450461Swpaul } 33550461Swpaul 33650461Swpaul /* Update the media status. */ 33750461Swpaul 33850461Swpaul if (msc->ml_state == ML_STATE_AUTO_OTHER) { 33950461Swpaul other_inst = other->mii_inst; 34050461Swpaul other->mii_inst = sc->mii_inst; 341213364Smarius if (IFM_INST(ife->ifm_media) == other->mii_inst) 342213364Smarius (void)(*other->mii_service)(other, mii, MII_POLLSTAT); 34350461Swpaul other->mii_inst = other_inst; 34495705Sphk sc->mii_media_active = other->mii_media_active; 34595705Sphk sc->mii_media_status = other->mii_media_status; 34650461Swpaul } else 34750461Swpaul ukphy_status(sc); 34850461Swpaul 34950461Swpaul /* Callback if something changed. */ 35084145Sjlemon mii_phy_update(sc, cmd); 35150461Swpaul return (0); 35250461Swpaul} 35350461Swpaul 35450461Swpaul/* 35550461Swpaul * The Micro Linear PHY comes out of reset with the 'autoneg 35650461Swpaul * enable' bit set, which we don't want. 35750461Swpaul */ 358105135Salfredstatic void 359105135Salfredmlphy_reset(sc) 36050461Swpaul struct mii_softc *sc; 36150461Swpaul{ 36250461Swpaul int reg; 36350461Swpaul 36450461Swpaul mii_phy_reset(sc); 36550461Swpaul reg = PHY_READ(sc, MII_BMCR); 36650461Swpaul reg &= ~BMCR_AUTOEN; 36750461Swpaul PHY_WRITE(sc, MII_BMCR, reg); 36850461Swpaul} 36950461Swpaul 37050461Swpaul/* 37150461Swpaul * If we negotiate a 10Mbps mode, we need to check for an alternate 37250461Swpaul * PHY and make sure it's enabled and set correctly. 37350461Swpaul */ 374105135Salfredstatic void 375105135Salfredmlphy_status(sc) 37650461Swpaul struct mii_softc *sc; 37750461Swpaul{ 37850461Swpaul struct mlphy_softc *msc = (struct mlphy_softc *)sc; 37950461Swpaul struct mii_data *mii = msc->ml_mii.mii_pdata; 38050461Swpaul struct mii_softc *other = NULL; 38150461Swpaul 38250461Swpaul /* See if there's another PHY on the bus with us. */ 383182067Simp other = mlphy_find_other(msc->ml_mii.mii_dev); 38450461Swpaul if (other == NULL) 38550461Swpaul return; 38650461Swpaul 38750461Swpaul ukphy_status(sc); 38850461Swpaul 38950461Swpaul if (IFM_SUBTYPE(mii->mii_media_active) != IFM_10_T) { 39050461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 39150461Swpaul mii_phy_reset(other); 39250461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 39350461Swpaul } 39450461Swpaul 39550461Swpaul if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 39650461Swpaul msc->ml_state = ML_STATE_AUTO_OTHER; 39750461Swpaul mlphy_reset(&msc->ml_mii); 39850461Swpaul PHY_WRITE(&msc->ml_mii, MII_BMCR, BMCR_ISO); 39950461Swpaul mii_phy_reset(other); 40096026Sphk mii_phy_auto(other); 40150461Swpaul } 40250461Swpaul} 403