rlphy.c revision 119418
150702Swpaul/* 250702Swpaul * Copyright (c) 1997, 1998, 1999 350702Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 450702Swpaul * 550702Swpaul * Redistribution and use in source and binary forms, with or without 650702Swpaul * modification, are permitted provided that the following conditions 750702Swpaul * are met: 850702Swpaul * 1. Redistributions of source code must retain the above copyright 950702Swpaul * notice, this list of conditions and the following disclaimer. 1050702Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1150702Swpaul * notice, this list of conditions and the following disclaimer in the 1250702Swpaul * documentation and/or other materials provided with the distribution. 1350702Swpaul * 3. All advertising materials mentioning features or use of this software 1450702Swpaul * must display the following acknowledgement: 1550702Swpaul * This product includes software developed by Bill Paul. 1650702Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1750702Swpaul * may be used to endorse or promote products derived from this software 1850702Swpaul * without specific prior written permission. 1950702Swpaul * 2050702Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2150702Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2250702Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2350702Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2450702Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2550702Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2650702Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2750702Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2850702Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2950702Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3050702Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3150702Swpaul */ 3250702Swpaul 33119418Sobrien#include <sys/cdefs.h> 34119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/mii/rlphy.c 119418 2003-08-24 17:55:58Z obrien $"); 35119418Sobrien 3650702Swpaul/* 3750702Swpaul * driver for RealTek 8139 internal PHYs 3850702Swpaul */ 3950702Swpaul 40113038Sobrien#include <sys/cdefs.h> 41113038Sobrien__FBSDID("$FreeBSD: head/sys/dev/mii/rlphy.c 119418 2003-08-24 17:55:58Z obrien $"); 42113038Sobrien 4350702Swpaul#include <sys/param.h> 4450702Swpaul#include <sys/systm.h> 4550702Swpaul#include <sys/kernel.h> 4650702Swpaul#include <sys/socket.h> 4750702Swpaul#include <sys/bus.h> 4850702Swpaul 4950702Swpaul#include <net/if.h> 5094149Swpaul#include <net/if_arp.h> 5150702Swpaul#include <net/if_media.h> 5250702Swpaul 5350702Swpaul#include <dev/mii/mii.h> 5450702Swpaul#include <dev/mii/miivar.h> 55109514Sobrien#include "miidevs.h" 5650702Swpaul 5794149Swpaul#include <machine/bus.h> 5894149Swpaul#include <pci/if_rlreg.h> 5994149Swpaul 6050702Swpaul#include "miibus_if.h" 6150702Swpaul 62105135Salfredstatic int rlphy_probe(device_t); 63105135Salfredstatic int rlphy_attach(device_t); 6450702Swpaul 6550702Swpaulstatic device_method_t rlphy_methods[] = { 6650702Swpaul /* device interface */ 6750702Swpaul DEVMETHOD(device_probe, rlphy_probe), 6850702Swpaul DEVMETHOD(device_attach, rlphy_attach), 6995722Sphk DEVMETHOD(device_detach, mii_phy_detach), 7050702Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 7150702Swpaul { 0, 0 } 7250702Swpaul}; 7350702Swpaul 7450702Swpaulstatic devclass_t rlphy_devclass; 7550702Swpaul 7650702Swpaulstatic driver_t rlphy_driver = { 7750702Swpaul "rlphy", 7850702Swpaul rlphy_methods, 7950702Swpaul sizeof(struct mii_softc) 8050702Swpaul}; 8150702Swpaul 8250702SwpaulDRIVER_MODULE(rlphy, miibus, rlphy_driver, rlphy_devclass, 0, 0); 8350702Swpaul 8492739Salfredstatic int rlphy_service(struct mii_softc *, struct mii_data *, int); 8594149Swpaulstatic void rlphy_status(struct mii_softc *); 8650702Swpaul 87105135Salfredstatic int 88105135Salfredrlphy_probe(dev) 8950702Swpaul device_t dev; 9050702Swpaul{ 9150702Swpaul struct mii_attach_args *ma; 9250702Swpaul device_t parent; 9350702Swpaul 9450702Swpaul ma = device_get_ivars(dev); 9550702Swpaul parent = device_get_parent(device_get_parent(dev)); 9650702Swpaul 9794149Swpaul /* Test for RealTek 8201L PHY */ 9894149Swpaul if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_REALTEK && 9994149Swpaul MII_MODEL(ma->mii_id2) == MII_MODEL_REALTEK_RTL8201L) { 10094149Swpaul device_set_desc(dev, MII_STR_REALTEK_RTL8201L); 10194149Swpaul return(0); 10294149Swpaul } 10394149Swpaul 10450702Swpaul /* 10550702Swpaul * RealTek PHY doesn't have vendor/device ID registers: 10650702Swpaul * the rl driver fakes up a return value of all zeros. 10750702Swpaul */ 10850702Swpaul if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 || 10950702Swpaul MII_MODEL(ma->mii_id2) != 0) 11050702Swpaul return (ENXIO); 11150702Swpaul 11250702Swpaul /* 11350702Swpaul * Make sure the parent is an `rl'. 11450702Swpaul */ 11550702Swpaul if (strcmp(device_get_name(parent), "rl") != 0) 11650702Swpaul return (ENXIO); 11750702Swpaul 11850702Swpaul device_set_desc(dev, "RealTek internal media interface"); 11950702Swpaul 12050702Swpaul return (0); 12150702Swpaul} 12250702Swpaul 123105135Salfredstatic int 124105135Salfredrlphy_attach(dev) 12550702Swpaul device_t dev; 12650702Swpaul{ 12750702Swpaul struct mii_softc *sc; 12850702Swpaul struct mii_attach_args *ma; 12950702Swpaul struct mii_data *mii; 13050702Swpaul 13150702Swpaul sc = device_get_softc(dev); 13250702Swpaul ma = device_get_ivars(dev); 13350702Swpaul sc->mii_dev = device_get_parent(dev); 13450702Swpaul mii = device_get_softc(sc->mii_dev); 13550702Swpaul 13650702Swpaul /* 13750702Swpaul * The RealTek PHY can never be isolated, so never allow non-zero 13850702Swpaul * instances! 13950702Swpaul */ 14050702Swpaul if (mii->mii_instance != 0) { 14150702Swpaul device_printf(dev, "ignoring this PHY, non-zero instance\n"); 14250702Swpaul return(ENXIO); 14350702Swpaul } 14450702Swpaul 14550758Swpaul LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 14650758Swpaul 14750758Swpaul sc->mii_inst = mii->mii_instance; 14850758Swpaul sc->mii_phy = ma->mii_phyno; 14950758Swpaul sc->mii_service = rlphy_service; 15050758Swpaul sc->mii_pdata = mii; 15150702Swpaul mii->mii_instance++; 15250702Swpaul 15350702Swpaul sc->mii_flags |= MIIF_NOISOLATE; 15450702Swpaul 15550702Swpaul#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 15650702Swpaul 15750702Swpaul#if 0 /* See above. */ 15850702Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 15950702Swpaul BMCR_ISO); 16050702Swpaul#endif 16150702Swpaul 16250702Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 16350702Swpaul BMCR_LOOP|BMCR_S100); 16450702Swpaul 16591343Sdanny mii_phy_reset(sc); 16650702Swpaul 16750702Swpaul sc->mii_capabilities = 16850702Swpaul PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 16950702Swpaul device_printf(dev, " "); 17095667Sphk mii_add_media(sc); 17150702Swpaul printf("\n"); 17250702Swpaul#undef ADD 17350702Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 17450702Swpaul return(0); 17550702Swpaul} 17650702Swpaul 17784145Sjlemonstatic int 17850702Swpaulrlphy_service(sc, mii, cmd) 17950702Swpaul struct mii_softc *sc; 18050702Swpaul struct mii_data *mii; 18150702Swpaul int cmd; 18250702Swpaul{ 18350702Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 18450702Swpaul 18550702Swpaul /* 18650702Swpaul * We can't isolate the RealTek PHY, so it has to be the only one! 18750702Swpaul */ 18850702Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 18950702Swpaul panic("rlphy_service: can't isolate RealTek PHY"); 19050702Swpaul 19150702Swpaul switch (cmd) { 19250702Swpaul case MII_POLLSTAT: 19350702Swpaul break; 19450702Swpaul 19550702Swpaul case MII_MEDIACHG: 19650702Swpaul /* 19750702Swpaul * If the interface is not up, don't do anything. 19850702Swpaul */ 19950702Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 20050702Swpaul break; 20150702Swpaul 20250702Swpaul switch (IFM_SUBTYPE(ife->ifm_media)) { 20350702Swpaul case IFM_AUTO: 20450702Swpaul /* 20550702Swpaul * If we're already in auto mode, just return. 20650702Swpaul */ 20750702Swpaul if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) 20850702Swpaul return (0); 20996026Sphk (void) mii_phy_auto(sc); 21050702Swpaul break; 21150702Swpaul case IFM_100_T4: 21250702Swpaul /* 21350702Swpaul * XXX Not supported as a manual setting right now. 21450702Swpaul */ 21550702Swpaul return (EINVAL); 21650702Swpaul default: 21750702Swpaul /* 21850702Swpaul * BMCR data is stored in the ifmedia entry. 21950702Swpaul */ 22050702Swpaul PHY_WRITE(sc, MII_ANAR, 22150702Swpaul mii_anar(ife->ifm_media)); 22250702Swpaul PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 22350702Swpaul } 22450702Swpaul break; 22550702Swpaul 22650702Swpaul case MII_TICK: 22750702Swpaul /* 22850702Swpaul * Is the interface even up? 22950702Swpaul */ 23050702Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 23150702Swpaul return (0); 23250702Swpaul 23350702Swpaul /* 23484145Sjlemon * Only used for autonegotiation. 23550702Swpaul */ 23684145Sjlemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 23784145Sjlemon break; 23850702Swpaul 23950702Swpaul /* 24050702Swpaul * The RealTek PHY's autonegotiation doesn't need to be 24150702Swpaul * kicked; it continues in the background. 24250702Swpaul */ 24350702Swpaul break; 24450702Swpaul } 24550702Swpaul 24650702Swpaul /* Update the media status. */ 24794149Swpaul rlphy_status(sc); 24850702Swpaul 24950702Swpaul /* Callback if something changed. */ 25084145Sjlemon mii_phy_update(sc, cmd); 25150702Swpaul return (0); 25250702Swpaul} 25394149Swpaul 254104094Sphkstatic void 25594149Swpaulrlphy_status(phy) 25694149Swpaul struct mii_softc *phy; 25794149Swpaul{ 25894149Swpaul struct mii_data *mii = phy->mii_pdata; 25994149Swpaul int bmsr, bmcr, anlpar; 26094149Swpaul device_t parent; 26194149Swpaul 26294149Swpaul mii->mii_media_status = IFM_AVALID; 26394149Swpaul mii->mii_media_active = IFM_ETHER; 26494149Swpaul 26594149Swpaul bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); 26694149Swpaul if (bmsr & BMSR_LINK) 26794149Swpaul mii->mii_media_status |= IFM_ACTIVE; 26894149Swpaul 26994149Swpaul bmcr = PHY_READ(phy, MII_BMCR); 27094149Swpaul if (bmcr & BMCR_ISO) { 27194149Swpaul mii->mii_media_active |= IFM_NONE; 27294149Swpaul mii->mii_media_status = 0; 27394149Swpaul return; 27494149Swpaul } 27594149Swpaul 27694149Swpaul if (bmcr & BMCR_LOOP) 27794149Swpaul mii->mii_media_active |= IFM_LOOP; 27894149Swpaul 27994149Swpaul if (bmcr & BMCR_AUTOEN) { 28094149Swpaul /* 28194149Swpaul * NWay autonegotiation takes the highest-order common 28294149Swpaul * bit of the ANAR and ANLPAR (i.e. best media advertised 28394149Swpaul * both by us and our link partner). 28494149Swpaul */ 28594149Swpaul if ((bmsr & BMSR_ACOMP) == 0) { 28694149Swpaul /* Erg, still trying, I guess... */ 28794149Swpaul mii->mii_media_active |= IFM_NONE; 28894149Swpaul return; 28994149Swpaul } 29094149Swpaul 29194149Swpaul if ((anlpar = PHY_READ(phy, MII_ANAR) & 29294149Swpaul PHY_READ(phy, MII_ANLPAR))) { 29394149Swpaul if (anlpar & ANLPAR_T4) 29494149Swpaul mii->mii_media_active |= IFM_100_T4; 29594149Swpaul else if (anlpar & ANLPAR_TX_FD) 29694149Swpaul mii->mii_media_active |= IFM_100_TX|IFM_FDX; 29794149Swpaul else if (anlpar & ANLPAR_TX) 29894149Swpaul mii->mii_media_active |= IFM_100_TX; 29994149Swpaul else if (anlpar & ANLPAR_10_FD) 30094149Swpaul mii->mii_media_active |= IFM_10_T|IFM_FDX; 30194149Swpaul else if (anlpar & ANLPAR_10) 30294149Swpaul mii->mii_media_active |= IFM_10_T; 30394149Swpaul else 30494149Swpaul mii->mii_media_active |= IFM_NONE; 30594149Swpaul return; 30694149Swpaul } 30794149Swpaul /* 30894149Swpaul * If the other side doesn't support NWAY, then the 30994149Swpaul * best we can do is determine if we have a 10Mbps or 31094149Swpaul * 100Mbps link. There's no way to know if the link 31194149Swpaul * is full or half duplex, so we default to half duplex 31294149Swpaul * and hope that the user is clever enough to manually 31394149Swpaul * change the media settings if we're wrong. 31494149Swpaul */ 31594149Swpaul 31694149Swpaul 31794149Swpaul /* 31894149Swpaul * The RealTek PHY supports non-NWAY link speed 31994149Swpaul * detection, however it does not report the link 32094149Swpaul * detection results via the ANLPAR or BMSR registers. 32194149Swpaul * (What? RealTek doesn't do things the way everyone 32294149Swpaul * else does? I'm just shocked, shocked I tell you.) 32394149Swpaul * To determine the link speed, we have to do one 32494149Swpaul * of two things: 32594149Swpaul * 32694149Swpaul * - If this is a standalone RealTek RTL8201(L) PHY, 32794149Swpaul * we can determine the link speed by testing bit 0 32894149Swpaul * in the magic, vendor-specific register at offset 32994149Swpaul * 0x19. 33094149Swpaul * 33194149Swpaul * - If this is a RealTek MAC with integrated PHY, we 33294149Swpaul * can test the 'SPEED10' bit of the MAC's media status 33394149Swpaul * register. 33494149Swpaul */ 33594149Swpaul parent = device_get_parent(phy->mii_dev); 33694149Swpaul if (strcmp(device_get_name(parent), "rl") != 0) { 33794149Swpaul if (PHY_READ(phy, 0x0019) & 0x01) 33894149Swpaul mii->mii_media_active |= IFM_100_TX; 33994149Swpaul else 34094149Swpaul mii->mii_media_active |= IFM_10_T; 34194149Swpaul } else { 34294149Swpaul if (PHY_READ(phy, RL_MEDIASTAT) & 34394149Swpaul RL_MEDIASTAT_SPEED10) 34494149Swpaul mii->mii_media_active |= IFM_10_T; 34594149Swpaul else 34694149Swpaul mii->mii_media_active |= IFM_100_TX; 34794149Swpaul } 34894149Swpaul 34994149Swpaul } else 35094149Swpaul mii->mii_media_active = mii_media_from_bmcr(bmcr); 35194149Swpaul} 352