1 /* $NetBSD: tlphy.c,v 1.18 1999/05/14 11:40:28 drochner Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-2-Clause-NetBSD AND BSD-2-Clause 5 * 6 * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to The NetBSD Foundation 10 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 11 * NASA Ames Research Center. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /*- 36 * Copyright (c) 1997 Manuel Bouyer. All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 49 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 50 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 52 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 56 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 */ 58 59 #include <sys/cdefs.h> 60 __FBSDID("$FreeBSD$"); 61 62 /* 63 * Driver for Texas Instruments's ThunderLAN PHYs 64 */ 65 66 #include <sys/param.h> 67 #include <sys/systm.h> 68 #include <sys/kernel.h> 69 #include <sys/socket.h> 70 #include <sys/errno.h> 71 #include <sys/module.h> 72 #include <sys/bus.h> 73 #include <sys/malloc.h> 74 75 #include <machine/bus.h> 76 77 #include <net/if.h> 78 #include <net/if_media.h> 79 80 #include <dev/mii/mii.h> 81 #include <dev/mii/miivar.h> 82 #include "miidevs.h" 83 84 #include <dev/mii/tlphyreg.h> 85 86 #include "miibus_if.h" 87 88 struct tlphy_softc { 89 struct mii_softc sc_mii; /* generic PHY */ 90 int sc_need_acomp; 91 }; 92 93 static int tlphy_probe(device_t); 94 static int tlphy_attach(device_t); 95 96 static device_method_t tlphy_methods[] = { 97 /* device interface */ 98 DEVMETHOD(device_probe, tlphy_probe), 99 DEVMETHOD(device_attach, tlphy_attach), 100 DEVMETHOD(device_detach, mii_phy_detach), 101 DEVMETHOD(device_shutdown, bus_generic_shutdown), 102 DEVMETHOD_END 103 }; 104 105 static devclass_t tlphy_devclass; 106 107 static driver_t tlphy_driver = { 108 "tlphy", 109 tlphy_methods, 110 sizeof(struct tlphy_softc) 111 }; 112 113 DRIVER_MODULE(tlphy, miibus, tlphy_driver, tlphy_devclass, 0, 0); 114 115 static int tlphy_service(struct mii_softc *, struct mii_data *, int); 116 static int tlphy_auto(struct tlphy_softc *); 117 static void tlphy_acomp(struct tlphy_softc *); 118 static void tlphy_status(struct mii_softc *); 119 120 static const struct mii_phydesc tlphys[] = { 121 MII_PHY_DESC(TI, TLAN10T), 122 MII_PHY_END 123 }; 124 125 static const struct mii_phy_funcs tlphy_funcs = { 126 tlphy_service, 127 tlphy_status, 128 mii_phy_reset 129 }; 130 131 static int 132 tlphy_probe(device_t dev) 133 { 134 135 if (!mii_dev_mac_match(dev, "tl")) 136 return (ENXIO); 137 return (mii_phy_dev_probe(dev, tlphys, BUS_PROBE_DEFAULT)); 138 } 139 140 static int 141 tlphy_attach(device_t dev) 142 { 143 device_t *devlist; 144 struct mii_softc *other, *sc_mii; 145 const char *sep = ""; 146 int capmask, devs, i; 147 148 sc_mii = device_get_softc(dev); 149 150 mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &tlphy_funcs, 0); 151 152 /* 153 * Note that if we're on a device that also supports 100baseTX, 154 * we are not going to want to use the built-in 10baseT port, 155 * since there will be another PHY on the MII wired up to the 156 * UTP connector. 157 */ 158 capmask = BMSR_DEFCAPMASK; 159 if (sc_mii->mii_inst && 160 device_get_children(sc_mii->mii_dev, &devlist, &devs) == 0) { 161 for (i = 0; i < devs; i++) { 162 if (devlist[i] != dev) { 163 other = device_get_softc(devlist[i]); 164 capmask &= ~other->mii_capabilities; 165 break; 166 } 167 } 168 free(devlist, M_TEMP); 169 } 170 171 PHY_RESET(sc_mii); 172 173 sc_mii->mii_capabilities = PHY_READ(sc_mii, MII_BMSR) & capmask; 174 175 #define ADD(m, c) \ 176 ifmedia_add(&sc_mii->mii_pdata->mii_media, (m), (c), NULL) 177 #define PRINT(s) printf("%s%s", sep, s); sep = ", " 178 179 if ((sc_mii->mii_flags & (MIIF_MACPRIV0 | MIIF_MACPRIV1)) != 0 && 180 (sc_mii->mii_capabilities & BMSR_MEDIAMASK) != 0) 181 device_printf(dev, " "); 182 if ((sc_mii->mii_flags & MIIF_MACPRIV0) != 0) { 183 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_2, 0, sc_mii->mii_inst), 184 0); 185 PRINT("10base2/BNC"); 186 } 187 if ((sc_mii->mii_flags & MIIF_MACPRIV1) != 0) { 188 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_5, 0, sc_mii->mii_inst), 189 0); 190 PRINT("10base5/AUI"); 191 } 192 if ((sc_mii->mii_capabilities & BMSR_MEDIAMASK) != 0) { 193 printf("%s", sep); 194 mii_phy_add_media(sc_mii); 195 } 196 if ((sc_mii->mii_flags & (MIIF_MACPRIV0 | MIIF_MACPRIV1)) != 0 && 197 (sc_mii->mii_capabilities & BMSR_MEDIAMASK) != 0) 198 printf("\n"); 199 #undef ADD 200 #undef PRINT 201 202 MIIBUS_MEDIAINIT(sc_mii->mii_dev); 203 return (0); 204 } 205 206 static int 207 tlphy_service(struct mii_softc *self, struct mii_data *mii, int cmd) 208 { 209 struct tlphy_softc *sc = (struct tlphy_softc *)self; 210 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 211 int reg; 212 213 if (sc->sc_need_acomp) 214 tlphy_acomp(sc); 215 216 switch (cmd) { 217 case MII_POLLSTAT: 218 break; 219 220 case MII_MEDIACHG: 221 switch (IFM_SUBTYPE(ife->ifm_media)) { 222 case IFM_AUTO: 223 /* 224 * The ThunderLAN PHY doesn't self-configure after 225 * an autonegotiation cycle, so there's no such 226 * thing as "already in auto mode". 227 */ 228 (void)tlphy_auto(sc); 229 break; 230 case IFM_10_2: 231 case IFM_10_5: 232 PHY_WRITE(&sc->sc_mii, MII_BMCR, 0); 233 PHY_WRITE(&sc->sc_mii, MII_TLPHY_CTRL, CTRL_AUISEL); 234 DELAY(100000); 235 break; 236 default: 237 PHY_WRITE(&sc->sc_mii, MII_TLPHY_CTRL, 0); 238 DELAY(100000); 239 mii_phy_setmedia(&sc->sc_mii); 240 } 241 break; 242 243 case MII_TICK: 244 /* 245 * Only used for autonegotiation. 246 */ 247 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 248 break; 249 250 /* 251 * Check to see if we have link. If we do, we don't 252 * need to restart the autonegotiation process. Read 253 * the BMSR twice in case it's latched. 254 * 255 * XXX WHAT ABOUT CHECKING LINK ON THE BNC/AUI?! 256 */ 257 reg = PHY_READ(&sc->sc_mii, MII_BMSR) | 258 PHY_READ(&sc->sc_mii, MII_BMSR); 259 if (reg & BMSR_LINK) 260 break; 261 262 /* 263 * Only retry autonegotiation every 5 seconds. 264 */ 265 if (++sc->sc_mii.mii_ticks <= MII_ANEGTICKS) 266 break; 267 268 sc->sc_mii.mii_ticks = 0; 269 PHY_RESET(&sc->sc_mii); 270 (void)tlphy_auto(sc); 271 return (0); 272 } 273 274 /* Update the media status. */ 275 PHY_STATUS(self); 276 277 /* Callback if something changed. */ 278 mii_phy_update(&sc->sc_mii, cmd); 279 return (0); 280 } 281 282 static void 283 tlphy_status(struct mii_softc *self) 284 { 285 struct tlphy_softc *sc = (struct tlphy_softc *)self; 286 struct mii_data *mii = sc->sc_mii.mii_pdata; 287 int bmsr, bmcr, tlctrl; 288 289 mii->mii_media_status = IFM_AVALID; 290 mii->mii_media_active = IFM_ETHER; 291 292 bmcr = PHY_READ(&sc->sc_mii, 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 tlctrl = PHY_READ(&sc->sc_mii, MII_TLPHY_CTRL); 300 if (tlctrl & CTRL_AUISEL) { 301 mii->mii_media_status = 0; 302 mii->mii_media_active = mii->mii_media.ifm_cur->ifm_media; 303 return; 304 } 305 306 bmsr = PHY_READ(&sc->sc_mii, MII_BMSR) | 307 PHY_READ(&sc->sc_mii, MII_BMSR); 308 if (bmsr & BMSR_LINK) 309 mii->mii_media_status |= IFM_ACTIVE; 310 311 if (bmcr & BMCR_LOOP) 312 mii->mii_media_active |= IFM_LOOP; 313 314 /* 315 * Grr, braindead ThunderLAN PHY doesn't have any way to 316 * tell which media is actually active. (Note it also 317 * doesn't self-configure after autonegotiation.) We 318 * just have to report what's in the BMCR. 319 */ 320 if (bmcr & BMCR_FDX) 321 mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(self); 322 else 323 mii->mii_media_active |= IFM_HDX; 324 mii->mii_media_active |= IFM_10_T; 325 } 326 327 static int 328 tlphy_auto(struct tlphy_softc *sc) 329 { 330 int error; 331 332 switch ((error = mii_phy_auto(&sc->sc_mii))) { 333 case EIO: 334 /* 335 * Just assume we're not in full-duplex mode. 336 * XXX Check link and try AUI/BNC? 337 */ 338 PHY_WRITE(&sc->sc_mii, MII_BMCR, 0); 339 break; 340 341 case EJUSTRETURN: 342 /* Flag that we need to program when it completes. */ 343 sc->sc_need_acomp = 1; 344 break; 345 346 default: 347 tlphy_acomp(sc); 348 } 349 350 return (error); 351 } 352 353 static void 354 tlphy_acomp(struct tlphy_softc *sc) 355 { 356 int aner, anlpar; 357 358 sc->sc_need_acomp = 0; 359 360 /* 361 * Grr, braindead ThunderLAN PHY doesn't self-configure 362 * after autonegotiation. We have to do it ourselves 363 * based on the link partner status. 364 */ 365 366 aner = PHY_READ(&sc->sc_mii, MII_ANER); 367 if (aner & ANER_LPAN) { 368 anlpar = PHY_READ(&sc->sc_mii, MII_ANLPAR) & 369 PHY_READ(&sc->sc_mii, MII_ANAR); 370 if (anlpar & ANAR_10_FD) { 371 PHY_WRITE(&sc->sc_mii, MII_BMCR, BMCR_FDX); 372 return; 373 } 374 } 375 PHY_WRITE(&sc->sc_mii, MII_BMCR, 0); 376 } 377