1 /* $NetBSD: igphy.c,v 1.8 2006/10/12 01:31:25 christos 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.8 2006/10/12 01:31:25 christos 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 __unused, struct cfdata *match __unused, 132 void *aux) 133 { 134 struct mii_attach_args *ma = aux; 135 136 if (mii_phy_match(ma, igphys) != NULL) 137 return 10; 138 139 return 0; 140 } 141 142 static void 143 igphyattach(struct device *parent __unused, struct device *self, void *aux) 144 { 145 struct mii_softc *sc = device_private(self); 146 struct mii_attach_args *ma = aux; 147 struct mii_data *mii = ma->mii_data; 148 const struct mii_phydesc *mpd; 149 150 mpd = mii_phy_match(ma, igphys); 151 aprint_naive(": Media interface\n"); 152 aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 153 154 sc->mii_inst = mii->mii_instance; 155 sc->mii_phy = ma->mii_phyno; 156 sc->mii_funcs = &igphy_funcs; 157 sc->mii_pdata = mii; 158 sc->mii_flags = ma->mii_flags; 159 sc->mii_anegticks = 10; 160 161 PHY_RESET(sc); 162 163 sc->mii_capabilities = 164 PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 165 if (sc->mii_capabilities & BMSR_EXTSTAT) 166 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 167 aprint_normal("%s: ", sc->mii_dev.dv_xname); 168 if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && 169 (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) 170 aprint_error("no media present"); 171 else 172 mii_phy_add_media(sc); 173 aprint_normal("\n"); 174 } 175 176 static void 177 igphy_load_dspcode(struct mii_softc *sc) 178 { 179 static const struct { 180 int reg; 181 uint16_t val; 182 } dspcode[] = { 183 { 0x1f95, 0x0001 }, 184 { 0x1f71, 0xbd21 }, 185 { 0x1f79, 0x0018 }, 186 { 0x1f30, 0x1600 }, 187 { 0x1f31, 0x0014 }, 188 { 0x1f32, 0x161c }, 189 { 0x1f94, 0x0003 }, 190 { 0x1f96, 0x003f }, 191 { 0x2010, 0x0008 }, 192 { 0, 0 }, 193 }; 194 int i; 195 196 delay(10); 197 198 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000); 199 PHY_WRITE(sc, 0x0000, 0x0140); 200 201 delay(5); 202 203 for (i = 0; dspcode[i].reg != 0; i++) 204 IGPHY_WRITE(sc, dspcode[i].reg, dspcode[i].val); 205 206 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000); 207 PHY_WRITE(sc, 0x0000, 0x3300); 208 } 209 210 static void 211 igphy_reset(struct mii_softc *sc) 212 { 213 uint16_t fused, fine, coarse; 214 215 mii_phy_reset(sc); 216 igphy_load_dspcode(sc); 217 218 fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS); 219 if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) { 220 fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS); 221 222 fine = fused & ANALOG_FUSE_FINE_MASK; 223 coarse = fused & ANALOG_FUSE_COARSE_MASK; 224 225 if (coarse > ANALOG_FUSE_COARSE_THRESH) { 226 coarse -= ANALOG_FUSE_COARSE_10; 227 fine -= ANALOG_FUSE_FINE_1; 228 } else if (coarse == ANALOG_FUSE_COARSE_THRESH) 229 fine -= ANALOG_FUSE_FINE_10; 230 231 fused = (fused & ANALOG_FUSE_POLY_MASK) | 232 (fine & ANALOG_FUSE_FINE_MASK) | 233 (coarse & ANALOG_FUSE_COARSE_MASK); 234 235 IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused); 236 IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS, 237 ANALOG_FUSE_ENABLE_SW_CONTROL); 238 } 239 PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT,0x0000); 240 } 241 242 243 static int 244 igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 245 { 246 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 247 uint16_t reg; 248 249 switch (cmd) { 250 case MII_POLLSTAT: 251 /* 252 * If we're not polling our PHY instance, just return. 253 */ 254 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 255 return (0); 256 break; 257 258 case MII_MEDIACHG: 259 /* 260 * If the media indicates a different PHY instance, 261 * isolate ourselves. 262 */ 263 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 264 reg = PHY_READ(sc, MII_BMCR); 265 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 266 return (0); 267 } 268 269 /* 270 * If the interface is not up, don't do anything. 271 */ 272 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 273 break; 274 275 mii_phy_setmedia(sc); 276 break; 277 278 case MII_TICK: 279 /* 280 * If we're not currently selected, just return. 281 */ 282 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 283 return (0); 284 285 igphy_smartspeed_workaround(sc); 286 287 if (mii_phy_tick(sc) == EJUSTRETURN) 288 return (0); 289 break; 290 291 case MII_DOWN: 292 mii_phy_down(sc); 293 return (0); 294 } 295 296 /* Update the media status. */ 297 mii_phy_status(sc); 298 299 /* Callback if something changed. */ 300 mii_phy_update(sc, cmd); 301 return (0); 302 } 303 304 305 static void 306 igphy_status(struct mii_softc *sc) 307 { 308 struct mii_data *mii = sc->mii_pdata; 309 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 310 uint16_t bmcr, pssr, gtsr, bmsr; 311 312 mii->mii_media_status = IFM_AVALID; 313 mii->mii_media_active = IFM_ETHER; 314 315 pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS); 316 317 if (pssr & PSSR_LINK_UP) 318 mii->mii_media_status |= IFM_ACTIVE; 319 320 bmcr = PHY_READ(sc, MII_BMCR); 321 if (bmcr & BMCR_ISO) { 322 mii->mii_media_active |= IFM_NONE; 323 mii->mii_media_status = 0; 324 return; 325 } 326 327 if (bmcr & BMCR_LOOP) 328 mii->mii_media_active |= IFM_LOOP; 329 330 bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 331 332 /* 333 * XXX can't check if the info is valid, no 334 * 'negotiation done' bit? 335 */ 336 if (bmcr & BMCR_AUTOEN) { 337 if ((bmsr & BMSR_ACOMP) == 0) { 338 mii->mii_media_active |= IFM_NONE; 339 return; 340 } 341 switch (pssr & PSSR_SPEED_MASK) { 342 case PSSR_SPEED_1000MBPS: 343 mii->mii_media_active |= IFM_1000_T; 344 gtsr = PHY_READ(sc, MII_100T2SR); 345 if (gtsr & GTSR_MS_RES) 346 mii->mii_media_active |= IFM_ETH_MASTER; 347 break; 348 349 case PSSR_SPEED_100MBPS: 350 mii->mii_media_active |= IFM_100_TX; 351 break; 352 353 case PSSR_SPEED_10MBPS: 354 mii->mii_media_active |= IFM_10_T; 355 break; 356 357 default: 358 mii->mii_media_active |= IFM_NONE; 359 mii->mii_media_status = 0; 360 return; 361 } 362 363 if (pssr & PSSR_FULL_DUPLEX) 364 mii->mii_media_active |= 365 IFM_FDX | mii_phy_flowstatus(sc); 366 } else 367 mii->mii_media_active = ife->ifm_media; 368 } 369 370 static void 371 igphy_smartspeed_workaround(struct mii_softc *sc) 372 { 373 struct igphy_softc *igsc = (struct igphy_softc *) sc; 374 uint16_t reg, gtsr, gtcr; 375 376 if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0) 377 return; 378 379 /* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */ 380 381 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 382 if ((reg & BMSR_LINK) == 0) { 383 switch (igsc->sc_smartspeed) { 384 case 0: 385 gtsr = PHY_READ(sc, MII_100T2SR); 386 if (!(gtsr & GTSR_MAN_MS_FLT)) 387 break; 388 gtsr = PHY_READ(sc, MII_100T2SR); 389 if (gtsr & GTSR_MAN_MS_FLT) { 390 gtcr = PHY_READ(sc, MII_100T2CR); 391 if (gtcr & GTCR_MAN_MS) { 392 gtcr &= ~GTCR_MAN_MS; 393 PHY_WRITE(sc, MII_100T2CR, 394 gtcr); 395 } 396 mii_phy_auto(sc, 0); 397 } 398 break; 399 case IGPHY_TICK_DOWNSHIFT: 400 gtcr = PHY_READ(sc, MII_100T2CR); 401 gtcr |= GTCR_MAN_MS; 402 PHY_WRITE(sc, MII_100T2CR, gtcr); 403 mii_phy_auto(sc, 0); 404 break; 405 default: 406 break; 407 } 408 if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX) 409 igsc->sc_smartspeed = 0; 410 } else 411 igsc->sc_smartspeed = 0; 412 } 413