mlphy.c revision 119418
150461Swpaul/* 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 119418 2003-08-24 17:55:58Z obrien $"); 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 13050461Swpaul return (0); 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); 14650461Swpaul mii = device_get_softc(sc->mii_dev); 14750461Swpaul LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 14850461Swpaul 14950461Swpaul 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 mii->mii_instance++; 15550461Swpaul 15650461Swpaul#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 15750461Swpaul 15850461Swpaul#if 0 /* See above. */ 15950461Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 16050461Swpaul BMCR_ISO); 16150461Swpaul#endif 16250461Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 16350461Swpaul BMCR_LOOP|BMCR_S100); 16450461Swpaul 16550461Swpaul sc->mii_flags &= ~MIIF_NOISOLATE; 16650461Swpaul mii_phy_reset(sc); 16750461Swpaul sc->mii_flags |= MIIF_NOISOLATE; 16850461Swpaul 16950461Swpaul sc->mii_capabilities = 17050461Swpaul PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 17150461Swpaul ma->mii_capmask = ~sc->mii_capabilities; 17250461Swpaul device_printf(dev, " "); 17395667Sphk mii_add_media(sc); 17450461Swpaul printf("\n"); 17550461Swpaul#undef ADD 17650461Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 17750461Swpaul return(0); 17850461Swpaul} 17950461Swpaul 18050461Swpaulstatic int 18150461Swpaulmlphy_service(xsc, mii, cmd) 18250461Swpaul struct mii_softc *xsc; 18350461Swpaul struct mii_data *mii; 18450461Swpaul int cmd; 18550461Swpaul{ 18650461Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 18750461Swpaul int reg; 18850461Swpaul struct mii_softc *other = NULL; 18950461Swpaul struct mlphy_softc *msc = (struct mlphy_softc *)xsc; 19050461Swpaul struct mii_softc *sc = (struct mii_softc *)&msc->ml_mii; 19150461Swpaul device_t *devlist; 19250461Swpaul int devs, i; 19350461Swpaul 19450461Swpaul /* 19550461Swpaul * See if there's another PHY on this bus with us. 19650461Swpaul * If so, we may need it for 10Mbps modes. 19750461Swpaul */ 19850461Swpaul device_get_children(msc->ml_mii.mii_dev, &devlist, &devs); 19950461Swpaul for (i = 0; i < devs; i++) { 20050461Swpaul if (strcmp(device_get_name(devlist[i]), "mlphy")) { 20150461Swpaul other = device_get_softc(devlist[i]); 20250461Swpaul break; 20350461Swpaul } 20450461Swpaul } 20572186Sasmodai free(devlist, M_TEMP); 20650461Swpaul 20750461Swpaul switch (cmd) { 20850461Swpaul case MII_POLLSTAT: 20950461Swpaul /* 21050461Swpaul * If we're not polling our PHY instance, just return. 21150461Swpaul */ 21250461Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 21350461Swpaul return (0); 21450461Swpaul break; 21550461Swpaul 21650461Swpaul case MII_MEDIACHG: 21750461Swpaul /* 21850461Swpaul * If the media indicates a different PHY instance, 21950461Swpaul * isolate ourselves. 22050461Swpaul */ 22150461Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 22250461Swpaul reg = PHY_READ(sc, MII_BMCR); 22350461Swpaul PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 22450461Swpaul return (0); 22550461Swpaul } 22650461Swpaul 22750461Swpaul /* 22850461Swpaul * If the interface is not up, don't do anything. 22950461Swpaul */ 23050461Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 23150461Swpaul break; 23250461Swpaul 23350461Swpaul switch (IFM_SUBTYPE(ife->ifm_media)) { 23450461Swpaul case IFM_AUTO: 23550461Swpaul /* 23650461Swpaul * For autonegotiation, reset and isolate the 23750461Swpaul * companion PHY (if any) and then do NWAY 23850461Swpaul * autonegotiation ourselves. 23950461Swpaul */ 24050461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 24150461Swpaul if (other != NULL) { 24250461Swpaul mii_phy_reset(other); 24350461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 24450461Swpaul } 24596026Sphk (void) mii_phy_auto(sc); 24650461Swpaul msc->ml_linked = 0; 24750461Swpaul return(0); 24850461Swpaul case IFM_10_T: 24950461Swpaul /* 25050461Swpaul * For 10baseT modes, reset and program the 25150461Swpaul * companion PHY (of any), then program ourselves 25250461Swpaul * to match. This will put us in pass-through 25350461Swpaul * mode and let the companion PHY do all the 25450461Swpaul * work. 25550461Swpaul * 25650461Swpaul * BMCR data is stored in the ifmedia entry. 25750461Swpaul */ 25850461Swpaul if (other != NULL) { 25950461Swpaul mii_phy_reset(other); 26050461Swpaul PHY_WRITE(other, MII_BMCR, ife->ifm_data); 26150461Swpaul } 26250461Swpaul PHY_WRITE(sc, MII_ANAR, mii_anar(ife->ifm_media)); 26350461Swpaul PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 26450461Swpaul msc->ml_state = 0; 26550461Swpaul break; 26650461Swpaul case IFM_100_TX: 26750461Swpaul /* 26850461Swpaul * For 100baseTX modes, reset and isolate the 26950461Swpaul * companion PHY (if any), then program ourselves 27050461Swpaul * accordingly. 27150461Swpaul * 27250461Swpaul * BMCR data is stored in the ifmedia entry. 27350461Swpaul */ 27450461Swpaul if (other != NULL) { 27550461Swpaul mii_phy_reset(other); 27650461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 27750461Swpaul } 27850461Swpaul PHY_WRITE(sc, MII_ANAR, mii_anar(ife->ifm_media)); 27950461Swpaul PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 28050461Swpaul msc->ml_state = 0; 28150461Swpaul break; 28250461Swpaul case IFM_100_T4: 28350461Swpaul /* 28450461Swpaul * XXX Not supported as a manual setting right now. 28550461Swpaul */ 28650461Swpaul return (EINVAL); 28750461Swpaul default: 28850461Swpaul break; 28950461Swpaul 29050461Swpaul } 29150461Swpaul break; 29250461Swpaul 29350461Swpaul case MII_TICK: 29450461Swpaul /* 29550461Swpaul * If we're not currently selected, just return. 29650461Swpaul */ 29750461Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 29850461Swpaul return (0); 29950461Swpaul 30050461Swpaul /* 30150461Swpaul * Is the interface even up? 30250461Swpaul */ 30350461Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 30450461Swpaul return (0); 30550461Swpaul 30650461Swpaul /* 30784145Sjlemon * Only used for autonegotiation. 30850461Swpaul */ 30984145Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 31084145Sjlemon break; 31150461Swpaul 31250461Swpaul /* 31350461Swpaul * Check to see if we have link. If we do, we don't 31450461Swpaul * need to restart the autonegotiation process. Read 31550461Swpaul * the BMSR twice in case it's latched. 31650461Swpaul * If we're in a 10Mbps mode, check the link of the 31750461Swpaul * 10Mbps PHY. Sometimes the Micro Linear PHY's 31850461Swpaul * linkstat bit will clear while the linkstat bit of 31950461Swpaul * the companion PHY will remain set. 32050461Swpaul */ 32150461Swpaul if (msc->ml_state == ML_STATE_AUTO_OTHER) { 32250461Swpaul reg = PHY_READ(other, MII_BMSR) | 32350461Swpaul PHY_READ(other, MII_BMSR); 32450461Swpaul } else { 32550461Swpaul reg = PHY_READ(sc, MII_BMSR) | 32650461Swpaul PHY_READ(sc, MII_BMSR); 32750461Swpaul } 32850461Swpaul 32950461Swpaul if (reg & BMSR_LINK) { 33050461Swpaul if (!msc->ml_linked) { 33150461Swpaul msc->ml_linked = 1; 33250461Swpaul mlphy_status(sc); 33350461Swpaul } 33484145Sjlemon break; 33550461Swpaul } 33650461Swpaul 33784145Sjlemon /* 33884145Sjlemon * Only retry autonegotiation every 5 seconds. 33984145Sjlemon */ 34084145Sjlemon if (++sc->mii_ticks != 5) 34184145Sjlemon return (0); 34284145Sjlemon 34384145Sjlemon sc->mii_ticks = 0; 34450461Swpaul msc->ml_linked = 0; 34550461Swpaul mii->mii_media_active = IFM_NONE; 34650461Swpaul mii_phy_reset(sc); 34750461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 34850461Swpaul if (other != NULL) { 34950461Swpaul mii_phy_reset(other); 35050461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 35150461Swpaul } 35296026Sphk mii_phy_auto(sc); 35396026Sphk return(0); 35450461Swpaul } 35550461Swpaul 35650461Swpaul /* Update the media status. */ 35750461Swpaul 35850461Swpaul if (msc->ml_state == ML_STATE_AUTO_OTHER) { 35950461Swpaul int other_inst; 36050461Swpaul other_inst = other->mii_inst; 36150461Swpaul other->mii_inst = sc->mii_inst; 36250461Swpaul (void) (*other->mii_service)(other, mii, MII_POLLSTAT); 36350461Swpaul other->mii_inst = other_inst; 36495705Sphk sc->mii_media_active = other->mii_media_active; 36595705Sphk sc->mii_media_status = other->mii_media_status; 36650461Swpaul } else 36750461Swpaul ukphy_status(sc); 36850461Swpaul 36950461Swpaul /* Callback if something changed. */ 37084145Sjlemon mii_phy_update(sc, cmd); 37150461Swpaul return (0); 37250461Swpaul} 37350461Swpaul 37450461Swpaul/* 37550461Swpaul * The Micro Linear PHY comes out of reset with the 'autoneg 37650461Swpaul * enable' bit set, which we don't want. 37750461Swpaul */ 378105135Salfredstatic void 379105135Salfredmlphy_reset(sc) 38050461Swpaul struct mii_softc *sc; 38150461Swpaul{ 38250461Swpaul int reg; 38350461Swpaul 38450461Swpaul mii_phy_reset(sc); 38550461Swpaul reg = PHY_READ(sc, MII_BMCR); 38650461Swpaul reg &= ~BMCR_AUTOEN; 38750461Swpaul PHY_WRITE(sc, MII_BMCR, reg); 38850461Swpaul 38950461Swpaul return; 39050461Swpaul} 39150461Swpaul 39250461Swpaul/* 39350461Swpaul * If we negotiate a 10Mbps mode, we need to check for an alternate 39450461Swpaul * PHY and make sure it's enabled and set correctly. 39550461Swpaul */ 396105135Salfredstatic void 397105135Salfredmlphy_status(sc) 39850461Swpaul struct mii_softc *sc; 39950461Swpaul{ 40050461Swpaul struct mlphy_softc *msc = (struct mlphy_softc *)sc; 40150461Swpaul struct mii_data *mii = msc->ml_mii.mii_pdata; 40250461Swpaul struct mii_softc *other = NULL; 40350461Swpaul device_t *devlist; 40450461Swpaul int devs, i; 40550461Swpaul 40650461Swpaul /* See if there's another PHY on the bus with us. */ 40750461Swpaul device_get_children(msc->ml_mii.mii_dev, &devlist, &devs); 40850461Swpaul for (i = 0; i < devs; i++) { 40950461Swpaul if (strcmp(device_get_name(devlist[i]), "mlphy")) { 41050461Swpaul other = device_get_softc(devlist[i]); 41150461Swpaul break; 41250461Swpaul } 41350461Swpaul } 41472186Sasmodai free(devlist, M_TEMP); 41550461Swpaul 41650461Swpaul if (other == NULL) 41750461Swpaul return; 41850461Swpaul 41950461Swpaul ukphy_status(sc); 42050461Swpaul 42150461Swpaul if (IFM_SUBTYPE(mii->mii_media_active) != IFM_10_T) { 42250461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 42350461Swpaul mii_phy_reset(other); 42450461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 42550461Swpaul } 42650461Swpaul 42750461Swpaul if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 42850461Swpaul msc->ml_state = ML_STATE_AUTO_OTHER; 42950461Swpaul mlphy_reset(&msc->ml_mii); 43050461Swpaul PHY_WRITE(&msc->ml_mii, MII_BMCR, BMCR_ISO); 43150461Swpaul mii_phy_reset(other); 43296026Sphk mii_phy_auto(other); 43350461Swpaul } 43450461Swpaul 43550461Swpaul return; 43650461Swpaul} 437