1112a855dSMarius Strobl /*- 2112a855dSMarius Strobl * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>. 3112a855dSMarius Strobl * All rights reserved. 4112a855dSMarius Strobl * 5112a855dSMarius Strobl * Redistribution and use in source and binary forms, with or without 6112a855dSMarius Strobl * modification, are permitted provided that the following conditions 7112a855dSMarius Strobl * are met: 8112a855dSMarius Strobl * 1. Redistributions of source code must retain the above copyright 9112a855dSMarius Strobl * notice, this list of conditions and the following disclaimer. 10112a855dSMarius Strobl * 2. Redistributions in binary form must reproduce the above copyright 11112a855dSMarius Strobl * notice, this list of conditions and the following disclaimer in the 12112a855dSMarius Strobl * documentation and/or other materials provided with the distribution. 13112a855dSMarius Strobl * 14112a855dSMarius Strobl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15112a855dSMarius Strobl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16112a855dSMarius Strobl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17112a855dSMarius Strobl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18112a855dSMarius Strobl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19112a855dSMarius Strobl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20112a855dSMarius Strobl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21112a855dSMarius Strobl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22112a855dSMarius Strobl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23112a855dSMarius Strobl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24112a855dSMarius Strobl * SUCH DAMAGE. 25112a855dSMarius Strobl * 26112a855dSMarius Strobl */ 27112a855dSMarius Strobl 28112a855dSMarius Strobl #include <sys/cdefs.h> 29112a855dSMarius Strobl __FBSDID("$FreeBSD$"); 30112a855dSMarius Strobl 31112a855dSMarius Strobl /* 32112a855dSMarius Strobl * driver for RealTek RTL8150 internal PHY 33112a855dSMarius Strobl */ 34112a855dSMarius Strobl 35112a855dSMarius Strobl #include <sys/param.h> 36112a855dSMarius Strobl #include <sys/systm.h> 37112a855dSMarius Strobl #include <sys/kernel.h> 38112a855dSMarius Strobl #include <sys/malloc.h> 39112a855dSMarius Strobl #include <sys/module.h> 40112a855dSMarius Strobl #include <sys/socket.h> 41112a855dSMarius Strobl #include <sys/bus.h> 42112a855dSMarius Strobl 43112a855dSMarius Strobl #include <net/if.h> 44112a855dSMarius Strobl #include <net/if_arp.h> 45112a855dSMarius Strobl #include <net/if_media.h> 46112a855dSMarius Strobl 47112a855dSMarius Strobl #include <dev/mii/mii.h> 48112a855dSMarius Strobl #include <dev/mii/miivar.h> 49112a855dSMarius Strobl #include "miidevs.h" 50112a855dSMarius Strobl 51112a855dSMarius Strobl #include <dev/usb/net/ruephyreg.h> 52112a855dSMarius Strobl 53112a855dSMarius Strobl #include "miibus_if.h" 54112a855dSMarius Strobl 55112a855dSMarius Strobl static int ruephy_probe(device_t); 56112a855dSMarius Strobl static int ruephy_attach(device_t); 57112a855dSMarius Strobl 58112a855dSMarius Strobl static device_method_t ruephy_methods[] = { 59112a855dSMarius Strobl /* device interface */ 60112a855dSMarius Strobl DEVMETHOD(device_probe, ruephy_probe), 61112a855dSMarius Strobl DEVMETHOD(device_attach, ruephy_attach), 62112a855dSMarius Strobl DEVMETHOD(device_detach, mii_phy_detach), 63112a855dSMarius Strobl DEVMETHOD(device_shutdown, bus_generic_shutdown), 64604f5f1fSMarius Strobl DEVMETHOD_END 65112a855dSMarius Strobl }; 66112a855dSMarius Strobl 67112a855dSMarius Strobl static devclass_t ruephy_devclass; 68112a855dSMarius Strobl 69112a855dSMarius Strobl static driver_t ruephy_driver = { 70*6d917491SHans Petter Selasky .name = "ruephy", 71*6d917491SHans Petter Selasky .methods = ruephy_methods, 72*6d917491SHans Petter Selasky .size = sizeof(struct mii_softc) 73112a855dSMarius Strobl }; 74112a855dSMarius Strobl 75112a855dSMarius Strobl DRIVER_MODULE(ruephy, miibus, ruephy_driver, ruephy_devclass, 0, 0); 76112a855dSMarius Strobl 77112a855dSMarius Strobl static int ruephy_service(struct mii_softc *, struct mii_data *, int); 78112a855dSMarius Strobl static void ruephy_reset(struct mii_softc *); 79112a855dSMarius Strobl static void ruephy_status(struct mii_softc *); 80112a855dSMarius Strobl 81112a855dSMarius Strobl /* 82112a855dSMarius Strobl * The RealTek RTL8150 internal PHY doesn't have vendor/device ID 83112a855dSMarius Strobl * registers; rue(4) fakes up a return value of all zeros. 84112a855dSMarius Strobl */ 85112a855dSMarius Strobl static const struct mii_phydesc ruephys[] = { 86112a855dSMarius Strobl { 0, 0, "RealTek RTL8150 internal media interface" }, 87112a855dSMarius Strobl MII_PHY_END 88112a855dSMarius Strobl }; 89112a855dSMarius Strobl 90112a855dSMarius Strobl static const struct mii_phy_funcs ruephy_funcs = { 91112a855dSMarius Strobl ruephy_service, 92112a855dSMarius Strobl ruephy_status, 93112a855dSMarius Strobl ruephy_reset 94112a855dSMarius Strobl }; 95112a855dSMarius Strobl 96112a855dSMarius Strobl static int 97112a855dSMarius Strobl ruephy_probe(device_t dev) 98112a855dSMarius Strobl { 99112a855dSMarius Strobl 100112a855dSMarius Strobl if (strcmp(device_get_name(device_get_parent(device_get_parent(dev))), 101112a855dSMarius Strobl "rue") == 0) 102112a855dSMarius Strobl return (mii_phy_dev_probe(dev, ruephys, BUS_PROBE_DEFAULT)); 103112a855dSMarius Strobl return (ENXIO); 104112a855dSMarius Strobl } 105112a855dSMarius Strobl 106112a855dSMarius Strobl static int 107112a855dSMarius Strobl ruephy_attach(device_t dev) 108112a855dSMarius Strobl { 109112a855dSMarius Strobl 110112a855dSMarius Strobl mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, 111112a855dSMarius Strobl &ruephy_funcs, 1); 112112a855dSMarius Strobl return (0); 113112a855dSMarius Strobl } 114112a855dSMarius Strobl 115112a855dSMarius Strobl static int 116112a855dSMarius Strobl ruephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 117112a855dSMarius Strobl { 118112a855dSMarius Strobl struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 119112a855dSMarius Strobl int reg; 120112a855dSMarius Strobl 121112a855dSMarius Strobl switch (cmd) { 122112a855dSMarius Strobl case MII_POLLSTAT: 123112a855dSMarius Strobl break; 124112a855dSMarius Strobl 125112a855dSMarius Strobl case MII_MEDIACHG: 126112a855dSMarius Strobl /* 127112a855dSMarius Strobl * If the interface is not up, don't do anything. 128112a855dSMarius Strobl */ 129112a855dSMarius Strobl if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 130112a855dSMarius Strobl break; 131112a855dSMarius Strobl 132112a855dSMarius Strobl mii_phy_setmedia(sc); 133112a855dSMarius Strobl break; 134112a855dSMarius Strobl 135112a855dSMarius Strobl case MII_TICK: 136112a855dSMarius Strobl /* 137112a855dSMarius Strobl * Is the interface even up? 138112a855dSMarius Strobl */ 139112a855dSMarius Strobl if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 140112a855dSMarius Strobl return (0); 141112a855dSMarius Strobl 142112a855dSMarius Strobl /* 143112a855dSMarius Strobl * Only used for autonegotiation. 144112a855dSMarius Strobl */ 145112a855dSMarius Strobl if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 146112a855dSMarius Strobl break; 147112a855dSMarius Strobl 148112a855dSMarius Strobl /* 149112a855dSMarius Strobl * Check to see if we have link. If we do, we don't 150112a855dSMarius Strobl * need to restart the autonegotiation process. Read 151112a855dSMarius Strobl * the MSR twice in case it's latched. 152112a855dSMarius Strobl */ 153112a855dSMarius Strobl reg = PHY_READ(sc, RUEPHY_MII_MSR) | 154112a855dSMarius Strobl PHY_READ(sc, RUEPHY_MII_MSR); 155112a855dSMarius Strobl if (reg & RUEPHY_MSR_LINK) 156112a855dSMarius Strobl break; 157112a855dSMarius Strobl 158112a855dSMarius Strobl /* Only retry autonegotiation every mii_anegticks seconds. */ 159112a855dSMarius Strobl if (sc->mii_ticks <= sc->mii_anegticks) 160112a855dSMarius Strobl break; 161112a855dSMarius Strobl 162112a855dSMarius Strobl sc->mii_ticks = 0; 163112a855dSMarius Strobl PHY_RESET(sc); 164112a855dSMarius Strobl if (mii_phy_auto(sc) == EJUSTRETURN) 165112a855dSMarius Strobl return (0); 166112a855dSMarius Strobl break; 167112a855dSMarius Strobl } 168112a855dSMarius Strobl 169112a855dSMarius Strobl /* Update the media status. */ 170112a855dSMarius Strobl PHY_STATUS(sc); 171112a855dSMarius Strobl 172112a855dSMarius Strobl /* Callback if something changed. */ 173112a855dSMarius Strobl mii_phy_update(sc, cmd); 174112a855dSMarius Strobl 175112a855dSMarius Strobl return (0); 176112a855dSMarius Strobl } 177112a855dSMarius Strobl 178112a855dSMarius Strobl static void 179112a855dSMarius Strobl ruephy_reset(struct mii_softc *sc) 180112a855dSMarius Strobl { 181112a855dSMarius Strobl 182112a855dSMarius Strobl mii_phy_reset(sc); 183112a855dSMarius Strobl 184112a855dSMarius Strobl /* 185112a855dSMarius Strobl * XXX RealTek RTL8150 PHY doesn't set the BMCR properly after 186112a855dSMarius Strobl * XXX reset, which breaks autonegotiation. 187112a855dSMarius Strobl */ 188112a855dSMarius Strobl PHY_WRITE(sc, MII_BMCR, (BMCR_S100 | BMCR_AUTOEN | BMCR_FDX)); 189112a855dSMarius Strobl } 190112a855dSMarius Strobl 191112a855dSMarius Strobl static void 192112a855dSMarius Strobl ruephy_status(struct mii_softc *phy) 193112a855dSMarius Strobl { 194112a855dSMarius Strobl struct mii_data *mii = phy->mii_pdata; 195112a855dSMarius Strobl struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 196112a855dSMarius Strobl int bmsr, bmcr, msr; 197112a855dSMarius Strobl 198112a855dSMarius Strobl mii->mii_media_status = IFM_AVALID; 199112a855dSMarius Strobl mii->mii_media_active = IFM_ETHER; 200112a855dSMarius Strobl 201112a855dSMarius Strobl msr = PHY_READ(phy, RUEPHY_MII_MSR) | PHY_READ(phy, RUEPHY_MII_MSR); 202112a855dSMarius Strobl if (msr & RUEPHY_MSR_LINK) 203112a855dSMarius Strobl mii->mii_media_status |= IFM_ACTIVE; 204112a855dSMarius Strobl 205112a855dSMarius Strobl bmcr = PHY_READ(phy, MII_BMCR); 206112a855dSMarius Strobl if (bmcr & BMCR_ISO) { 207112a855dSMarius Strobl mii->mii_media_active |= IFM_NONE; 208112a855dSMarius Strobl mii->mii_media_status = 0; 209112a855dSMarius Strobl return; 210112a855dSMarius Strobl } 211112a855dSMarius Strobl 212112a855dSMarius Strobl bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); 213112a855dSMarius Strobl if (bmcr & BMCR_AUTOEN) { 214112a855dSMarius Strobl if ((bmsr & BMSR_ACOMP) == 0) { 215112a855dSMarius Strobl /* Erg, still trying, I guess... */ 216112a855dSMarius Strobl mii->mii_media_active |= IFM_NONE; 217112a855dSMarius Strobl return; 218112a855dSMarius Strobl } 219112a855dSMarius Strobl 220112a855dSMarius Strobl if (msr & RUEPHY_MSR_SPEED100) 221112a855dSMarius Strobl mii->mii_media_active |= IFM_100_TX; 222112a855dSMarius Strobl else 223112a855dSMarius Strobl mii->mii_media_active |= IFM_10_T; 224112a855dSMarius Strobl 225112a855dSMarius Strobl if (msr & RUEPHY_MSR_DUPLEX) 226112a855dSMarius Strobl mii->mii_media_active |= 227112a855dSMarius Strobl IFM_FDX | mii_phy_flowstatus(phy); 228112a855dSMarius Strobl else 229112a855dSMarius Strobl mii->mii_media_active |= IFM_HDX; 230112a855dSMarius Strobl } else 231112a855dSMarius Strobl mii->mii_media_active = ife->ifm_media; 232112a855dSMarius Strobl } 233