1 /* $OpenBSD: ipgphy.c,v 1.19 2015/07/19 06:28:12 yuo 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 }; 64 65 struct cfdriver ipgphy_cd = { 66 NULL, "ipgphy", DV_DULL 67 }; 68 69 int ipgphy_service(struct mii_softc *, struct mii_data *, int); 70 void ipgphy_status(struct mii_softc *); 71 int ipgphy_mii_phy_auto(struct mii_softc *); 72 void ipgphy_load_dspcode(struct mii_softc *); 73 void ipgphy_reset(struct mii_softc *); 74 75 const struct mii_phy_funcs ipgphy_funcs = { 76 ipgphy_service, ipgphy_status, ipgphy_reset, 77 }; 78 79 static const struct mii_phydesc ipgphys[] = { 80 { MII_OUI_ICPLUS, MII_MODEL_ICPLUS_IP1000A, 81 MII_STR_ICPLUS_IP1000A }, 82 { MII_OUI_ICPLUS, MII_MODEL_ICPLUS_IP1001, 83 MII_STR_ICPLUS_IP1001 }, 84 85 { 0, 86 0 }, 87 }; 88 89 int 90 ipgphy_probe(struct device *parent, void *match, void *aux) 91 { 92 struct mii_attach_args *ma = aux; 93 94 if (mii_phy_match(ma, ipgphys) != NULL) 95 return (10); 96 97 return (0); 98 } 99 100 void 101 ipgphy_attach(struct device *parent, struct device *self, 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 const struct mii_phydesc *mpd; 107 108 mpd = mii_phy_match(ma, ipgphys); 109 printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 110 111 sc->mii_inst = mii->mii_instance; 112 sc->mii_phy = ma->mii_phyno; 113 sc->mii_funcs = &ipgphy_funcs; 114 sc->mii_model = MII_MODEL(ma->mii_id2); 115 sc->mii_pdata = mii; 116 sc->mii_flags = ma->mii_flags; 117 118 sc->mii_flags |= MIIF_NOISOLATE; 119 120 PHY_RESET(sc); 121 122 sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 123 if (sc->mii_capabilities & BMSR_EXTSTAT) 124 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 125 126 mii_phy_add_media(sc); 127 128 } 129 130 int 131 ipgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 132 { 133 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 134 uint32_t gig, reg, speed; 135 136 switch (cmd) { 137 case MII_POLLSTAT: 138 /* 139 * If we're not polling our PHY instance, just return. 140 */ 141 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 142 return (0); 143 break; 144 145 case MII_MEDIACHG: 146 /* 147 * If the media indicates a different PHY instance, 148 * isolate ourselves. 149 */ 150 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 151 reg = PHY_READ(sc, MII_BMCR); 152 PHY_WRITE(sc, MII_BMCR, reg | 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 = BMCR_S1000; 176 break; 177 178 case IFM_100_TX: 179 speed = BMCR_S100; 180 break; 181 182 case IFM_10_T: 183 speed = BMCR_S10; 184 break; 185 186 default: 187 return (EINVAL); 188 } 189 190 if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) { 191 speed |= BMCR_FDX; 192 gig = GTCR_ADV_1000TFDX; 193 } else 194 gig = GTCR_ADV_1000THDX; 195 196 PHY_WRITE(sc, MII_100T2CR, 0); 197 PHY_WRITE(sc, MII_BMCR, speed); 198 199 if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) 200 break; 201 202 PHY_WRITE(sc, MII_100T2CR, gig); 203 PHY_WRITE(sc, MII_BMCR, speed); 204 205 if (mii->mii_media.ifm_media & IFM_ETH_MASTER) 206 gig |= GTCR_MAN_MS | GTCR_ADV_MS; 207 208 PHY_WRITE(sc, MII_100T2CR, 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, MII_BMSR) | PHY_READ(sc, MII_BMSR); 277 if (bmsr & BMSR_LINK) 278 mii->mii_media_status |= IFM_ACTIVE; 279 280 bmcr = PHY_READ(sc, MII_BMCR); 281 if (bmcr & BMCR_LOOP) 282 mii->mii_media_active |= IFM_LOOP; 283 284 if (bmcr & BMCR_AUTOEN) { 285 if ((bmsr & BMSR_ACOMP) == 0) { 286 /* Erg, still trying, I guess... */ 287 mii->mii_media_active |= IFM_NONE; 288 return; 289 } 290 291 if (sc->mii_model == MII_MODEL_ICPLUS_IP1001) { 292 stat = PHY_READ(sc, IPGPHY_LSR); 293 switch (stat & IPGPHY_LSR_SPEED_MASK) { 294 case IPGPHY_LSR_SPEED_10: 295 mii->mii_media_active |= IFM_10_T; 296 break; 297 case IPGPHY_LSR_SPEED_100: 298 mii->mii_media_active |= IFM_100_TX; 299 break; 300 case IPGPHY_LSR_SPEED_1000: 301 mii->mii_media_active |= IFM_1000_T; 302 break; 303 default: 304 mii->mii_media_active |= IFM_NONE; 305 return; 306 } 307 308 if (stat & IPGPHY_LSR_FULL_DUPLEX) 309 mii->mii_media_active |= IFM_FDX; 310 else 311 mii->mii_media_active |= IFM_HDX; 312 } else { 313 stat = PHY_READ(sc, STGE_PhyCtrl); 314 switch (PC_LinkSpeed(stat)) { 315 case PC_LinkSpeed_Down: 316 mii->mii_media_active |= IFM_NONE; 317 return; 318 case PC_LinkSpeed_10: 319 mii->mii_media_active |= IFM_10_T; 320 break; 321 case PC_LinkSpeed_100: 322 mii->mii_media_active |= IFM_100_TX; 323 break; 324 case PC_LinkSpeed_1000: 325 mii->mii_media_active |= IFM_1000_T; 326 break; 327 default: 328 mii->mii_media_active |= IFM_NONE; 329 return; 330 } 331 332 if (stat & PC_PhyDuplexStatus) 333 mii->mii_media_active |= IFM_FDX; 334 else 335 mii->mii_media_active |= IFM_HDX; 336 } 337 338 if (mii->mii_media_active & IFM_FDX) 339 mii->mii_media_active |= mii_phy_flowstatus(sc); 340 341 if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { 342 if (PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) 343 mii->mii_media_active |= IFM_ETH_MASTER; 344 } 345 } else 346 mii->mii_media_active = ife->ifm_media; 347 } 348 349 int 350 ipgphy_mii_phy_auto(struct mii_softc *sc) 351 { 352 uint32_t reg = 0; 353 354 if (sc->mii_model == MII_MODEL_ICPLUS_IP1001) { 355 reg = PHY_READ(sc, MII_ANAR); 356 reg &= ~(ANAR_PAUSE_SYM | ANAR_PAUSE_ASYM); 357 reg |= ANAR_NP; 358 } 359 360 reg |= ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD; 361 362 if (sc->mii_flags & MIIF_DOPAUSE) 363 reg |= ANAR_PAUSE_SYM | ANAR_PAUSE_ASYM; 364 365 PHY_WRITE(sc, MII_ANAR, reg | ANAR_CSMA); 366 367 reg = GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; 368 if (sc->mii_model != MII_MODEL_ICPLUS_IP1001) 369 reg |= GTCR_ADV_MS; 370 PHY_WRITE(sc, MII_100T2CR, reg); 371 372 PHY_WRITE(sc, MII_BMCR, (BMCR_FDX | BMCR_AUTOEN | BMCR_STARTNEG)); 373 374 return (EJUSTRETURN); 375 } 376 377 void 378 ipgphy_load_dspcode(struct mii_softc *sc) 379 { 380 PHY_WRITE(sc, 31, 0x0001); 381 PHY_WRITE(sc, 27, 0x01e0); 382 PHY_WRITE(sc, 31, 0x0002); 383 PHY_WRITE(sc, 27, 0xeb8e); 384 PHY_WRITE(sc, 31, 0x0000); 385 PHY_WRITE(sc, 30, 0x005e); 386 PHY_WRITE(sc, 9, 0x0700); 387 388 DELAY(50); 389 } 390 391 void 392 ipgphy_reset(struct mii_softc *sc) 393 { 394 struct ifnet *ifp = sc->mii_pdata->mii_ifp; 395 struct stge_softc *stge_sc; 396 uint32_t reg; 397 398 mii_phy_reset(sc); 399 400 /* clear autoneg/full-duplex as we don't want it after reset */ 401 reg = PHY_READ(sc, MII_BMCR); 402 reg &= ~(BMCR_AUTOEN | BMCR_FDX); 403 PHY_WRITE(sc, MII_BMCR, reg); 404 405 if (sc->mii_model == MII_MODEL_ICPLUS_IP1000A && 406 strcmp(ifp->if_xname, "stge") == 0) { 407 stge_sc = ifp->if_softc; 408 if (stge_sc->sc_rev >= 0x40 && stge_sc->sc_rev <= 0x4e) 409 ipgphy_load_dspcode(sc); 410 } 411 } 412