rlphy.c revision 165991
1139749Simp/*- 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 165991 2007-01-13 00:17:39Z marius $"); 35119418Sobrien 3650702Swpaul/* 3750702Swpaul * driver for RealTek 8139 internal PHYs 3850702Swpaul */ 3950702Swpaul 4050702Swpaul#include <sys/param.h> 4150702Swpaul#include <sys/systm.h> 4250702Swpaul#include <sys/kernel.h> 43129876Sphk#include <sys/module.h> 4450702Swpaul#include <sys/socket.h> 4550702Swpaul#include <sys/bus.h> 4650702Swpaul 4750702Swpaul#include <net/if.h> 4894149Swpaul#include <net/if_arp.h> 4950702Swpaul#include <net/if_media.h> 5050702Swpaul 5150702Swpaul#include <dev/mii/mii.h> 5250702Swpaul#include <dev/mii/miivar.h> 53109514Sobrien#include "miidevs.h" 5450702Swpaul 5594149Swpaul#include <machine/bus.h> 5694149Swpaul#include <pci/if_rlreg.h> 5794149Swpaul 5850702Swpaul#include "miibus_if.h" 5950702Swpaul 60105135Salfredstatic int rlphy_probe(device_t); 61105135Salfredstatic int rlphy_attach(device_t); 6250702Swpaul 6350702Swpaulstatic device_method_t rlphy_methods[] = { 6450702Swpaul /* device interface */ 6550702Swpaul DEVMETHOD(device_probe, rlphy_probe), 6650702Swpaul DEVMETHOD(device_attach, rlphy_attach), 6795722Sphk DEVMETHOD(device_detach, mii_phy_detach), 6850702Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 6950702Swpaul { 0, 0 } 7050702Swpaul}; 7150702Swpaul 7250702Swpaulstatic devclass_t rlphy_devclass; 7350702Swpaul 7450702Swpaulstatic driver_t rlphy_driver = { 7550702Swpaul "rlphy", 7650702Swpaul rlphy_methods, 7750702Swpaul sizeof(struct mii_softc) 7850702Swpaul}; 7950702Swpaul 8050702SwpaulDRIVER_MODULE(rlphy, miibus, rlphy_driver, rlphy_devclass, 0, 0); 8150702Swpaul 8292739Salfredstatic int rlphy_service(struct mii_softc *, struct mii_data *, int); 8394149Swpaulstatic void rlphy_status(struct mii_softc *); 8450702Swpaul 85165991Smarius/* 86165991Smarius * RealTek internal PHYs don't have vendor/device ID registers; 87165991Smarius * re(4) and rl(4) fake up a return value of all zeros. 88165991Smarius */ 89165991Smariusstatic const struct mii_phydesc rlintphys[] = { 90165991Smarius { 0, 0, "RealTek internal media interface" }, 91165991Smarius MII_PHY_END 92165991Smarius}; 93165991Smarius 94164827Smariusstatic const struct mii_phydesc rlphys[] = { 95164827Smarius MII_PHY_DESC(REALTEK, RTL8201L), 96164827Smarius MII_PHY_END 97164827Smarius}; 98164827Smarius 99105135Salfredstatic int 100150763Simprlphy_probe(device_t dev) 10150702Swpaul{ 102164827Smarius const char *nic; 103164827Smarius int rv; 10450702Swpaul 105164827Smarius rv = mii_phy_dev_probe(dev, rlphys, BUS_PROBE_DEFAULT); 106164827Smarius if (rv <= 0) 107164827Smarius return (rv); 10850702Swpaul 109164827Smarius nic = device_get_name(device_get_parent(device_get_parent(dev))); 110165991Smarius if (strcmp(nic, "rl") == 0 && strcmp(nic, "re") == 0) 111165991Smarius return (mii_phy_dev_probe(dev, rlintphys, BUS_PROBE_DEFAULT)); 112165991Smarius return (ENXIO); 11350702Swpaul} 11450702Swpaul 115105135Salfredstatic int 116150763Simprlphy_attach(device_t dev) 11750702Swpaul{ 11850702Swpaul struct mii_softc *sc; 11950702Swpaul struct mii_attach_args *ma; 12050702Swpaul struct mii_data *mii; 12150702Swpaul 12250702Swpaul sc = device_get_softc(dev); 12350702Swpaul ma = device_get_ivars(dev); 12450702Swpaul sc->mii_dev = device_get_parent(dev); 12550702Swpaul mii = device_get_softc(sc->mii_dev); 12650702Swpaul 12750702Swpaul /* 12850702Swpaul * The RealTek PHY can never be isolated, so never allow non-zero 12950702Swpaul * instances! 13050702Swpaul */ 13150702Swpaul if (mii->mii_instance != 0) { 13250702Swpaul device_printf(dev, "ignoring this PHY, non-zero instance\n"); 133164711Smarius return (ENXIO); 13450702Swpaul } 13550702Swpaul 13650758Swpaul LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 13750758Swpaul 13850758Swpaul sc->mii_inst = mii->mii_instance; 13950758Swpaul sc->mii_phy = ma->mii_phyno; 14050758Swpaul sc->mii_service = rlphy_service; 14150758Swpaul sc->mii_pdata = mii; 14250702Swpaul mii->mii_instance++; 14350702Swpaul 14450702Swpaul sc->mii_flags |= MIIF_NOISOLATE; 14550702Swpaul 14650702Swpaul#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 14750702Swpaul 14850702Swpaul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 149165985Smarius MII_MEDIA_100_TX); 15050702Swpaul 15191343Sdanny mii_phy_reset(sc); 15250702Swpaul 15350702Swpaul sc->mii_capabilities = 15450702Swpaul PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 15550702Swpaul device_printf(dev, " "); 156164711Smarius mii_phy_add_media(sc); 15750702Swpaul printf("\n"); 15850702Swpaul#undef ADD 15950702Swpaul MIIBUS_MEDIAINIT(sc->mii_dev); 160164711Smarius return (0); 16150702Swpaul} 16250702Swpaul 16384145Sjlemonstatic int 164150763Simprlphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 16550702Swpaul{ 16650702Swpaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 16750702Swpaul 16850702Swpaul /* 16950702Swpaul * We can't isolate the RealTek PHY, so it has to be the only one! 17050702Swpaul */ 17150702Swpaul if (IFM_INST(ife->ifm_media) != sc->mii_inst) 17250702Swpaul panic("rlphy_service: can't isolate RealTek PHY"); 17350702Swpaul 17450702Swpaul switch (cmd) { 17550702Swpaul case MII_POLLSTAT: 17650702Swpaul break; 17750702Swpaul 17850702Swpaul case MII_MEDIACHG: 17950702Swpaul /* 18050702Swpaul * If the interface is not up, don't do anything. 18150702Swpaul */ 18250702Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 18350702Swpaul break; 18450702Swpaul 185164711Smarius mii_phy_setmedia(sc); 18650702Swpaul break; 18750702Swpaul 18850702Swpaul case MII_TICK: 18950702Swpaul /* 19050702Swpaul * Is the interface even up? 19150702Swpaul */ 19250702Swpaul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 19350702Swpaul return (0); 19450702Swpaul 19550702Swpaul /* 19650702Swpaul * The RealTek PHY's autonegotiation doesn't need to be 19750702Swpaul * kicked; it continues in the background. 19850702Swpaul */ 19950702Swpaul break; 20050702Swpaul } 20150702Swpaul 20250702Swpaul /* Update the media status. */ 20394149Swpaul rlphy_status(sc); 20450702Swpaul 20550702Swpaul /* Callback if something changed. */ 20684145Sjlemon mii_phy_update(sc, cmd); 20750702Swpaul return (0); 20850702Swpaul} 20994149Swpaul 210104094Sphkstatic void 211150763Simprlphy_status(struct mii_softc *phy) 21294149Swpaul{ 21394149Swpaul struct mii_data *mii = phy->mii_pdata; 214164711Smarius struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 21594149Swpaul int bmsr, bmcr, anlpar; 21694149Swpaul 21794149Swpaul mii->mii_media_status = IFM_AVALID; 21894149Swpaul mii->mii_media_active = IFM_ETHER; 21994149Swpaul 22094149Swpaul bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); 22194149Swpaul if (bmsr & BMSR_LINK) 22294149Swpaul mii->mii_media_status |= IFM_ACTIVE; 22394149Swpaul 22494149Swpaul bmcr = PHY_READ(phy, MII_BMCR); 22594149Swpaul if (bmcr & BMCR_ISO) { 22694149Swpaul mii->mii_media_active |= IFM_NONE; 22794149Swpaul mii->mii_media_status = 0; 22894149Swpaul return; 22994149Swpaul } 23094149Swpaul 23194149Swpaul if (bmcr & BMCR_LOOP) 23294149Swpaul mii->mii_media_active |= IFM_LOOP; 23394149Swpaul 23494149Swpaul if (bmcr & BMCR_AUTOEN) { 23594149Swpaul /* 23694149Swpaul * NWay autonegotiation takes the highest-order common 23794149Swpaul * bit of the ANAR and ANLPAR (i.e. best media advertised 23894149Swpaul * both by us and our link partner). 23994149Swpaul */ 24094149Swpaul if ((bmsr & BMSR_ACOMP) == 0) { 24194149Swpaul /* Erg, still trying, I guess... */ 24294149Swpaul mii->mii_media_active |= IFM_NONE; 24394149Swpaul return; 24494149Swpaul } 24594149Swpaul 24694149Swpaul if ((anlpar = PHY_READ(phy, MII_ANAR) & 24794149Swpaul PHY_READ(phy, MII_ANLPAR))) { 24894149Swpaul if (anlpar & ANLPAR_T4) 24994149Swpaul mii->mii_media_active |= IFM_100_T4; 25094149Swpaul else if (anlpar & ANLPAR_TX_FD) 25194149Swpaul mii->mii_media_active |= IFM_100_TX|IFM_FDX; 25294149Swpaul else if (anlpar & ANLPAR_TX) 25394149Swpaul mii->mii_media_active |= IFM_100_TX; 25494149Swpaul else if (anlpar & ANLPAR_10_FD) 25594149Swpaul mii->mii_media_active |= IFM_10_T|IFM_FDX; 25694149Swpaul else if (anlpar & ANLPAR_10) 25794149Swpaul mii->mii_media_active |= IFM_10_T; 25894149Swpaul else 259164711Smarius mii->mii_media_active |= IFM_NONE; 26094149Swpaul return; 26194149Swpaul } 26294149Swpaul /* 26394149Swpaul * If the other side doesn't support NWAY, then the 26494149Swpaul * best we can do is determine if we have a 10Mbps or 265164711Smarius * 100Mbps link. There's no way to know if the link 26694149Swpaul * is full or half duplex, so we default to half duplex 26794149Swpaul * and hope that the user is clever enough to manually 26894149Swpaul * change the media settings if we're wrong. 26994149Swpaul */ 27094149Swpaul 27194149Swpaul /* 27294149Swpaul * The RealTek PHY supports non-NWAY link speed 27394149Swpaul * detection, however it does not report the link 27494149Swpaul * detection results via the ANLPAR or BMSR registers. 27594149Swpaul * (What? RealTek doesn't do things the way everyone 27694149Swpaul * else does? I'm just shocked, shocked I tell you.) 27794149Swpaul * To determine the link speed, we have to do one 27894149Swpaul * of two things: 27994149Swpaul * 28094149Swpaul * - If this is a standalone RealTek RTL8201(L) PHY, 28194149Swpaul * we can determine the link speed by testing bit 0 28294149Swpaul * in the magic, vendor-specific register at offset 28394149Swpaul * 0x19. 28494149Swpaul * 28594149Swpaul * - If this is a RealTek MAC with integrated PHY, we 28694149Swpaul * can test the 'SPEED10' bit of the MAC's media status 28794149Swpaul * register. 28894149Swpaul */ 289164711Smarius if (strcmp(mii->mii_ifp->if_dname, "rl") != 0) { 29094149Swpaul if (PHY_READ(phy, 0x0019) & 0x01) 29194149Swpaul mii->mii_media_active |= IFM_100_TX; 29294149Swpaul else 29394149Swpaul mii->mii_media_active |= IFM_10_T; 29494149Swpaul } else { 29594149Swpaul if (PHY_READ(phy, RL_MEDIASTAT) & 29694149Swpaul RL_MEDIASTAT_SPEED10) 29794149Swpaul mii->mii_media_active |= IFM_10_T; 29894149Swpaul else 29994149Swpaul mii->mii_media_active |= IFM_100_TX; 30094149Swpaul } 30194149Swpaul } else 302164711Smarius mii->mii_media_active = ife->ifm_media; 30394149Swpaul} 304