1 /* $OpenBSD: brgphy.c,v 1.14 2002/11/26 06:01:28 nate Exp $ */ 2 3 /* 4 * Copyright (c) 2000 5 * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Bill Paul. 18 * 4. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 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 32 * THE POSSIBILITY OF SUCH DAMAGE. 33 * 34 * $FreeBSD: brgphy.c,v 1.8 2002/03/22 06:38:52 wpaul Exp $ 35 */ 36 37 /* 38 * Driver for the Broadcom BCR5400 1000baseTX PHY. Speed is always 39 * 1000mbps; all we need to negotiate here is full or half duplex. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/kernel.h> 45 #include <sys/device.h> 46 #include <sys/malloc.h> 47 #include <sys/socket.h> 48 #include <sys/errno.h> 49 50 #include <net/if.h> 51 #include <net/if_media.h> 52 53 #include <dev/mii/mii.h> 54 #include <dev/mii/miivar.h> 55 #include <dev/mii/miidevs.h> 56 57 #include <dev/mii/brgphyreg.h> 58 59 int brgphy_probe(struct device *, void *, void *); 60 void brgphy_attach(struct device *, struct device *, void *); 61 62 struct cfattach brgphy_ca = { 63 sizeof(struct mii_softc), brgphy_probe, brgphy_attach, mii_phy_detach, 64 mii_phy_activate 65 }; 66 67 struct cfdriver brgphy_cd = { 68 NULL, "brgphy", DV_DULL 69 }; 70 71 int brgphy_service(struct mii_softc *, struct mii_data *, int); 72 void brgphy_status(struct mii_softc *); 73 74 int brgphy_mii_phy_auto(struct mii_softc *, int); 75 extern void mii_phy_auto_timeout(void *); 76 void brgphy_reset(struct mii_softc *); 77 void brgphy_load_dspcode(struct mii_softc *); 78 79 int 80 brgphy_probe(parent, match, aux) 81 struct device *parent; 82 void *match, *aux; 83 { 84 struct mii_attach_args *ma = aux; 85 86 if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxBROADCOM && 87 (MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5400 || 88 MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5401 || 89 MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5411 || 90 MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5421S || 91 MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5701 || 92 MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5703)) 93 return(10); 94 95 return(0); 96 } 97 98 void 99 brgphy_attach(parent, self, aux) 100 struct device *parent, *self; 101 void *aux; 102 { 103 struct mii_softc *sc = (struct mii_softc *)self; 104 struct mii_attach_args *ma = aux; 105 struct mii_data *mii = ma->mii_data; 106 char *model; 107 108 if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5400 || 109 MII_MODEL(ma->mii_id2) == MII_MODEL_BROADCOM_BCM5400) 110 model = MII_STR_BROADCOM_BCM5400; 111 if (MII_MODEL(ma->mii_id2) == MII_MODEL_BROADCOM_BCM5401) 112 model = MII_STR_BROADCOM_BCM5401; 113 if (MII_MODEL(ma->mii_id2) == MII_MODEL_BROADCOM_BCM5411) 114 model = MII_STR_BROADCOM_BCM5411; 115 if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5421S) 116 model = MII_STR_xxBROADCOM_BCM5421S; 117 if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5701) 118 model = MII_STR_xxBROADCOM_BCM5701; 119 if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5703) 120 model = MII_STR_xxBROADCOM_BCM5703; 121 122 printf(": %s, rev. %d\n", model, MII_REV(ma->mii_id2)); 123 124 sc->mii_inst = mii->mii_instance; 125 sc->mii_phy = ma->mii_phyno; 126 sc->mii_service = brgphy_service; 127 sc->mii_status = brgphy_status; 128 sc->mii_pdata = mii; 129 sc->mii_flags |= MIIF_NOISOLATE; 130 sc->mii_anegticks = 10; 131 132 brgphy_reset(sc); 133 134 sc->mii_capabilities = 135 PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 136 if (sc->mii_capabilities & BMSR_EXTSTAT) 137 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 138 if ((sc->mii_capabilities & BMSR_MEDIAMASK) || 139 (sc->mii_extcapabilities & EXTSR_MEDIAMASK)) 140 mii_phy_add_media(sc); 141 } 142 143 int 144 brgphy_service(sc, mii, cmd) 145 struct mii_softc *sc; 146 struct mii_data *mii; 147 int cmd; 148 { 149 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 150 int reg, speed; 151 152 if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0) 153 return (ENXIO); 154 155 switch (cmd) { 156 case MII_POLLSTAT: 157 /* 158 * If we're not polling our PHY instance, just return. 159 */ 160 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 161 return (0); 162 break; 163 164 case MII_MEDIACHG: 165 /* 166 * If the media indicates a different PHY instance, 167 * isolate ourselves. 168 */ 169 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 170 reg = PHY_READ(sc, MII_BMCR); 171 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 172 return (0); 173 } 174 175 /* 176 * If the interface is not up, don't do anything. 177 */ 178 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 179 break; 180 181 PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, 182 BRGPHY_PHY_EXTCTL_HIGH_LA|BRGPHY_PHY_EXTCTL_EN_LTR); 183 PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 184 BRGPHY_AUXCTL_LONG_PKT|BRGPHY_AUXCTL_TX_TST); 185 PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00); 186 187 switch (IFM_SUBTYPE(ife->ifm_media)) { 188 case IFM_AUTO: 189 #ifdef foo 190 /* 191 * If we're already in auto mode, just return. 192 */ 193 if (PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_AUTOEN) 194 return (0); 195 #endif 196 (void) brgphy_mii_phy_auto(sc, 1); 197 break; 198 case IFM_1000_T: 199 speed = BRGPHY_S1000; 200 goto setit; 201 case IFM_100_T4: 202 speed = BRGPHY_S100; 203 goto setit; 204 case IFM_100_TX: 205 speed = BRGPHY_S100; 206 goto setit; 207 case IFM_10_T: 208 speed = BRGPHY_S10; 209 setit: 210 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { 211 PHY_WRITE(sc, BRGPHY_MII_BMCR, 212 BRGPHY_BMCR_FDX|speed); 213 } else { 214 PHY_WRITE(sc, BRGPHY_MII_BMCR, speed); 215 } 216 PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE); 217 218 if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) 219 break; 220 221 /* 222 * On IFM_1000_X only, 223 * when setting the link manually, one side must 224 * be the master and the other the slave. However 225 * ifmedia doesn't give us a good way to specify 226 * this, so we fake it by using one of the LINK 227 * flags. If LINK0 is set, we program the PHY to 228 * be a master, otherwise it's a slave. 229 */ 230 if ((mii->mii_ifp->if_flags & IFF_LINK0)) { 231 PHY_WRITE(sc, BRGPHY_MII_1000CTL, 232 BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC); 233 } else { 234 PHY_WRITE(sc, BRGPHY_MII_1000CTL, 235 BRGPHY_1000CTL_MSE); 236 } 237 break; 238 default: 239 return (EINVAL); 240 } 241 break; 242 243 case MII_TICK: 244 /* 245 * If we're not currently selected, just return. 246 */ 247 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 248 return (0); 249 250 /* 251 * Only used for autonegotiation. 252 */ 253 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 254 return (0); 255 256 /* 257 * Is the interface even up? 258 */ 259 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 260 return (0); 261 262 /* 263 * Only retry autonegotiation every 5 seconds. 264 */ 265 if (++sc->mii_ticks != sc->mii_anegticks) 266 return (0); 267 268 sc->mii_ticks = 0; 269 270 /* 271 * Check to see if we have link. If we do, we don't 272 * need to restart the autonegotiation process. Read 273 * the BMSR twice in case it's latched. 274 */ 275 reg = PHY_READ(sc, BRGPHY_MII_AUXSTS); 276 if (reg & BRGPHY_AUXSTS_LINK) 277 break; 278 279 brgphy_reset(sc); 280 if (brgphy_mii_phy_auto(sc, 0) == EJUSTRETURN) 281 return(0); 282 break; 283 } 284 285 /* Update the media status. */ 286 brgphy_status(sc); 287 288 /* Callback if something changed. */ 289 mii_phy_update(sc, cmd); 290 return (0); 291 } 292 293 void 294 brgphy_status(sc) 295 struct mii_softc *sc; 296 { 297 struct mii_data *mii = sc->mii_pdata; 298 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 299 int bmsr, bmcr; 300 301 mii->mii_media_status = IFM_AVALID; 302 mii->mii_media_active = IFM_ETHER; 303 304 bmsr = PHY_READ(sc, BRGPHY_MII_BMSR); 305 if (PHY_READ(sc, BRGPHY_MII_AUXSTS) & BRGPHY_AUXSTS_LINK) 306 mii->mii_media_status |= IFM_ACTIVE; 307 308 bmcr = PHY_READ(sc, BRGPHY_MII_BMCR); 309 310 if (bmcr & BRGPHY_BMCR_LOOP) 311 mii->mii_media_active |= IFM_LOOP; 312 313 if (bmcr & BRGPHY_BMCR_AUTOEN) { 314 if ((bmsr & BRGPHY_BMSR_ACOMP) == 0) { 315 /* Erg, still trying, I guess... */ 316 mii->mii_media_active |= IFM_NONE; 317 return; 318 } 319 320 switch (PHY_READ(sc, BRGPHY_MII_AUXSTS) & 321 BRGPHY_AUXSTS_AN_RES) { 322 case BRGPHY_RES_1000FD: 323 mii->mii_media_active |= IFM_1000_T | IFM_FDX; 324 break; 325 case BRGPHY_RES_1000HD: 326 mii->mii_media_active |= IFM_1000_T | IFM_HDX; 327 break; 328 case BRGPHY_RES_100FD: 329 mii->mii_media_active |= IFM_100_TX | IFM_FDX; 330 break; 331 case BRGPHY_RES_100T4: 332 mii->mii_media_active |= IFM_100_T4; 333 break; 334 case BRGPHY_RES_100HD: 335 mii->mii_media_active |= IFM_100_TX | IFM_HDX; 336 break; 337 case BRGPHY_RES_10FD: 338 mii->mii_media_active |= IFM_10_T | IFM_FDX; 339 break; 340 case BRGPHY_RES_10HD: 341 mii->mii_media_active |= IFM_10_T | IFM_HDX; 342 break; 343 } 344 return; 345 } 346 347 mii->mii_media_active = ife->ifm_media; 348 } 349 350 351 int 352 brgphy_mii_phy_auto(mii, waitfor) 353 struct mii_softc *mii; 354 int waitfor; 355 { 356 int bmsr, ktcr = 0, i; 357 358 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 359 brgphy_reset(mii); 360 PHY_WRITE(mii, BRGPHY_MII_BMCR, 0); 361 DELAY(1000); 362 ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL); 363 PHY_WRITE(mii, BRGPHY_MII_1000CTL, ktcr | 364 BRGPHY_1000CTL_AFD|BRGPHY_1000CTL_AHD); 365 ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL); 366 DELAY(1000); 367 PHY_WRITE(mii, BRGPHY_MII_ANAR, 368 BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA); 369 DELAY(1000); 370 PHY_WRITE(mii, BRGPHY_MII_BMCR, 371 BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG); 372 PHY_WRITE(mii, BRGPHY_MII_IMR, 0xFF00); 373 } 374 375 if (waitfor) { 376 /* Wait 500ms for it to complete. */ 377 for (i = 0; i < 500; i++) { 378 if ((bmsr = PHY_READ(mii, BRGPHY_MII_BMSR)) & 379 BRGPHY_BMSR_ACOMP) 380 return (0); 381 DELAY(1000); 382 #if 0 383 if ((bmsr & BMSR_ACOMP) == 0) 384 printf("%s: autonegotiation failed to complete\n", 385 mii->mii_dev.dv_xname); 386 #endif 387 } 388 389 /* 390 * Don't need to worry about clearing MIIF_DOINGAUTO. 391 * If that's set, a timeout is pending, and it will 392 * clear the flag. 393 */ 394 return (EIO); 395 } 396 397 /* 398 * Just let it finish asynchronously. This is for the benefit of 399 * the tick handler driving autonegotiation. Don't want 500ms 400 * delays all the time while the system is running! 401 */ 402 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 403 mii->mii_flags |= MIIF_DOINGAUTO; 404 timeout_set(&mii->mii_phy_timo, mii_phy_auto_timeout, mii); 405 timeout_add(&mii->mii_phy_timo, hz >> 1); 406 } 407 return (EJUSTRETURN); 408 } 409 410 void 411 brgphy_reset(sc) 412 struct mii_softc *sc; 413 { 414 brgphy_load_dspcode(sc); 415 } 416 417 struct bcm_dspcode { 418 int reg; 419 u_int16_t val; 420 }; 421 422 static const struct bcm_dspcode bcm5401_dspcode[] = { 423 { BRGPHY_MII_AUXCTL, 0x4c20 }, 424 { BRGPHY_MII_DSP_ADDR_REG, 0x0012 }, 425 { BRGPHY_MII_DSP_RW_PORT, 0x1804 }, 426 { BRGPHY_MII_DSP_ADDR_REG, 0x0013 }, 427 { BRGPHY_MII_DSP_RW_PORT, 0x1204 }, 428 { BRGPHY_MII_DSP_ADDR_REG, 0x8006 }, 429 { BRGPHY_MII_DSP_RW_PORT, 0x0132 }, 430 { BRGPHY_MII_DSP_ADDR_REG, 0x8006 }, 431 { BRGPHY_MII_DSP_RW_PORT, 0x0232 }, 432 { BRGPHY_MII_DSP_ADDR_REG, 0x201f }, 433 { BRGPHY_MII_DSP_RW_PORT, 0x0a20 }, 434 { 0, 0 }, 435 }; 436 437 static const struct bcm_dspcode bcm5411_dspcode[] = { 438 { 0x1c, 0x8c23 }, 439 { 0x1c, 0x8ca3 }, 440 { 0x1c, 0x8c23 }, 441 { 0, 0 }, 442 }; 443 444 void 445 brgphy_load_dspcode(sc) 446 struct mii_softc *sc; 447 { 448 const struct bcm_dspcode *dsp = NULL; 449 int id2, i; 450 451 id2 = PHY_READ(sc, MII_PHYIDR2); 452 453 mii_phy_reset(sc); 454 455 switch (MII_MODEL(id2)) { 456 case MII_MODEL_BROADCOM_BCM5400: 457 dsp = bcm5401_dspcode; 458 break; 459 case MII_MODEL_BROADCOM_BCM5401: 460 if (MII_REV(id2) == 1 || MII_REV(id2) == 3) 461 dsp = bcm5401_dspcode; 462 break; 463 case MII_MODEL_BROADCOM_BCM5411: 464 dsp = bcm5411_dspcode; 465 break; 466 } 467 468 if (dsp == NULL) 469 return; 470 471 for (i = 0; dsp[i].reg != 0; i++) 472 PHY_WRITE(sc, dsp[i].reg, dsp[i].val); 473 } 474