1160638Syongari/*- 2160638Syongari * Copyright (c) 2006, Pyun YongHyeon <yongari@FreeBSD.org> 3160638Syongari * All rights reserved. 4160638Syongari * 5160638Syongari * Redistribution and use in source and binary forms, with or without 6160638Syongari * modification, are permitted provided that the following conditions 7160638Syongari * are met: 8160638Syongari * 1. Redistributions of source code must retain the above copyright 9160638Syongari * notice unmodified, this list of conditions, and the following 10160638Syongari * disclaimer. 11160638Syongari * 2. Redistributions in binary form must reproduce the above copyright 12160638Syongari * notice, this list of conditions and the following disclaimer in the 13160638Syongari * documentation and/or other materials provided with the distribution. 14160638Syongari * 15160638Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16160638Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17160638Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18160638Syongari * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19160638Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20160638Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21160638Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22160638Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23160638Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24160638Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25160638Syongari * SUCH DAMAGE. 26160638Syongari * 27160638Syongari */ 28160638Syongari 29160638Syongari#include <sys/cdefs.h> 30160638Syongari__FBSDID("$FreeBSD$"); 31160638Syongari 32160638Syongari/* 33177930Syongari * Driver for the IC Plus IP1000A/IP1001 10/100/1000 PHY. 34160638Syongari */ 35160638Syongari 36160638Syongari#include <sys/param.h> 37160638Syongari#include <sys/systm.h> 38160638Syongari#include <sys/kernel.h> 39160638Syongari#include <sys/module.h> 40160638Syongari#include <sys/socket.h> 41160638Syongari#include <sys/bus.h> 42160638Syongari 43160638Syongari#include <net/if.h> 44160638Syongari#include <net/if_media.h> 45160638Syongari 46160638Syongari#include <dev/mii/mii.h> 47160638Syongari#include <dev/mii/miivar.h> 48160638Syongari#include "miidevs.h" 49160638Syongari 50160638Syongari#include <dev/mii/ip1000phyreg.h> 51160638Syongari 52160638Syongari#include "miibus_if.h" 53160638Syongari 54160638Syongari#include <machine/bus.h> 55160638Syongari#include <dev/stge/if_stgereg.h> 56160638Syongari 57160638Syongaristatic int ip1000phy_probe(device_t); 58160638Syongaristatic int ip1000phy_attach(device_t); 59160638Syongari 60160638Syongaristatic device_method_t ip1000phy_methods[] = { 61160638Syongari /* device interface */ 62160638Syongari DEVMETHOD(device_probe, ip1000phy_probe), 63160638Syongari DEVMETHOD(device_attach, ip1000phy_attach), 64160638Syongari DEVMETHOD(device_detach, mii_phy_detach), 65160638Syongari DEVMETHOD(device_shutdown, bus_generic_shutdown), 66229093Shselasky DEVMETHOD_END 67160638Syongari}; 68160638Syongari 69160638Syongaristatic devclass_t ip1000phy_devclass; 70160638Syongaristatic driver_t ip1000phy_driver = { 71160638Syongari "ip1000phy", 72160638Syongari ip1000phy_methods, 73221747Smarius sizeof(struct mii_softc) 74160638Syongari}; 75160638Syongari 76160638SyongariDRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, ip1000phy_devclass, 0, 0); 77160638Syongari 78160638Syongaristatic int ip1000phy_service(struct mii_softc *, struct mii_data *, int); 79160638Syongaristatic void ip1000phy_status(struct mii_softc *); 80160638Syongaristatic void ip1000phy_reset(struct mii_softc *); 81215297Smariusstatic int ip1000phy_mii_phy_auto(struct mii_softc *, int); 82160638Syongari 83160638Syongaristatic const struct mii_phydesc ip1000phys[] = { 84221407Smarius MII_PHY_DESC(xxICPLUS, IP1000A), 85221407Smarius MII_PHY_DESC(xxICPLUS, IP1001), 86160638Syongari MII_PHY_END 87160638Syongari}; 88160638Syongari 89221407Smariusstatic const struct mii_phy_funcs ip1000phy_funcs = { 90221407Smarius ip1000phy_service, 91221407Smarius ip1000phy_status, 92221407Smarius ip1000phy_reset 93221407Smarius}; 94221407Smarius 95160638Syongaristatic int 96160638Syongariip1000phy_probe(device_t dev) 97160638Syongari{ 98160638Syongari 99164827Smarius return (mii_phy_dev_probe(dev, ip1000phys, BUS_PROBE_DEFAULT)); 100160638Syongari} 101160638Syongari 102160638Syongaristatic int 103160638Syongariip1000phy_attach(device_t dev) 104160638Syongari{ 105160638Syongari struct mii_attach_args *ma; 106221407Smarius u_int flags; 107160638Syongari 108160638Syongari ma = device_get_ivars(dev); 109221407Smarius flags = MIIF_NOISOLATE | MIIF_NOMANPAUSE; 110221407Smarius if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxICPLUS_IP1000A && 111221407Smarius strcmp(ma->mii_data->mii_ifp->if_dname, "stge") == 0 && 112221407Smarius (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0) 113221407Smarius flags |= MIIF_PHYPRIV0; 114221746Smarius mii_phy_dev_attach(dev, flags, &ip1000phy_funcs, 1); 115213364Smarius return (0); 116160638Syongari} 117160638Syongari 118160638Syongaristatic int 119160638Syongariip1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 120160638Syongari{ 121160638Syongari struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 122160638Syongari uint32_t gig, reg, speed; 123160638Syongari 124160638Syongari switch (cmd) { 125160638Syongari case MII_POLLSTAT: 126160638Syongari break; 127160638Syongari 128160638Syongari case MII_MEDIACHG: 129160638Syongari /* 130160638Syongari * If the interface is not up, don't do anything. 131160638Syongari */ 132160638Syongari if ((mii->mii_ifp->if_flags & IFF_UP) == 0) { 133160638Syongari break; 134160638Syongari } 135160638Syongari 136221407Smarius PHY_RESET(sc); 137160638Syongari switch (IFM_SUBTYPE(ife->ifm_media)) { 138160638Syongari case IFM_AUTO: 139215297Smarius (void)ip1000phy_mii_phy_auto(sc, ife->ifm_media); 140160638Syongari goto done; 141160638Syongari 142160638Syongari case IFM_1000_T: 143160638Syongari /* 144160638Syongari * XXX 145160638Syongari * Manual 1000baseT setting doesn't seem to work. 146160638Syongari */ 147160638Syongari speed = IP1000PHY_BMCR_1000; 148160638Syongari break; 149160638Syongari 150160638Syongari case IFM_100_TX: 151160638Syongari speed = IP1000PHY_BMCR_100; 152160638Syongari break; 153160638Syongari 154160638Syongari case IFM_10_T: 155160638Syongari speed = IP1000PHY_BMCR_10; 156160638Syongari break; 157160638Syongari 158160638Syongari default: 159160638Syongari return (EINVAL); 160160638Syongari } 161160638Syongari 162217412Smarius if ((ife->ifm_media & IFM_FDX) != 0) { 163160638Syongari speed |= IP1000PHY_BMCR_FDX; 164160638Syongari gig = IP1000PHY_1000CR_1000T_FDX; 165160638Syongari } else 166160638Syongari gig = IP1000PHY_1000CR_1000T; 167160638Syongari 168217412Smarius if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 169217412Smarius gig |= 170217412Smarius IP1000PHY_1000CR_MASTER | IP1000PHY_1000CR_MANUAL; 171217412Smarius if ((ife->ifm_media & IFM_ETH_MASTER) != 0) 172217412Smarius gig |= IP1000PHY_1000CR_MMASTER; 173217412Smarius } else 174217412Smarius gig = 0; 175217412Smarius PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig); 176160638Syongari PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed); 177160638Syongari 178160638Syongaridone: 179160638Syongari break; 180160638Syongari 181160638Syongari case MII_TICK: 182160638Syongari /* 183160638Syongari * Is the interface even up? 184160638Syongari */ 185160638Syongari if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 186160638Syongari return (0); 187160638Syongari 188160638Syongari /* 189160638Syongari * Only used for autonegotiation. 190160638Syongari */ 191160638Syongari if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 192160638Syongari sc->mii_ticks = 0; 193160638Syongari break; 194160638Syongari } 195160638Syongari 196160638Syongari /* 197160638Syongari * check for link. 198160638Syongari */ 199160638Syongari reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 200160638Syongari if (reg & BMSR_LINK) { 201160638Syongari sc->mii_ticks = 0; 202160638Syongari break; 203160638Syongari } 204160638Syongari 205160638Syongari /* Announce link loss right after it happens */ 206160638Syongari if (sc->mii_ticks++ == 0) 207160638Syongari break; 208160638Syongari 209160638Syongari /* 210160638Syongari * Only retry autonegotiation every mii_anegticks seconds. 211160638Syongari */ 212160638Syongari if (sc->mii_ticks <= sc->mii_anegticks) 213189564Syongari break; 214160638Syongari 215160638Syongari sc->mii_ticks = 0; 216215297Smarius ip1000phy_mii_phy_auto(sc, ife->ifm_media); 217160638Syongari break; 218160638Syongari } 219160638Syongari 220160638Syongari /* Update the media status. */ 221221407Smarius PHY_STATUS(sc); 222160638Syongari 223160638Syongari /* Callback if something changed. */ 224160638Syongari mii_phy_update(sc, cmd); 225160638Syongari return (0); 226160638Syongari} 227160638Syongari 228160638Syongaristatic void 229160638Syongariip1000phy_status(struct mii_softc *sc) 230160638Syongari{ 231160638Syongari struct mii_data *mii = sc->mii_pdata; 232160638Syongari uint32_t bmsr, bmcr, stat; 233160638Syongari 234160638Syongari mii->mii_media_status = IFM_AVALID; 235160638Syongari mii->mii_media_active = IFM_ETHER; 236160638Syongari 237160638Syongari bmsr = PHY_READ(sc, IP1000PHY_MII_BMSR) | 238160638Syongari PHY_READ(sc, IP1000PHY_MII_BMSR); 239160638Syongari if ((bmsr & IP1000PHY_BMSR_LINK) != 0) 240160638Syongari mii->mii_media_status |= IFM_ACTIVE; 241160638Syongari 242160638Syongari bmcr = PHY_READ(sc, IP1000PHY_MII_BMCR); 243160638Syongari if ((bmcr & IP1000PHY_BMCR_LOOP) != 0) 244160638Syongari mii->mii_media_active |= IFM_LOOP; 245160638Syongari 246160638Syongari if ((bmcr & IP1000PHY_BMCR_AUTOEN) != 0) { 247160638Syongari if ((bmsr & IP1000PHY_BMSR_ANEGCOMP) == 0) { 248160638Syongari /* Erg, still trying, I guess... */ 249160638Syongari mii->mii_media_active |= IFM_NONE; 250160638Syongari return; 251160638Syongari } 252160638Syongari } 253160638Syongari 254221407Smarius if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) { 255177930Syongari stat = PHY_READ(sc, IP1000PHY_LSR); 256177930Syongari switch (stat & IP1000PHY_LSR_SPEED_MASK) { 257177930Syongari case IP1000PHY_LSR_SPEED_10: 258177930Syongari mii->mii_media_active |= IFM_10_T; 259177930Syongari break; 260177930Syongari case IP1000PHY_LSR_SPEED_100: 261177930Syongari mii->mii_media_active |= IFM_100_TX; 262177930Syongari break; 263177930Syongari case IP1000PHY_LSR_SPEED_1000: 264177930Syongari mii->mii_media_active |= IFM_1000_T; 265177930Syongari break; 266189565Syongari default: 267189565Syongari mii->mii_media_active |= IFM_NONE; 268189565Syongari return; 269177930Syongari } 270177930Syongari if ((stat & IP1000PHY_LSR_FULL_DUPLEX) != 0) 271177930Syongari mii->mii_media_active |= IFM_FDX; 272177930Syongari else 273177930Syongari mii->mii_media_active |= IFM_HDX; 274177930Syongari } else { 275177930Syongari stat = PHY_READ(sc, STGE_PhyCtrl); 276177930Syongari switch (PC_LinkSpeed(stat)) { 277177930Syongari case PC_LinkSpeed_Down: 278177930Syongari mii->mii_media_active |= IFM_NONE; 279177930Syongari return; 280177930Syongari case PC_LinkSpeed_10: 281177930Syongari mii->mii_media_active |= IFM_10_T; 282177930Syongari break; 283177930Syongari case PC_LinkSpeed_100: 284177930Syongari mii->mii_media_active |= IFM_100_TX; 285177930Syongari break; 286177930Syongari case PC_LinkSpeed_1000: 287177930Syongari mii->mii_media_active |= IFM_1000_T; 288177930Syongari break; 289189565Syongari default: 290189565Syongari mii->mii_media_active |= IFM_NONE; 291189565Syongari return; 292177930Syongari } 293177930Syongari if ((stat & PC_PhyDuplexStatus) != 0) 294177930Syongari mii->mii_media_active |= IFM_FDX; 295177930Syongari else 296177930Syongari mii->mii_media_active |= IFM_HDX; 297160638Syongari } 298160638Syongari 299215297Smarius if ((mii->mii_media_active & IFM_FDX) != 0) 300215297Smarius mii->mii_media_active |= mii_phy_flowstatus(sc); 301160638Syongari 302160638Syongari if ((mii->mii_media_active & IFM_1000_T) != 0) { 303160638Syongari stat = PHY_READ(sc, IP1000PHY_MII_1000SR); 304160638Syongari if ((stat & IP1000PHY_1000SR_MASTER) != 0) 305215297Smarius mii->mii_media_active |= IFM_ETH_MASTER; 306160638Syongari } 307160638Syongari} 308160638Syongari 309160638Syongaristatic int 310215297Smariusip1000phy_mii_phy_auto(struct mii_softc *sc, int media) 311160638Syongari{ 312160638Syongari uint32_t reg; 313160638Syongari 314189567Syongari reg = 0; 315221407Smarius if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) { 316189567Syongari reg = PHY_READ(sc, IP1000PHY_MII_ANAR); 317215923Smarius reg &= ~(IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE); 318200693Syongari reg |= IP1000PHY_ANAR_NP; 319200693Syongari } 320189567Syongari reg |= IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX | 321215297Smarius IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX; 322215297Smarius if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 323215297Smarius reg |= IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE; 324189567Syongari PHY_WRITE(sc, IP1000PHY_MII_ANAR, reg | IP1000PHY_ANAR_CSMA); 325189567Syongari 326160638Syongari reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX; 327229543Syongari if (sc->mii_mpd_model != MII_MODEL_xxICPLUS_IP1001) 328229543Syongari reg |= IP1000PHY_1000CR_MASTER; 329189567Syongari PHY_WRITE(sc, IP1000PHY_MII_1000CR, reg); 330189567Syongari PHY_WRITE(sc, IP1000PHY_MII_BMCR, (IP1000PHY_BMCR_FDX | 331160638Syongari IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_STARTNEG)); 332160638Syongari 333160638Syongari return (EJUSTRETURN); 334160638Syongari} 335160638Syongari 336160638Syongaristatic void 337160638Syongariip1000phy_load_dspcode(struct mii_softc *sc) 338160638Syongari{ 339160638Syongari 340160638Syongari PHY_WRITE(sc, 31, 0x0001); 341160638Syongari PHY_WRITE(sc, 27, 0x01e0); 342160638Syongari PHY_WRITE(sc, 31, 0x0002); 343160638Syongari PHY_WRITE(sc, 27, 0xeb8e); 344160638Syongari PHY_WRITE(sc, 31, 0x0000); 345160638Syongari PHY_WRITE(sc, 30, 0x005e); 346160638Syongari PHY_WRITE(sc, 9, 0x0700); 347160638Syongari 348160638Syongari DELAY(50); 349160638Syongari} 350160638Syongari 351160638Syongaristatic void 352160638Syongariip1000phy_reset(struct mii_softc *sc) 353160638Syongari{ 354160638Syongari uint32_t reg; 355160638Syongari 356160638Syongari mii_phy_reset(sc); 357160638Syongari 358160638Syongari /* clear autoneg/full-duplex as we don't want it after reset */ 359160638Syongari reg = PHY_READ(sc, IP1000PHY_MII_BMCR); 360160638Syongari reg &= ~(IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_FDX); 361160638Syongari PHY_WRITE(sc, MII_BMCR, reg); 362160638Syongari 363213893Smarius if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) 364213893Smarius ip1000phy_load_dspcode(sc); 365160638Syongari} 366