1 /* $OpenBSD: ipgphy.c,v 1.14 2011/04/07 15:30:16 miod Exp $ */ 2 3 /*- 4 * Copyright (c) 2006, Pyun YongHyeon <yongari@FreeBSD.org> 5 * 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 unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 /* 32 * Driver for the IC Plus IP1000A/IP1001 10/100/1000 PHY. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/device.h> 39 #include <sys/socket.h> 40 #include <sys/errno.h> 41 42 #include <machine/bus.h> 43 44 #include <net/if.h> 45 #include <net/if_media.h> 46 47 #include <netinet/in.h> 48 #include <netinet/if_ether.h> 49 50 #include <dev/mii/mii.h> 51 #include <dev/mii/miivar.h> 52 #include <dev/mii/miidevs.h> 53 54 #include <dev/mii/ipgphyreg.h> 55 56 #include <dev/pci/if_stgereg.h> 57 58 int ipgphy_probe(struct device *, void *, void *); 59 void ipgphy_attach(struct device *, struct device *, void *); 60 61 struct cfattach ipgphy_ca = { 62 sizeof(struct mii_softc), ipgphy_probe, ipgphy_attach, mii_phy_detach, 63 mii_phy_activate 64 }; 65 66 struct cfdriver ipgphy_cd = { 67 NULL, "ipgphy", DV_DULL 68 }; 69 70 int ipgphy_service(struct mii_softc *, struct mii_data *, int); 71 void ipgphy_status(struct mii_softc *); 72 int ipgphy_mii_phy_auto(struct mii_softc *); 73 void ipgphy_load_dspcode(struct mii_softc *); 74 void ipgphy_reset(struct mii_softc *); 75 76 const struct mii_phy_funcs ipgphy_funcs = { 77 ipgphy_service, ipgphy_status, ipgphy_reset, 78 }; 79 80 static const struct mii_phydesc ipgphys[] = { 81 { MII_OUI_ICPLUS, MII_MODEL_ICPLUS_IP1000A, 82 MII_STR_ICPLUS_IP1000A }, 83 { MII_OUI_ICPLUS, MII_MODEL_ICPLUS_IP1001, 84 MII_STR_ICPLUS_IP1001 }, 85 86 { 0, 87 0 }, 88 }; 89 90 int 91 ipgphy_probe(struct device *parent, void *match, void *aux) 92 { 93 struct mii_attach_args *ma = aux; 94 95 if (mii_phy_match(ma, ipgphys) != NULL) 96 return (10); 97 98 return (0); 99 } 100 101 void 102 ipgphy_attach(struct device *parent, struct device *self, void *aux) 103 { 104 struct mii_softc *sc = (struct mii_softc *)self; 105 struct mii_attach_args *ma = aux; 106 struct mii_data *mii = ma->mii_data; 107 const struct mii_phydesc *mpd; 108 109 mpd = mii_phy_match(ma, ipgphys); 110 printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 111 112 sc->mii_inst = mii->mii_instance; 113 sc->mii_phy = ma->mii_phyno; 114 sc->mii_funcs = &ipgphy_funcs; 115 sc->mii_model = MII_MODEL(ma->mii_id2); 116 sc->mii_pdata = mii; 117 118 sc->mii_flags |= MIIF_NOISOLATE; 119 120 sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 121 if (sc->mii_capabilities & BMSR_EXTSTAT) 122 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 123 124 mii_phy_add_media(sc); 125 126 PHY_RESET(sc); 127 } 128 129 int 130 ipgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 131 { 132 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 133 uint32_t gig, reg, speed; 134 135 switch (cmd) { 136 case MII_POLLSTAT: 137 /* 138 * If we're not polling our PHY instance, just return. 139 */ 140 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 141 return (0); 142 break; 143 144 case MII_MEDIACHG: 145 /* 146 * If the media indicates a different PHY instance, 147 * isolate ourselves. 148 */ 149 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 150 reg = PHY_READ(sc, IPGPHY_MII_BMCR); 151 PHY_WRITE(sc, IPGPHY_MII_BMCR, 152 reg | IPGPHY_BMCR_ISO); 153 return (0); 154 } 155 156 /* 157 * If the interface is not up, don't do anything. 158 */ 159 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 160 break; 161 162 PHY_RESET(sc); 163 164 switch (IFM_SUBTYPE(ife->ifm_media)) { 165 case IFM_AUTO: 166 (void)ipgphy_mii_phy_auto(sc); 167 goto done; 168 break; 169 170 case IFM_1000_T: 171 /* 172 * XXX 173 * Manual 1000baseT setting doesn't seem to work. 174 */ 175 speed = IPGPHY_BMCR_1000; 176 break; 177 178 case IFM_100_TX: 179 speed = IPGPHY_BMCR_100; 180 break; 181 182 case IFM_10_T: 183 speed = IPGPHY_BMCR_10; 184 break; 185 186 default: 187 return (EINVAL); 188 } 189 190 if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) { 191 speed |= IPGPHY_BMCR_FDX; 192 gig = IPGPHY_1000CR_1000T_FDX; 193 } else 194 gig = IPGPHY_1000CR_1000T; 195 196 PHY_WRITE(sc, IPGPHY_MII_1000CR, 0); 197 PHY_WRITE(sc, IPGPHY_MII_BMCR, speed); 198 199 if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) 200 break; 201 202 PHY_WRITE(sc, IPGPHY_MII_1000CR, gig); 203 PHY_WRITE(sc, IPGPHY_MII_BMCR, speed); 204 205 if (mii->mii_media.ifm_media & IFM_ETH_MASTER) 206 gig |= IPGPHY_1000CR_MASTER | IPGPHY_1000CR_MANUAL; 207 208 PHY_WRITE(sc, IPGPHY_MII_1000CR, gig); 209 210 done: 211 break; 212 213 case MII_TICK: 214 /* 215 * If we're not currently selected, just return. 216 */ 217 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 218 return (0); 219 220 /* 221 * Is the interface even up? 222 */ 223 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 224 return (0); 225 226 /* 227 * Only used for autonegotiation. 228 */ 229 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 230 sc->mii_ticks = 0; 231 break; 232 } 233 234 /* 235 * check for link. 236 */ 237 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 238 if (reg & BMSR_LINK) { 239 sc->mii_ticks = 0; 240 break; 241 } 242 243 /* Announce link loss right after it happens */ 244 if (sc->mii_ticks++ == 0) 245 break; 246 247 /* 248 * Only retry autonegotiation every mii_anegticks seconds. 249 */ 250 if (sc->mii_ticks <= sc->mii_anegticks) 251 break; 252 253 sc->mii_ticks = 0; 254 ipgphy_mii_phy_auto(sc); 255 break; 256 } 257 258 /* Update the media status. */ 259 mii_phy_status(sc); 260 261 /* Callback if something changed. */ 262 mii_phy_update(sc, cmd); 263 return (0); 264 } 265 266 void 267 ipgphy_status(struct mii_softc *sc) 268 { 269 struct mii_data *mii = sc->mii_pdata; 270 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 271 uint32_t bmsr, bmcr, stat; 272 273 mii->mii_media_status = IFM_AVALID; 274 mii->mii_media_active = IFM_ETHER; 275 276 bmsr = PHY_READ(sc, IPGPHY_MII_BMSR) | 277 PHY_READ(sc, IPGPHY_MII_BMSR); 278 if (bmsr & IPGPHY_BMSR_LINK) 279 mii->mii_media_status |= IFM_ACTIVE; 280 281 bmcr = PHY_READ(sc, IPGPHY_MII_BMCR); 282 if (bmcr & IPGPHY_BMCR_LOOP) 283 mii->mii_media_active |= IFM_LOOP; 284 285 if (bmcr & IPGPHY_BMCR_AUTOEN) { 286 if ((bmsr & IPGPHY_BMSR_ANEGCOMP) == 0) { 287 /* Erg, still trying, I guess... */ 288 mii->mii_media_active |= IFM_NONE; 289 return; 290 } 291 292 if (sc->mii_model == MII_MODEL_ICPLUS_IP1001) { 293 stat = PHY_READ(sc, IPGPHY_LSR); 294 switch (stat & IPGPHY_LSR_SPEED_MASK) { 295 case IPGPHY_LSR_SPEED_10: 296 mii->mii_media_active |= IFM_10_T; 297 break; 298 case IPGPHY_LSR_SPEED_100: 299 mii->mii_media_active |= IFM_100_TX; 300 break; 301 case IPGPHY_LSR_SPEED_1000: 302 mii->mii_media_active |= IFM_1000_T; 303 break; 304 default: 305 mii->mii_media_active |= IFM_NONE; 306 return; 307 } 308 309 if (stat & IPGPHY_LSR_FULL_DUPLEX) 310 mii->mii_media_active |= IFM_FDX; 311 else 312 mii->mii_media_active |= IFM_HDX; 313 } else { 314 stat = PHY_READ(sc, STGE_PhyCtrl); 315 switch (PC_LinkSpeed(stat)) { 316 case PC_LinkSpeed_Down: 317 mii->mii_media_active |= IFM_NONE; 318 return; 319 case PC_LinkSpeed_10: 320 mii->mii_media_active |= IFM_10_T; 321 break; 322 case PC_LinkSpeed_100: 323 mii->mii_media_active |= IFM_100_TX; 324 break; 325 case PC_LinkSpeed_1000: 326 mii->mii_media_active |= IFM_1000_T; 327 break; 328 default: 329 mii->mii_media_active |= IFM_NONE; 330 return; 331 } 332 333 if (stat & PC_PhyDuplexStatus) 334 mii->mii_media_active |= IFM_FDX; 335 else 336 mii->mii_media_active |= IFM_HDX; 337 } 338 339 if (mii->mii_media_active & IFM_FDX) 340 mii->mii_media_active |= mii_phy_flowstatus(sc); 341 342 if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { 343 if (PHY_READ(sc, IPGPHY_MII_1000SR) & 344 IPGPHY_1000SR_MASTER) 345 mii->mii_media_active |= IFM_ETH_MASTER; 346 } 347 } else 348 mii->mii_media_active = ife->ifm_media; 349 } 350 351 int 352 ipgphy_mii_phy_auto(struct mii_softc *sc) 353 { 354 uint32_t reg = 0; 355 356 if (sc->mii_model == MII_MODEL_ICPLUS_IP1001) { 357 reg = PHY_READ(sc, IPGPHY_MII_ANAR); 358 reg |= IPGPHY_ANAR_NP; 359 } 360 361 reg |= IPGPHY_ANAR_10T | IPGPHY_ANAR_10T_FDX | 362 IPGPHY_ANAR_100TX | IPGPHY_ANAR_100TX_FDX; 363 364 if (sc->mii_flags & MIIF_DOPAUSE) 365 reg |= IPGPHY_ANAR_PAUSE | IPGPHY_ANAR_APAUSE; 366 367 PHY_WRITE(sc, IPGPHY_MII_ANAR, reg | IPGPHY_ANAR_CSMA); 368 369 reg = IPGPHY_1000CR_1000T | IPGPHY_1000CR_1000T_FDX; 370 reg |= IPGPHY_1000CR_MASTER; 371 PHY_WRITE(sc, IPGPHY_MII_1000CR, reg); 372 373 PHY_WRITE(sc, IPGPHY_MII_BMCR, (IPGPHY_BMCR_FDX | 374 IPGPHY_BMCR_AUTOEN | IPGPHY_BMCR_STARTNEG)); 375 376 return (EJUSTRETURN); 377 } 378 379 void 380 ipgphy_load_dspcode(struct mii_softc *sc) 381 { 382 PHY_WRITE(sc, 31, 0x0001); 383 PHY_WRITE(sc, 27, 0x01e0); 384 PHY_WRITE(sc, 31, 0x0002); 385 PHY_WRITE(sc, 27, 0xeb8e); 386 PHY_WRITE(sc, 31, 0x0000); 387 PHY_WRITE(sc, 30, 0x005e); 388 PHY_WRITE(sc, 9, 0x0700); 389 390 DELAY(50); 391 } 392 393 void 394 ipgphy_reset(struct mii_softc *sc) 395 { 396 struct ifnet *ifp = sc->mii_pdata->mii_ifp; 397 struct stge_softc *stge_sc; 398 uint32_t reg; 399 400 mii_phy_reset(sc); 401 402 /* clear autoneg/full-duplex as we don't want it after reset */ 403 reg = PHY_READ(sc, IPGPHY_MII_BMCR); 404 reg &= ~(IPGPHY_BMCR_AUTOEN | IPGPHY_BMCR_FDX); 405 PHY_WRITE(sc, MII_BMCR, reg); 406 407 if (sc->mii_model == MII_MODEL_ICPLUS_IP1000A && 408 strcmp(ifp->if_xname, "stge") == 0) { 409 stge_sc = ifp->if_softc; 410 if (stge_sc->sc_rev >= 0x40 && stge_sc->sc_rev <= 0x4e) 411 ipgphy_load_dspcode(sc); 412 } 413 } 414