1*e6713fe5SPyun YongHyeon /*- 2*e6713fe5SPyun YongHyeon * Copyright (c) 2010, Pyun YongHyeon <yongari@FreeBSD.org> 3*e6713fe5SPyun YongHyeon * All rights reserved. 4*e6713fe5SPyun YongHyeon * 5*e6713fe5SPyun YongHyeon * Redistribution and use in source and binary forms, with or without 6*e6713fe5SPyun YongHyeon * modification, are permitted provided that the following conditions 7*e6713fe5SPyun YongHyeon * are met: 8*e6713fe5SPyun YongHyeon * 1. Redistributions of source code must retain the above copyright 9*e6713fe5SPyun YongHyeon * notice unmodified, this list of conditions, and the following 10*e6713fe5SPyun YongHyeon * disclaimer. 11*e6713fe5SPyun YongHyeon * 2. Redistributions in binary form must reproduce the above copyright 12*e6713fe5SPyun YongHyeon * notice, this list of conditions and the following disclaimer in the 13*e6713fe5SPyun YongHyeon * documentation and/or other materials provided with the distribution. 14*e6713fe5SPyun YongHyeon * 15*e6713fe5SPyun YongHyeon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*e6713fe5SPyun YongHyeon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*e6713fe5SPyun YongHyeon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*e6713fe5SPyun YongHyeon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*e6713fe5SPyun YongHyeon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*e6713fe5SPyun YongHyeon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*e6713fe5SPyun YongHyeon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*e6713fe5SPyun YongHyeon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*e6713fe5SPyun YongHyeon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*e6713fe5SPyun YongHyeon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*e6713fe5SPyun YongHyeon * SUCH DAMAGE. 26*e6713fe5SPyun YongHyeon */ 27*e6713fe5SPyun YongHyeon 28*e6713fe5SPyun YongHyeon #include <sys/cdefs.h> 29*e6713fe5SPyun YongHyeon __FBSDID("$FreeBSD$"); 30*e6713fe5SPyun YongHyeon 31*e6713fe5SPyun YongHyeon /* 32*e6713fe5SPyun YongHyeon * Driver for the RDC Semiconductor R6040 10/100 PHY. 33*e6713fe5SPyun YongHyeon */ 34*e6713fe5SPyun YongHyeon 35*e6713fe5SPyun YongHyeon #include <sys/param.h> 36*e6713fe5SPyun YongHyeon #include <sys/systm.h> 37*e6713fe5SPyun YongHyeon #include <sys/kernel.h> 38*e6713fe5SPyun YongHyeon #include <sys/module.h> 39*e6713fe5SPyun YongHyeon #include <sys/socket.h> 40*e6713fe5SPyun YongHyeon #include <sys/bus.h> 41*e6713fe5SPyun YongHyeon 42*e6713fe5SPyun YongHyeon #include <net/if.h> 43*e6713fe5SPyun YongHyeon #include <net/if_media.h> 44*e6713fe5SPyun YongHyeon 45*e6713fe5SPyun YongHyeon #include <dev/mii/mii.h> 46*e6713fe5SPyun YongHyeon #include <dev/mii/miivar.h> 47*e6713fe5SPyun YongHyeon #include "miidevs.h" 48*e6713fe5SPyun YongHyeon 49*e6713fe5SPyun YongHyeon #include <dev/mii/rdcphyreg.h> 50*e6713fe5SPyun YongHyeon 51*e6713fe5SPyun YongHyeon #include "miibus_if.h" 52*e6713fe5SPyun YongHyeon 53*e6713fe5SPyun YongHyeon static device_probe_t rdcphy_probe; 54*e6713fe5SPyun YongHyeon static device_attach_t rdcphy_attach; 55*e6713fe5SPyun YongHyeon 56*e6713fe5SPyun YongHyeon struct rdcphy_softc { 57*e6713fe5SPyun YongHyeon struct mii_softc mii_sc; 58*e6713fe5SPyun YongHyeon int mii_model; 59*e6713fe5SPyun YongHyeon int mii_link_tick; 60*e6713fe5SPyun YongHyeon #define RDCPHY_MANNEG_TICK 3 61*e6713fe5SPyun YongHyeon }; 62*e6713fe5SPyun YongHyeon 63*e6713fe5SPyun YongHyeon static device_method_t rdcphy_methods[] = { 64*e6713fe5SPyun YongHyeon /* device interface */ 65*e6713fe5SPyun YongHyeon DEVMETHOD(device_probe, rdcphy_probe), 66*e6713fe5SPyun YongHyeon DEVMETHOD(device_attach, rdcphy_attach), 67*e6713fe5SPyun YongHyeon DEVMETHOD(device_detach, mii_phy_detach), 68*e6713fe5SPyun YongHyeon DEVMETHOD(device_shutdown, bus_generic_shutdown), 69*e6713fe5SPyun YongHyeon KOBJMETHOD_END 70*e6713fe5SPyun YongHyeon }; 71*e6713fe5SPyun YongHyeon 72*e6713fe5SPyun YongHyeon static devclass_t rdcphy_devclass; 73*e6713fe5SPyun YongHyeon 74*e6713fe5SPyun YongHyeon static driver_t rdcphy_driver = { 75*e6713fe5SPyun YongHyeon "rdcphy", 76*e6713fe5SPyun YongHyeon rdcphy_methods, 77*e6713fe5SPyun YongHyeon sizeof(struct rdcphy_softc) 78*e6713fe5SPyun YongHyeon }; 79*e6713fe5SPyun YongHyeon 80*e6713fe5SPyun YongHyeon DRIVER_MODULE(rdcphy, miibus, rdcphy_driver, rdcphy_devclass, 0, 0); 81*e6713fe5SPyun YongHyeon 82*e6713fe5SPyun YongHyeon static int rdcphy_service(struct mii_softc *, struct mii_data *, int); 83*e6713fe5SPyun YongHyeon static void rdcphy_status(struct mii_softc *); 84*e6713fe5SPyun YongHyeon 85*e6713fe5SPyun YongHyeon static const struct mii_phydesc rdcphys[] = { 86*e6713fe5SPyun YongHyeon MII_PHY_DESC(RDC, R6040), 87*e6713fe5SPyun YongHyeon MII_PHY_END 88*e6713fe5SPyun YongHyeon }; 89*e6713fe5SPyun YongHyeon 90*e6713fe5SPyun YongHyeon static int 91*e6713fe5SPyun YongHyeon rdcphy_probe(device_t dev) 92*e6713fe5SPyun YongHyeon { 93*e6713fe5SPyun YongHyeon 94*e6713fe5SPyun YongHyeon return (mii_phy_dev_probe(dev, rdcphys, BUS_PROBE_DEFAULT)); 95*e6713fe5SPyun YongHyeon } 96*e6713fe5SPyun YongHyeon 97*e6713fe5SPyun YongHyeon static int 98*e6713fe5SPyun YongHyeon rdcphy_attach(device_t dev) 99*e6713fe5SPyun YongHyeon { 100*e6713fe5SPyun YongHyeon struct rdcphy_softc *rsc; 101*e6713fe5SPyun YongHyeon struct mii_softc *sc; 102*e6713fe5SPyun YongHyeon struct mii_attach_args *ma; 103*e6713fe5SPyun YongHyeon struct mii_data *mii; 104*e6713fe5SPyun YongHyeon 105*e6713fe5SPyun YongHyeon rsc = device_get_softc(dev); 106*e6713fe5SPyun YongHyeon sc = &rsc->mii_sc; 107*e6713fe5SPyun YongHyeon ma = device_get_ivars(dev); 108*e6713fe5SPyun YongHyeon sc->mii_dev = device_get_parent(dev); 109*e6713fe5SPyun YongHyeon mii = ma->mii_data; 110*e6713fe5SPyun YongHyeon LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 111*e6713fe5SPyun YongHyeon 112*e6713fe5SPyun YongHyeon sc->mii_flags = miibus_get_flags(dev); 113*e6713fe5SPyun YongHyeon sc->mii_inst = mii->mii_instance++; 114*e6713fe5SPyun YongHyeon sc->mii_phy = ma->mii_phyno; 115*e6713fe5SPyun YongHyeon sc->mii_service = rdcphy_service; 116*e6713fe5SPyun YongHyeon sc->mii_pdata = mii; 117*e6713fe5SPyun YongHyeon 118*e6713fe5SPyun YongHyeon rsc->mii_model = MII_MODEL(ma->mii_id2); 119*e6713fe5SPyun YongHyeon if (bootverbose) 120*e6713fe5SPyun YongHyeon device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", 121*e6713fe5SPyun YongHyeon MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2), 122*e6713fe5SPyun YongHyeon MII_REV(ma->mii_id2)); 123*e6713fe5SPyun YongHyeon 124*e6713fe5SPyun YongHyeon mii_phy_reset(sc); 125*e6713fe5SPyun YongHyeon 126*e6713fe5SPyun YongHyeon sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 127*e6713fe5SPyun YongHyeon if (sc->mii_capabilities & BMSR_EXTSTAT) 128*e6713fe5SPyun YongHyeon sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 129*e6713fe5SPyun YongHyeon device_printf(dev, " "); 130*e6713fe5SPyun YongHyeon mii_phy_add_media(sc); 131*e6713fe5SPyun YongHyeon printf("\n"); 132*e6713fe5SPyun YongHyeon 133*e6713fe5SPyun YongHyeon MIIBUS_MEDIAINIT(sc->mii_dev); 134*e6713fe5SPyun YongHyeon return (0); 135*e6713fe5SPyun YongHyeon } 136*e6713fe5SPyun YongHyeon 137*e6713fe5SPyun YongHyeon static int 138*e6713fe5SPyun YongHyeon rdcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 139*e6713fe5SPyun YongHyeon { 140*e6713fe5SPyun YongHyeon struct rdcphy_softc *rsc; 141*e6713fe5SPyun YongHyeon struct ifmedia_entry *ife; 142*e6713fe5SPyun YongHyeon 143*e6713fe5SPyun YongHyeon rsc = (struct rdcphy_softc *)sc; 144*e6713fe5SPyun YongHyeon ife = mii->mii_media.ifm_cur; 145*e6713fe5SPyun YongHyeon 146*e6713fe5SPyun YongHyeon switch (cmd) { 147*e6713fe5SPyun YongHyeon case MII_POLLSTAT: 148*e6713fe5SPyun YongHyeon break; 149*e6713fe5SPyun YongHyeon 150*e6713fe5SPyun YongHyeon case MII_MEDIACHG: 151*e6713fe5SPyun YongHyeon /* 152*e6713fe5SPyun YongHyeon * If the interface is not up, don't do anything. 153*e6713fe5SPyun YongHyeon */ 154*e6713fe5SPyun YongHyeon if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 155*e6713fe5SPyun YongHyeon break; 156*e6713fe5SPyun YongHyeon 157*e6713fe5SPyun YongHyeon mii_phy_setmedia(sc); 158*e6713fe5SPyun YongHyeon switch (IFM_SUBTYPE(ife->ifm_media)) { 159*e6713fe5SPyun YongHyeon case IFM_100_TX: 160*e6713fe5SPyun YongHyeon case IFM_10_T: 161*e6713fe5SPyun YongHyeon /* 162*e6713fe5SPyun YongHyeon * Report fake lost link event to parent 163*e6713fe5SPyun YongHyeon * driver. This will stop MAC of parent 164*e6713fe5SPyun YongHyeon * driver and make it possible to reconfigure 165*e6713fe5SPyun YongHyeon * MAC after completion of link establishment. 166*e6713fe5SPyun YongHyeon * Note, the parent MAC seems to require 167*e6713fe5SPyun YongHyeon * restarting MAC when underlying any PHY 168*e6713fe5SPyun YongHyeon * configuration was changed even if the 169*e6713fe5SPyun YongHyeon * resolved speed/duplex was not changed at 170*e6713fe5SPyun YongHyeon * all. 171*e6713fe5SPyun YongHyeon */ 172*e6713fe5SPyun YongHyeon mii->mii_media_status = 0; 173*e6713fe5SPyun YongHyeon mii->mii_media_active = IFM_ETHER | IFM_NONE; 174*e6713fe5SPyun YongHyeon rsc->mii_link_tick = RDCPHY_MANNEG_TICK; 175*e6713fe5SPyun YongHyeon /* Immediately report link down. */ 176*e6713fe5SPyun YongHyeon mii_phy_update(sc, MII_MEDIACHG); 177*e6713fe5SPyun YongHyeon return (0); 178*e6713fe5SPyun YongHyeon default: 179*e6713fe5SPyun YongHyeon break; 180*e6713fe5SPyun YongHyeon } 181*e6713fe5SPyun YongHyeon break; 182*e6713fe5SPyun YongHyeon 183*e6713fe5SPyun YongHyeon case MII_TICK: 184*e6713fe5SPyun YongHyeon if (mii_phy_tick(sc) == EJUSTRETURN) 185*e6713fe5SPyun YongHyeon return (0); 186*e6713fe5SPyun YongHyeon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 187*e6713fe5SPyun YongHyeon /* 188*e6713fe5SPyun YongHyeon * It seems the PHY hardware does not correctly 189*e6713fe5SPyun YongHyeon * report link status changes when manual link 190*e6713fe5SPyun YongHyeon * configuration is in progress. It is also 191*e6713fe5SPyun YongHyeon * possible for the PHY to complete establishing 192*e6713fe5SPyun YongHyeon * a link within one second such that mii(4) 193*e6713fe5SPyun YongHyeon * did not notice the link change. To workaround 194*e6713fe5SPyun YongHyeon * the issue, emulate lost link event and wait 195*e6713fe5SPyun YongHyeon * for 3 seconds when manual link configuration 196*e6713fe5SPyun YongHyeon * is in progress. 3 seconds would be long 197*e6713fe5SPyun YongHyeon * enough to absorb transient link flips. 198*e6713fe5SPyun YongHyeon */ 199*e6713fe5SPyun YongHyeon if (rsc->mii_link_tick > 0) { 200*e6713fe5SPyun YongHyeon rsc->mii_link_tick--; 201*e6713fe5SPyun YongHyeon return (0); 202*e6713fe5SPyun YongHyeon } 203*e6713fe5SPyun YongHyeon } 204*e6713fe5SPyun YongHyeon break; 205*e6713fe5SPyun YongHyeon } 206*e6713fe5SPyun YongHyeon 207*e6713fe5SPyun YongHyeon /* Update the media status. */ 208*e6713fe5SPyun YongHyeon rdcphy_status(sc); 209*e6713fe5SPyun YongHyeon 210*e6713fe5SPyun YongHyeon /* Callback if something changed. */ 211*e6713fe5SPyun YongHyeon mii_phy_update(sc, cmd); 212*e6713fe5SPyun YongHyeon return (0); 213*e6713fe5SPyun YongHyeon } 214*e6713fe5SPyun YongHyeon 215*e6713fe5SPyun YongHyeon static void 216*e6713fe5SPyun YongHyeon rdcphy_status(struct mii_softc *sc) 217*e6713fe5SPyun YongHyeon { 218*e6713fe5SPyun YongHyeon struct mii_data *mii; 219*e6713fe5SPyun YongHyeon struct ifmedia_entry *ife; 220*e6713fe5SPyun YongHyeon int bmsr, bmcr, physts; 221*e6713fe5SPyun YongHyeon 222*e6713fe5SPyun YongHyeon mii = sc->mii_pdata; 223*e6713fe5SPyun YongHyeon ife = mii->mii_media.ifm_cur; 224*e6713fe5SPyun YongHyeon 225*e6713fe5SPyun YongHyeon mii->mii_media_status = IFM_AVALID; 226*e6713fe5SPyun YongHyeon mii->mii_media_active = IFM_ETHER; 227*e6713fe5SPyun YongHyeon 228*e6713fe5SPyun YongHyeon bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 229*e6713fe5SPyun YongHyeon physts = PHY_READ(sc, MII_RDCPHY_STATUS); 230*e6713fe5SPyun YongHyeon 231*e6713fe5SPyun YongHyeon if ((physts & STATUS_LINK_UP) != 0) 232*e6713fe5SPyun YongHyeon mii->mii_media_status |= IFM_ACTIVE; 233*e6713fe5SPyun YongHyeon 234*e6713fe5SPyun YongHyeon bmcr = PHY_READ(sc, MII_BMCR); 235*e6713fe5SPyun YongHyeon if ((bmcr & BMCR_ISO) != 0) { 236*e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_NONE; 237*e6713fe5SPyun YongHyeon mii->mii_media_status = 0; 238*e6713fe5SPyun YongHyeon return; 239*e6713fe5SPyun YongHyeon } 240*e6713fe5SPyun YongHyeon 241*e6713fe5SPyun YongHyeon if ((bmcr & BMCR_LOOP) != 0) 242*e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_LOOP; 243*e6713fe5SPyun YongHyeon 244*e6713fe5SPyun YongHyeon if ((bmcr & BMCR_AUTOEN) != 0) { 245*e6713fe5SPyun YongHyeon if ((bmsr & BMSR_ACOMP) == 0) { 246*e6713fe5SPyun YongHyeon /* Erg, still trying, I guess... */ 247*e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_NONE; 248*e6713fe5SPyun YongHyeon return; 249*e6713fe5SPyun YongHyeon } 250*e6713fe5SPyun YongHyeon } 251*e6713fe5SPyun YongHyeon 252*e6713fe5SPyun YongHyeon switch (physts & STATUS_SPEED_MASK) { 253*e6713fe5SPyun YongHyeon case STATUS_SPEED_100: 254*e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_100_TX; 255*e6713fe5SPyun YongHyeon break; 256*e6713fe5SPyun YongHyeon case STATUS_SPEED_10: 257*e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_10_T; 258*e6713fe5SPyun YongHyeon break; 259*e6713fe5SPyun YongHyeon default: 260*e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_NONE; 261*e6713fe5SPyun YongHyeon return; 262*e6713fe5SPyun YongHyeon } 263*e6713fe5SPyun YongHyeon if ((physts & STATUS_FULL_DUPLEX) != 0) 264*e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 265*e6713fe5SPyun YongHyeon else 266*e6713fe5SPyun YongHyeon mii->mii_media_active |= IFM_HDX; 267*e6713fe5SPyun YongHyeon } 268