Annotation of sys/dev/ic/smc91cxx.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: smc91cxx.c,v 1.26 2006/06/23 06:27:11 miod Exp $ */
! 2: /* $NetBSD: smc91cxx.c,v 1.11 1998/08/08 23:51:41 mycroft Exp $ */
! 3:
! 4: /*-
! 5: * Copyright (c) 1997 The NetBSD Foundation, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to The NetBSD Foundation
! 9: * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
! 10: * NASA Ames Research Center.
! 11: *
! 12: * Redistribution and use in source and binary forms, with or without
! 13: * modification, are permitted provided that the following conditions
! 14: * are met:
! 15: * 1. Redistributions of source code must retain the above copyright
! 16: * notice, this list of conditions and the following disclaimer.
! 17: * 2. Redistributions in binary form must reproduce the above copyright
! 18: * notice, this list of conditions and the following disclaimer in the
! 19: * documentation and/or other materials provided with the distribution.
! 20: * 3. All advertising materials mentioning features or use of this software
! 21: * must display the following acknowledgement:
! 22: * This product includes software developed by the NetBSD
! 23: * Foundation, Inc. and its contributors.
! 24: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 25: * contributors may be used to endorse or promote products derived
! 26: * from this software without specific prior written permission.
! 27: *
! 28: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 29: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 30: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 31: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 32: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 33: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 34: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 35: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 36: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 37: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 38: * POSSIBILITY OF SUCH DAMAGE.
! 39: */
! 40:
! 41: /*
! 42: * Copyright (c) 1996 Gardner Buchanan <gbuchanan@shl.com>
! 43: * All rights reserved.
! 44: *
! 45: * Redistribution and use in source and binary forms, with or without
! 46: * modification, are permitted provided that the following conditions
! 47: * are met:
! 48: * 1. Redistributions of source code must retain the above copyright
! 49: * notice, this list of conditions and the following disclaimer.
! 50: * 2. Redistributions in binary form must reproduce the above copyright
! 51: * notice, this list of conditions and the following disclaimer in the
! 52: * documentation and/or other materials provided with the distribution.
! 53: * 3. All advertising materials mentioning features or use of this software
! 54: * must display the following acknowledgement:
! 55: * This product includes software developed by Gardner Buchanan.
! 56: * 4. The name of Gardner Buchanan may not be used to endorse or promote
! 57: * products derived from this software without specific prior written
! 58: * permission.
! 59: *
! 60: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 61: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 62: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 63: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 64: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 65: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 66: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 67: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 68: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 69: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 70: *
! 71: * from FreeBSD Id: if_sn.c,v 1.4 1996/03/18 15:47:16 gardner Exp
! 72: */
! 73:
! 74: /*
! 75: * Core driver for the SMC 91Cxx family of Ethernet chips.
! 76: *
! 77: * Memory allocation interrupt logic is drived from an SMC 91C90 driver
! 78: * written for NetBSD/amiga by Michael Hitch.
! 79: */
! 80:
! 81: #include "bpfilter.h"
! 82:
! 83: #include <sys/param.h>
! 84: #include <sys/systm.h>
! 85: #include <sys/mbuf.h>
! 86: #include <sys/syslog.h>
! 87: #include <sys/socket.h>
! 88: #include <sys/device.h>
! 89: #include <sys/timeout.h>
! 90: #include <sys/kernel.h>
! 91: #include <sys/malloc.h>
! 92: #include <sys/ioctl.h>
! 93: #include <sys/errno.h>
! 94:
! 95: #include <machine/bus.h>
! 96: #include <machine/intr.h>
! 97:
! 98: #include <net/if.h>
! 99: #include <net/if_dl.h>
! 100: #include <net/if_media.h>
! 101:
! 102: #ifdef INET
! 103: #include <netinet/in.h>
! 104: #include <netinet/if_ether.h>
! 105: #include <netinet/in_systm.h>
! 106: #include <netinet/in_var.h>
! 107: #include <netinet/ip.h>
! 108: #endif
! 109:
! 110: #if NBPFILTER > 0
! 111: #include <net/bpf.h>
! 112: #endif
! 113:
! 114: #include <dev/mii/mii.h>
! 115: #include <dev/mii/miivar.h>
! 116: #include <dev/mii/mii_bitbang.h>
! 117:
! 118: #include <dev/ic/smc91cxxreg.h>
! 119: #include <dev/ic/smc91cxxvar.h>
! 120:
! 121: #ifndef __BUS_SPACE_HAS_STREAM_METHODS
! 122: #define bus_space_write_multi_stream_2 bus_space_write_multi_2
! 123: #define bus_space_write_multi_stream_4 bus_space_write_multi_4
! 124: #define bus_space_read_multi_stream_2 bus_space_read_multi_2
! 125: #define bus_space_read_multi_stream_4 bus_space_read_multi_4
! 126: #endif /* __BUS_SPACE_HAS_STREAM_METHODS */
! 127:
! 128: /* XXX Hardware padding doesn't work yet(?) */
! 129: #define SMC91CXX_SW_PAD
! 130:
! 131: const char *smc91cxx_idstrs[] = {
! 132: NULL, /* 0 */
! 133: NULL, /* 1 */
! 134: NULL, /* 2 */
! 135: "SMC91C90/91C92", /* 3 */
! 136: "SMC91C94/91C96", /* 4 */
! 137: "SMC91C95", /* 5 */
! 138: NULL, /* 6 */
! 139: "SMC91C100", /* 7 */
! 140: "SMC91C100FD", /* 8 */
! 141: NULL, /* 9 */
! 142: NULL, /* 10 */
! 143: NULL, /* 11 */
! 144: NULL, /* 12 */
! 145: NULL, /* 13 */
! 146: NULL, /* 14 */
! 147: NULL, /* 15 */
! 148: };
! 149:
! 150: /* Supported media types. */
! 151: const int smc91cxx_media[] = {
! 152: IFM_ETHER|IFM_10_T,
! 153: IFM_ETHER|IFM_10_5,
! 154: };
! 155: #define NSMC91CxxMEDIA (sizeof(smc91cxx_media) / sizeof(smc91cxx_media[0]))
! 156:
! 157: /*
! 158: * MII bit-bang glue.
! 159: */
! 160: u_int32_t smc91cxx_mii_bitbang_read(struct device *);
! 161: void smc91cxx_mii_bitbang_write(struct device *, u_int32_t);
! 162:
! 163: const struct mii_bitbang_ops smc91cxx_mii_bitbang_ops = {
! 164: smc91cxx_mii_bitbang_read,
! 165: smc91cxx_mii_bitbang_write,
! 166: {
! 167: MR_MDO, /* MII_BIT_MDO */
! 168: MR_MDI, /* MII_BIT_MDI */
! 169: MR_MCLK, /* MII_BIT_MDC */
! 170: MR_MDOE, /* MII_BIT_DIR_HOST_PHY */
! 171: 0, /* MII_BIT_DIR_PHY_HOST */
! 172: }
! 173: };
! 174:
! 175: struct cfdriver sm_cd = {
! 176: NULL, "sm", DV_IFNET
! 177: };
! 178:
! 179: /* MII callbacks */
! 180: int smc91cxx_mii_readreg(struct device *, int, int);
! 181: void smc91cxx_mii_writereg(struct device *, int, int, int);
! 182: void smc91cxx_statchg(struct device *);
! 183: void smc91cxx_tick(void *);
! 184:
! 185: int smc91cxx_mediachange(struct ifnet *);
! 186: void smc91cxx_mediastatus(struct ifnet *, struct ifmediareq *);
! 187:
! 188: int smc91cxx_set_media(struct smc91cxx_softc *, int);
! 189:
! 190: void smc91cxx_read(struct smc91cxx_softc *);
! 191: void smc91cxx_reset(struct smc91cxx_softc *);
! 192: void smc91cxx_start(struct ifnet *);
! 193: void smc91cxx_resume(struct smc91cxx_softc *);
! 194: void smc91cxx_watchdog(struct ifnet *);
! 195: int smc91cxx_ioctl(struct ifnet *, u_long, caddr_t);
! 196:
! 197: static __inline int ether_cmp(void *, void *);
! 198: static __inline int
! 199: ether_cmp(va, vb)
! 200: void *va, *vb;
! 201: {
! 202: u_int8_t *a = va;
! 203: u_int8_t *b = vb;
! 204:
! 205: return ((a[5] != b[5]) || (a[4] != b[4]) || (a[3] != b[3]) ||
! 206: (a[2] != b[2]) || (a[1] != b[1]) || (a[0] != b[0]));
! 207: }
! 208:
! 209: void
! 210: smc91cxx_attach(sc, myea)
! 211: struct smc91cxx_softc *sc;
! 212: u_int8_t *myea;
! 213: {
! 214: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 215: bus_space_tag_t bst = sc->sc_bst;
! 216: bus_space_handle_t bsh = sc->sc_bsh;
! 217: struct ifmedia *ifm = &sc->sc_mii.mii_media;
! 218: u_int32_t miicapabilities;
! 219: u_int16_t tmp;
! 220: int i, aui;
! 221: const char *idstr;
! 222:
! 223: /* Make sure the chip is stopped. */
! 224: smc91cxx_stop(sc);
! 225:
! 226: SMC_SELECT_BANK(sc, 3);
! 227: tmp = bus_space_read_2(bst, bsh, REVISION_REG_W);
! 228: sc->sc_chipid = RR_ID(tmp);
! 229: /* check magic number */
! 230: if ((tmp & BSR_DETECT_MASK) != BSR_DETECT_VALUE) {
! 231: idstr = NULL;
! 232: printf("%s: invalid BSR 0x%04x\n", sc->sc_dev.dv_xname, tmp);
! 233: } else
! 234: idstr = smc91cxx_idstrs[RR_ID(tmp)];
! 235: #ifdef SMC_DEBUG
! 236: printf("\n%s: ", sc->sc_dev.dv_xname);
! 237: if (idstr != NULL)
! 238: printf("%s, ", idstr);
! 239: else
! 240: printf("unknown chip id %d, ", sc->sc_chipid);
! 241: printf("revision %d", RR_REV(tmp));
! 242: #endif
! 243:
! 244: /* Read the station address from the chip. */
! 245: SMC_SELECT_BANK(sc, 1);
! 246: if (myea == NULL) {
! 247: for (i = 0; i < ETHER_ADDR_LEN; i += 2) {
! 248: tmp = bus_space_read_2(bst, bsh, IAR_ADDR0_REG_W + i);
! 249: sc->sc_arpcom.ac_enaddr[i + 1] = (tmp >>8) & 0xff;
! 250: sc->sc_arpcom.ac_enaddr[i] = tmp & 0xff;
! 251: }
! 252: } else {
! 253: bcopy(myea, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
! 254: }
! 255:
! 256: printf(": address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr));
! 257:
! 258: /* Initialize the ifnet structure. */
! 259: bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
! 260: ifp->if_softc = sc;
! 261: ifp->if_start = smc91cxx_start;
! 262: ifp->if_ioctl = smc91cxx_ioctl;
! 263: ifp->if_watchdog = smc91cxx_watchdog;
! 264: ifp->if_flags =
! 265: IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
! 266: IFQ_SET_READY(&ifp->if_snd);
! 267:
! 268: /* Attach the interface. */
! 269: if_attach(ifp);
! 270: ether_ifattach(ifp);
! 271:
! 272: /*
! 273: * Initialize our media structures and MII info. We will
! 274: * probe the MII if we are on the SMC91Cxx
! 275: */
! 276: sc->sc_mii.mii_ifp = ifp;
! 277: sc->sc_mii.mii_readreg = smc91cxx_mii_readreg;
! 278: sc->sc_mii.mii_writereg = smc91cxx_mii_writereg;
! 279: sc->sc_mii.mii_statchg = smc91cxx_statchg;
! 280: ifmedia_init(ifm, 0, smc91cxx_mediachange, smc91cxx_mediastatus);
! 281:
! 282: SMC_SELECT_BANK(sc, 1);
! 283: tmp = bus_space_read_2(bst, bsh, CONFIG_REG_W);
! 284:
! 285: miicapabilities = BMSR_MEDIAMASK|BMSR_ANEG;
! 286: switch (sc->sc_chipid) {
! 287: case CHIP_91100:
! 288: /*
! 289: * The 91100 does not have full-duplex capabilities,
! 290: * even if the PHY does.
! 291: */
! 292: miicapabilities &= ~(BMSR_100TXFDX | BMSR_10TFDX);
! 293: case CHIP_91100FD:
! 294: if (tmp & CR_MII_SELECT) {
! 295: #ifdef SMC_DEBUG
! 296: printf("%s: default media MII\n",
! 297: sc->sc_dev.dv_xname);
! 298: #endif
! 299: mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff,
! 300: MII_PHY_ANY, MII_OFFSET_ANY, 0);
! 301: if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
! 302: ifmedia_add(&sc->sc_mii.mii_media,
! 303: IFM_ETHER|IFM_NONE, 0, NULL);
! 304: ifmedia_set(&sc->sc_mii.mii_media,
! 305: IFM_ETHER|IFM_NONE);
! 306: } else {
! 307: ifmedia_set(&sc->sc_mii.mii_media,
! 308: IFM_ETHER|IFM_AUTO);
! 309: }
! 310: sc->sc_flags |= SMC_FLAGS_HAS_MII;
! 311: break;
! 312: }
! 313: /*FALLTHROUGH*/
! 314: default:
! 315: aui = tmp & CR_AUI_SELECT;
! 316: #ifdef SMC_DEBUG
! 317: printf("%s: default media %s\n", sc->sc_dev.dv_xname,
! 318: aui ? "AUI" : "UTP");
! 319: #endif
! 320: for (i = 0; i < NSMC91CxxMEDIA; i++)
! 321: ifmedia_add(ifm, smc91cxx_media[i], 0, NULL);
! 322: ifmedia_set(ifm, IFM_ETHER | (aui ? IFM_10_5 : IFM_10_T));
! 323: break;
! 324: }
! 325:
! 326: /* The attach is successful. */
! 327: sc->sc_flags |= SMC_FLAGS_ATTACHED;
! 328: }
! 329:
! 330: /*
! 331: * Change media according to request.
! 332: */
! 333: int
! 334: smc91cxx_mediachange(ifp)
! 335: struct ifnet *ifp;
! 336: {
! 337: struct smc91cxx_softc *sc = ifp->if_softc;
! 338:
! 339: return (smc91cxx_set_media(sc, sc->sc_mii.mii_media.ifm_media));
! 340: }
! 341:
! 342: int
! 343: smc91cxx_set_media(sc, media)
! 344: struct smc91cxx_softc *sc;
! 345: int media;
! 346: {
! 347: bus_space_tag_t bst = sc->sc_bst;
! 348: bus_space_handle_t bsh = sc->sc_bsh;
! 349: u_int16_t tmp;
! 350:
! 351: /*
! 352: * If the interface is not currently powered on, just return.
! 353: * When it is enabled later, smc91cxx_init() will properly set
! 354: * up the media for us.
! 355: */
! 356: if ((sc->sc_flags & SMC_FLAGS_ENABLED) == 0)
! 357: return (0);
! 358:
! 359: if (IFM_TYPE(media) != IFM_ETHER)
! 360: return (EINVAL);
! 361:
! 362: if (sc->sc_flags & SMC_FLAGS_HAS_MII)
! 363: return (mii_mediachg(&sc->sc_mii));
! 364:
! 365: switch (IFM_SUBTYPE(media)) {
! 366: case IFM_10_T:
! 367: case IFM_10_5:
! 368: SMC_SELECT_BANK(sc, 1);
! 369: tmp = bus_space_read_2(bst, bsh, CONFIG_REG_W);
! 370: if (IFM_SUBTYPE(media) == IFM_10_5)
! 371: tmp |= CR_AUI_SELECT;
! 372: else
! 373: tmp &= ~CR_AUI_SELECT;
! 374: bus_space_write_2(bst, bsh, CONFIG_REG_W, tmp);
! 375: delay(20000); /* XXX is this needed? */
! 376: break;
! 377:
! 378: default:
! 379: return (EINVAL);
! 380: }
! 381:
! 382: return (0);
! 383: }
! 384:
! 385: /*
! 386: * Notify the world which media we're using.
! 387: */
! 388: void
! 389: smc91cxx_mediastatus(ifp, ifmr)
! 390: struct ifnet *ifp;
! 391: struct ifmediareq *ifmr;
! 392: {
! 393: struct smc91cxx_softc *sc = ifp->if_softc;
! 394: bus_space_tag_t bst = sc->sc_bst;
! 395: bus_space_handle_t bsh = sc->sc_bsh;
! 396: u_int16_t tmp;
! 397:
! 398: if ((sc->sc_flags & SMC_FLAGS_ENABLED) == 0) {
! 399: ifmr->ifm_active = IFM_ETHER | IFM_NONE;
! 400: ifmr->ifm_status = 0;
! 401: return;
! 402: }
! 403:
! 404: /*
! 405: * If we have MII, go ask the PHY what's going on.
! 406: */
! 407: if (sc->sc_flags & SMC_FLAGS_HAS_MII) {
! 408: mii_pollstat(&sc->sc_mii);
! 409: ifmr->ifm_active = sc->sc_mii.mii_media_active;
! 410: ifmr->ifm_status = sc->sc_mii.mii_media_status;
! 411: return;
! 412: }
! 413:
! 414: SMC_SELECT_BANK(sc, 1);
! 415: tmp = bus_space_read_2(bst, bsh, CONFIG_REG_W);
! 416: ifmr->ifm_active =
! 417: IFM_ETHER | ((tmp & CR_AUI_SELECT) ? IFM_10_5 : IFM_10_T);
! 418: }
! 419:
! 420: /*
! 421: * Reset and initialize the chip.
! 422: */
! 423: void
! 424: smc91cxx_init(sc)
! 425: struct smc91cxx_softc *sc;
! 426: {
! 427: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 428: bus_space_tag_t bst = sc->sc_bst;
! 429: bus_space_handle_t bsh = sc->sc_bsh;
! 430: u_int16_t tmp;
! 431: int s, i;
! 432:
! 433: s = splnet();
! 434:
! 435: /*
! 436: * This resets the registers mostly to defaults, but doesn't
! 437: * affect the EEPROM. After the reset cycle, we pause briefly
! 438: * for the chip to recover.
! 439: *
! 440: * XXX how long are we really supposed to delay? --thorpej
! 441: */
! 442: SMC_SELECT_BANK(sc, 0);
! 443: bus_space_write_2(bst, bsh, RECV_CONTROL_REG_W, RCR_SOFTRESET);
! 444: delay(100);
! 445: bus_space_write_2(bst, bsh, RECV_CONTROL_REG_W, 0);
! 446: delay(200);
! 447:
! 448: bus_space_write_2(bst, bsh, TXMIT_CONTROL_REG_W, 0);
! 449:
! 450: /* Set the Ethernet address. */
! 451: SMC_SELECT_BANK(sc, 1);
! 452: for (i = 0; i < ETHER_ADDR_LEN; i++ )
! 453: bus_space_write_1(bst, bsh, IAR_ADDR0_REG_W + i,
! 454: sc->sc_arpcom.ac_enaddr[i]);
! 455:
! 456: /*
! 457: * Set the control register to automatically release successfully
! 458: * transmitted packets (making the best use of our limited memory)
! 459: * and enable the EPH interrupt on certain TX errors.
! 460: */
! 461: bus_space_write_2(bst, bsh, CONTROL_REG_W, (CTR_AUTO_RELEASE |
! 462: CTR_TE_ENABLE | CTR_CR_ENABLE | CTR_LE_ENABLE));
! 463:
! 464: /*
! 465: * Reset the MMU and wait for it to be un-busy.
! 466: */
! 467: SMC_SELECT_BANK(sc, 2);
! 468: bus_space_write_2(bst, bsh, MMU_CMD_REG_W, MMUCR_RESET);
! 469: while (bus_space_read_2(bst, bsh, MMU_CMD_REG_W) & MMUCR_BUSY)
! 470: /* XXX bound this loop! */ ;
! 471:
! 472: /*
! 473: * Disable all interrupts.
! 474: */
! 475: bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
! 476:
! 477: /*
! 478: * Set current media.
! 479: */
! 480: smc91cxx_set_media(sc, sc->sc_mii.mii_media.ifm_cur->ifm_media);
! 481:
! 482: /*
! 483: * Set the receive filter. We want receive enable and auto
! 484: * strip of CRC from received packet. If we are in promisc. mode,
! 485: * then set that bit as well.
! 486: *
! 487: * XXX Initialize multicast filter. For now, we just accept
! 488: * XXX all multicast.
! 489: */
! 490: SMC_SELECT_BANK(sc, 0);
! 491:
! 492: tmp = RCR_ENABLE | RCR_STRIP_CRC | RCR_ALMUL;
! 493: if (ifp->if_flags & IFF_PROMISC)
! 494: tmp |= RCR_PROMISC;
! 495:
! 496: bus_space_write_2(bst, bsh, RECV_CONTROL_REG_W, tmp);
! 497:
! 498: /*
! 499: * Set transmitter control to "enabled".
! 500: */
! 501: tmp = TCR_ENABLE;
! 502:
! 503: #ifndef SMC91CXX_SW_PAD
! 504: /*
! 505: * Enable hardware padding of transmitted packets.
! 506: * XXX doesn't work?
! 507: */
! 508: tmp |= TCR_PAD_ENABLE;
! 509: #endif
! 510:
! 511: bus_space_write_2(bst, bsh, TXMIT_CONTROL_REG_W, tmp);
! 512:
! 513: /*
! 514: * Now, enable interrupts.
! 515: */
! 516: SMC_SELECT_BANK(sc, 2);
! 517:
! 518: bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
! 519: IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | IM_TX_INT);
! 520:
! 521: /* Interface is now running, with no output active. */
! 522: ifp->if_flags |= IFF_RUNNING;
! 523: ifp->if_flags &= ~IFF_OACTIVE;
! 524:
! 525: if (sc->sc_flags & SMC_FLAGS_HAS_MII) {
! 526: /* Start the one second clock. */
! 527: timeout_set(&sc->sc_mii_timeout, smc91cxx_tick, sc);
! 528: timeout_add(&sc->sc_mii_timeout, hz);
! 529: }
! 530:
! 531: /*
! 532: * Attempt to start any pending transmission.
! 533: */
! 534: smc91cxx_start(ifp);
! 535:
! 536: splx(s);
! 537: }
! 538:
! 539: /*
! 540: * Start output on an interface.
! 541: * Must be called at splnet or interrupt level.
! 542: */
! 543: void
! 544: smc91cxx_start(ifp)
! 545: struct ifnet *ifp;
! 546: {
! 547: struct smc91cxx_softc *sc = ifp->if_softc;
! 548: bus_space_tag_t bst = sc->sc_bst;
! 549: bus_space_handle_t bsh = sc->sc_bsh;
! 550: u_int len;
! 551: struct mbuf *m, *top;
! 552: u_int16_t length, npages;
! 553: u_int8_t packetno;
! 554: int timo, pad;
! 555:
! 556: if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
! 557: return;
! 558:
! 559: again:
! 560: /*
! 561: * Peek at the next packet.
! 562: */
! 563: IFQ_POLL(&ifp->if_snd, m);
! 564: if (m == NULL)
! 565: return;
! 566:
! 567: /*
! 568: * Compute the frame length and set pad to give an overall even
! 569: * number of bytes. Below, we assume that the packet length
! 570: * is even.
! 571: */
! 572: for (len = 0, top = m; m != NULL; m = m->m_next)
! 573: len += m->m_len;
! 574: pad = (len & 1);
! 575:
! 576: /*
! 577: * We drop packets that are too large. Perhaps we should
! 578: * truncate them instead?
! 579: */
! 580: if ((len + pad) > (ETHER_MAX_LEN - ETHER_CRC_LEN)) {
! 581: printf("%s: large packet discarded\n", sc->sc_dev.dv_xname);
! 582: ifp->if_oerrors++;
! 583: IFQ_DEQUEUE(&ifp->if_snd, m);
! 584: m_freem(m);
! 585: goto readcheck;
! 586: }
! 587:
! 588: #ifdef SMC91CXX_SW_PAD
! 589: /*
! 590: * Not using hardware padding; pad to ETHER_MIN_LEN.
! 591: */
! 592: if (len < (ETHER_MIN_LEN - ETHER_CRC_LEN))
! 593: pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len;
! 594: #endif
! 595:
! 596: length = pad + len;
! 597:
! 598: /*
! 599: * The MMU has a 256 byte page size. The MMU expects us to
! 600: * ask for "npages - 1". We include space for the status word,
! 601: * byte count, and control bytes in the allocation request.
! 602: */
! 603: npages = (length + 6) >> 8;
! 604:
! 605: /*
! 606: * Now allocate the memory.
! 607: */
! 608: SMC_SELECT_BANK(sc, 2);
! 609: bus_space_write_2(bst, bsh, MMU_CMD_REG_W, MMUCR_ALLOC | npages);
! 610:
! 611: timo = MEMORY_WAIT_TIME;
! 612: do {
! 613: if (bus_space_read_1(bst, bsh, INTR_STAT_REG_B) & IM_ALLOC_INT)
! 614: break;
! 615: delay(1);
! 616: } while (--timo);
! 617:
! 618: packetno = bus_space_read_1(bst, bsh, ALLOC_RESULT_REG_B);
! 619:
! 620: if (packetno & ARR_FAILED || timo == 0) {
! 621: /*
! 622: * No transmit memory is available. Record the number
! 623: * of requestd pages and enable the allocation completion
! 624: * interrupt. Set up the watchdog timer in case we miss
! 625: * the interrupt. Mark the interface as active so that
! 626: * no one else attempts to transmit while we're allocating
! 627: * memory.
! 628: */
! 629: bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
! 630: bus_space_read_1(bst, bsh, INTR_MASK_REG_B) | IM_ALLOC_INT);
! 631:
! 632: ifp->if_timer = 5;
! 633: ifp->if_flags |= IFF_OACTIVE;
! 634:
! 635: return;
! 636: }
! 637:
! 638: /*
! 639: * We have a packet number - set the data window.
! 640: */
! 641: bus_space_write_1(bst, bsh, PACKET_NUM_REG_B, packetno);
! 642:
! 643: /*
! 644: * Point to the beginning of the packet.
! 645: */
! 646: bus_space_write_2(bst, bsh, POINTER_REG_W, PTR_AUTOINC /* | 0x0000 */);
! 647:
! 648: /*
! 649: * Send the packet length (+6 for stats, length, and control bytes)
! 650: * and the status word (set to zeros).
! 651: */
! 652: bus_space_write_2(bst, bsh, DATA_REG_W, 0);
! 653: bus_space_write_1(bst, bsh, DATA_REG_B, (length + 6) & 0xff);
! 654: bus_space_write_1(bst, bsh, DATA_REG_B, ((length + 6) >> 8) & 0xff);
! 655:
! 656: /*
! 657: * Get the packet from the kernel. This will include the Ethernet
! 658: * frame header, MAC address, etc.
! 659: */
! 660: IFQ_DEQUEUE(&ifp->if_snd, m);
! 661:
! 662: /*
! 663: * Push the packet out to the card.
! 664: */
! 665: for (top = m; m != NULL; m = m->m_next) {
! 666: /* Words... */
! 667: if (m->m_len > 1)
! 668: bus_space_write_multi_stream_2(bst, bsh, DATA_REG_W,
! 669: mtod(m, u_int16_t *), m->m_len >> 1);
! 670:
! 671: /* ...and the remaining byte, if any. */
! 672: if (m->m_len & 1)
! 673: bus_space_write_1(bst, bsh, DATA_REG_B,
! 674: *(u_int8_t *)(mtod(m, u_int8_t *) + (m->m_len - 1)));
! 675: }
! 676:
! 677: #ifdef SMC91CXX_SW_PAD
! 678: /*
! 679: * Push out padding.
! 680: */
! 681: while (pad > 1) {
! 682: bus_space_write_2(bst, bsh, DATA_REG_W, 0);
! 683: pad -= 2;
! 684: }
! 685: if (pad)
! 686: bus_space_write_1(bst, bsh, DATA_REG_B, 0);
! 687: #endif
! 688:
! 689: /*
! 690: * Push out control byte and unused packet byte. The control byte
! 691: * is 0, meaning the packet is even lengthed and no special
! 692: * CRC handling is necessary.
! 693: */
! 694: bus_space_write_2(bst, bsh, DATA_REG_W, 0);
! 695:
! 696: /*
! 697: * Enable transmit interrupts and let the chip go. Set a watchdog
! 698: * in case we miss the interrupt.
! 699: */
! 700: bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
! 701: bus_space_read_1(bst, bsh, INTR_MASK_REG_B) |
! 702: IM_TX_INT | IM_TX_EMPTY_INT);
! 703:
! 704: bus_space_write_2(bst, bsh, MMU_CMD_REG_W, MMUCR_ENQUEUE);
! 705:
! 706: ifp->if_timer = 5;
! 707:
! 708: #if NBPFILTER > 0
! 709: /* Hand off a copy to the bpf. */
! 710: if (ifp->if_bpf)
! 711: bpf_mtap(ifp->if_bpf, top, BPF_DIRECTION_OUT);
! 712: #endif
! 713:
! 714: ifp->if_opackets++;
! 715: m_freem(top);
! 716:
! 717: readcheck:
! 718: /*
! 719: * Check for incoming pcakets. We don't want to overflow the small
! 720: * RX FIFO. If nothing has arrived, attempt to queue another
! 721: * transmit packet.
! 722: */
! 723: if (bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W) & FIFO_REMPTY)
! 724: goto again;
! 725: }
! 726:
! 727: /*
! 728: * Interrupt service routine.
! 729: */
! 730: int
! 731: smc91cxx_intr(arg)
! 732: void *arg;
! 733: {
! 734: struct smc91cxx_softc *sc = arg;
! 735: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 736: bus_space_tag_t bst = sc->sc_bst;
! 737: bus_space_handle_t bsh = sc->sc_bsh;
! 738: u_int8_t mask, interrupts, status;
! 739: u_int16_t packetno, tx_status, card_stats;
! 740:
! 741: if ((sc->sc_flags & SMC_FLAGS_ENABLED) == 0 ||
! 742: (sc->sc_dev.dv_flags & DVF_ACTIVE) == 0)
! 743: return (0);
! 744:
! 745: SMC_SELECT_BANK(sc, 2);
! 746:
! 747: /*
! 748: * Obtain the current interrupt mask.
! 749: */
! 750: mask = bus_space_read_1(bst, bsh, INTR_MASK_REG_B);
! 751:
! 752: /*
! 753: * Get the set of interrupt which occurred and eliminate any
! 754: * which are not enabled.
! 755: */
! 756: interrupts = bus_space_read_1(bst, bsh, INTR_STAT_REG_B);
! 757: status = interrupts & mask;
! 758:
! 759: /* Ours? */
! 760: if (status == 0)
! 761: return (0);
! 762:
! 763: /*
! 764: * It's ours; disable all interrupts while we process them.
! 765: */
! 766: bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
! 767:
! 768: /*
! 769: * Receive overrun interrupts.
! 770: */
! 771: if (status & IM_RX_OVRN_INT) {
! 772: bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_RX_OVRN_INT);
! 773: ifp->if_ierrors++;
! 774: }
! 775:
! 776: /*
! 777: * Receive interrupts.
! 778: */
! 779: if (status & IM_RCV_INT) {
! 780: #if 1 /* DIAGNOSTIC */
! 781: packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W);
! 782: if (packetno & FIFO_REMPTY) {
! 783: printf("%s: receive interrupt on empty fifo\n",
! 784: sc->sc_dev.dv_xname);
! 785: goto out;
! 786: } else
! 787: #endif
! 788: smc91cxx_read(sc);
! 789: }
! 790:
! 791: /*
! 792: * Memory allocation interrupts.
! 793: */
! 794: if (status & IM_ALLOC_INT) {
! 795: /* Disable this interrupt. */
! 796: mask &= ~IM_ALLOC_INT;
! 797:
! 798: /*
! 799: * Release the just-allocated memory. We will reallocate
! 800: * it through the normal start logic.
! 801: */
! 802: while (bus_space_read_2(bst, bsh, MMU_CMD_REG_W) & MMUCR_BUSY)
! 803: /* XXX bound this loop! */ ;
! 804: bus_space_write_2(bst, bsh, MMU_CMD_REG_W, MMUCR_FREEPKT);
! 805:
! 806: ifp->if_flags &= ~IFF_OACTIVE;
! 807: ifp->if_timer = 0;
! 808: }
! 809:
! 810: /*
! 811: * Transmit complete interrupt. Handle transmission error messages.
! 812: * This will only be called on error condition because of AUTO RELEASE
! 813: * mode.
! 814: */
! 815: if (status & IM_TX_INT) {
! 816: bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_TX_INT);
! 817:
! 818: packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W) &
! 819: FIFO_TX_MASK;
! 820:
! 821: /*
! 822: * Select this as the packet to read from.
! 823: */
! 824: bus_space_write_1(bst, bsh, PACKET_NUM_REG_B, packetno);
! 825:
! 826: /*
! 827: * Position the pointer to the beginning of the packet.
! 828: */
! 829: bus_space_write_2(bst, bsh, POINTER_REG_W,
! 830: PTR_AUTOINC | PTR_READ /* | 0x0000 */);
! 831:
! 832: /*
! 833: * Fetch the TX status word. This will be a copy of
! 834: * the EPH_STATUS_REG_W at the time of the transmission
! 835: * failure.
! 836: */
! 837: tx_status = bus_space_read_2(bst, bsh, DATA_REG_W);
! 838:
! 839: if (tx_status & EPHSR_TX_SUC)
! 840: printf("%s: successful packet caused TX interrupt?!\n",
! 841: sc->sc_dev.dv_xname);
! 842: else
! 843: ifp->if_oerrors++;
! 844:
! 845: if (tx_status & EPHSR_LATCOL)
! 846: ifp->if_collisions++;
! 847:
! 848: /*
! 849: * Some of these errors disable the transmitter; reenable it.
! 850: */
! 851: SMC_SELECT_BANK(sc, 0);
! 852: #ifdef SMC91CXX_SW_PAD
! 853: bus_space_write_2(bst, bsh, TXMIT_CONTROL_REG_W, TCR_ENABLE);
! 854: #else
! 855: bus_space_write_2(bst, bsh, TXMIT_CONTROL_REG_W,
! 856: TCR_ENABLE | TCR_PAD_ENABLE);
! 857: #endif
! 858:
! 859: /*
! 860: * Kill the failed packet and wait for the MMU to unbusy.
! 861: */
! 862: SMC_SELECT_BANK(sc, 2);
! 863: while (bus_space_read_2(bst, bsh, MMU_CMD_REG_W) & MMUCR_BUSY)
! 864: /* XXX bound this loop! */ ;
! 865: bus_space_write_2(bst, bsh, MMU_CMD_REG_W, MMUCR_FREEPKT);
! 866:
! 867: ifp->if_timer = 0;
! 868: }
! 869:
! 870: /*
! 871: * Transmit underrun interrupts. We use this opportunity to
! 872: * update transmit statistics from the card.
! 873: */
! 874: if (status & IM_TX_EMPTY_INT) {
! 875: bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_TX_EMPTY_INT);
! 876:
! 877: /* Disable this interrupt. */
! 878: mask &= ~IM_TX_EMPTY_INT;
! 879:
! 880: SMC_SELECT_BANK(sc, 0);
! 881: card_stats = bus_space_read_2(bst, bsh, COUNTER_REG_W);
! 882:
! 883: /* Single collisions. */
! 884: ifp->if_collisions += card_stats & ECR_COLN_MASK;
! 885:
! 886: /* Multiple collisions. */
! 887: ifp->if_collisions += (card_stats & ECR_MCOLN_MASK) >> 4;
! 888:
! 889: SMC_SELECT_BANK(sc, 2);
! 890:
! 891: ifp->if_timer = 0;
! 892: }
! 893:
! 894: /*
! 895: * Other errors. Reset the interface.
! 896: */
! 897: if (status & IM_EPH_INT) {
! 898: smc91cxx_stop(sc);
! 899: smc91cxx_init(sc);
! 900: }
! 901:
! 902: /*
! 903: * Attempt to queue more packets for transmission.
! 904: */
! 905: smc91cxx_start(ifp);
! 906:
! 907: out:
! 908: /*
! 909: * Reenable the interrupts we wish to receive now that processing
! 910: * is complete.
! 911: */
! 912: mask |= bus_space_read_1(bst, bsh, INTR_MASK_REG_B);
! 913: bus_space_write_1(bst, bsh, INTR_MASK_REG_B, mask);
! 914:
! 915: return (1);
! 916: }
! 917:
! 918: /*
! 919: * Read a packet from the card and pass it up to the kernel.
! 920: * NOTE! WE EXPECT TO BE IN REGISTER WINDOW 2!
! 921: */
! 922: void
! 923: smc91cxx_read(sc)
! 924: struct smc91cxx_softc *sc;
! 925: {
! 926: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 927: bus_space_tag_t bst = sc->sc_bst;
! 928: bus_space_handle_t bsh = sc->sc_bsh;
! 929: struct mbuf *m;
! 930: u_int16_t status, packetno, packetlen;
! 931: u_int8_t *data;
! 932:
! 933: again:
! 934: /*
! 935: * Set data pointer to the beginning of the packet. Since
! 936: * PTR_RCV is set, the packet number will be found automatically
! 937: * in FIFO_PORTS_REG_W, FIFO_RX_MASK.
! 938: */
! 939: bus_space_write_2(bst, bsh, POINTER_REG_W,
! 940: PTR_READ | PTR_RCV | PTR_AUTOINC /* | 0x0000 */);
! 941:
! 942: /*
! 943: * First two words are status and packet length.
! 944: */
! 945: status = bus_space_read_2(bst, bsh, DATA_REG_W);
! 946: packetlen = bus_space_read_2(bst, bsh, DATA_REG_W);
! 947:
! 948: /*
! 949: * The packet length includes 3 extra words: status, length,
! 950: * and an extra word that includes the control byte.
! 951: */
! 952: packetlen -= 6;
! 953:
! 954: /*
! 955: * Account for receive errors and discard.
! 956: */
! 957: if (status & RS_ERRORS) {
! 958: ifp->if_ierrors++;
! 959: goto out;
! 960: }
! 961:
! 962: /*
! 963: * Adjust for odd-length packet.
! 964: */
! 965: if (status & RS_ODDFRAME)
! 966: packetlen++;
! 967:
! 968: /*
! 969: * Allocate a header mbuf.
! 970: */
! 971: MGETHDR(m, M_DONTWAIT, MT_DATA);
! 972: if (m == NULL)
! 973: goto out;
! 974: m->m_pkthdr.rcvif = ifp;
! 975: m->m_pkthdr.len = packetlen;
! 976:
! 977: /*
! 978: * Always put the packet in a cluster.
! 979: * XXX should chain small mbufs if less than threshold.
! 980: */
! 981: MCLGET(m, M_DONTWAIT);
! 982: if ((m->m_flags & M_EXT) == 0) {
! 983: m_freem(m);
! 984: ifp->if_ierrors++;
! 985: printf("%s: can't allocate cluster for incoming packet\n",
! 986: sc->sc_dev.dv_xname);
! 987: goto out;
! 988: }
! 989:
! 990: /*
! 991: * Pull the packet off the interface. Make sure the payload
! 992: * is aligned.
! 993: */
! 994: m->m_data = (caddr_t) ALIGN(mtod(m, caddr_t) +
! 995: sizeof(struct ether_header)) - sizeof(struct ether_header);
! 996:
! 997: data = mtod(m, u_int8_t *);
! 998: if (packetlen > 1)
! 999: bus_space_read_multi_stream_2(bst, bsh, DATA_REG_W,
! 1000: (u_int16_t *)data, packetlen >> 1);
! 1001: if (packetlen & 1) {
! 1002: data += packetlen & ~1;
! 1003: *data = bus_space_read_1(bst, bsh, DATA_REG_B);
! 1004: }
! 1005:
! 1006: ifp->if_ipackets++;
! 1007:
! 1008: #if NBPFILTER > 0
! 1009: /*
! 1010: * Hand the packet off to bpf listeners. If there's a bpf listener,
! 1011: * we need to check if the packet is ours.
! 1012: */
! 1013: if (ifp->if_bpf)
! 1014: bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
! 1015: #endif
! 1016:
! 1017: m->m_pkthdr.len = m->m_len = packetlen;
! 1018: ether_input_mbuf(ifp, m);
! 1019:
! 1020: out:
! 1021: /*
! 1022: * Tell the card to free the memory occupied by this packet.
! 1023: */
! 1024: while (bus_space_read_2(bst, bsh, MMU_CMD_REG_W) & MMUCR_BUSY)
! 1025: /* XXX bound this loop! */ ;
! 1026: bus_space_write_2(bst, bsh, MMU_CMD_REG_W, MMUCR_RELEASE);
! 1027:
! 1028: /*
! 1029: * Check for another packet.
! 1030: */
! 1031: packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W);
! 1032: if (packetno & FIFO_REMPTY)
! 1033: return;
! 1034: goto again;
! 1035: }
! 1036:
! 1037: /*
! 1038: * Process an ioctl request.
! 1039: */
! 1040: int
! 1041: smc91cxx_ioctl(ifp, cmd, data)
! 1042: struct ifnet *ifp;
! 1043: u_long cmd;
! 1044: caddr_t data;
! 1045: {
! 1046: struct smc91cxx_softc *sc = ifp->if_softc;
! 1047: struct ifaddr *ifa = (struct ifaddr *)data;
! 1048: struct ifreq *ifr = (struct ifreq *)data;
! 1049: int s, error = 0;
! 1050:
! 1051: s = splnet();
! 1052:
! 1053: switch (cmd) {
! 1054: case SIOCSIFADDR:
! 1055: if ((error = smc91cxx_enable(sc)) != 0)
! 1056: break;
! 1057: ifp->if_flags |= IFF_UP;
! 1058: switch (ifa->ifa_addr->sa_family) {
! 1059: #ifdef INET
! 1060: case AF_INET:
! 1061: smc91cxx_init(sc);
! 1062: arp_ifinit(&sc->sc_arpcom, ifa);
! 1063: break;
! 1064: #endif
! 1065: default:
! 1066: smc91cxx_init(sc);
! 1067: break;
! 1068: }
! 1069: break;
! 1070:
! 1071: case SIOCSIFFLAGS:
! 1072: if ((ifp->if_flags & IFF_UP) == 0 &&
! 1073: (ifp->if_flags & IFF_RUNNING) != 0) {
! 1074: /*
! 1075: * If interface is marked down and it is running,
! 1076: * stop it.
! 1077: */
! 1078: smc91cxx_stop(sc);
! 1079: ifp->if_flags &= ~IFF_RUNNING;
! 1080: smc91cxx_disable(sc);
! 1081: } else if ((ifp->if_flags & IFF_UP) != 0 &&
! 1082: (ifp->if_flags & IFF_RUNNING) == 0) {
! 1083: /*
! 1084: * If interface is marked up and it is stopped,
! 1085: * start it.
! 1086: */
! 1087: if ((error = smc91cxx_enable(sc)) != 0)
! 1088: break;
! 1089: smc91cxx_init(sc);
! 1090: } else if ((ifp->if_flags & IFF_UP) != 0) {
! 1091: /*
! 1092: * Reset the interface to pick up changes in any
! 1093: * other flags that affect hardware registers.
! 1094: */
! 1095: smc91cxx_reset(sc);
! 1096: }
! 1097: break;
! 1098:
! 1099: case SIOCADDMULTI:
! 1100: case SIOCDELMULTI:
! 1101: if ((sc->sc_flags & SMC_FLAGS_ENABLED) == 0) {
! 1102: error = EIO;
! 1103: break;
! 1104: }
! 1105:
! 1106: error = (cmd == SIOCADDMULTI) ?
! 1107: ether_addmulti(ifr, &sc->sc_arpcom) :
! 1108: ether_delmulti(ifr, &sc->sc_arpcom);
! 1109: if (error == ENETRESET) {
! 1110: /*
! 1111: * Multicast list has changed; set the hardware
! 1112: * filter accordingly.
! 1113: */
! 1114: if (ifp->if_flags & IFF_RUNNING)
! 1115: smc91cxx_reset(sc);
! 1116: error = 0;
! 1117: }
! 1118: break;
! 1119:
! 1120: case SIOCGIFMEDIA:
! 1121: case SIOCSIFMEDIA:
! 1122: error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
! 1123: break;
! 1124:
! 1125: default:
! 1126: error = EINVAL;
! 1127: break;
! 1128: }
! 1129:
! 1130: splx(s);
! 1131: return (error);
! 1132: }
! 1133:
! 1134: /*
! 1135: * Reset the interface.
! 1136: */
! 1137: void
! 1138: smc91cxx_reset(sc)
! 1139: struct smc91cxx_softc *sc;
! 1140: {
! 1141: int s;
! 1142:
! 1143: s = splnet();
! 1144: smc91cxx_stop(sc);
! 1145: smc91cxx_init(sc);
! 1146: splx(s);
! 1147: }
! 1148:
! 1149: /*
! 1150: * Watchdog timer.
! 1151: */
! 1152: void
! 1153: smc91cxx_watchdog(ifp)
! 1154: struct ifnet *ifp;
! 1155: {
! 1156: struct smc91cxx_softc *sc = ifp->if_softc;
! 1157:
! 1158: log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
! 1159: ++sc->sc_arpcom.ac_if.if_oerrors;
! 1160:
! 1161: smc91cxx_reset(sc);
! 1162: }
! 1163:
! 1164: /*
! 1165: * Stop output on the interface.
! 1166: */
! 1167: void
! 1168: smc91cxx_stop(sc)
! 1169: struct smc91cxx_softc *sc;
! 1170: {
! 1171: bus_space_tag_t bst = sc->sc_bst;
! 1172: bus_space_handle_t bsh = sc->sc_bsh;
! 1173:
! 1174: /*
! 1175: * Clear interrupt mask; disable all interrupts.
! 1176: */
! 1177: SMC_SELECT_BANK(sc, 2);
! 1178: bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
! 1179:
! 1180: /*
! 1181: * Disable transmitter and receiver.
! 1182: */
! 1183: SMC_SELECT_BANK(sc, 0);
! 1184: bus_space_write_2(bst, bsh, RECV_CONTROL_REG_W, 0);
! 1185: bus_space_write_2(bst, bsh, TXMIT_CONTROL_REG_W, 0);
! 1186:
! 1187: /*
! 1188: * Cancel watchdog timer.
! 1189: */
! 1190: sc->sc_arpcom.ac_if.if_timer = 0;
! 1191: }
! 1192:
! 1193: /*
! 1194: * Enable power on the interface.
! 1195: */
! 1196: int
! 1197: smc91cxx_enable(sc)
! 1198: struct smc91cxx_softc *sc;
! 1199: {
! 1200: if ((sc->sc_flags & SMC_FLAGS_ENABLED) == 0 && sc->sc_enable != NULL) {
! 1201: if ((*sc->sc_enable)(sc) != 0) {
! 1202: printf("%s: device enable failed\n",
! 1203: sc->sc_dev.dv_xname);
! 1204: return (EIO);
! 1205: }
! 1206: }
! 1207:
! 1208: sc->sc_flags |= SMC_FLAGS_ENABLED;
! 1209: return (0);
! 1210: }
! 1211:
! 1212: /*
! 1213: * Disable power on the interface.
! 1214: */
! 1215: void
! 1216: smc91cxx_disable(sc)
! 1217: struct smc91cxx_softc *sc;
! 1218: {
! 1219: if ((sc->sc_flags & SMC_FLAGS_ENABLED) != 0 && sc->sc_disable != NULL) {
! 1220: (*sc->sc_disable)(sc);
! 1221: sc->sc_flags &= ~SMC_FLAGS_ENABLED;
! 1222: }
! 1223: }
! 1224:
! 1225: int
! 1226: smc91cxx_activate(self, act)
! 1227: struct device *self;
! 1228: enum devact act;
! 1229: {
! 1230: #if 0
! 1231: struct smc91cxx_softc *sc = (struct smc91cxx_softc *)self;
! 1232: #endif
! 1233: int rv = 0, s;
! 1234:
! 1235: s = splnet();
! 1236: switch (act) {
! 1237: case DVACT_ACTIVATE:
! 1238: break;
! 1239:
! 1240: case DVACT_DEACTIVATE:
! 1241: #if 0
! 1242: if_deactivate(&sc->sc_ic.ic_if);
! 1243: #endif
! 1244: break;
! 1245: }
! 1246: splx(s);
! 1247: return(rv);
! 1248: }
! 1249:
! 1250: int
! 1251: smc91cxx_detach(self, flags)
! 1252: struct device *self;
! 1253: int flags;
! 1254: {
! 1255: struct smc91cxx_softc *sc = (struct smc91cxx_softc *)self;
! 1256: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 1257:
! 1258: /* Succeed now if there's no work to do. */
! 1259: if ((sc->sc_flags & SMC_FLAGS_ATTACHED) == 0)
! 1260: return(0);
! 1261:
! 1262: /* smc91cxx_disable() checks SMC_FLAGS_ENABLED */
! 1263: smc91cxx_disable(sc);
! 1264:
! 1265: /* smc91cxx_attach() never fails */
! 1266:
! 1267: /* Delete all media. */
! 1268: ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
! 1269:
! 1270: #if NBPFILTER > 0
! 1271: bpfdetach(ifp);
! 1272: #endif
! 1273: ether_ifdetach(ifp);
! 1274: if_detach(ifp);
! 1275:
! 1276: return (0);
! 1277: }
! 1278:
! 1279: u_int32_t
! 1280: smc91cxx_mii_bitbang_read(self)
! 1281: struct device *self;
! 1282: {
! 1283: struct smc91cxx_softc *sc = (void *) self;
! 1284:
! 1285: /* We're already in bank 3. */
! 1286: return (bus_space_read_2(sc->sc_bst, sc->sc_bsh, MGMT_REG_W));
! 1287: }
! 1288:
! 1289: void
! 1290: smc91cxx_mii_bitbang_write(self, val)
! 1291: struct device *self;
! 1292: u_int32_t val;
! 1293: {
! 1294: struct smc91cxx_softc *sc = (void *) self;
! 1295:
! 1296: /* We're already in bank 3. */
! 1297: bus_space_write_2(sc->sc_bst, sc->sc_bsh, MGMT_REG_W, val);
! 1298: }
! 1299:
! 1300: int
! 1301: smc91cxx_mii_readreg(self, phy, reg)
! 1302: struct device *self;
! 1303: int phy, reg;
! 1304: {
! 1305: struct smc91cxx_softc *sc = (void *) self;
! 1306: int val;
! 1307:
! 1308: SMC_SELECT_BANK(sc, 3);
! 1309:
! 1310: val = mii_bitbang_readreg(self, &smc91cxx_mii_bitbang_ops, phy, reg);
! 1311:
! 1312: SMC_SELECT_BANK(sc, 2);
! 1313:
! 1314: return (val);
! 1315: }
! 1316:
! 1317: void
! 1318: smc91cxx_mii_writereg(self, phy, reg, val)
! 1319: struct device *self;
! 1320: int phy, reg, val;
! 1321: {
! 1322: struct smc91cxx_softc *sc = (void *) self;
! 1323:
! 1324: SMC_SELECT_BANK(sc, 3);
! 1325:
! 1326: mii_bitbang_writereg(self, &smc91cxx_mii_bitbang_ops, phy, reg, val);
! 1327:
! 1328: SMC_SELECT_BANK(sc, 2);
! 1329: }
! 1330:
! 1331: void
! 1332: smc91cxx_statchg(self)
! 1333: struct device *self;
! 1334: {
! 1335: struct smc91cxx_softc *sc = (struct smc91cxx_softc *)self;
! 1336: bus_space_tag_t bst = sc->sc_bst;
! 1337: bus_space_handle_t bsh = sc->sc_bsh;
! 1338: int mctl;
! 1339:
! 1340: SMC_SELECT_BANK(sc, 0);
! 1341: mctl = bus_space_read_2(bst, bsh, TXMIT_CONTROL_REG_W);
! 1342: if (sc->sc_mii.mii_media_active & IFM_FDX)
! 1343: mctl |= TCR_SWFDUP;
! 1344: else
! 1345: mctl &= ~TCR_SWFDUP;
! 1346: bus_space_write_2(bst, bsh, TXMIT_CONTROL_REG_W, mctl);
! 1347: SMC_SELECT_BANK(sc, 2); /* back to operating window */
! 1348: }
! 1349:
! 1350: /*
! 1351: * One second timer, used to tick the MII.
! 1352: */
! 1353: void
! 1354: smc91cxx_tick(arg)
! 1355: void *arg;
! 1356: {
! 1357: struct smc91cxx_softc *sc = arg;
! 1358: int s;
! 1359:
! 1360: #ifdef DIAGNOSTIC
! 1361: if ((sc->sc_flags & SMC_FLAGS_HAS_MII) == 0)
! 1362: panic("smc91cxx_tick");
! 1363: #endif
! 1364:
! 1365: if ((sc->sc_dev.dv_flags & DVF_ACTIVE) == 0)
! 1366: return;
! 1367:
! 1368: s = splnet();
! 1369: mii_tick(&sc->sc_mii);
! 1370: splx(s);
! 1371:
! 1372: timeout_add(&sc->sc_mii_timeout, hz);
! 1373: }
CVSweb