Annotation of sys/dev/mii/dcphy.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: dcphy.c,v 1.18 2006/12/27 19:11:08 kettenis Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1997, 1998, 1999
! 5: * Bill Paul <wpaul@ee.columbia.edu>. 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, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 3. All advertising materials mentioning features or use of this software
! 16: * must display the following acknowledgement:
! 17: * This product includes software developed by Bill Paul.
! 18: * 4. Neither the name of the author nor the names of any co-contributors
! 19: * may be used to endorse or promote products derived from this software
! 20: * without specific prior written permission.
! 21: *
! 22: * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
! 23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 25: * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
! 26: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 27: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 28: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 29: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 30: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 31: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
! 32: * THE POSSIBILITY OF SUCH DAMAGE.
! 33: *
! 34: * $FreeBSD: src/sys/dev/mii/dcphy.c,v 1.6 2000/10/05 17:36:14 wpaul Exp $
! 35: */
! 36:
! 37: /*
! 38: * Pseudo-driver for internal NWAY support on DEC 21143 and workalike
! 39: * controllers. Technically we're abusing the miibus code to handle
! 40: * media selection and NWAY support here since there is no MII
! 41: * interface. However the logical operations are roughly the same,
! 42: * and the alternative is to create a fake MII interface in the driver,
! 43: * which is harder to do.
! 44: */
! 45:
! 46: #include <sys/param.h>
! 47: #include <sys/device.h>
! 48: #include <sys/systm.h>
! 49: #include <sys/kernel.h>
! 50: #include <sys/socket.h>
! 51: #include <sys/timeout.h>
! 52: #include <sys/errno.h>
! 53:
! 54: #include <net/if.h>
! 55: #include <net/if_dl.h>
! 56: #include <net/if_types.h>
! 57: #include <net/if_media.h>
! 58: #include <netinet/in.h>
! 59: #include <netinet/if_ether.h>
! 60:
! 61: #include <dev/mii/mii.h>
! 62: #include <dev/mii/miivar.h>
! 63: #include <dev/mii/miidevs.h>
! 64:
! 65: #include <machine/bus.h>
! 66:
! 67: #include <dev/pci/pcivar.h>
! 68:
! 69: #include <dev/ic/dcreg.h>
! 70:
! 71: #define DC_SETBIT(sc, reg, x) \
! 72: CSR_WRITE_4(sc, reg, \
! 73: CSR_READ_4(sc, reg) | x)
! 74:
! 75: #define DC_CLRBIT(sc, reg, x) \
! 76: CSR_WRITE_4(sc, reg, \
! 77: CSR_READ_4(sc, reg) & ~x)
! 78:
! 79: #define MIIF_AUTOTIMEOUT 0x0004
! 80:
! 81: /*
! 82: * This is the subsystem ID for the built-in 21143 ethernet
! 83: * in several Compaq Presario systems. Apparently these are
! 84: * 10Mbps only, so we need to treat them specially.
! 85: */
! 86: #define COMPAQ_PRESARIO_ID 0xb0bb0e11
! 87:
! 88: int dcphy_match(struct device *, void *, void *);
! 89: void dcphy_attach(struct device *, struct device *, void *);
! 90:
! 91: struct cfattach dcphy_ca = {
! 92: sizeof(struct mii_softc), dcphy_match, dcphy_attach, mii_phy_detach,
! 93: mii_phy_activate
! 94: };
! 95:
! 96: struct cfdriver dcphy_cd = {
! 97: NULL, "dcphy", DV_DULL
! 98: };
! 99:
! 100: int dcphy_service(struct mii_softc *, struct mii_data *, int);
! 101: void dcphy_status(struct mii_softc *);
! 102: int dcphy_mii_phy_auto(struct mii_softc *, int);
! 103: void dcphy_reset(struct mii_softc *);
! 104:
! 105: const struct mii_phy_funcs dcphy_funcs = {
! 106: dcphy_service, dcphy_status, dcphy_reset,
! 107: };
! 108:
! 109: int
! 110: dcphy_match(struct device *parent, void *match, void *aux)
! 111: {
! 112: struct mii_attach_args *ma = aux;
! 113:
! 114: /*
! 115: * The dc driver will report the 21143 vendor and device
! 116: * ID to let us know that it wants us to attach.
! 117: */
! 118: if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC &&
! 119: MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC)
! 120: return (10);
! 121:
! 122: return (0);
! 123: }
! 124:
! 125: void
! 126: dcphy_attach(struct device *parent, struct device *self, void *aux)
! 127: {
! 128: struct mii_softc *sc = (struct mii_softc *)self;
! 129: struct mii_attach_args *ma = aux;
! 130: struct mii_data *mii = ma->mii_data;
! 131: struct dc_softc *dc_sc;
! 132:
! 133: printf(": internal PHY\n");
! 134: sc->mii_inst = mii->mii_instance;
! 135: sc->mii_phy = ma->mii_phyno;
! 136: sc->mii_funcs = &dcphy_funcs;
! 137: sc->mii_pdata = mii;
! 138: sc->mii_flags = ma->mii_flags;
! 139: sc->mii_anegticks = 50;
! 140:
! 141: sc->mii_flags |= MIIF_NOISOLATE;
! 142:
! 143: dc_sc = mii->mii_ifp->if_softc;
! 144: CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0);
! 145: CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0);
! 146:
! 147: #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
! 148:
! 149: switch(dc_sc->dc_csid) {
! 150: case COMPAQ_PRESARIO_ID:
! 151: /* Example of how to only allow 10Mbps modes. */
! 152: sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
! 153: break;
! 154: default:
! 155: if (dc_sc->dc_pmode == DC_PMODE_SIA) {
! 156: sc->mii_capabilities =
! 157: BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
! 158: } else {
! 159: ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
! 160: sc->mii_inst), BMCR_LOOP|BMCR_S100);
! 161:
! 162: sc->mii_capabilities =
! 163: BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX|
! 164: BMSR_10TFDX|BMSR_10THDX;
! 165: }
! 166: break;
! 167: }
! 168:
! 169: if (dc_sc->dc_type == DC_TYPE_21145)
! 170: sc->mii_capabilities = BMSR_10THDX;
! 171:
! 172: sc->mii_capabilities &= ma->mii_capmask;
! 173: if (sc->mii_capabilities & BMSR_MEDIAMASK)
! 174: mii_phy_add_media(sc);
! 175: #undef ADD
! 176: }
! 177:
! 178: int
! 179: dcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
! 180: {
! 181: struct dc_softc *dc_sc;
! 182: struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
! 183: int reg;
! 184: u_int32_t mode;
! 185:
! 186: if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
! 187: return (ENXIO);
! 188:
! 189: dc_sc = mii->mii_ifp->if_softc;
! 190:
! 191: switch (cmd) {
! 192: case MII_POLLSTAT:
! 193: /*
! 194: * If we're not polling our PHY instance, just return.
! 195: */
! 196: if (IFM_INST(ife->ifm_media) != sc->mii_inst)
! 197: return (0);
! 198: break;
! 199:
! 200: case MII_MEDIACHG:
! 201: /*
! 202: * If the media indicates a different PHY instance,
! 203: * isolate ourselves.
! 204: */
! 205: if (IFM_INST(ife->ifm_media) != sc->mii_inst)
! 206: return (0);
! 207:
! 208: /*
! 209: * If the interface is not up, don't do anything.
! 210: */
! 211: if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
! 212: break;
! 213:
! 214: sc->mii_flags = 0;
! 215: mii->mii_media_active = IFM_NONE;
! 216: mode = CSR_READ_4(dc_sc, DC_NETCFG);
! 217: mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL|
! 218: DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL);
! 219:
! 220: switch (IFM_SUBTYPE(ife->ifm_media)) {
! 221: case IFM_AUTO:
! 222: /*PHY_RESET(sc);*/
! 223: sc->mii_flags &= ~MIIF_DOINGAUTO;
! 224: (void) dcphy_mii_phy_auto(sc, 0);
! 225: break;
! 226: case IFM_100_TX:
! 227: PHY_RESET(sc);
! 228: DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
! 229: mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS|
! 230: DC_NETCFG_SCRAMBLER;
! 231: if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
! 232: mode |= DC_NETCFG_FULLDUPLEX;
! 233: else
! 234: mode &= ~DC_NETCFG_FULLDUPLEX;
! 235: CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
! 236: break;
! 237: case IFM_10_T:
! 238: DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
! 239: DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF);
! 240: if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
! 241: DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D);
! 242: else
! 243: DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F);
! 244: DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
! 245: DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
! 246: mode &= ~DC_NETCFG_PORTSEL;
! 247: mode |= DC_NETCFG_SPEEDSEL;
! 248: if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
! 249: mode |= DC_NETCFG_FULLDUPLEX;
! 250: else
! 251: mode &= ~DC_NETCFG_FULLDUPLEX;
! 252: CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
! 253: break;
! 254: default:
! 255: return (EINVAL);
! 256: }
! 257: break;
! 258:
! 259: case MII_TICK:
! 260: /*
! 261: * If we're not currently selected, just return.
! 262: */
! 263: if (IFM_INST(ife->ifm_media) != sc->mii_inst)
! 264: return (0);
! 265:
! 266: /*
! 267: * Is the interface even up?
! 268: */
! 269: if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
! 270: return (0);
! 271:
! 272: /*
! 273: * Only used for autonegotiation.
! 274: */
! 275: if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
! 276: break;
! 277:
! 278: reg = CSR_READ_4(dc_sc, DC_10BTSTAT);
! 279: if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))
! 280: break;
! 281:
! 282: /*
! 283: * Only retry autonegotiation every mii_anegticks seconds.
! 284: *
! 285: * Otherwise, fall through to calling dcphy_status()
! 286: * since real Intel 21143 chips don't show valid link
! 287: * status until autonegotiation is switched off, and
! 288: * that only happens in dcphy_status(). Without this,
! 289: * successful autonegotation is never recognised on
! 290: * these chips.
! 291: */
! 292: if (++sc->mii_ticks <= sc->mii_anegticks)
! 293: break;
! 294:
! 295: sc->mii_ticks = 0;
! 296: sc->mii_flags &= ~MIIF_DOINGAUTO;
! 297: dcphy_mii_phy_auto(sc, 0);
! 298:
! 299: break;
! 300: }
! 301:
! 302: /* Update the media status. */
! 303: mii_phy_status(sc);
! 304:
! 305: /* Callback if something changed. */
! 306: mii_phy_update(sc, cmd);
! 307: return (0);
! 308: }
! 309:
! 310: void
! 311: dcphy_status(struct mii_softc *sc)
! 312: {
! 313: struct mii_data *mii = sc->mii_pdata;
! 314: int reg, anlpar, tstat = 0;
! 315: struct dc_softc *dc_sc;
! 316:
! 317: dc_sc = mii->mii_ifp->if_softc;
! 318:
! 319: mii->mii_media_status = IFM_AVALID;
! 320: mii->mii_media_active = IFM_ETHER;
! 321:
! 322: reg = CSR_READ_4(dc_sc, DC_10BTSTAT);
! 323: if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))
! 324: mii->mii_media_status |= IFM_ACTIVE;
! 325:
! 326: if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) {
! 327: /* Erg, still trying, I guess... */
! 328: tstat = CSR_READ_4(dc_sc, DC_10BTSTAT);
! 329: if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) {
! 330: if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) &&
! 331: (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE)
! 332: goto skip;
! 333: mii->mii_media_active |= IFM_NONE;
! 334: return;
! 335: }
! 336:
! 337: if (tstat & DC_TSTAT_LP_CAN_NWAY) {
! 338: anlpar = tstat >> 16;
! 339: if (anlpar & ANLPAR_T4 &&
! 340: sc->mii_capabilities & BMSR_100TXHDX)
! 341: mii->mii_media_active |= IFM_100_T4|IFM_HDX;
! 342: else if (anlpar & ANLPAR_TX_FD &&
! 343: sc->mii_capabilities & BMSR_100TXFDX)
! 344: mii->mii_media_active |= IFM_100_TX|IFM_FDX;
! 345: else if (anlpar & ANLPAR_TX &&
! 346: sc->mii_capabilities & BMSR_100TXHDX)
! 347: mii->mii_media_active |= IFM_100_TX|IFM_HDX;
! 348: else if (anlpar & ANLPAR_10_FD)
! 349: mii->mii_media_active |= IFM_10_T|IFM_FDX;
! 350: else if (anlpar & ANLPAR_10)
! 351: mii->mii_media_active |= IFM_10_T|IFM_HDX;
! 352: else
! 353: mii->mii_media_active |= IFM_NONE;
! 354: if (DC_IS_INTEL(dc_sc))
! 355: DC_CLRBIT(dc_sc, DC_10BTCTRL,
! 356: DC_TCTL_AUTONEGENBL);
! 357: return;
! 358: }
! 359:
! 360: /*
! 361: * If the other side doesn't support NWAY, then the
! 362: * best we can do is determine if we have a 10Mbps or
! 363: * 100Mbps link. There's no way to know if the link
! 364: * is full or half duplex, so we default to half duplex
! 365: * and hope that the user is clever enough to manually
! 366: * change the media settings if we're wrong.
! 367: */
! 368: if (!(reg & DC_TSTAT_LS100))
! 369: mii->mii_media_active |= IFM_100_TX|IFM_HDX;
! 370: else if (!(reg & DC_TSTAT_LS10))
! 371: mii->mii_media_active |= IFM_10_T|IFM_HDX;
! 372: else
! 373: mii->mii_media_active |= IFM_NONE;
! 374: if (DC_IS_INTEL(dc_sc))
! 375: DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
! 376: return;
! 377: }
! 378:
! 379: skip:
! 380: if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL)
! 381: mii->mii_media_active |= IFM_10_T;
! 382: else
! 383: mii->mii_media_active |= IFM_100_TX;
! 384:
! 385: if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX)
! 386: mii->mii_media_active |= IFM_FDX;
! 387: else
! 388: mii->mii_media_active |= IFM_HDX;
! 389: }
! 390:
! 391: int
! 392: dcphy_mii_phy_auto(struct mii_softc *mii, int waitfor)
! 393: {
! 394: int i;
! 395: struct dc_softc *sc;
! 396:
! 397: sc = mii->mii_pdata->mii_ifp->if_softc;
! 398:
! 399: if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
! 400: DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
! 401: DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
! 402: DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
! 403: if (mii->mii_capabilities & BMSR_100TXHDX)
! 404: CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF);
! 405: else
! 406: CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF);
! 407: DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
! 408: DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
! 409: DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE);
! 410: }
! 411:
! 412: if (waitfor) {
! 413: /* Wait 500ms for it to complete. */
! 414: for (i = 0; i < 500; i++) {
! 415: if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT)
! 416: == DC_ASTAT_AUTONEGCMP)
! 417: return (0);
! 418: DELAY(1000);
! 419: }
! 420: /*
! 421: * Don't need to worry about clearing MIIF_DOINGAUTO.
! 422: * If that's set, a timeout is pending, and it will
! 423: * clear the flag.
! 424: */
! 425: return (EIO);
! 426: }
! 427:
! 428: /*
! 429: * Just let it finish asynchronously. This is for the benefit of
! 430: * the tick handler driving autonegotiation. Don't want 500ms
! 431: * delays all the time while the system is running!
! 432: */
! 433: if ((mii->mii_flags & MIIF_DOINGAUTO) == 0)
! 434: mii->mii_flags |= MIIF_DOINGAUTO;
! 435:
! 436: return (EJUSTRETURN);
! 437: }
! 438:
! 439: void
! 440: dcphy_reset(struct mii_softc *mii)
! 441: {
! 442: struct dc_softc *sc;
! 443:
! 444: sc = mii->mii_pdata->mii_ifp->if_softc;
! 445:
! 446: DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
! 447: DELAY(1000);
! 448: DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
! 449:
! 450: return;
! 451: }
CVSweb