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: releng/10.3/sys/dev/mii/mlphy.c 227908 2011-11-23 20:27:26Z 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; 73214264Smarius device_t ml_dev; 7450461Swpaul int ml_state; 7550461Swpaul int ml_linked; 7650461Swpaul}; 7750461Swpaul 78105135Salfredstatic int mlphy_probe(device_t); 79105135Salfredstatic int mlphy_attach(device_t); 8050461Swpaul 8150461Swpaulstatic device_method_t mlphy_methods[] = { 8250461Swpaul /* device interface */ 8350461Swpaul DEVMETHOD(device_probe, mlphy_probe), 8450461Swpaul DEVMETHOD(device_attach, mlphy_attach), 8595722Sphk DEVMETHOD(device_detach, mii_phy_detach), 8650461Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 87227908Smarius DEVMETHOD_END 8850461Swpaul}; 8950461Swpaul 9050461Swpaulstatic devclass_t mlphy_devclass; 9150461Swpaul 9250461Swpaulstatic driver_t mlphy_driver = { 9350461Swpaul "mlphy", 9450461Swpaul mlphy_methods, 9550461Swpaul sizeof(struct mlphy_softc) 9650461Swpaul}; 9750461Swpaul 9850461SwpaulDRIVER_MODULE(mlphy, miibus, mlphy_driver, mlphy_devclass, 0, 0); 9950461Swpaul 100214264Smariusstatic struct mii_softc *mlphy_find_other(struct mlphy_softc *); 10192739Salfredstatic int mlphy_service(struct mii_softc *, struct mii_data *, int); 10292739Salfredstatic void mlphy_reset(struct mii_softc *); 10392739Salfredstatic void mlphy_status(struct mii_softc *); 10450461Swpaul 105221407Smariusstatic const struct mii_phy_funcs mlphy_funcs = { 106221407Smarius mlphy_service, 107221407Smarius mlphy_status, 108221407Smarius mlphy_reset 109221407Smarius}; 110221407Smarius 111105135Salfredstatic int 112105135Salfredmlphy_probe(dev) 11350461Swpaul device_t dev; 11450461Swpaul{ 11550461Swpaul struct mii_attach_args *ma; 11650461Swpaul 11750461Swpaul ma = device_get_ivars(dev); 11850461Swpaul 11950461Swpaul /* 12050461Swpaul * Micro Linear PHY reports oui == 0 model == 0 12150461Swpaul */ 12259235Swpaul if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 || 12350461Swpaul MII_MODEL(ma->mii_id2) != 0) 12450461Swpaul return (ENXIO); 12550461Swpaul 12650461Swpaul /* 12750461Swpaul * Make sure the parent is a `tl'. So far, I have only 12850461Swpaul * encountered the 6692 on an Olicom card with a ThunderLAN 12950461Swpaul * controller chip. 13050461Swpaul */ 131214264Smarius if (strcmp(device_get_name(device_get_parent(device_get_parent(dev))), 132214264Smarius "tl") != 0) 13350461Swpaul return (ENXIO); 13450461Swpaul 13550461Swpaul device_set_desc(dev, "Micro Linear 6692 media interface"); 13650461Swpaul 137160076Syongari return (BUS_PROBE_DEFAULT); 13850461Swpaul} 13950461Swpaul 140105135Salfredstatic int 141105135Salfredmlphy_attach(dev) 14250461Swpaul device_t dev; 14350461Swpaul{ 14450461Swpaul struct mlphy_softc *msc; 14550461Swpaul struct mii_softc *sc; 14650461Swpaul 14750461Swpaul msc = device_get_softc(dev); 14850461Swpaul sc = &msc->ml_mii; 149214264Smarius msc->ml_dev = dev; 150221407Smarius mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &mlphy_funcs, 0); 15150461Swpaul 152221407Smarius PHY_RESET(sc); 15350461Swpaul 154221407Smarius sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask; 155214264Smarius /* Let the companion PHY (if any) only handle the media we don't. */ 156221407Smarius sc->mii_capmask = ~sc->mii_capabilities; 15750461Swpaul device_printf(dev, " "); 158214264Smarius mii_phy_add_media(sc); 15950461Swpaul printf("\n"); 160221407Smarius 16150461Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 162164830Smarius return (0); 16350461Swpaul} 16450461Swpaul 165182067Simpstatic struct mii_softc * 166214264Smariusmlphy_find_other(struct mlphy_softc *msc) 167182067Simp{ 168182067Simp device_t *devlist; 169182067Simp struct mii_softc *retval; 170182067Simp int i, devs; 171182067Simp 172182067Simp retval = NULL; 173214264Smarius if (device_get_children(msc->ml_mii.mii_dev, &devlist, &devs) != 0) 174182067Simp return (NULL); 175214264Smarius for (i = 0; i < devs; i++) { 176214264Smarius if (devlist[i] != msc->ml_dev) { 177182067Simp retval = device_get_softc(devlist[i]); 178182067Simp break; 179182067Simp } 180214264Smarius } 181182067Simp free(devlist, M_TEMP); 182182067Simp return (retval); 183182067Simp} 184182067Simp 18550461Swpaulstatic int 18650461Swpaulmlphy_service(xsc, mii, cmd) 18750461Swpaul struct mii_softc *xsc; 18850461Swpaul struct mii_data *mii; 18950461Swpaul int cmd; 19050461Swpaul{ 19150461Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 19250461Swpaul struct mii_softc *other = NULL; 19350461Swpaul struct mlphy_softc *msc = (struct mlphy_softc *)xsc; 19450461Swpaul struct mii_softc *sc = (struct mii_softc *)&msc->ml_mii; 195182067Simp int other_inst, reg; 19650461Swpaul 19750461Swpaul /* 19850461Swpaul * See if there's another PHY on this bus with us. 19950461Swpaul * If so, we may need it for 10Mbps modes. 20050461Swpaul */ 201214264Smarius other = mlphy_find_other(msc); 20250461Swpaul 20350461Swpaul switch (cmd) { 20450461Swpaul case MII_POLLSTAT: 20550461Swpaul break; 20650461Swpaul 20750461Swpaul case MII_MEDIACHG: 20850461Swpaul /* 20950461Swpaul * If the interface is not up, don't do anything. 21050461Swpaul */ 21150461Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 21250461Swpaul break; 21350461Swpaul 21450461Swpaul switch (IFM_SUBTYPE(ife->ifm_media)) { 21550461Swpaul case IFM_AUTO: 21650461Swpaul /* 21750461Swpaul * For autonegotiation, reset and isolate the 21850461Swpaul * companion PHY (if any) and then do NWAY 21950461Swpaul * autonegotiation ourselves. 22050461Swpaul */ 22150461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 22250461Swpaul if (other != NULL) { 223221407Smarius PHY_RESET(other); 22450461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 22550461Swpaul } 226214264Smarius (void)mii_phy_auto(sc); 22750461Swpaul msc->ml_linked = 0; 228164830Smarius return (0); 22950461Swpaul case IFM_10_T: 23050461Swpaul /* 23150461Swpaul * For 10baseT modes, reset and program the 23250461Swpaul * companion PHY (of any), then program ourselves 23350461Swpaul * to match. This will put us in pass-through 23450461Swpaul * mode and let the companion PHY do all the 23550461Swpaul * work. 23650461Swpaul * 23750461Swpaul * BMCR data is stored in the ifmedia entry. 23850461Swpaul */ 23950461Swpaul if (other != NULL) { 240221407Smarius PHY_RESET(other); 24150461Swpaul PHY_WRITE(other, MII_BMCR, ife->ifm_data); 24250461Swpaul } 243214264Smarius mii_phy_setmedia(sc); 24450461Swpaul msc->ml_state = 0; 24550461Swpaul break; 24650461Swpaul case IFM_100_TX: 24750461Swpaul /* 24850461Swpaul * For 100baseTX modes, reset and isolate the 24950461Swpaul * companion PHY (if any), then program ourselves 25050461Swpaul * accordingly. 25150461Swpaul * 25250461Swpaul * BMCR data is stored in the ifmedia entry. 25350461Swpaul */ 25450461Swpaul if (other != NULL) { 255221407Smarius PHY_RESET(other); 25650461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 25750461Swpaul } 258214264Smarius mii_phy_setmedia(sc); 25950461Swpaul msc->ml_state = 0; 26050461Swpaul break; 261214264Smarius default: 26250461Swpaul return (EINVAL); 26350461Swpaul 26450461Swpaul } 26550461Swpaul break; 26650461Swpaul 26750461Swpaul case MII_TICK: 26850461Swpaul /* 26950461Swpaul * Is the interface even up? 27050461Swpaul */ 27150461Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 27250461Swpaul return (0); 27350461Swpaul 27450461Swpaul /* 27584145Sjlemon * Only used for autonegotiation. 27650461Swpaul */ 27784145Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 27884145Sjlemon break; 27950461Swpaul 28050461Swpaul /* 28150461Swpaul * Check to see if we have link. If we do, we don't 28250461Swpaul * need to restart the autonegotiation process. Read 28350461Swpaul * the BMSR twice in case it's latched. 28450461Swpaul * If we're in a 10Mbps mode, check the link of the 28550461Swpaul * 10Mbps PHY. Sometimes the Micro Linear PHY's 28650461Swpaul * linkstat bit will clear while the linkstat bit of 28750461Swpaul * the companion PHY will remain set. 28850461Swpaul */ 28950461Swpaul if (msc->ml_state == ML_STATE_AUTO_OTHER) { 29050461Swpaul reg = PHY_READ(other, MII_BMSR) | 29150461Swpaul PHY_READ(other, MII_BMSR); 29250461Swpaul } else { 29350461Swpaul reg = PHY_READ(sc, MII_BMSR) | 29450461Swpaul PHY_READ(sc, MII_BMSR); 29550461Swpaul } 29650461Swpaul 29750461Swpaul if (reg & BMSR_LINK) { 29850461Swpaul if (!msc->ml_linked) { 29950461Swpaul msc->ml_linked = 1; 300221407Smarius PHY_STATUS(sc); 30150461Swpaul } 30284145Sjlemon break; 30350461Swpaul } 30450461Swpaul 30584145Sjlemon /* 30684145Sjlemon * Only retry autonegotiation every 5 seconds. 30784145Sjlemon */ 308164830Smarius if (++sc->mii_ticks <= MII_ANEGTICKS) 309128870Sandre break; 310164830Smarius 31184145Sjlemon sc->mii_ticks = 0; 31250461Swpaul msc->ml_linked = 0; 31350461Swpaul mii->mii_media_active = IFM_NONE; 314221407Smarius PHY_RESET(sc); 31550461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 31650461Swpaul if (other != NULL) { 317221407Smarius PHY_RESET(other); 31850461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 31950461Swpaul } 32096026Sphk mii_phy_auto(sc); 321164830Smarius return (0); 32250461Swpaul } 32350461Swpaul 32450461Swpaul /* Update the media status. */ 32550461Swpaul 32650461Swpaul if (msc->ml_state == ML_STATE_AUTO_OTHER) { 32750461Swpaul other_inst = other->mii_inst; 32850461Swpaul other->mii_inst = sc->mii_inst; 329213364Smarius if (IFM_INST(ife->ifm_media) == other->mii_inst) 330221407Smarius (void)PHY_SERVICE(other, mii, MII_POLLSTAT); 33150461Swpaul other->mii_inst = other_inst; 33295705Sphk sc->mii_media_active = other->mii_media_active; 33395705Sphk sc->mii_media_status = other->mii_media_status; 33450461Swpaul } else 33550461Swpaul ukphy_status(sc); 33650461Swpaul 33750461Swpaul /* Callback if something changed. */ 33884145Sjlemon mii_phy_update(sc, cmd); 33950461Swpaul return (0); 34050461Swpaul} 34150461Swpaul 34250461Swpaul/* 34350461Swpaul * The Micro Linear PHY comes out of reset with the 'autoneg 34450461Swpaul * enable' bit set, which we don't want. 34550461Swpaul */ 346105135Salfredstatic void 347105135Salfredmlphy_reset(sc) 34850461Swpaul struct mii_softc *sc; 34950461Swpaul{ 35050461Swpaul int reg; 35150461Swpaul 35250461Swpaul mii_phy_reset(sc); 35350461Swpaul reg = PHY_READ(sc, MII_BMCR); 35450461Swpaul reg &= ~BMCR_AUTOEN; 35550461Swpaul PHY_WRITE(sc, MII_BMCR, reg); 35650461Swpaul} 35750461Swpaul 35850461Swpaul/* 35950461Swpaul * If we negotiate a 10Mbps mode, we need to check for an alternate 36050461Swpaul * PHY and make sure it's enabled and set correctly. 36150461Swpaul */ 362105135Salfredstatic void 363105135Salfredmlphy_status(sc) 36450461Swpaul struct mii_softc *sc; 36550461Swpaul{ 36650461Swpaul struct mlphy_softc *msc = (struct mlphy_softc *)sc; 36750461Swpaul struct mii_data *mii = msc->ml_mii.mii_pdata; 36850461Swpaul struct mii_softc *other = NULL; 36950461Swpaul 37050461Swpaul /* See if there's another PHY on the bus with us. */ 371214264Smarius other = mlphy_find_other(msc); 37250461Swpaul if (other == NULL) 37350461Swpaul return; 37450461Swpaul 37550461Swpaul ukphy_status(sc); 37650461Swpaul 37750461Swpaul if (IFM_SUBTYPE(mii->mii_media_active) != IFM_10_T) { 37850461Swpaul msc->ml_state = ML_STATE_AUTO_SELF; 379221407Smarius PHY_RESET(other); 38050461Swpaul PHY_WRITE(other, MII_BMCR, BMCR_ISO); 38150461Swpaul } 38250461Swpaul 38350461Swpaul if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 38450461Swpaul msc->ml_state = ML_STATE_AUTO_OTHER; 385221407Smarius PHY_RESET(&msc->ml_mii); 38650461Swpaul PHY_WRITE(&msc->ml_mii, MII_BMCR, BMCR_ISO); 387221407Smarius PHY_RESET(other); 38896026Sphk mii_phy_auto(other); 38950461Swpaul } 39050461Swpaul} 391