| 1 | /* $NetBSD: igphy.c,v 1.26 2016/07/07 06:55:41 msaitoh 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 | * |
| 59 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
| 60 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| 61 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 62 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
| 63 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 64 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 65 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 66 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 67 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 68 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 69 | * POSSIBILITY OF SUCH DAMAGE. |
| 70 | */ |
| 71 | |
| 72 | #include <sys/cdefs.h> |
| 73 | __KERNEL_RCSID(0, "$NetBSD: igphy.c,v 1.26 2016/07/07 06:55:41 msaitoh Exp $" ); |
| 74 | |
| 75 | #ifdef _KERNEL_OPT |
| 76 | #include "opt_mii.h" |
| 77 | #endif |
| 78 | |
| 79 | #include <sys/param.h> |
| 80 | #include <sys/systm.h> |
| 81 | #include <sys/kernel.h> |
| 82 | #include <sys/device.h> |
| 83 | #include <sys/socket.h> |
| 84 | #include <sys/errno.h> |
| 85 | |
| 86 | #include <net/if.h> |
| 87 | #include <net/if_media.h> |
| 88 | |
| 89 | #include <dev/mii/mii.h> |
| 90 | #include <dev/mii/miivar.h> |
| 91 | #include <dev/mii/miidevs.h> |
| 92 | #include <dev/mii/igphyreg.h> |
| 93 | #include <dev/mii/igphyvar.h> |
| 94 | #include <dev/pci/if_wmvar.h> |
| 95 | |
| 96 | static void igphy_reset(struct mii_softc *); |
| 97 | static void igphy_load_dspcode(struct mii_softc *); |
| 98 | static void igphy_load_dspcode_igp3(struct mii_softc *); |
| 99 | static void igphy_smartspeed_workaround(struct mii_softc *sc); |
| 100 | |
| 101 | static int igphymatch(device_t, cfdata_t, void *); |
| 102 | static void igphyattach(device_t, device_t, void *); |
| 103 | |
| 104 | CFATTACH_DECL_NEW(igphy, sizeof(struct igphy_softc), |
| 105 | igphymatch, igphyattach, mii_phy_detach, mii_phy_activate); |
| 106 | |
| 107 | static int igphy_service(struct mii_softc *, struct mii_data *, int); |
| 108 | static void igphy_status(struct mii_softc *); |
| 109 | |
| 110 | static const struct mii_phy_funcs igphy_funcs = { |
| 111 | igphy_service, igphy_status, igphy_reset, |
| 112 | }; |
| 113 | |
| 114 | static const struct mii_phydesc igphys[] = { |
| 115 | { MII_OUI_yyINTEL, MII_MODEL_yyINTEL_IGP01E1000, |
| 116 | MII_STR_yyINTEL_IGP01E1000 }, |
| 117 | |
| 118 | { MII_OUI_yyINTEL, MII_MODEL_yyINTEL_I82566, |
| 119 | MII_STR_yyINTEL_I82566 }, |
| 120 | |
| 121 | {0, 0, |
| 122 | NULL }, |
| 123 | }; |
| 124 | |
| 125 | static int |
| 126 | igphymatch(device_t parent, cfdata_t match, void *aux) |
| 127 | { |
| 128 | struct mii_attach_args *ma = aux; |
| 129 | |
| 130 | if (mii_phy_match(ma, igphys) != NULL) |
| 131 | return 10; |
| 132 | |
| 133 | return 0; |
| 134 | } |
| 135 | |
| 136 | static void |
| 137 | igphyattach(device_t parent, device_t self, void *aux) |
| 138 | { |
| 139 | struct mii_softc *sc = device_private(self); |
| 140 | struct mii_attach_args *ma = aux; |
| 141 | struct mii_data *mii = ma->mii_data; |
| 142 | const struct mii_phydesc *mpd; |
| 143 | struct igphy_softc *igsc = (struct igphy_softc *) sc; |
| 144 | prop_dictionary_t dict; |
| 145 | |
| 146 | mpd = mii_phy_match(ma, igphys); |
| 147 | aprint_naive(": Media interface\n" ); |
| 148 | aprint_normal(": %s, rev. %d\n" , mpd->mpd_name, MII_REV(ma->mii_id2)); |
| 149 | |
| 150 | dict = device_properties(parent); |
| 151 | if (!prop_dictionary_get_uint32(dict, "mactype" , &igsc->sc_mactype)) |
| 152 | aprint_error("WARNING! Failed to get mactype\n" ); |
| 153 | if (!prop_dictionary_get_uint32(dict, "macflags" , &igsc->sc_macflags)) |
| 154 | aprint_error("WARNING! Failed to get macflags\n" ); |
| 155 | |
| 156 | sc->mii_dev = self; |
| 157 | sc->mii_inst = mii->mii_instance; |
| 158 | sc->mii_phy = ma->mii_phyno; |
| 159 | sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2); |
| 160 | sc->mii_mpd_model = MII_MODEL(ma->mii_id2); |
| 161 | sc->mii_mpd_rev = MII_REV(ma->mii_id2); |
| 162 | sc->mii_funcs = &igphy_funcs; |
| 163 | sc->mii_pdata = mii; |
| 164 | sc->mii_flags = ma->mii_flags; |
| 165 | sc->mii_anegticks = MII_ANEGTICKS_GIGE; |
| 166 | |
| 167 | PHY_RESET(sc); |
| 168 | |
| 169 | sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; |
| 170 | if (sc->mii_capabilities & BMSR_EXTSTAT) |
| 171 | sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); |
| 172 | aprint_normal_dev(self, "" ); |
| 173 | if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && |
| 174 | (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) |
| 175 | aprint_error("no media present" ); |
| 176 | else |
| 177 | mii_phy_add_media(sc); |
| 178 | aprint_normal("\n" ); |
| 179 | } |
| 180 | |
| 181 | typedef struct { |
| 182 | int reg; |
| 183 | uint16_t val; |
| 184 | } dspcode; |
| 185 | |
| 186 | static const dspcode igp1code[] = { |
| 187 | { 0x1f95, 0x0001 }, |
| 188 | { 0x1f71, 0xbd21 }, |
| 189 | { 0x1f79, 0x0018 }, |
| 190 | { 0x1f30, 0x1600 }, |
| 191 | { 0x1f31, 0x0014 }, |
| 192 | { 0x1f32, 0x161c }, |
| 193 | { 0x1f94, 0x0003 }, |
| 194 | { 0x1f96, 0x003f }, |
| 195 | { 0x2010, 0x0008 }, |
| 196 | { 0, 0 }, |
| 197 | }; |
| 198 | |
| 199 | static const dspcode igp1code_r2[] = { |
| 200 | { 0x1f73, 0x0099 }, |
| 201 | { 0, 0 }, |
| 202 | }; |
| 203 | |
| 204 | static const dspcode igp3code[] = { |
| 205 | { 0x2f5b, 0x9018}, |
| 206 | { 0x2f52, 0x0000}, |
| 207 | { 0x2fb1, 0x8b24}, |
| 208 | { 0x2fb2, 0xf8f0}, |
| 209 | { 0x2010, 0x10b0}, |
| 210 | { 0x2011, 0x0000}, |
| 211 | { 0x20dd, 0x249a}, |
| 212 | { 0x20de, 0x00d3}, |
| 213 | { 0x28b4, 0x04ce}, |
| 214 | { 0x2f70, 0x29e4}, |
| 215 | { 0x0000, 0x0140}, |
| 216 | { 0x1f30, 0x1606}, |
| 217 | { 0x1f31, 0xb814}, |
| 218 | { 0x1f35, 0x002a}, |
| 219 | { 0x1f3e, 0x0067}, |
| 220 | { 0x1f54, 0x0065}, |
| 221 | { 0x1f55, 0x002a}, |
| 222 | { 0x1f56, 0x002a}, |
| 223 | { 0x1f72, 0x3fb0}, |
| 224 | { 0x1f76, 0xc0ff}, |
| 225 | { 0x1f77, 0x1dec}, |
| 226 | { 0x1f78, 0xf9ef}, |
| 227 | { 0x1f79, 0x0210}, |
| 228 | { 0x1895, 0x0003}, |
| 229 | { 0x1796, 0x0008}, |
| 230 | { 0x1798, 0xd008}, |
| 231 | { 0x1898, 0xd918}, |
| 232 | { 0x187a, 0x0800}, |
| 233 | { 0x0019, 0x008d}, |
| 234 | { 0x001b, 0x2080}, |
| 235 | { 0x0014, 0x0045}, |
| 236 | { 0x0000, 0x1340}, |
| 237 | { 0, 0 }, |
| 238 | }; |
| 239 | |
| 240 | /* DSP patch for igp1 and igp2 */ |
| 241 | static void |
| 242 | igphy_load_dspcode(struct mii_softc *sc) |
| 243 | { |
| 244 | struct igphy_softc *igsc = (struct igphy_softc *) sc; |
| 245 | const dspcode *code; |
| 246 | uint16_t reg; |
| 247 | int i; |
| 248 | |
| 249 | /* This workaround is only for 82541 and 82547 */ |
| 250 | switch (igsc->sc_mactype) { |
| 251 | case WM_T_82541: |
| 252 | case WM_T_82547: |
| 253 | code = igp1code; |
| 254 | break; |
| 255 | case WM_T_82541_2: |
| 256 | case WM_T_82547_2: |
| 257 | code = igp1code_r2; |
| 258 | break; |
| 259 | default: |
| 260 | return; /* byebye */ |
| 261 | } |
| 262 | |
| 263 | /* Delay after phy reset to enable NVM configuration to load */ |
| 264 | delay(20000); |
| 265 | |
| 266 | /* |
| 267 | * Save off the current value of register 0x2F5B to be restored at |
| 268 | * the end of this routine. |
| 269 | */ |
| 270 | reg = IGPHY_READ(sc, 0x2f5b); |
| 271 | |
| 272 | /* Disabled the PHY transmitter */ |
| 273 | IGPHY_WRITE(sc, 0x2f5b, 0x0003); |
| 274 | |
| 275 | delay(20000); |
| 276 | |
| 277 | PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000); |
| 278 | PHY_WRITE(sc, 0x0000, 0x0140); |
| 279 | |
| 280 | delay(5000); |
| 281 | |
| 282 | for (i = 0; !((code[i].reg == 0) && (code[i].val == 0)); i++) |
| 283 | IGPHY_WRITE(sc, code[i].reg, code[i].val); |
| 284 | |
| 285 | PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000); |
| 286 | PHY_WRITE(sc, 0x0000, 0x3300); |
| 287 | |
| 288 | delay(20000); |
| 289 | |
| 290 | /* Now enable the transmitter */ |
| 291 | IGPHY_WRITE(sc, 0x2f5b, reg); |
| 292 | } |
| 293 | |
| 294 | static void |
| 295 | igphy_load_dspcode_igp3(struct mii_softc *sc) |
| 296 | { |
| 297 | const dspcode *code = igp3code; |
| 298 | int i; |
| 299 | |
| 300 | for (i = 0; !((code[i].reg == 0) && (code[i].val == 0)); i++) |
| 301 | IGPHY_WRITE(sc, code[i].reg, code[i].val); |
| 302 | } |
| 303 | |
| 304 | static void |
| 305 | igphy_reset(struct mii_softc *sc) |
| 306 | { |
| 307 | struct igphy_softc *igsc = (struct igphy_softc *) sc; |
| 308 | uint16_t fused, fine, coarse; |
| 309 | |
| 310 | mii_phy_reset(sc); |
| 311 | delay(150); |
| 312 | |
| 313 | switch (igsc->sc_mactype) { |
| 314 | case WM_T_82541: |
| 315 | case WM_T_82547: |
| 316 | case WM_T_82541_2: |
| 317 | case WM_T_82547_2: |
| 318 | igphy_load_dspcode(sc); |
| 319 | break; |
| 320 | case WM_T_ICH8: |
| 321 | case WM_T_ICH9: |
| 322 | if ((igsc->sc_macflags & WM_F_EEPROM_INVALID) != 0) |
| 323 | igphy_load_dspcode_igp3(sc); |
| 324 | break; |
| 325 | default: /* Not for ICH10, PCH and 8257[12] */ |
| 326 | break; |
| 327 | } |
| 328 | |
| 329 | if (igsc->sc_mactype == WM_T_82547) { |
| 330 | fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_SPARE_FUSE_STATUS); |
| 331 | if ((fused & ANALOG_SPARE_FUSE_ENABLED) == 0) { |
| 332 | fused = IGPHY_READ(sc, MII_IGPHY_ANALOG_FUSE_STATUS); |
| 333 | |
| 334 | fine = fused & ANALOG_FUSE_FINE_MASK; |
| 335 | coarse = fused & ANALOG_FUSE_COARSE_MASK; |
| 336 | |
| 337 | if (coarse > ANALOG_FUSE_COARSE_THRESH) { |
| 338 | coarse -= ANALOG_FUSE_COARSE_10; |
| 339 | fine -= ANALOG_FUSE_FINE_1; |
| 340 | } else if (coarse == ANALOG_FUSE_COARSE_THRESH) |
| 341 | fine -= ANALOG_FUSE_FINE_10; |
| 342 | |
| 343 | fused = (fused & ANALOG_FUSE_POLY_MASK) | |
| 344 | (fine & ANALOG_FUSE_FINE_MASK) | |
| 345 | (coarse & ANALOG_FUSE_COARSE_MASK); |
| 346 | |
| 347 | IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_CONTROL, fused); |
| 348 | IGPHY_WRITE(sc, MII_IGPHY_ANALOG_FUSE_BYPASS, |
| 349 | ANALOG_FUSE_ENABLE_SW_CONTROL); |
| 350 | } |
| 351 | } |
| 352 | PHY_WRITE(sc, MII_IGPHY_PAGE_SELECT, 0x0000); |
| 353 | } |
| 354 | |
| 355 | |
| 356 | static int |
| 357 | igphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) |
| 358 | { |
| 359 | struct ifmedia_entry *ife = mii->mii_media.ifm_cur; |
| 360 | uint16_t reg; |
| 361 | |
| 362 | switch (cmd) { |
| 363 | case MII_POLLSTAT: |
| 364 | /* |
| 365 | * If we're not polling our PHY instance, just return. |
| 366 | */ |
| 367 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) |
| 368 | return (0); |
| 369 | break; |
| 370 | |
| 371 | case MII_MEDIACHG: |
| 372 | /* |
| 373 | * If the media indicates a different PHY instance, |
| 374 | * isolate ourselves. |
| 375 | */ |
| 376 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) { |
| 377 | reg = PHY_READ(sc, MII_BMCR); |
| 378 | PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); |
| 379 | return (0); |
| 380 | } |
| 381 | |
| 382 | /* |
| 383 | * If the interface is not up, don't do anything. |
| 384 | */ |
| 385 | if ((mii->mii_ifp->if_flags & IFF_UP) == 0) |
| 386 | break; |
| 387 | |
| 388 | reg = PHY_READ(sc, MII_IGPHY_PORT_CTRL); |
| 389 | if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { |
| 390 | reg |= PSCR_AUTO_MDIX; |
| 391 | reg &= ~PSCR_FORCE_MDI_MDIX; |
| 392 | PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg); |
| 393 | } else { |
| 394 | reg &= ~(PSCR_AUTO_MDIX | PSCR_FORCE_MDI_MDIX); |
| 395 | PHY_WRITE(sc, MII_IGPHY_PORT_CTRL, reg); |
| 396 | } |
| 397 | |
| 398 | mii_phy_setmedia(sc); |
| 399 | break; |
| 400 | |
| 401 | case MII_TICK: |
| 402 | /* |
| 403 | * If we're not currently selected, just return. |
| 404 | */ |
| 405 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) |
| 406 | return (0); |
| 407 | |
| 408 | igphy_smartspeed_workaround(sc); |
| 409 | |
| 410 | if (mii_phy_tick(sc) == EJUSTRETURN) |
| 411 | return (0); |
| 412 | break; |
| 413 | |
| 414 | case MII_DOWN: |
| 415 | mii_phy_down(sc); |
| 416 | return (0); |
| 417 | } |
| 418 | |
| 419 | /* Update the media status. */ |
| 420 | mii_phy_status(sc); |
| 421 | |
| 422 | /* Callback if something changed. */ |
| 423 | mii_phy_update(sc, cmd); |
| 424 | return (0); |
| 425 | } |
| 426 | |
| 427 | |
| 428 | static void |
| 429 | igphy_status(struct mii_softc *sc) |
| 430 | { |
| 431 | struct mii_data *mii = sc->mii_pdata; |
| 432 | struct ifmedia_entry *ife = mii->mii_media.ifm_cur; |
| 433 | uint16_t bmcr, pssr, gtsr, bmsr; |
| 434 | |
| 435 | mii->mii_media_status = IFM_AVALID; |
| 436 | mii->mii_media_active = IFM_ETHER; |
| 437 | |
| 438 | pssr = PHY_READ(sc, MII_IGPHY_PORT_STATUS); |
| 439 | |
| 440 | if (pssr & PSSR_LINK_UP) |
| 441 | mii->mii_media_status |= IFM_ACTIVE; |
| 442 | |
| 443 | bmcr = PHY_READ(sc, MII_BMCR); |
| 444 | if (bmcr & BMCR_ISO) { |
| 445 | mii->mii_media_active |= IFM_NONE; |
| 446 | mii->mii_media_status = 0; |
| 447 | return; |
| 448 | } |
| 449 | |
| 450 | if (bmcr & BMCR_LOOP) |
| 451 | mii->mii_media_active |= IFM_LOOP; |
| 452 | |
| 453 | bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); |
| 454 | |
| 455 | /* |
| 456 | * XXX can't check if the info is valid, no |
| 457 | * 'negotiation done' bit? |
| 458 | */ |
| 459 | if (bmcr & BMCR_AUTOEN) { |
| 460 | if ((bmsr & BMSR_ACOMP) == 0) { |
| 461 | mii->mii_media_active |= IFM_NONE; |
| 462 | return; |
| 463 | } |
| 464 | switch (pssr & PSSR_SPEED_MASK) { |
| 465 | case PSSR_SPEED_1000MBPS: |
| 466 | mii->mii_media_active |= IFM_1000_T; |
| 467 | gtsr = PHY_READ(sc, MII_100T2SR); |
| 468 | if (gtsr & GTSR_MS_RES) |
| 469 | mii->mii_media_active |= IFM_ETH_MASTER; |
| 470 | break; |
| 471 | |
| 472 | case PSSR_SPEED_100MBPS: |
| 473 | mii->mii_media_active |= IFM_100_TX; |
| 474 | break; |
| 475 | |
| 476 | case PSSR_SPEED_10MBPS: |
| 477 | mii->mii_media_active |= IFM_10_T; |
| 478 | break; |
| 479 | |
| 480 | default: |
| 481 | mii->mii_media_active |= IFM_NONE; |
| 482 | mii->mii_media_status = 0; |
| 483 | return; |
| 484 | } |
| 485 | |
| 486 | if (pssr & PSSR_FULL_DUPLEX) |
| 487 | mii->mii_media_active |= |
| 488 | IFM_FDX | mii_phy_flowstatus(sc); |
| 489 | else |
| 490 | mii->mii_media_active |= IFM_HDX; |
| 491 | } else |
| 492 | mii->mii_media_active = ife->ifm_media; |
| 493 | } |
| 494 | |
| 495 | static void |
| 496 | igphy_smartspeed_workaround(struct mii_softc *sc) |
| 497 | { |
| 498 | struct igphy_softc *igsc = (struct igphy_softc *) sc; |
| 499 | uint16_t reg, gtsr, gtcr; |
| 500 | |
| 501 | /* This workaround is only for 82541 and 82547 */ |
| 502 | switch (igsc->sc_mactype) { |
| 503 | case WM_T_82541: |
| 504 | case WM_T_82541_2: |
| 505 | case WM_T_82547: |
| 506 | case WM_T_82547_2: |
| 507 | break; |
| 508 | default: |
| 509 | /* byebye */ |
| 510 | return; |
| 511 | } |
| 512 | |
| 513 | if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0) |
| 514 | return; |
| 515 | |
| 516 | /* XXX Assume 1000TX-FDX is advertized if doing autonegotiation. */ |
| 517 | |
| 518 | reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); |
| 519 | if ((reg & BMSR_LINK) == 0) { |
| 520 | switch (igsc->sc_smartspeed) { |
| 521 | case 0: |
| 522 | gtsr = PHY_READ(sc, MII_100T2SR); |
| 523 | if (!(gtsr & GTSR_MAN_MS_FLT)) |
| 524 | break; |
| 525 | gtsr = PHY_READ(sc, MII_100T2SR); |
| 526 | if (gtsr & GTSR_MAN_MS_FLT) { |
| 527 | gtcr = PHY_READ(sc, MII_100T2CR); |
| 528 | if (gtcr & GTCR_MAN_MS) { |
| 529 | gtcr &= ~GTCR_MAN_MS; |
| 530 | PHY_WRITE(sc, MII_100T2CR, |
| 531 | gtcr); |
| 532 | } |
| 533 | mii_phy_auto(sc, 0); |
| 534 | } |
| 535 | break; |
| 536 | case IGPHY_TICK_DOWNSHIFT: |
| 537 | gtcr = PHY_READ(sc, MII_100T2CR); |
| 538 | gtcr |= GTCR_MAN_MS; |
| 539 | PHY_WRITE(sc, MII_100T2CR, gtcr); |
| 540 | mii_phy_auto(sc, 0); |
| 541 | break; |
| 542 | default: |
| 543 | break; |
| 544 | } |
| 545 | if (igsc->sc_smartspeed++ == IGPHY_TICK_MAX) |
| 546 | igsc->sc_smartspeed = 0; |
| 547 | } else |
| 548 | igsc->sc_smartspeed = 0; |
| 549 | } |
| 550 | |