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