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