1 /* $NetBSD: igphy.c,v 1.5 2004/10/05 20:20:00 thorpej Exp $ */ 2 3 /* 4 * The Intel copyright applies to the analog register setup, and the 5 * (currently disabled) SmartSpeed workaround code. 6 */ 7 8 /******************************************************************************* 9 10 Copyright (c) 2001-2003, Intel Corporation 11 All rights reserved. 12 13 Redistribution and use in source and binary forms, with or without 14 modification, are permitted provided that the following conditions are met: 15 16 1. Redistributions of source code must retain the above copyright notice, 17 this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in the 21 documentation and/or other materials provided with the distribution. 22 23 3. Neither the name of the Intel Corporation nor the names of its 24 contributors may be used to endorse or promote products derived from 25 this software without specific prior written permission. 26 27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 POSSIBILITY OF SUCH DAMAGE. 38 39 *******************************************************************************/ 40 41 42 /*- 43 * Copyright (c) 1998, 1999, 2000, 2003 The NetBSD Foundation, Inc. 44 * All rights reserved. 45 * 46 * This code is derived from software contributed to The NetBSD Foundation 47 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 48 * NASA Ames Research Center, and by Frank van der Linden. 49 * 50 * Redistribution and use in source and binary forms, with or without 51 * modification, are permitted provided that the following conditions 52 * are met: 53 * 1. Redistributions of source code must retain the above copyright 54 * notice, this list of conditions and the following disclaimer. 55 * 2. Redistributions in binary form must reproduce the above copyright 56 * notice, this list of conditions and the following disclaimer in the 57 * documentation and/or other materials provided with the distribution. 58 * 3. All advertising materials mentioning features or use of this software 59 * must display the following acknowledgement: 60 * This product includes software developed by the NetBSD 61 * Foundation, Inc. and its contributors. 62 * 4. Neither the name of The NetBSD Foundation nor the names of its 63 * contributors may be used to endorse or promote products derived 64 * from this software without specific prior written permission. 65 * 66 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 67 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 68 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 69 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 70 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 71 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 72 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 73 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 74 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 75 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 76 * POSSIBILITY OF SUCH DAMAGE. 77 */ 78 79 #include <sys/cdefs.h> 80 __KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.5 2004/10/05 20:20:00 thorpej Exp $"); 81 82 #include "opt_mii.h" 83 84 #include <sys/param.h> 85 #include <sys/systm.h> 86 #include <sys/kernel.h> 87 #include <sys/device.h> 88 #include <sys/socket.h> 89 #include <sys/errno.h> 90 91 #include <net/if.h> 92 #include <net/if_media.h> 93 94 #include <dev/mii/mii.h> 95 #include <dev/mii/miivar.h> 96 #include <dev/mii/miidevs.h> 97 98 #include <dev/mii/igphyreg.h> 99 100 struct igphy_softc { 101 struct mii_softc sc_mii; 102 int sc_smartspeed; 103 }; 104 105 static void igphy_reset(struct mii_softc *); 106 static void igphy_load_dspcode(struct mii_softc *); 107 static void igphy_smartspeed_workaround(struct mii_softc *sc); 108 109 static int igphymatch(struct device *, struct cfdata *, void *); 110 static void igphyattach(struct device *, struct device *, void *); 111 112 CFATTACH_DECL(igphy, sizeof(struct igphy_softc), 113 igphymatch, igphyattach, mii_phy_detach, mii_phy_activate); 114 115 static int igphy_service(struct mii_softc *, struct mii_data *, int); 116 static void igphy_status(struct mii_softc *); 117 118 static const struct mii_phy_funcs igphy_funcs = { 119 igphy_service, igphy_status, igphy_reset, 120 }; 121 122 static const struct mii_phydesc igphys[] = { 123 { MII_OUI_yyINTEL, MII_MODEL_yyINTEL_IGP01E1000, 124 MII_STR_yyINTEL_IGP01E1000 }, 125 126 {0, 0, 127 NULL }, 128 }; 129 130 static int 131 igphymatch(struct device *parent, struct cfdata *match, void *aux) 132 { 133 struct mii_attach_args *ma = aux; 134 135 if (mii_phy_match(ma, igphys) != NULL) 136 return 10; 137 138 return 0; 139 } 140 141 static void 142 igphyattach(struct device *parent, struct device *self, void *aux) 143 { 144 struct mii_softc *sc = (struct mii_softc *)self; 145 struct mii_attach_args *ma = aux; 146 struct mii_data *mii = ma->mii_data; 147 const struct mii_phydesc *mpd; 148 149 mpd = mii_phy_match(ma, igphys); 150 aprint_naive(": Media interface\n"); 151 aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 152 153 sc->mii_inst = mii->mii_instance; 154 sc->mii_phy = ma->mii_phyno; 155 sc->mii_funcs = &igphy_funcs; 156 sc->mii_pdata = mii; 157 sc->mii_flags = ma->mii_flags; 158 sc->mii_anegticks = 10; 159 160 PHY_RESET(sc); 161 162 sc->mii_capabilities = 163 PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 164 if (sc->mii_capabilities & BMSR_EXTSTAT) 165 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 166 aprint_normal("%s: ", sc->mii_dev.dv_xname); 167 if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && 168 (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) 169 aprint_error("no media present"); 170 else 171 mii_phy_add_media(sc); 172 aprint_normal("\n"); 173 } 174 175 static void 176 igphy_load_dspcode(struct mii_softc *sc) 177 { 178 static const struct { 179 int reg; 180 uint16_t val; 181 } dspcode[] = { 182 { 0x1f95, 0x0001 }, 183 { 0x1f71, 0xbd21 }, 184 { 0x1f79, 0x0018 }, 185 { 0x1f30, 0x1600 }, 186 { 0x1f31, 0x0014 }, 187 { 0x1f32, 0x161c }, 188 { 0x1f94, 0x0003 }, 189 { 0x1f96, 0x003f }, 190 { 0x2010, 0x0008 }, 191 { 0, 0 }, 192 }; 193 int i; 194 195 delay(10); 196 197 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000); 198 PHY_WRITE(sc, 0x0000, 0x0140); 199 200 delay(5); 201 202 for (i = 0; dspcode[i].reg != 0; i++) 203 IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 204 205 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000); 206 PHY_WRITE(sc, 0x0000, 0x3300); 207 } 208 209 static void 210 igphy_reset(struct mii_softc *sc) 211 { 212 uint16_t fused, fine, coarse; 213 214 mii_phy_reset(sc); 215 igphy_load_dspcode(sc); 216 217 fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS); 218 if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) { 219 fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS); 220 221 fine = fused & ANALOG_FUSE_FINE_MASK; 222 coarse = fused & ANALOG_FUSE_COARSE_MASK; 223 224 if (coarse > ANALOG_FUSE_COARSE_THRESH) { 225 coarse -= ANALOG_FUSE_COARSE_10; 226 fine -= ANALOG_FUSE_FINE_1; 227 } else if (coarse == ANALOG_FUSE_COARSE_THRESH) 228 fine -= ANALOG_FUSE_FINE_10; 229 230 fused = (fused & ANALOG_FUSE_POLY_MASK) | 231 (fine & ANALOG_FUSE_FINE_MASK) | 232 (coarse & ANALOG_FUSE_COARSE_MASK); 233 234 IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused); 235 IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS, 236 ANALOG_FUSE_ENABLE_SW_CONTROL); 237 } 238 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000); 239 } 240 241 242 static int 243 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 244 { 245 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 246 uint16_t reg; 247 248 switch (cmd) { 249 case MII_POLLSTAT: 250 /* 251 * If we're not polling our PHY instance, just return. 252 */ 253 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 254 return (0); 255 break; 256 257 case MII_MEDIACHG: 258 /* 259 * If the media indicates a different PHY instance, 260 * isolate ourselves. 261 */ 262 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 263 reg = PHY_READ(sc, MII_BMCR); 264 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 265 return (0); 266 } 267 268 /* 269 * If the interface is not up, don't do anything. 270 */ 271 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 272 break; 273 274 mii_phy_setmedia(sc); 275 break; 276 277 case MII_TICK: 278 /* 279 * If we're not currently selected, just return. 280 */ 281 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 282 return (0); 283 284 igphy_smartspeed_workaround(sc); 285 286 if (mii_phy_tick(sc) == EJUSTRETURN) 287 return (0); 288 break; 289 290 case MII_DOWN: 291 mii_phy_down(sc); 292 return (0); 293 } 294 295 /* Update the media status. */ 296 mii_phy_status(sc); 297 298 /* Callback if something changed. */ 299 mii_phy_update(sc, cmd); 300 return (0); 301 } 302 303 304 static void 305 igphy_status(struct mii_softc *sc) 306 { 307 struct mii_data *mii = sc->mii_pdata; 308 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 309 uint16_t bmcr, pssr, gtsr, bmsr; 310 311 mii->mii_media_status = IFM_AVALID; 312 mii->mii_media_active = IFM_ETHER; 313 314 pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS); 315 316 if (pssr & PSSR_LINK_UP) 317 mii->mii_media_status |= IFM_ACTIVE; 318 319 bmcr = PHY_READ(sc, MII_BMCR); 320 if (bmcr & BMCR_ISO) { 321 mii->mii_media_active |= IFM_NONE; 322 mii->mii_media_status = 0; 323 return; 324 } 325 326 if (bmcr & BMCR_LOOP) 327 mii->mii_media_active |= IFM_LOOP; 328 329 bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 330 331 /* 332 * XXX can't check if the info is valid, no 333 * 'negotiation done' bit? 334 */ 335 if (bmcr & BMCR_AUTOEN) { 336 if ((bmsr & BMSR_ACOMP) == 0) { 337 mii->mii_media_active |= IFM_NONE; 338 return; 339 } 340 switch (pssr & PSSR_SPEED_MASK) { 341 case PSSR_SPEED_1000MBPS: 342 mii->mii_media_active |= IFM_1000_T; 343 gtsr = PHY_READ(sc, MII_100T2SR); 344 if (gtsr & GTSR_MS_RES) 345 mii->mii_media_active |= IFM_ETH_MASTER; 346 break; 347 348 case PSSR_SPEED_100MBPS: 349 mii->mii_media_active |= IFM_100_TX; 350 break; 351 352 case PSSR_SPEED_10MBPS: 353 mii->mii_media_active |= IFM_10_T; 354 break; 355 356 default: 357 mii->mii_media_active |= IFM_NONE; 358 mii->mii_media_status = 0; 359 return; 360 } 361 362 if (pssr & PSSR_FULL_DUPLEX) 363 mii->mii_media_active |= 364 IFM_FDX | mii_phy_flowstatus(sc); 365 } else 366 mii->mii_media_active = ife->ifm_media; 367 } 368 369 static void 370 igphy_smartspeed_workaround(struct mii_softc *sc) 371 { 372 struct igphy_softc *igsc = (struct igphy_softc *) sc; 373 uint16_t reg, gtsr, gtcr; 374 375 if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0) 376 return; 377 378 /* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */ 379 380 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 381 if ((reg & BMSR_LINK) == 0) { 382 switch (igsc->sc_smartspeed) { 383 case 0: 384 gtsr = PHY_READ(sc, MII_100T2SR); 385 if (!(gtsr & GTSR_MAN_MS_FLT)) 386 break; 387 gtsr = PHY_READ(sc, MII_100T2SR); 388 if (gtsr & GTSR_MAN_MS_FLT) { 389 gtcr = PHY_READ(sc, MII_100T2CR); 390 if (gtcr & GTCR_MAN_MS) { 391 gtcr &= ~GTCR_MAN_MS; 392 PHY_WRITE(sc, MII_100T2CR, 393 gtcr); 394 } 395 mii_phy_auto(sc, 0); 396 } 397 break; 398 case IGPHY_TICK_DOWNSHIFT: 399 gtcr = PHY_READ(sc, MII_100T2CR); 400 gtcr |= GTCR_MAN_MS; 401 PHY_WRITE(sc, MII_100T2CR, gtcr); 402 mii_phy_auto(sc, 0); 403 break; 404 default: 405 break; 406 } 407 if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX) 408 igsc->sc_smartspeed = 0; 409 } else 410 igsc->sc_smartspeed = 0; 411 } 412