1 /*- 2 * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@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, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/dev/mii/ruephy.c,v 1.1.4.1 2003/07/30 13:57:35 akiyama Exp $ 27 * $DragonFly: src/sys/dev/netif/mii_layer/ruephy.c,v 1.3 2005/10/24 16:55:40 dillon Exp $ 28 */ 29 30 /* 31 * driver for RealTek RTL8150 internal PHY 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/malloc.h> 38 #include <sys/socket.h> 39 #include <sys/bus.h> 40 41 #include <net/if.h> 42 #include <net/if_media.h> 43 44 #include <dev/netif/mii_layer/mii.h> 45 #include <dev/netif/mii_layer/miivar.h> 46 #include <dev/netif/mii_layer/ruephyreg.h> 47 48 #include "miibus_if.h" 49 50 static int ruephy_probe(device_t); 51 static int ruephy_attach(device_t); 52 static int ruephy_detach(device_t); 53 54 static device_method_t ruephy_methods[] = { 55 /* device interface */ 56 DEVMETHOD(device_probe, ruephy_probe), 57 DEVMETHOD(device_attach, ruephy_attach), 58 DEVMETHOD(device_detach, ruephy_detach), 59 DEVMETHOD(device_shutdown, bus_generic_shutdown), 60 { 0, 0 } 61 }; 62 63 static devclass_t ruephy_devclass; 64 65 static driver_t ruephy_driver = { 66 "ruephy", 67 ruephy_methods, 68 sizeof(struct mii_softc) 69 }; 70 71 DRIVER_MODULE(ruephy, miibus, ruephy_driver, ruephy_devclass, 0, 0); 72 73 static int ruephy_service(struct mii_softc *, struct mii_data *, int); 74 static void ruephy_reset(struct mii_softc *); 75 static void ruephy_status(struct mii_softc *); 76 77 static int 78 ruephy_probe(device_t dev) 79 { 80 struct mii_attach_args *ma; 81 device_t parent; 82 83 ma = device_get_ivars(dev); 84 parent = device_get_parent(device_get_parent(dev)); 85 86 /* 87 * RealTek RTL8150 PHY doesn't have vendor/device ID registers: 88 * the rue driver fakes up a return value of all zeros. 89 */ 90 if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 || 91 MII_MODEL(ma->mii_id2) != 0) 92 return (ENXIO); 93 94 /* 95 * Make sure the parent is an 'rue'. 96 */ 97 if (strcmp(device_get_name(parent), "rue") != 0) 98 return (ENXIO); 99 100 device_set_desc(dev, "RealTek RTL8150 internal media interface"); 101 102 return (0); 103 } 104 105 static int 106 ruephy_attach(device_t dev) 107 { 108 struct mii_softc *sc; 109 struct mii_attach_args *ma; 110 struct mii_data *mii; 111 112 sc = device_get_softc(dev); 113 ma = device_get_ivars(dev); 114 mii_softc_init(sc, ma); 115 sc->mii_dev = device_get_parent(dev); 116 mii = device_get_softc(sc->mii_dev); 117 118 /* 119 * The RealTek PHY can never be isolated, so never allow non-zero 120 * instances! 121 */ 122 if (mii->mii_instance != 0) { 123 device_printf(dev, "ignoring this PHY, non-zero instance\n"); 124 return (ENXIO); 125 } 126 127 LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 128 129 sc->mii_inst = mii->mii_instance; 130 sc->mii_service = ruephy_service; 131 sc->mii_pdata = mii; 132 mii->mii_instance++; 133 134 sc->mii_flags |= MIIF_NOISOLATE; 135 136 #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 137 138 ruephy_reset(sc); 139 140 sc->mii_capabilities = 141 PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 142 device_printf(dev, " "); 143 if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) 144 printf("no media present"); 145 else 146 mii_add_media(sc, sc->mii_capabilities); 147 printf("\n"); 148 149 #undef ADD 150 151 MIIBUS_MEDIAINIT(sc->mii_dev); 152 return (0); 153 } 154 155 static int 156 ruephy_detach(device_t dev) 157 { 158 struct mii_softc *sc; 159 struct mii_data *mii; 160 161 sc = device_get_softc(dev); 162 mii = device_get_softc(device_get_softc(dev)); 163 mii_phy_auto_stop(sc); 164 sc->mii_dev = NULL; 165 LIST_REMOVE(sc, mii_list); 166 167 return (0); 168 } 169 170 static int 171 ruephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 172 { 173 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 174 int reg; 175 176 /* 177 * We can't isolate the RealTek RTL8150 PHY, 178 * so it has to be the only one! 179 */ 180 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 181 panic("ruephy_service: can't isolate RealTek RTL8150 PHY"); 182 183 switch (cmd) { 184 case MII_POLLSTAT: 185 break; 186 187 case MII_MEDIACHG: 188 /* 189 * If the interface is not up, don't do anything. 190 */ 191 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 192 break; 193 194 switch (IFM_SUBTYPE(ife->ifm_media)) { 195 case IFM_AUTO: 196 /* 197 * If we're already in auto mode, just return. 198 */ 199 if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) 200 return (0); 201 (void) mii_phy_auto(sc, 1); 202 break; 203 case IFM_100_T4: 204 /* 205 * XXX Not supported as a manual setting right now. 206 */ 207 return (EINVAL); 208 default: 209 /* 210 * BMCR data is stored in the ifmedia entry. 211 */ 212 PHY_WRITE(sc, MII_ANAR, 213 mii_anar(ife->ifm_media)); 214 PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 215 } 216 break; 217 218 case MII_TICK: 219 /* 220 * Is the interface even up? 221 */ 222 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 223 return (0); 224 225 /* 226 * Only used for autonegotiation. 227 */ 228 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 229 break; 230 231 /* 232 * Check to see if we have link. If we do, we don't 233 * need to restart the autonegotiation process. Read 234 * the MSR twice in case it's latched. 235 */ 236 reg = PHY_READ(sc, RUEPHY_MII_MSR) | 237 PHY_READ(sc, RUEPHY_MII_MSR); 238 if (reg & RUEPHY_MSR_LINK) 239 break; 240 241 /* 242 * Only retry autonegotiation every 5 seconds. 243 */ 244 if (++sc->mii_ticks != 5) 245 return (0); 246 247 sc->mii_ticks = 0; 248 ruephy_reset(sc); 249 if (mii_phy_auto(sc, 0) == EJUSTRETURN) 250 return (0); 251 break; 252 } 253 254 /* Update the media status. */ 255 ruephy_status(sc); 256 257 /* Callback if something changed. */ 258 if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { 259 MIIBUS_STATCHG(sc->mii_dev); 260 sc->mii_active = mii->mii_media_active; 261 } 262 263 return (0); 264 } 265 266 static void 267 ruephy_reset(struct mii_softc *sc) 268 { 269 270 mii_phy_reset(sc); 271 272 /* 273 * XXX RealTek RTL8150 PHY doesn't set the BMCR properly after 274 * XXX reset, which breaks autonegotiation. 275 */ 276 PHY_WRITE(sc, MII_BMCR, (BMCR_S100 | BMCR_AUTOEN | BMCR_FDX)); 277 } 278 279 static void 280 ruephy_status(struct mii_softc *phy) 281 { 282 struct mii_data *mii = phy->mii_pdata; 283 int bmsr, bmcr, msr; 284 285 mii->mii_media_status = IFM_AVALID; 286 mii->mii_media_active = IFM_ETHER; 287 288 msr = PHY_READ(phy, RUEPHY_MII_MSR) | PHY_READ(phy, RUEPHY_MII_MSR); 289 if (msr & RUEPHY_MSR_LINK) 290 mii->mii_media_status |= IFM_ACTIVE; 291 292 bmcr = PHY_READ(phy, MII_BMCR); 293 if (bmcr & BMCR_ISO) { 294 mii->mii_media_active |= IFM_NONE; 295 mii->mii_media_status = 0; 296 return; 297 } 298 299 bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); 300 301 if (bmcr & BMCR_AUTOEN) { 302 if ((bmsr & BMSR_ACOMP) == 0) { 303 /* Erg, still trying, I guess... */ 304 mii->mii_media_active |= IFM_NONE; 305 return; 306 } 307 308 if (msr & RUEPHY_MSR_SPEED100) 309 mii->mii_media_active |= IFM_100_TX; 310 else 311 mii->mii_media_active |= IFM_10_T; 312 313 if (msr & RUEPHY_MSR_DUPLEX) 314 mii->mii_media_active |= IFM_FDX; 315 } else 316 mii->mii_media_active = mii_media_from_bmcr(bmcr); 317 } 318