ip1000phy.c revision 221746
12061Sjkh/*- 22626Scsgr * Copyright (c) 2006, Pyun YongHyeon <yongari@FreeBSD.org> 32061Sjkh * All rights reserved. 42061Sjkh * 52061Sjkh * Redistribution and use in source and binary forms, with or without 62061Sjkh * modification, are permitted provided that the following conditions 72061Sjkh * are met: 82061Sjkh * 1. Redistributions of source code must retain the above copyright 92160Scsgr * notice unmodified, this list of conditions, and the following 102160Scsgr * disclaimer. 112061Sjkh * 2. Redistributions in binary form must reproduce the above copyright 122061Sjkh * notice, this list of conditions and the following disclaimer in the 132160Scsgr * documentation and/or other materials provided with the distribution. 142626Scsgr * 152061Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 162160Scsgr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 171594Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 182061Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 192160Scsgr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 202061Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 211594Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 222061Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 232061Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 242061Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 252061Sjkh * SUCH DAMAGE. 262061Sjkh * 272061Sjkh */ 282061Sjkh 292061Sjkh#include <sys/cdefs.h> 302061Sjkh__FBSDID("$FreeBSD: head/sys/dev/mii/ip1000phy.c 221746 2011-05-10 18:38:01Z marius $"); 312061Sjkh 322061Sjkh/* 332061Sjkh * Driver for the IC Plus IP1000A/IP1001 10/100/1000 PHY. 342061Sjkh */ 352061Sjkh 362061Sjkh#include <sys/param.h> 372061Sjkh#include <sys/systm.h> 382061Sjkh#include <sys/kernel.h> 392061Sjkh#include <sys/module.h> 402061Sjkh#include <sys/socket.h> 412061Sjkh#include <sys/bus.h> 422061Sjkh 432160Scsgr#include <net/if.h> 442061Sjkh#include <net/if_media.h> 452061Sjkh 462626Scsgr#include <dev/mii/mii.h> 472626Scsgr#include <dev/mii/miivar.h> 482626Scsgr#include "miidevs.h" 492626Scsgr 502061Sjkh#include <dev/mii/ip1000phyreg.h> 512061Sjkh 522061Sjkh#include "miibus_if.h" 532061Sjkh 542061Sjkh#include <machine/bus.h> 552061Sjkh#include <dev/stge/if_stgereg.h> 562160Scsgr 572160Scsgrstatic int ip1000phy_probe(device_t); 582160Scsgrstatic int ip1000phy_attach(device_t); 592061Sjkh 602061Sjkhstatic device_method_t ip1000phy_methods[] = { 612061Sjkh /* device interface */ 622061Sjkh DEVMETHOD(device_probe, ip1000phy_probe), 632061Sjkh DEVMETHOD(device_attach, ip1000phy_attach), 642061Sjkh DEVMETHOD(device_detach, mii_phy_detach), 652061Sjkh DEVMETHOD(device_shutdown, bus_generic_shutdown), 662061Sjkh { 0, 0 } 672061Sjkh}; 682061Sjkh 692061Sjkhstatic devclass_t ip1000phy_devclass; 702061Sjkhstatic driver_t ip1000phy_driver = { 711594Srgrimes "ip1000phy", 722061Sjkh ip1000phy_methods, 732061Sjkh sizeof (struct mii_softc) 742061Sjkh}; 752061Sjkh 762061SjkhDRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, ip1000phy_devclass, 0, 0); 772061Sjkh 782061Sjkhstatic int ip1000phy_service(struct mii_softc *, struct mii_data *, int); 792061Sjkhstatic void ip1000phy_status(struct mii_softc *); 802061Sjkhstatic void ip1000phy_reset(struct mii_softc *); 812061Sjkhstatic int ip1000phy_mii_phy_auto(struct mii_softc *, int); 822061Sjkh 832061Sjkhstatic const struct mii_phydesc ip1000phys[] = { 842061Sjkh MII_PHY_DESC(xxICPLUS, IP1000A), 852061Sjkh MII_PHY_DESC(xxICPLUS, IP1001), 862061Sjkh MII_PHY_END 872061Sjkh}; 882061Sjkh 892061Sjkhstatic const struct mii_phy_funcs ip1000phy_funcs = { 902061Sjkh ip1000phy_service, 912061Sjkh ip1000phy_status, 922061Sjkh ip1000phy_reset 932468Spaul}; 942061Sjkh 952061Sjkhstatic int 962061Sjkhip1000phy_probe(device_t dev) 972061Sjkh{ 982061Sjkh 992061Sjkh return (mii_phy_dev_probe(dev, ip1000phys, BUS_PROBE_DEFAULT)); 1002061Sjkh} 1012302Spaul 1022061Sjkhstatic int 1032061Sjkhip1000phy_attach(device_t dev) 1042061Sjkh{ 1052061Sjkh struct mii_attach_args *ma; 1062302Spaul u_int flags; 1072061Sjkh 1082302Spaul ma = device_get_ivars(dev); 1092302Spaul flags = MIIF_NOISOLATE | MIIF_NOMANPAUSE; 1102302Spaul if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxICPLUS_IP1000A && 1112302Spaul strcmp(ma->mii_data->mii_ifp->if_dname, "stge") == 0 && 1122302Spaul (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0) 1132302Spaul flags |= MIIF_PHYPRIV0; 1142302Spaul mii_phy_dev_attach(dev, flags, &ip1000phy_funcs, 1); 1152302Spaul return (0); 1162302Spaul} 1172302Spaul 1182302Spaulstatic int 1192302Spaulip1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 1202302Spaul{ 1212302Spaul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 1222061Sjkh uint32_t gig, reg, speed; 1232061Sjkh 1242061Sjkh switch (cmd) { 1252061Sjkh case MII_POLLSTAT: 1262061Sjkh break; 1272061Sjkh 1282061Sjkh case MII_MEDIACHG: 1292061Sjkh /* 1302061Sjkh * If the interface is not up, don't do anything. 1312061Sjkh */ 1322061Sjkh if ((mii->mii_ifp->if_flags & IFF_UP) == 0) { 1332061Sjkh break; 1342061Sjkh } 1352061Sjkh 1362061Sjkh PHY_RESET(sc); 1372061Sjkh switch (IFM_SUBTYPE(ife->ifm_media)) { 1382061Sjkh case IFM_AUTO: 1392061Sjkh (void)ip1000phy_mii_phy_auto(sc, ife->ifm_media); 1402061Sjkh goto done; 1412061Sjkh 1422061Sjkh case IFM_1000_T: 1432061Sjkh /* 1442061Sjkh * XXX 1452061Sjkh * Manual 1000baseT setting doesn't seem to work. 1462061Sjkh */ 1472061Sjkh speed = IP1000PHY_BMCR_1000; 1482061Sjkh break; 1492061Sjkh 1502061Sjkh case IFM_100_TX: 1512061Sjkh speed = IP1000PHY_BMCR_100; 1522061Sjkh break; 1532061Sjkh 1542061Sjkh case IFM_10_T: 1552061Sjkh speed = IP1000PHY_BMCR_10; 1562061Sjkh break; 1572061Sjkh 1582061Sjkh default: 1592061Sjkh return (EINVAL); 1602061Sjkh } 1612061Sjkh 1622061Sjkh if ((ife->ifm_media & IFM_FDX) != 0) { 1632061Sjkh speed |= IP1000PHY_BMCR_FDX; 1642061Sjkh gig = IP1000PHY_1000CR_1000T_FDX; 1652061Sjkh } else 1662068Sjkh gig = IP1000PHY_1000CR_1000T; 1672273Spaul 1682626Scsgr if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 1692061Sjkh gig |= 1702061Sjkh IP1000PHY_1000CR_MASTER | IP1000PHY_1000CR_MANUAL; 1712061Sjkh if ((ife->ifm_media & IFM_ETH_MASTER) != 0) 1722061Sjkh gig |= IP1000PHY_1000CR_MMASTER; 1732061Sjkh } else 1742061Sjkh gig = 0; 1752626Scsgr PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig); 1762626Scsgr PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed); 1772626Scsgr 1782626Scsgrdone: 1792061Sjkh break; 1802061Sjkh 1812061Sjkh case MII_TICK: 1822061Sjkh /* 1832061Sjkh * Is the interface even up? 1842061Sjkh */ 1852061Sjkh if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 1862061Sjkh return (0); 1872061Sjkh 1882061Sjkh /* 1892468Spaul * Only used for autonegotiation. 1902061Sjkh */ 1912273Spaul if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 1922061Sjkh sc->mii_ticks = 0; 1932160Scsgr break; 1942160Scsgr } 1952160Scsgr 1962160Scsgr /* 1972279Spaul * check for link. 1982061Sjkh */ 1992061Sjkh reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 2002279Spaul if (reg & BMSR_LINK) { 2012468Spaul sc->mii_ticks = 0; 2022468Spaul break; 2032626Scsgr } 2042061Sjkh 2052061Sjkh /* Announce link loss right after it happens */ 2062061Sjkh if (sc->mii_ticks++ == 0) 2072061Sjkh break; 2082061Sjkh 2092061Sjkh /* 2102061Sjkh * Only retry autonegotiation every mii_anegticks seconds. 2112061Sjkh */ 2122061Sjkh if (sc->mii_ticks <= sc->mii_anegticks) 2132626Scsgr break; 2142626Scsgr 2152626Scsgr sc->mii_ticks = 0; 2162626Scsgr ip1000phy_mii_phy_auto(sc, ife->ifm_media); 2172626Scsgr break; 2182626Scsgr } 2192626Scsgr 2202626Scsgr /* Update the media status. */ 2212626Scsgr PHY_STATUS(sc); 2222626Scsgr 2232626Scsgr /* Callback if something changed. */ 2242061Sjkh mii_phy_update(sc, cmd); 2252061Sjkh return (0); 2262061Sjkh} 2272061Sjkh 2282061Sjkhstatic void 2292061Sjkhip1000phy_status(struct mii_softc *sc) 2302273Spaul{ 2312061Sjkh struct mii_data *mii = sc->mii_pdata; 2322061Sjkh uint32_t bmsr, bmcr, stat; 2332061Sjkh 2342061Sjkh mii->mii_media_status = IFM_AVALID; 2351594Srgrimes mii->mii_media_active = IFM_ETHER; 236 237 bmsr = PHY_READ(sc, IP1000PHY_MII_BMSR) | 238 PHY_READ(sc, IP1000PHY_MII_BMSR); 239 if ((bmsr & IP1000PHY_BMSR_LINK) != 0) 240 mii->mii_media_status |= IFM_ACTIVE; 241 242 bmcr = PHY_READ(sc, IP1000PHY_MII_BMCR); 243 if ((bmcr & IP1000PHY_BMCR_LOOP) != 0) 244 mii->mii_media_active |= IFM_LOOP; 245 246 if ((bmcr & IP1000PHY_BMCR_AUTOEN) != 0) { 247 if ((bmsr & IP1000PHY_BMSR_ANEGCOMP) == 0) { 248 /* Erg, still trying, I guess... */ 249 mii->mii_media_active |= IFM_NONE; 250 return; 251 } 252 } 253 254 if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) { 255 stat = PHY_READ(sc, IP1000PHY_LSR); 256 switch (stat & IP1000PHY_LSR_SPEED_MASK) { 257 case IP1000PHY_LSR_SPEED_10: 258 mii->mii_media_active |= IFM_10_T; 259 break; 260 case IP1000PHY_LSR_SPEED_100: 261 mii->mii_media_active |= IFM_100_TX; 262 break; 263 case IP1000PHY_LSR_SPEED_1000: 264 mii->mii_media_active |= IFM_1000_T; 265 break; 266 default: 267 mii->mii_media_active |= IFM_NONE; 268 return; 269 } 270 if ((stat & IP1000PHY_LSR_FULL_DUPLEX) != 0) 271 mii->mii_media_active |= IFM_FDX; 272 else 273 mii->mii_media_active |= IFM_HDX; 274 } else { 275 stat = PHY_READ(sc, STGE_PhyCtrl); 276 switch (PC_LinkSpeed(stat)) { 277 case PC_LinkSpeed_Down: 278 mii->mii_media_active |= IFM_NONE; 279 return; 280 case PC_LinkSpeed_10: 281 mii->mii_media_active |= IFM_10_T; 282 break; 283 case PC_LinkSpeed_100: 284 mii->mii_media_active |= IFM_100_TX; 285 break; 286 case PC_LinkSpeed_1000: 287 mii->mii_media_active |= IFM_1000_T; 288 break; 289 default: 290 mii->mii_media_active |= IFM_NONE; 291 return; 292 } 293 if ((stat & PC_PhyDuplexStatus) != 0) 294 mii->mii_media_active |= IFM_FDX; 295 else 296 mii->mii_media_active |= IFM_HDX; 297 } 298 299 if ((mii->mii_media_active & IFM_FDX) != 0) 300 mii->mii_media_active |= mii_phy_flowstatus(sc); 301 302 if ((mii->mii_media_active & IFM_1000_T) != 0) { 303 stat = PHY_READ(sc, IP1000PHY_MII_1000SR); 304 if ((stat & IP1000PHY_1000SR_MASTER) != 0) 305 mii->mii_media_active |= IFM_ETH_MASTER; 306 } 307} 308 309static int 310ip1000phy_mii_phy_auto(struct mii_softc *sc, int media) 311{ 312 uint32_t reg; 313 314 reg = 0; 315 if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) { 316 reg = PHY_READ(sc, IP1000PHY_MII_ANAR); 317 reg &= ~(IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE); 318 reg |= IP1000PHY_ANAR_NP; 319 } 320 reg |= IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX | 321 IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX; 322 if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 323 reg |= IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE; 324 PHY_WRITE(sc, IP1000PHY_MII_ANAR, reg | IP1000PHY_ANAR_CSMA); 325 326 reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX; 327 reg |= IP1000PHY_1000CR_MASTER; 328 PHY_WRITE(sc, IP1000PHY_MII_1000CR, reg); 329 PHY_WRITE(sc, IP1000PHY_MII_BMCR, (IP1000PHY_BMCR_FDX | 330 IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_STARTNEG)); 331 332 return (EJUSTRETURN); 333} 334 335static void 336ip1000phy_load_dspcode(struct mii_softc *sc) 337{ 338 339 PHY_WRITE(sc, 31, 0x0001); 340 PHY_WRITE(sc, 27, 0x01e0); 341 PHY_WRITE(sc, 31, 0x0002); 342 PHY_WRITE(sc, 27, 0xeb8e); 343 PHY_WRITE(sc, 31, 0x0000); 344 PHY_WRITE(sc, 30, 0x005e); 345 PHY_WRITE(sc, 9, 0x0700); 346 347 DELAY(50); 348} 349 350static void 351ip1000phy_reset(struct mii_softc *sc) 352{ 353 uint32_t reg; 354 355 mii_phy_reset(sc); 356 357 /* clear autoneg/full-duplex as we don't want it after reset */ 358 reg = PHY_READ(sc, IP1000PHY_MII_BMCR); 359 reg &= ~(IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_FDX); 360 PHY_WRITE(sc, MII_BMCR, reg); 361 362 if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) 363 ip1000phy_load_dspcode(sc); 364} 365