mlphy.c revision 96026
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 * 3250480Speter * $FreeBSD: head/sys/dev/mii/mlphy.c 96026 2002-05-04 11:00:30Z phk $ 3350461Swpaul */ 3450461Swpaul 3550461Swpaul/* 3650461Swpaul * driver for Micro Linear 6692 PHYs 3750461Swpaul * 3850461Swpaul * The Micro Linear 6692 is a strange beast, and dealing with it using 3950461Swpaul * this code framework is tricky. The 6692 is actually a 100Mbps-only 4050461Swpaul * device, which means that a second PHY is required to support 10Mbps 4150461Swpaul * modes. However, even though the 6692 does not support 10Mbps modes, 4250461Swpaul * it can still advertise them when performing autonegotiation. If a 4350461Swpaul * 10Mbps mode is negotiated, we must program the registers of the 4450461Swpaul * companion PHY accordingly in addition to programming the registers 4550461Swpaul * of the 6692. 4650461Swpaul * 4750461Swpaul * This device also does not have vendor/device ID registers. 4850461Swpaul */ 4950461Swpaul 5050461Swpaul#include <sys/param.h> 5150461Swpaul#include <sys/systm.h> 5250461Swpaul#include <sys/kernel.h> 5350461Swpaul#include <sys/socket.h> 5450461Swpaul#include <sys/module.h> 5550461Swpaul#include <sys/bus.h> 5672196Sjhb#include <sys/malloc.h> 5750461Swpaul 5850461Swpaul#include <net/if.h> 5950461Swpaul#include <net/if_media.h> 6050461Swpaul 6150461Swpaul#include <dev/mii/mii.h> 6250461Swpaul#include <dev/mii/miivar.h> 6350461Swpaul 6450461Swpaul#include "miibus_if.h" 6550461Swpaul 6650461Swpaul#define ML_STATE_AUTO_SELF 1 6750461Swpaul#define ML_STATE_AUTO_OTHER 2 6850461Swpaul 6950461Swpaulstruct mlphy_softc { 7050461Swpaul struct mii_softc ml_mii; 7150461Swpaul int ml_state; 7250461Swpaul int ml_linked; 7350461Swpaul}; 7450461Swpaul 7592739Salfredstatic int mlphy_probe (device_t); 7692739Salfredstatic int mlphy_attach (device_t); 7750461Swpaul 7850461Swpaulstatic device_method_t mlphy_methods[] = { 7950461Swpaul /* device interface */ 8050461Swpaul DEVMETHOD(device_probe, mlphy_probe), 8150461Swpaul DEVMETHOD(device_attach, mlphy_attach), 8295722Sphk DEVMETHOD(device_detach, mii_phy_detach), 8350461Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 8450461Swpaul { 0, 0 } 8550461Swpaul}; 8650461Swpaul 8750461Swpaulstatic devclass_t mlphy_devclass; 8850461Swpaul 8950461Swpaulstatic driver_t mlphy_driver = { 9050461Swpaul "mlphy", 9150461Swpaul mlphy_methods, 9250461Swpaul sizeof(struct mlphy_softc) 9350461Swpaul}; 9450461Swpaul 9550461SwpaulDRIVER_MODULE(mlphy, miibus, mlphy_driver, mlphy_devclass, 0, 0); 9650461Swpaul 9792739Salfredstatic int mlphy_service(struct mii_softc *, struct mii_data *, int); 9892739Salfredstatic void mlphy_reset(struct mii_softc *); 9992739Salfredstatic void mlphy_status(struct mii_softc *); 10050461Swpaul 10150461Swpaulstatic int mlphy_probe(dev) 10250461Swpaul device_t dev; 10350461Swpaul{ 10450461Swpaul struct mii_attach_args *ma; 10550461Swpaul device_t parent; 10650461Swpaul 10750461Swpaul ma = device_get_ivars(dev); 10850461Swpaul parent = device_get_parent(device_get_parent(dev)); 10950461Swpaul 11050461Swpaul /* 11150461Swpaul * Micro Linear PHY reports oui == 0 model == 0 11250461Swpaul */ 11359235Swpaul if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 || 11450461Swpaul MII_MODEL(ma->mii_id2) != 0) 11550461Swpaul return (ENXIO); 11650461Swpaul 11750461Swpaul /* 11850461Swpaul * Make sure the parent is a `tl'. So far, I have only 11950461Swpaul * encountered the 6692 on an Olicom card with a ThunderLAN 12050461Swpaul * controller chip. 12150461Swpaul */ 12250461Swpaul if (strcmp(device_get_name(parent), "tl") != 0) 12350461Swpaul return (ENXIO); 12450461Swpaul 12550461Swpaul device_set_desc(dev, "Micro Linear 6692 media interface"); 12650461Swpaul 12750461Swpaul return (0); 12850461Swpaul} 12950461Swpaul 13050461Swpaulstatic int mlphy_attach(dev) 13150461Swpaul device_t dev; 13250461Swpaul{ 13350461Swpaul struct mlphy_softc *msc; 13450461Swpaul struct mii_softc *sc; 13550461Swpaul struct mii_attach_args *ma; 13650461Swpaul struct mii_data *mii; 13750461Swpaul 13850461Swpaul msc = device_get_softc(dev); 13950461Swpaul sc = &msc->ml_mii; 14050461Swpaul ma = device_get_ivars(dev); 14150461Swpaul sc->mii_dev = device_get_parent(dev); 14250461Swpaul mii = device_get_softc(sc->mii_dev); 14350461Swpaul LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 14450461Swpaul 14550461Swpaul sc->mii_inst = mii->mii_instance; 14650461Swpaul sc->mii_phy = ma->mii_phyno; 14750461Swpaul sc->mii_service = mlphy_service; 14850461Swpaul sc->mii_pdata = mii; 14950461Swpaul 15050461Swpaul mii->mii_instance++; 15150461Swpaul 15250461Swpaul#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 15350461Swpaul 15450461Swpaul#if 0 /* See above. */ 15550461Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 15650461Swpaul BMCR_ISO); 15750461Swpaul#endif 15850461Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 15950461Swpaul BMCR_LOOP|BMCR_S100); 16050461Swpaul 16150461Swpaul sc->mii_flags &= ~MIIF_NOISOLATE; 16250461Swpaul mii_phy_reset(sc); 16350461Swpaul sc->mii_flags |= MIIF_NOISOLATE; 16450461Swpaul 16550461Swpaul sc->mii_capabilities = 16650461Swpaul PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 16750461Swpaul ma->mii_capmask = ~sc->mii_capabilities; 16850461Swpaul device_printf(dev, " "); 16995667Sphk mii_add_media(sc); 17050461Swpaul printf("\n"); 17150461Swpaul#undef ADD 17250461Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 17350461Swpaul return(0); 17450461Swpaul} 17550461Swpaul 17650461Swpaulstatic int 17750461Swpaulmlphy_service(xsc, mii, cmd) 17850461Swpaul struct mii_softc *xsc; 17950461Swpaul struct mii_data *mii; 18050461Swpaul int cmd; 18150461Swpaul{ 18250461Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 18350461Swpaul int reg; 18450461Swpaul struct mii_softc *other = NULL; 18550461Swpaul struct mlphy_softc *msc = (struct mlphy_softc *)xsc; 18650461Swpaul struct mii_softc *sc = (struct mii_softc *)&msc->ml_mii; 18750461Swpaul device_t *devlist; 18850461Swpaul int devs, i; 18950461Swpaul 19050461Swpaul /* 19150461Swpaul * See if there's another PHY on this bus with us. 19250461Swpaul * If so, we may need it for 10Mbps modes. 19350461Swpaul */ 19450461Swpaul device_get_children(msc->ml_mii.mii_dev, &devlist, &devs); 19550461Swpaul for (i = 0; i < devs; i++) { 19650461Swpaul if (strcmp(device_get_name(devlist[i]), "mlphy")) { 19750461Swpaul other = device_get_softc(devlist[i]); 19850461Swpaul break; 19950461Swpaul } 20050461Swpaul } 20172186Sasmodai free(devlist, M_TEMP); 20250461Swpaul 20350461Swpaul switch (cmd) { 20450461Swpaul case MII_POLLSTAT: 20550461Swpaul /* 20650461Swpaul * If we're not polling our PHY instance, just return. 20750461Swpaul */ 20850461Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 20950461Swpaul return (0); 21050461Swpaul break; 21150461Swpaul 21250461Swpaul case MII_MEDIACHG: 21350461Swpaul /* 21450461Swpaul * If the media indicates a different PHY instance, 21550461Swpaul * isolate ourselves. 21650461Swpaul */ 21750461Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 21850461Swpaul reg = PHY_READ(sc, MII_BMCR); 21950461Swpaul PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 22050461Swpaul return (0); 22150461Swpaul } 22250461Swpaul 22350461Swpaul /* 22450461Swpaul * If the interface is not up, don't do anything. 22550461Swpaul */ 22650461Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 22750461Swpaul break; 22850461Swpaul 22950461Swpaul switch (IFM_SUBTYPE(ife->ifm_media)) { 23050461Swpaul case IFM_AUTO: 23150461Swpaul /* 23250461Swpaul * For autonegotiation, reset and isolate the 23350461Swpaul * companion PHY (if any) and then do NWAY 23450461Swpaul * autonegotiation ourselves. 23550461Swpaul */ 23650461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 23750461Swpaul if (other != NULL) { 23850461Swpaul mii_phy_reset(other); 23950461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 24050461Swpaul } 24196026Sphk (void) mii_phy_auto(sc); 24250461Swpaul msc->ml_linked = 0; 24350461Swpaul return(0); 24450461Swpaul break; 24550461Swpaul case IFM_10_T: 24650461Swpaul /* 24750461Swpaul * For 10baseT modes, reset and program the 24850461Swpaul * companion PHY (of any), then program ourselves 24950461Swpaul * to match. This will put us in pass-through 25050461Swpaul * mode and let the companion PHY do all the 25150461Swpaul * work. 25250461Swpaul * 25350461Swpaul * BMCR data is stored in the ifmedia entry. 25450461Swpaul */ 25550461Swpaul if (other != NULL) { 25650461Swpaul mii_phy_reset(other); 25750461Swpaul PHY_WRITE(other, MII_BMCR, ife->ifm_data); 25850461Swpaul } 25950461Swpaul PHY_WRITE(sc, MII_ANAR, mii_anar(ife->ifm_media)); 26050461Swpaul PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 26150461Swpaul msc->ml_state = 0; 26250461Swpaul break; 26350461Swpaul case IFM_100_TX: 26450461Swpaul /* 26550461Swpaul * For 100baseTX modes, reset and isolate the 26650461Swpaul * companion PHY (if any), then program ourselves 26750461Swpaul * accordingly. 26850461Swpaul * 26950461Swpaul * BMCR data is stored in the ifmedia entry. 27050461Swpaul */ 27150461Swpaul if (other != NULL) { 27250461Swpaul mii_phy_reset(other); 27350461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 27450461Swpaul } 27550461Swpaul PHY_WRITE(sc, MII_ANAR, mii_anar(ife->ifm_media)); 27650461Swpaul PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 27750461Swpaul msc->ml_state = 0; 27850461Swpaul break; 27950461Swpaul case IFM_100_T4: 28050461Swpaul /* 28150461Swpaul * XXX Not supported as a manual setting right now. 28250461Swpaul */ 28350461Swpaul return (EINVAL); 28450461Swpaul default: 28550461Swpaul break; 28650461Swpaul 28750461Swpaul } 28850461Swpaul break; 28950461Swpaul 29050461Swpaul case MII_TICK: 29150461Swpaul /* 29250461Swpaul * If we're not currently selected, just return. 29350461Swpaul */ 29450461Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 29550461Swpaul return (0); 29650461Swpaul 29750461Swpaul /* 29850461Swpaul * Is the interface even up? 29950461Swpaul */ 30050461Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 30150461Swpaul return (0); 30250461Swpaul 30350461Swpaul /* 30484145Sjlemon * Only used for autonegotiation. 30550461Swpaul */ 30684145Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 30784145Sjlemon break; 30850461Swpaul 30950461Swpaul /* 31050461Swpaul * Check to see if we have link. If we do, we don't 31150461Swpaul * need to restart the autonegotiation process. Read 31250461Swpaul * the BMSR twice in case it's latched. 31350461Swpaul * If we're in a 10Mbps mode, check the link of the 31450461Swpaul * 10Mbps PHY. Sometimes the Micro Linear PHY's 31550461Swpaul * linkstat bit will clear while the linkstat bit of 31650461Swpaul * the companion PHY will remain set. 31750461Swpaul */ 31850461Swpaul if (msc->ml_state == ML_STATE_AUTO_OTHER) { 31950461Swpaul reg = PHY_READ(other, MII_BMSR) | 32050461Swpaul PHY_READ(other, MII_BMSR); 32150461Swpaul } else { 32250461Swpaul reg = PHY_READ(sc, MII_BMSR) | 32350461Swpaul PHY_READ(sc, MII_BMSR); 32450461Swpaul } 32550461Swpaul 32650461Swpaul if (reg & BMSR_LINK) { 32750461Swpaul if (!msc->ml_linked) { 32850461Swpaul msc->ml_linked = 1; 32950461Swpaul mlphy_status(sc); 33050461Swpaul } 33184145Sjlemon break; 33250461Swpaul } 33350461Swpaul 33484145Sjlemon /* 33584145Sjlemon * Only retry autonegotiation every 5 seconds. 33684145Sjlemon */ 33784145Sjlemon if (++sc->mii_ticks != 5) 33884145Sjlemon return (0); 33984145Sjlemon 34084145Sjlemon sc->mii_ticks = 0; 34150461Swpaul msc->ml_linked = 0; 34250461Swpaul mii->mii_media_active = IFM_NONE; 34350461Swpaul mii_phy_reset(sc); 34450461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 34550461Swpaul if (other != NULL) { 34650461Swpaul mii_phy_reset(other); 34750461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 34850461Swpaul } 34996026Sphk mii_phy_auto(sc); 35096026Sphk return(0); 35150461Swpaul } 35250461Swpaul 35350461Swpaul /* Update the media status. */ 35450461Swpaul 35550461Swpaul if (msc->ml_state == ML_STATE_AUTO_OTHER) { 35650461Swpaul int other_inst; 35750461Swpaul other_inst = other->mii_inst; 35850461Swpaul other->mii_inst = sc->mii_inst; 35950461Swpaul (void) (*other->mii_service)(other, mii, MII_POLLSTAT); 36050461Swpaul other->mii_inst = other_inst; 36195705Sphk sc->mii_media_active = other->mii_media_active; 36295705Sphk sc->mii_media_status = other->mii_media_status; 36350461Swpaul } else 36450461Swpaul ukphy_status(sc); 36550461Swpaul 36650461Swpaul /* Callback if something changed. */ 36784145Sjlemon mii_phy_update(sc, cmd); 36850461Swpaul return (0); 36950461Swpaul} 37050461Swpaul 37150461Swpaul/* 37250461Swpaul * The Micro Linear PHY comes out of reset with the 'autoneg 37350461Swpaul * enable' bit set, which we don't want. 37450461Swpaul */ 37550461Swpaulstatic void mlphy_reset(sc) 37650461Swpaul struct mii_softc *sc; 37750461Swpaul{ 37850461Swpaul int reg; 37950461Swpaul 38050461Swpaul mii_phy_reset(sc); 38150461Swpaul reg = PHY_READ(sc, MII_BMCR); 38250461Swpaul reg &= ~BMCR_AUTOEN; 38350461Swpaul PHY_WRITE(sc, MII_BMCR, reg); 38450461Swpaul 38550461Swpaul return; 38650461Swpaul} 38750461Swpaul 38850461Swpaul/* 38950461Swpaul * If we negotiate a 10Mbps mode, we need to check for an alternate 39050461Swpaul * PHY and make sure it's enabled and set correctly. 39150461Swpaul */ 39250461Swpaulstatic void mlphy_status(sc) 39350461Swpaul struct mii_softc *sc; 39450461Swpaul{ 39550461Swpaul struct mlphy_softc *msc = (struct mlphy_softc *)sc; 39650461Swpaul struct mii_data *mii = msc->ml_mii.mii_pdata; 39750461Swpaul struct mii_softc *other = NULL; 39850461Swpaul device_t *devlist; 39950461Swpaul int devs, i; 40050461Swpaul 40150461Swpaul /* See if there's another PHY on the bus with us. */ 40250461Swpaul device_get_children(msc->ml_mii.mii_dev, &devlist, &devs); 40350461Swpaul for (i = 0; i < devs; i++) { 40450461Swpaul if (strcmp(device_get_name(devlist[i]), "mlphy")) { 40550461Swpaul other = device_get_softc(devlist[i]); 40650461Swpaul break; 40750461Swpaul } 40850461Swpaul } 40972186Sasmodai free(devlist, M_TEMP); 41050461Swpaul 41150461Swpaul if (other == NULL) 41250461Swpaul return; 41350461Swpaul 41450461Swpaul ukphy_status(sc); 41550461Swpaul 41650461Swpaul if (IFM_SUBTYPE(mii->mii_media_active) != IFM_10_T) { 41750461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 41850461Swpaul mii_phy_reset(other); 41950461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 42050461Swpaul } 42150461Swpaul 42250461Swpaul if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 42350461Swpaul msc->ml_state = ML_STATE_AUTO_OTHER; 42450461Swpaul mlphy_reset(&msc->ml_mii); 42550461Swpaul PHY_WRITE(&msc->ml_mii, MII_BMCR, BMCR_ISO); 42650461Swpaul mii_phy_reset(other); 42796026Sphk mii_phy_auto(other); 42850461Swpaul } 42950461Swpaul 43050461Swpaul return; 43150461Swpaul} 432