Annotation of sys/dev/mii/ipgphy.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ipgphy.c,v 1.7 2006/12/31 15:04:33 krw 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 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: #ifdef INET
! 48: #include <netinet/in.h>
! 49: #include <netinet/if_ether.h>
! 50: #endif
! 51:
! 52: #include <dev/mii/mii.h>
! 53: #include <dev/mii/miivar.h>
! 54: #include <dev/mii/miidevs.h>
! 55:
! 56: #include <dev/mii/ipgphyreg.h>
! 57:
! 58: #include <dev/pci/if_stgereg.h>
! 59:
! 60: int ipgphy_probe(struct device *, void *, void *);
! 61: void ipgphy_attach(struct device *, struct device *, void *);
! 62:
! 63: struct cfattach ipgphy_ca = {
! 64: sizeof(struct mii_softc), ipgphy_probe, ipgphy_attach, mii_phy_detach,
! 65: mii_phy_activate
! 66: };
! 67:
! 68: struct cfdriver ipgphy_cd = {
! 69: NULL, "ipgphy", DV_DULL
! 70: };
! 71:
! 72: int ipgphy_service(struct mii_softc *, struct mii_data *, int);
! 73: void ipgphy_status(struct mii_softc *);
! 74: int ipgphy_mii_phy_auto(struct mii_softc *);
! 75: void ipgphy_load_dspcode(struct mii_softc *);
! 76: void ipgphy_reset(struct mii_softc *);
! 77:
! 78: const struct mii_phy_funcs ipgphy_funcs = {
! 79: ipgphy_service, ipgphy_status, ipgphy_reset,
! 80: };
! 81:
! 82: static const struct mii_phydesc ipgphys[] = {
! 83: { MII_OUI_ICPLUS, MII_MODEL_ICPLUS_IP1000A,
! 84: MII_STR_ICPLUS_IP1000A },
! 85:
! 86: { 0,
! 87: NULL },
! 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_pdata = mii;
! 116: sc->mii_anegticks = MII_ANEGTICKS_GIGE;
! 117:
! 118: sc->mii_flags |= MIIF_NOISOLATE;
! 119:
! 120: #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
! 121:
! 122: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
! 123: BMCR_ISO);
! 124:
! 125: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
! 126: IPGPHY_BMCR_10);
! 127: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
! 128: IPGPHY_BMCR_10 | IPGPHY_BMCR_FDX);
! 129: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
! 130: IPGPHY_BMCR_100);
! 131: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
! 132: IPGPHY_BMCR_100 | IPGPHY_BMCR_FDX);
! 133: /* 1000baseT half-duplex, really supported? */
! 134: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst),
! 135: IPGPHY_BMCR_1000);
! 136: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst),
! 137: IPGPHY_BMCR_1000 | IPGPHY_BMCR_FDX);
! 138: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
! 139: #undef ADD
! 140:
! 141: PHY_RESET(sc);
! 142: }
! 143:
! 144: int
! 145: ipgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
! 146: {
! 147: struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
! 148: uint32_t gig, reg, speed;
! 149:
! 150: switch (cmd) {
! 151: case MII_POLLSTAT:
! 152: /*
! 153: * If we're not polling our PHY instance, just return.
! 154: */
! 155: if (IFM_INST(ife->ifm_media) != sc->mii_inst)
! 156: return (0);
! 157: break;
! 158:
! 159: case MII_MEDIACHG:
! 160: /*
! 161: * If the media indicates a different PHY instance,
! 162: * isolate ourselves.
! 163: */
! 164: if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
! 165: reg = PHY_READ(sc, IPGPHY_MII_BMCR);
! 166: PHY_WRITE(sc, IPGPHY_MII_BMCR,
! 167: reg | IPGPHY_BMCR_ISO);
! 168: return (0);
! 169: }
! 170:
! 171: /*
! 172: * If the interface is not up, don't do anything.
! 173: */
! 174: if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
! 175: break;
! 176:
! 177: PHY_RESET(sc);
! 178:
! 179: switch (IFM_SUBTYPE(ife->ifm_media)) {
! 180: case IFM_AUTO:
! 181: (void)ipgphy_mii_phy_auto(sc);
! 182: goto done;
! 183: break;
! 184:
! 185: case IFM_1000_T:
! 186: /*
! 187: * XXX
! 188: * Manual 1000baseT setting doesn't seem to work.
! 189: */
! 190: speed = IPGPHY_BMCR_1000;
! 191: break;
! 192:
! 193: case IFM_100_TX:
! 194: speed = IPGPHY_BMCR_100;
! 195: break;
! 196:
! 197: case IFM_10_T:
! 198: speed = IPGPHY_BMCR_10;
! 199: break;
! 200:
! 201: default:
! 202: return (EINVAL);
! 203: }
! 204:
! 205: if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) {
! 206: speed |= IPGPHY_BMCR_FDX;
! 207: gig = IPGPHY_1000CR_1000T_FDX;
! 208: } else
! 209: gig = IPGPHY_1000CR_1000T;
! 210:
! 211: PHY_WRITE(sc, IPGPHY_MII_1000CR, 0);
! 212: PHY_WRITE(sc, IPGPHY_MII_BMCR, speed);
! 213:
! 214: if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
! 215: break;
! 216:
! 217: PHY_WRITE(sc, IPGPHY_MII_1000CR, gig);
! 218: PHY_WRITE(sc, IPGPHY_MII_BMCR, speed);
! 219:
! 220: if (mii->mii_media.ifm_media & IFM_ETH_MASTER)
! 221: gig |= IPGPHY_1000CR_MASTER | IPGPHY_1000CR_MANUAL;
! 222:
! 223: PHY_WRITE(sc, IPGPHY_MII_1000CR, gig);
! 224:
! 225: done:
! 226: break;
! 227:
! 228: case MII_TICK:
! 229: /*
! 230: * If we're not currently selected, just return.
! 231: */
! 232: if (IFM_INST(ife->ifm_media) != sc->mii_inst)
! 233: return (0);
! 234: /*
! 235: * Is the interface even up?
! 236: */
! 237: if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
! 238: return (0);
! 239:
! 240: /*
! 241: * Only used for autonegotiation.
! 242: */
! 243: if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
! 244: sc->mii_ticks = 0;
! 245: break;
! 246: }
! 247:
! 248: /*
! 249: * check for link.
! 250: */
! 251: reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
! 252: if (reg & BMSR_LINK) {
! 253: sc->mii_ticks = 0;
! 254: break;
! 255: }
! 256:
! 257: /* Announce link loss right after it happens */
! 258: if (sc->mii_ticks++ == 0)
! 259: break;
! 260:
! 261: /*
! 262: * Only retry autonegotiation every mii_anegticks seconds.
! 263: */
! 264: if (sc->mii_ticks <= sc->mii_anegticks)
! 265: return (0);
! 266:
! 267: sc->mii_ticks = 0;
! 268: ipgphy_mii_phy_auto(sc);
! 269: break;
! 270: }
! 271:
! 272: /* Update the media status. */
! 273: mii_phy_status(sc);
! 274:
! 275: /* Callback if something changed. */
! 276: mii_phy_update(sc, cmd);
! 277: return (0);
! 278: }
! 279:
! 280: void
! 281: ipgphy_status(struct mii_softc *sc)
! 282: {
! 283: struct mii_data *mii = sc->mii_pdata;
! 284: uint32_t bmsr, bmcr, stat;
! 285:
! 286: mii->mii_media_status = IFM_AVALID;
! 287: mii->mii_media_active = IFM_ETHER;
! 288:
! 289: bmsr = PHY_READ(sc, IPGPHY_MII_BMSR) |
! 290: PHY_READ(sc, IPGPHY_MII_BMSR);
! 291: if ((bmsr & IPGPHY_BMSR_LINK) != 0)
! 292: mii->mii_media_status |= IFM_ACTIVE;
! 293:
! 294: bmcr = PHY_READ(sc, IPGPHY_MII_BMCR);
! 295: if ((bmcr & IPGPHY_BMCR_LOOP) != 0)
! 296: mii->mii_media_active |= IFM_LOOP;
! 297:
! 298: if ((bmcr & IPGPHY_BMCR_AUTOEN) != 0) {
! 299: if ((bmsr & IPGPHY_BMSR_ANEGCOMP) == 0) {
! 300: /* Erg, still trying, I guess... */
! 301: mii->mii_media_active |= IFM_NONE;
! 302: return;
! 303: }
! 304: }
! 305:
! 306: stat = PHY_READ(sc, STGE_PhyCtrl);
! 307: switch (PC_LinkSpeed(stat)) {
! 308: case PC_LinkSpeed_Down:
! 309: mii->mii_media_active |= IFM_NONE;
! 310: return;
! 311: case PC_LinkSpeed_10:
! 312: mii->mii_media_active |= IFM_10_T;
! 313: break;
! 314: case PC_LinkSpeed_100:
! 315: mii->mii_media_active |= IFM_100_TX;
! 316: break;
! 317: case PC_LinkSpeed_1000:
! 318: mii->mii_media_active |= IFM_1000_T;
! 319: break;
! 320: }
! 321:
! 322: if ((stat & PC_PhyDuplexStatus) != 0)
! 323: mii->mii_media_active |= mii_phy_flowstatus(sc) | IFM_FDX;
! 324: else
! 325: mii->mii_media_active |= IFM_HDX;
! 326:
! 327: stat = PHY_READ(sc, IPGPHY_MII_1000SR);
! 328: if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
! 329: stat & IPGPHY_1000SR_MASTER)
! 330: mii->mii_media_active |= IFM_ETH_MASTER;
! 331: }
! 332:
! 333: int
! 334: ipgphy_mii_phy_auto(struct mii_softc *mii)
! 335: {
! 336: uint32_t reg;
! 337:
! 338: reg = IPGPHY_ANAR_10T | IPGPHY_ANAR_10T_FDX |
! 339: IPGPHY_ANAR_100TX | IPGPHY_ANAR_100TX_FDX;
! 340:
! 341: if (sc->mii_flags & MIIF_DOPAUSE)
! 342: reg |= IPGPHY_ANAR_PAUSE | IPGPHY_ANAR_APAUSE;
! 343:
! 344: PHY_WRITE(mii, IPGPHY_MII_ANAR, reg);
! 345: reg = IPGPHY_1000CR_1000T | IPGPHY_1000CR_1000T_FDX;
! 346: reg |= IPGPHY_1000CR_MASTER;
! 347: PHY_WRITE(mii, IPGPHY_MII_1000CR, reg);
! 348: PHY_WRITE(mii, IPGPHY_MII_BMCR, (IPGPHY_BMCR_FDX |
! 349: IPGPHY_BMCR_AUTOEN | IPGPHY_BMCR_STARTNEG));
! 350:
! 351: return (EJUSTRETURN);
! 352: }
! 353:
! 354: void
! 355: ipgphy_load_dspcode(struct mii_softc *sc)
! 356: {
! 357: PHY_WRITE(sc, 31, 0x0001);
! 358: PHY_WRITE(sc, 27, 0x01e0);
! 359: PHY_WRITE(sc, 31, 0x0002);
! 360: PHY_WRITE(sc, 27, 0xeb8e);
! 361: PHY_WRITE(sc, 31, 0x0000);
! 362: PHY_WRITE(sc, 30, 0x005e);
! 363: PHY_WRITE(sc, 9, 0x0700);
! 364:
! 365: DELAY(50);
! 366: }
! 367:
! 368: void
! 369: ipgphy_reset(struct mii_softc *sc)
! 370: {
! 371: struct stge_softc *stge_sc;
! 372: struct ifnet *ifp;
! 373: uint32_t reg;
! 374:
! 375: mii_phy_reset(sc);
! 376:
! 377: /* clear autoneg/full-duplex as we don't want it after reset */
! 378: reg = PHY_READ(sc, IPGPHY_MII_BMCR);
! 379: reg &= ~(IPGPHY_BMCR_AUTOEN | IPGPHY_BMCR_FDX);
! 380: PHY_WRITE(sc, MII_BMCR, reg);
! 381:
! 382: ifp = sc->mii_pdata->mii_ifp;
! 383:
! 384: if (strcmp(ifp->if_xname, "stge") == 0) {
! 385: stge_sc = ifp->if_softc;
! 386: if (stge_sc->sc_rev >= 0x40 && stge_sc->sc_rev <= 0x4e)
! 387: ipgphy_load_dspcode(sc);
! 388: }
! 389: }
CVSweb