Annotation of sys/dev/ic/mtd8xx.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: mtd8xx.c,v 1.12 2006/03/25 22:41:43 djm Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2003 Oleg Safiullin <form@pdp11.org.ru>
! 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: #include "bpfilter.h"
! 32:
! 33: #include <sys/param.h>
! 34: #include <sys/mbuf.h>
! 35: #include <sys/systm.h>
! 36: #include <sys/device.h>
! 37: #include <sys/socket.h>
! 38: #include <sys/ioctl.h>
! 39:
! 40: #include <net/if.h>
! 41: #include <net/if_media.h>
! 42:
! 43: #if NBPFILTER > 0
! 44: #include <net/bpf.h>
! 45: #endif
! 46:
! 47: #ifdef INET
! 48: #include <netinet/in.h>
! 49: #include <netinet/if_ether.h>
! 50: #endif
! 51:
! 52: #include <machine/bus.h>
! 53:
! 54: #include <dev/mii/mii.h>
! 55: #include <dev/mii/miivar.h>
! 56:
! 57: #include <dev/pci/pcidevs.h>
! 58: #include <dev/pci/pcivar.h>
! 59:
! 60: #include <dev/ic/mtd8xxreg.h>
! 61: #include <dev/ic/mtd8xxvar.h>
! 62:
! 63:
! 64: static int mtd_ifmedia_upd(struct ifnet *);
! 65: static void mtd_ifmedia_sts(struct ifnet *, struct ifmediareq *);
! 66:
! 67: static u_int32_t mtd_mii_command(struct mtd_softc *, int, int, int);
! 68: static int mtd_miibus_readreg(struct device *, int, int);
! 69: static void mtd_miibus_writereg(struct device *, int, int, int);
! 70: static void mtd_miibus_statchg(struct device *);
! 71: static void mtd_setmulti(struct mtd_softc *);
! 72:
! 73: static int mtd_encap(struct mtd_softc *, struct mbuf *, u_int32_t *);
! 74: static int mtd_list_rx_init(struct mtd_softc *);
! 75: static void mtd_list_tx_init(struct mtd_softc *);
! 76: static int mtd_newbuf(struct mtd_softc *, int, struct mbuf *);
! 77:
! 78: static void mtd_reset(struct mtd_softc *sc);
! 79: static int mtd_ioctl(struct ifnet *, u_long, caddr_t);
! 80: static void mtd_init(struct ifnet *);
! 81: static void mtd_start(struct ifnet *);
! 82: static void mtd_stop(struct ifnet *);
! 83: static void mtd_watchdog(struct ifnet *);
! 84:
! 85: static void mtd_rxeof(struct mtd_softc *);
! 86: static int mtd_rx_resync(struct mtd_softc *);
! 87: static void mtd_txeof(struct mtd_softc *);
! 88:
! 89:
! 90: void
! 91: mtd_attach(struct mtd_softc *sc)
! 92: {
! 93: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 94: u_int32_t enaddr[2];
! 95: int i;
! 96:
! 97: /* Reset the adapter. */
! 98: mtd_reset(sc);
! 99:
! 100: if (bus_dmamem_alloc(sc->sc_dmat, sizeof(struct mtd_list_data),
! 101: PAGE_SIZE, 0, sc->sc_listseg, 1, &sc->sc_listnseg,
! 102: BUS_DMA_NOWAIT) != 0) {
! 103: printf(": can't alloc list mem\n");
! 104: return;
! 105: }
! 106: if (bus_dmamem_map(sc->sc_dmat, sc->sc_listseg, sc->sc_listnseg,
! 107: sizeof(struct mtd_list_data), &sc->sc_listkva,
! 108: BUS_DMA_NOWAIT) != 0) {
! 109: printf(": can't map list mem\n");
! 110: return;
! 111: }
! 112: if (bus_dmamap_create(sc->sc_dmat, sizeof(struct mtd_list_data), 1,
! 113: sizeof(struct mtd_list_data), 0, BUS_DMA_NOWAIT,
! 114: &sc->sc_listmap) != 0) {
! 115: printf(": can't alloc list map\n");
! 116: return;
! 117: }
! 118: if (bus_dmamap_load(sc->sc_dmat, sc->sc_listmap, sc->sc_listkva,
! 119: sizeof(struct mtd_list_data), NULL, BUS_DMA_NOWAIT) != 0) {
! 120: printf(": can't load list map\n");
! 121: return;
! 122: }
! 123: sc->mtd_ldata = (struct mtd_list_data *)sc->sc_listkva;
! 124: bzero(sc->mtd_ldata, sizeof(struct mtd_list_data));
! 125:
! 126: for (i = 0; i < MTD_RX_LIST_CNT; i++) {
! 127: if (bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES,
! 128: 0, BUS_DMA_NOWAIT,
! 129: &sc->mtd_cdata.mtd_rx_chain[i].sd_map) != 0) {
! 130: printf(": can't create rx map\n");
! 131: return;
! 132: }
! 133: }
! 134: if (bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0,
! 135: BUS_DMA_NOWAIT, &sc->sc_rx_sparemap) != 0) {
! 136: printf(": can't create rx spare map\n");
! 137: return;
! 138: }
! 139:
! 140: for (i = 0; i < MTD_TX_LIST_CNT; i++) {
! 141: if (bus_dmamap_create(sc->sc_dmat, MCLBYTES,
! 142: MTD_TX_LIST_CNT - 5, MCLBYTES, 0, BUS_DMA_NOWAIT,
! 143: &sc->mtd_cdata.mtd_tx_chain[i].sd_map) != 0) {
! 144: printf(": can't create tx map\n");
! 145: return;
! 146: }
! 147: }
! 148: if (bus_dmamap_create(sc->sc_dmat, MCLBYTES, MTD_TX_LIST_CNT - 5,
! 149: MCLBYTES, 0, BUS_DMA_NOWAIT, &sc->sc_tx_sparemap) != 0) {
! 150: printf(": can't create tx spare map\n");
! 151: return;
! 152: }
! 153:
! 154:
! 155: /* Get station address. */
! 156: enaddr[0] = letoh32(CSR_READ_4(MTD_PAR0));
! 157: enaddr[1] = letoh32(CSR_READ_4(MTD_PAR4));
! 158: bcopy(enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
! 159: printf(" address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr));
! 160:
! 161: /* Initialize interface */
! 162: ifp->if_softc = sc;
! 163: ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
! 164: ifp->if_ioctl = mtd_ioctl;
! 165: ifp->if_start = mtd_start;
! 166: ifp->if_watchdog = mtd_watchdog;
! 167: ifp->if_baudrate = 10000000;
! 168: IFQ_SET_READY(&ifp->if_snd);
! 169: bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
! 170:
! 171: ifp->if_capabilities = IFCAP_VLAN_MTU;
! 172:
! 173: /*
! 174: * Initialize our media structures and probe the MII.
! 175: */
! 176: sc->sc_mii.mii_ifp = ifp;
! 177: sc->sc_mii.mii_readreg = mtd_miibus_readreg;
! 178: sc->sc_mii.mii_writereg = mtd_miibus_writereg;
! 179: sc->sc_mii.mii_statchg = mtd_miibus_statchg;
! 180: ifmedia_init(&sc->sc_mii.mii_media, 0, mtd_ifmedia_upd,
! 181: mtd_ifmedia_sts);
! 182: mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, MII_PHY_ANY,
! 183: MII_OFFSET_ANY, 0);
! 184: if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
! 185: ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER | IFM_NONE, 0,
! 186: NULL);
! 187: ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_NONE);
! 188: } else
! 189: ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);
! 190:
! 191: /*
! 192: * Attach us everywhere
! 193: */
! 194: if_attach(ifp);
! 195: ether_ifattach(ifp);
! 196: }
! 197:
! 198:
! 199: static int
! 200: mtd_ifmedia_upd(struct ifnet *ifp)
! 201: {
! 202: struct mtd_softc *sc = ifp->if_softc;
! 203:
! 204: return (mii_mediachg(&sc->sc_mii));
! 205: }
! 206:
! 207:
! 208: static void
! 209: mtd_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
! 210: {
! 211: struct mtd_softc *sc = ifp->if_softc;
! 212:
! 213: mii_pollstat(&sc->sc_mii);
! 214: ifmr->ifm_active = sc->sc_mii.mii_media_active;
! 215: ifmr->ifm_status = sc->sc_mii.mii_media_status;
! 216: }
! 217:
! 218:
! 219: static u_int32_t
! 220: mtd_mii_command(struct mtd_softc *sc, int opcode, int phy, int reg)
! 221: {
! 222: u_int32_t miir, mask, data;
! 223: int i;
! 224:
! 225: miir = (CSR_READ_4(MTD_MIIMGT) & ~MIIMGT_MASK) | MIIMGT_WRITE |
! 226: MIIMGT_MDO;
! 227:
! 228: for (i = 0; i < 32; i++) {
! 229: miir &= ~MIIMGT_MDC;
! 230: CSR_WRITE_4(MTD_MIIMGT, miir);
! 231: miir |= MIIMGT_MDC;
! 232: CSR_WRITE_4(MTD_MIIMGT, miir);
! 233: }
! 234:
! 235: data = opcode | (phy << 7) | (reg << 2);
! 236:
! 237: for (mask = 0; mask; mask >>= 1) {
! 238: miir &= ~(MIIMGT_MDC | MIIMGT_MDO);
! 239: if (mask & data)
! 240: miir |= MIIMGT_MDO;
! 241: CSR_WRITE_4(MTD_MIIMGT, miir);
! 242: miir |= MIIMGT_MDC;
! 243: CSR_WRITE_4(MTD_MIIMGT, miir);
! 244: DELAY(30);
! 245:
! 246: if (mask == 0x4 && opcode == MII_OPCODE_RD)
! 247: miir &= ~MIIMGT_WRITE;
! 248: }
! 249: return (miir);
! 250: }
! 251:
! 252:
! 253:
! 254: static int
! 255: mtd_miibus_readreg(struct device *self, int phy, int reg)
! 256: {
! 257: struct mtd_softc *sc = (void *)self;
! 258:
! 259: if (sc->sc_devid == PCI_PRODUCT_MYSON_MTD803)
! 260: return (phy ? 0 : (int)CSR_READ_2(MTD_PHYCSR + (reg << 1)));
! 261: else {
! 262: u_int32_t miir, mask, data;
! 263:
! 264: miir = mtd_mii_command(sc, MII_OPCODE_RD, phy, reg);
! 265: for (mask = 0x8000, data = 0; mask; mask >>= 1) {
! 266: miir &= ~MIIMGT_MDC;
! 267: CSR_WRITE_4(MTD_MIIMGT, miir);
! 268: miir = CSR_READ_4(MTD_MIIMGT);
! 269: if (miir & MIIMGT_MDI)
! 270: data |= mask;
! 271: miir |= MIIMGT_MDC;
! 272: CSR_WRITE_4(MTD_MIIMGT, miir);
! 273: DELAY(30);
! 274: }
! 275: miir &= ~MIIMGT_MDC;
! 276: CSR_WRITE_4(MTD_MIIMGT, miir);
! 277:
! 278: return ((int)data);
! 279: }
! 280: }
! 281:
! 282:
! 283: static void
! 284: mtd_miibus_writereg(struct device *self, int phy, int reg, int val)
! 285: {
! 286: struct mtd_softc *sc = (void *)self;
! 287:
! 288: if (sc->sc_devid == PCI_PRODUCT_MYSON_MTD803) {
! 289: if (!phy)
! 290: CSR_WRITE_2(MTD_PHYCSR + (reg << 1), val);
! 291: } else {
! 292: u_int32_t miir, mask;
! 293:
! 294: miir = mtd_mii_command(sc, MII_OPCODE_WR, phy, reg);
! 295: for (mask = 0x8000; mask; mask >>= 1) {
! 296: miir &= ~(MIIMGT_MDC | MIIMGT_MDO);
! 297: if (mask & (u_int32_t)val)
! 298: miir |= MIIMGT_MDO;
! 299: CSR_WRITE_4(MTD_MIIMGT, miir);
! 300: miir |= MIIMGT_MDC;
! 301: CSR_WRITE_4(MTD_MIIMGT, miir);
! 302: DELAY(1);
! 303: }
! 304: miir &= ~MIIMGT_MDC;
! 305: CSR_WRITE_4(MTD_MIIMGT, miir);
! 306: }
! 307: }
! 308:
! 309:
! 310: static void
! 311: mtd_miibus_statchg(struct device *self)
! 312: {
! 313: /* NOTHING */
! 314: }
! 315:
! 316:
! 317: void
! 318: mtd_setmulti(struct mtd_softc *sc)
! 319: {
! 320: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 321: u_int32_t rxfilt, crc, hash[2] = { 0, 0 };
! 322: struct ether_multistep step;
! 323: struct ether_multi *enm;
! 324: int mcnt = 0;
! 325:
! 326: allmulti:
! 327: rxfilt = CSR_READ_4(MTD_TCRRCR) & ~RCR_AM;
! 328: if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
! 329: rxfilt |= RCR_AM;
! 330: CSR_WRITE_4(MTD_TCRRCR, rxfilt);
! 331: CSR_WRITE_4(MTD_MAR0, 0xffffffff);
! 332: CSR_WRITE_4(MTD_MAR4, 0xffffffff);
! 333: return;
! 334: }
! 335:
! 336: /* First, zot all the existing hash bits. */
! 337: CSR_WRITE_4(MTD_MAR0, 0);
! 338: CSR_WRITE_4(MTD_MAR4, 0);
! 339:
! 340: /* Now program new ones. */
! 341: ETHER_FIRST_MULTI(step, &sc->sc_arpcom, enm);
! 342: while (enm != NULL) {
! 343: if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
! 344: ifp->if_flags |= IFF_ALLMULTI;
! 345: goto allmulti;
! 346: }
! 347: crc = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26;
! 348: hash[crc >> 5] |= 1 << (crc & 0xf);
! 349: ++mcnt;
! 350: ETHER_NEXT_MULTI(step, enm);
! 351: }
! 352:
! 353: if (mcnt)
! 354: rxfilt |= RCR_AM;
! 355: CSR_WRITE_4(MTD_MAR0, hash[0]);
! 356: CSR_WRITE_4(MTD_MAR4, hash[1]);
! 357: CSR_WRITE_4(MTD_TCRRCR, rxfilt);
! 358: }
! 359:
! 360:
! 361: /*
! 362: * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
! 363: * pointers to the fragment pointers.
! 364: */
! 365: int
! 366: mtd_encap(struct mtd_softc *sc, struct mbuf *m_head, u_int32_t *txidx)
! 367: {
! 368: struct mtd_tx_desc *f = NULL;
! 369: int frag, cur, cnt = 0, i, total_len = 0;
! 370: bus_dmamap_t map;
! 371:
! 372: /*
! 373: * Start packing the mbufs in this chain into
! 374: * the fragment pointers. Stop when we run out
! 375: * of fragments or hit the end of the mbuf chain.
! 376: */
! 377: map = sc->sc_tx_sparemap;
! 378:
! 379: if (bus_dmamap_load_mbuf(sc->sc_dmat, map,
! 380: m_head, BUS_DMA_NOWAIT) != 0)
! 381: return (1);
! 382:
! 383: cur = frag = *txidx;
! 384:
! 385: for (i = 0; i < map->dm_nsegs; i++) {
! 386: if ((MTD_TX_LIST_CNT -
! 387: (sc->mtd_cdata.mtd_tx_cnt + cnt)) < 5) {
! 388: bus_dmamap_unload(sc->sc_dmat, map);
! 389: return (1);
! 390: }
! 391:
! 392: f = &sc->mtd_ldata->mtd_tx_list[frag];
! 393: f->td_tcw = htole32(map->dm_segs[i].ds_len);
! 394: total_len += map->dm_segs[i].ds_len;
! 395: if (cnt == 0) {
! 396: f->td_tsw = 0;
! 397: f->td_tcw |= htole32(TCW_FD | TCW_CRC | TCW_PAD);
! 398: } else
! 399: f->td_tsw = htole32(TSW_OWN);
! 400: f->td_buf = htole32(map->dm_segs[i].ds_addr);
! 401: cur = frag;
! 402: frag = (frag + 1) % MTD_TX_LIST_CNT;
! 403: cnt++;
! 404: }
! 405:
! 406: sc->mtd_cdata.mtd_tx_cnt += cnt;
! 407: sc->mtd_cdata.mtd_tx_chain[cur].sd_mbuf = m_head;
! 408: sc->sc_tx_sparemap = sc->mtd_cdata.mtd_tx_chain[cur].sd_map;
! 409: sc->mtd_cdata.mtd_tx_chain[cur].sd_map = map;
! 410: sc->mtd_ldata->mtd_tx_list[cur].td_tcw |= htole32(TCW_LD | TCW_IC);
! 411: if (sc->sc_devid == PCI_PRODUCT_MYSON_MTD891)
! 412: sc->mtd_ldata->mtd_tx_list[cur].td_tcw |=
! 413: htole32(TCW_EIC | TCW_RTLC);
! 414:
! 415: bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
! 416: BUS_DMASYNC_PREWRITE);
! 417:
! 418: sc->mtd_ldata->mtd_tx_list[*txidx].td_tsw = htole32(TSW_OWN);
! 419: sc->mtd_ldata->mtd_tx_list[*txidx].td_tcw |=
! 420: htole32(total_len << TCW_PKTS_SHIFT);
! 421:
! 422: bus_dmamap_sync(sc->sc_dmat, sc->sc_listmap,
! 423: offsetof(struct mtd_list_data, mtd_tx_list[0]),
! 424: sizeof(struct mtd_tx_desc) * MTD_TX_LIST_CNT,
! 425: BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
! 426:
! 427: *txidx = frag;
! 428:
! 429: return (0);
! 430: }
! 431:
! 432:
! 433: /*
! 434: * Initialize the transmit descriptors.
! 435: */
! 436: static void
! 437: mtd_list_tx_init(struct mtd_softc *sc)
! 438: {
! 439: struct mtd_chain_data *cd;
! 440: struct mtd_list_data *ld;
! 441: int i;
! 442:
! 443: cd = &sc->mtd_cdata;
! 444: ld = sc->mtd_ldata;
! 445: for (i = 0; i < MTD_TX_LIST_CNT; i++) {
! 446: cd->mtd_tx_chain[i].sd_mbuf = NULL;
! 447: ld->mtd_tx_list[i].td_tsw = 0;
! 448: ld->mtd_tx_list[i].td_tcw = 0;
! 449: ld->mtd_tx_list[i].td_buf = 0;
! 450: ld->mtd_tx_list[i].td_next = htole32(
! 451: sc->sc_listmap->dm_segs[0].ds_addr +
! 452: offsetof(struct mtd_list_data,
! 453: mtd_tx_list[(i + 1) % MTD_TX_LIST_CNT]));
! 454: }
! 455:
! 456: cd->mtd_tx_prod = cd->mtd_tx_cons = cd->mtd_tx_cnt = 0;
! 457: }
! 458:
! 459:
! 460: /*
! 461: * Initialize the RX descriptors and allocate mbufs for them. Note that
! 462: * we arrange the descriptors in a closed ring, so that the last descriptor
! 463: * points back to the first.
! 464: */
! 465: static int
! 466: mtd_list_rx_init(struct mtd_softc *sc)
! 467: {
! 468: struct mtd_list_data *ld;
! 469: int i;
! 470:
! 471: ld = sc->mtd_ldata;
! 472:
! 473: for (i = 0; i < MTD_RX_LIST_CNT; i++) {
! 474: if (mtd_newbuf(sc, i, NULL))
! 475: return (1);
! 476: ld->mtd_rx_list[i].rd_next = htole32(
! 477: sc->sc_listmap->dm_segs[0].ds_addr +
! 478: offsetof(struct mtd_list_data,
! 479: mtd_rx_list[(i + 1) % MTD_RX_LIST_CNT])
! 480: );
! 481: }
! 482:
! 483: sc->mtd_cdata.mtd_rx_prod = 0;
! 484:
! 485: return (0);
! 486: }
! 487:
! 488:
! 489: /*
! 490: * Initialize an RX descriptor and attach an MBUF cluster.
! 491: */
! 492: static int
! 493: mtd_newbuf(struct mtd_softc *sc, int i, struct mbuf *m)
! 494: {
! 495: struct mbuf *m_new = NULL;
! 496: struct mtd_rx_desc *c;
! 497: bus_dmamap_t map;
! 498:
! 499: c = &sc->mtd_ldata->mtd_rx_list[i];
! 500:
! 501: if (m == NULL) {
! 502: MGETHDR(m_new, M_DONTWAIT, MT_DATA);
! 503: if (m_new == NULL)
! 504: return (1);
! 505:
! 506: MCLGET(m_new, M_DONTWAIT);
! 507: if (!(m_new->m_flags & M_EXT)) {
! 508: m_freem(m_new);
! 509: return (1);
! 510: }
! 511: m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
! 512: if (bus_dmamap_load(sc->sc_dmat, sc->sc_rx_sparemap,
! 513: mtod(m_new, caddr_t), MCLBYTES, NULL,
! 514: BUS_DMA_NOWAIT) != 0) {
! 515: m_freem(m_new);
! 516: return (1);
! 517: }
! 518: map = sc->mtd_cdata.mtd_rx_chain[i].sd_map;
! 519: sc->mtd_cdata.mtd_rx_chain[i].sd_map = sc->sc_rx_sparemap;
! 520: sc->sc_rx_sparemap = map;
! 521: } else {
! 522: m_new = m;
! 523: m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
! 524: m_new->m_data = m_new->m_ext.ext_buf;
! 525: }
! 526:
! 527: m_adj(m_new, sizeof(u_int64_t));
! 528:
! 529: bus_dmamap_sync(sc->sc_dmat, sc->mtd_cdata.mtd_rx_chain[i].sd_map, 0,
! 530: sc->mtd_cdata.mtd_rx_chain[i].sd_map->dm_mapsize,
! 531: BUS_DMASYNC_PREREAD);
! 532:
! 533: sc->mtd_cdata.mtd_rx_chain[i].sd_mbuf = m_new;
! 534: c->rd_buf = htole32(
! 535: sc->mtd_cdata.mtd_rx_chain[i].sd_map->dm_segs[0].ds_addr +
! 536: sizeof(u_int64_t));
! 537: c->rd_rcw = htole32(ETHER_MAX_DIX_LEN);
! 538: c->rd_rsr = htole32(RSR_OWN);
! 539:
! 540: bus_dmamap_sync(sc->sc_dmat, sc->sc_listmap,
! 541: offsetof(struct mtd_list_data, mtd_rx_list[i]),
! 542: sizeof(struct mtd_rx_desc),
! 543: BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
! 544:
! 545: return (0);
! 546: }
! 547:
! 548:
! 549: static void
! 550: mtd_reset(struct mtd_softc *sc)
! 551: {
! 552: int i;
! 553:
! 554: /* Set software reset bit */
! 555: CSR_WRITE_4(MTD_BCR, BCR_SWR);
! 556:
! 557: /*
! 558: * Wait until software reset completed.
! 559: */
! 560: for (i = 0; i < MTD_TIMEOUT; ++i) {
! 561: DELAY(10);
! 562: if (!(CSR_READ_4(MTD_BCR) & BCR_SWR)) {
! 563: /*
! 564: * Wait a little while for the chip to get
! 565: * its brains in order.
! 566: */
! 567: DELAY(1000);
! 568: return;
! 569: }
! 570: }
! 571:
! 572: /* Reset timed out. */
! 573: printf("%s: reset never completed!\n", sc->sc_dev.dv_xname);
! 574: }
! 575:
! 576:
! 577: static int
! 578: mtd_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
! 579: {
! 580: struct mtd_softc *sc = ifp->if_softc;
! 581: struct ifreq *ifr = (struct ifreq *)data;
! 582: struct ifaddr *ifa = (struct ifaddr *)data;
! 583: int s, error;
! 584:
! 585: s = splnet();
! 586: if ((error = ether_ioctl(ifp, &sc->sc_arpcom, command, data)) > 0) {
! 587: splx(s);
! 588: return (error);
! 589: }
! 590:
! 591: switch (command) {
! 592: case SIOCSIFADDR:
! 593: ifp->if_flags |= IFF_UP;
! 594: mtd_init(ifp);
! 595: switch (ifa->ifa_addr->sa_family) {
! 596: #ifdef INET
! 597: case AF_INET:
! 598: arp_ifinit(&sc->sc_arpcom, ifa);
! 599: break;
! 600: #endif /* INET */
! 601: }
! 602: break;
! 603: case SIOCSIFMTU:
! 604: if (ifr->ifr_mtu >= ETHERMIN && ifr->ifr_mtu <= ETHERMTU)
! 605: ifp->if_mtu = ifr->ifr_mtu;
! 606: else
! 607: error = EINVAL;
! 608: break;
! 609:
! 610: case SIOCSIFFLAGS:
! 611: if (ifp->if_flags & IFF_UP)
! 612: mtd_init(ifp);
! 613: else {
! 614: if (ifp->if_flags & IFF_RUNNING)
! 615: mtd_stop(ifp);
! 616: }
! 617: error = 0;
! 618: break;
! 619: case SIOCADDMULTI:
! 620: case SIOCDELMULTI:
! 621: error = (command == SIOCADDMULTI) ?
! 622: ether_addmulti(ifr, &sc->sc_arpcom) :
! 623: ether_delmulti(ifr, &sc->sc_arpcom);
! 624:
! 625: if (error == ENETRESET) {
! 626: /*
! 627: * Multicast list has changed; set the hardware
! 628: * filter accordingly.
! 629: */
! 630: if (ifp->if_flags & IFF_RUNNING)
! 631: mtd_setmulti(sc);
! 632: error = 0;
! 633: }
! 634: break;
! 635: case SIOCGIFMEDIA:
! 636: case SIOCSIFMEDIA:
! 637: error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, command);
! 638: break;
! 639: default:
! 640: error = EINVAL;
! 641: break;
! 642: }
! 643:
! 644: splx(s);
! 645: return (error);
! 646: }
! 647:
! 648:
! 649: static void
! 650: mtd_init(struct ifnet *ifp)
! 651: {
! 652: struct mtd_softc *sc = ifp->if_softc;
! 653: int s;
! 654:
! 655: s = splnet();
! 656:
! 657: /*
! 658: * Cancel pending I/O and free all RX/TX buffers.
! 659: */
! 660: mtd_stop(ifp);
! 661:
! 662: /*
! 663: * Set cache alignment and burst length.
! 664: */
! 665: CSR_WRITE_4(MTD_BCR, BCR_PBL8);
! 666: CSR_WRITE_4(MTD_TCRRCR, TCR_TFTSF | RCR_RBLEN | RCR_RPBL512);
! 667: if (sc->sc_devid == PCI_PRODUCT_MYSON_MTD891) {
! 668: CSR_SETBIT(MTD_BCR, BCR_PROG);
! 669: CSR_SETBIT(MTD_TCRRCR, TCR_ENHANCED);
! 670: }
! 671:
! 672: if (ifp->if_flags & IFF_PROMISC)
! 673: CSR_SETBIT(MTD_TCRRCR, RCR_PROM);
! 674: else
! 675: CSR_CLRBIT(MTD_TCRRCR, RCR_PROM);
! 676:
! 677: if (ifp->if_flags & IFF_BROADCAST)
! 678: CSR_SETBIT(MTD_TCRRCR, RCR_AB);
! 679: else
! 680: CSR_CLRBIT(MTD_TCRRCR, RCR_AB);
! 681:
! 682: mtd_setmulti(sc);
! 683:
! 684: if (mtd_list_rx_init(sc)) {
! 685: printf("%s: can't allocate memeory for rx buffers\n",
! 686: sc->sc_dev.dv_xname);
! 687: splx(s);
! 688: return;
! 689: }
! 690: mtd_list_tx_init(sc);
! 691:
! 692: CSR_WRITE_4(MTD_RXLBA, sc->sc_listmap->dm_segs[0].ds_addr +
! 693: offsetof(struct mtd_list_data, mtd_rx_list[0]));
! 694: CSR_WRITE_4(MTD_TXLBA, sc->sc_listmap->dm_segs[0].ds_addr +
! 695: offsetof(struct mtd_list_data, mtd_tx_list[0]));
! 696:
! 697: /*
! 698: * Enable interrupts.
! 699: */
! 700: CSR_WRITE_4(MTD_IMR, IMR_INTRS);
! 701: CSR_WRITE_4(MTD_ISR, 0xffffffff);
! 702:
! 703: /* Enable receiver and transmitter */
! 704: CSR_SETBIT(MTD_TCRRCR, TCR_TE | RCR_RE);
! 705: CSR_WRITE_4(MTD_RXPDR, 0xffffffff);
! 706:
! 707: ifp->if_flags |= IFF_RUNNING;
! 708: ifp->if_flags &= ~IFF_OACTIVE;
! 709: splx(s);
! 710: }
! 711:
! 712:
! 713: /*
! 714: * Main transmit routine. To avoid having to do mbuf copies, we put pointers
! 715: * to the mbuf data regions directly in the transmit lists. We also save a
! 716: * copy of the pointers since the transmit list fragment pointers are
! 717: * physical addresses.
! 718: */
! 719: static void
! 720: mtd_start(struct ifnet *ifp)
! 721: {
! 722: struct mtd_softc *sc = ifp->if_softc;
! 723: struct mbuf *m_head = NULL;
! 724: int idx;
! 725:
! 726: if (sc->mtd_cdata.mtd_tx_cnt) {
! 727: ifp->if_flags |= IFF_OACTIVE;
! 728: return;
! 729: }
! 730:
! 731: idx = sc->mtd_cdata.mtd_tx_prod;
! 732: while (sc->mtd_cdata.mtd_tx_chain[idx].sd_mbuf == NULL) {
! 733: IFQ_DEQUEUE(&ifp->if_snd, m_head);
! 734: if (m_head == NULL)
! 735: break;
! 736:
! 737: if (mtd_encap(sc, m_head, &idx)) {
! 738: ifp->if_flags |= IFF_OACTIVE;
! 739: break;
! 740: }
! 741:
! 742: /*
! 743: * If there's a BPF listener, bounce a copy of this frame
! 744: * to him.
! 745: */
! 746: #if NBPFILTER > 0
! 747: if (ifp->if_bpf != NULL)
! 748: bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
! 749: #endif
! 750: }
! 751:
! 752: if (idx == sc->mtd_cdata.mtd_tx_prod)
! 753: return;
! 754:
! 755: /* Transmit */
! 756: sc->mtd_cdata.mtd_tx_prod = idx;
! 757: CSR_WRITE_4(MTD_TXPDR, 0xffffffff);
! 758:
! 759: /*
! 760: * Set a timeout in case the chip goes out to lunch.
! 761: */
! 762: ifp->if_timer = 5;
! 763: }
! 764:
! 765:
! 766: static void
! 767: mtd_stop(struct ifnet *ifp)
! 768: {
! 769: struct mtd_softc *sc = ifp->if_softc;
! 770: int i;
! 771:
! 772: ifp->if_timer = 0;
! 773: ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
! 774:
! 775: CSR_CLRBIT(MTD_TCRRCR, (RCR_RE | TCR_TE));
! 776: CSR_WRITE_4(MTD_IMR, 0);
! 777: CSR_WRITE_4(MTD_TXLBA, 0);
! 778: CSR_WRITE_4(MTD_RXLBA, 0);
! 779:
! 780: /*
! 781: * Free data in the RX lists.
! 782: */
! 783: for (i = 0; i < MTD_RX_LIST_CNT; i++) {
! 784: if (sc->mtd_cdata.mtd_rx_chain[i].sd_map->dm_nsegs != 0) {
! 785: bus_dmamap_t map = sc->mtd_cdata.mtd_rx_chain[i].sd_map;
! 786:
! 787: bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
! 788: BUS_DMASYNC_POSTREAD);
! 789: bus_dmamap_unload(sc->sc_dmat, map);
! 790: }
! 791: if (sc->mtd_cdata.mtd_rx_chain[i].sd_mbuf != NULL) {
! 792: m_freem(sc->mtd_cdata.mtd_rx_chain[i].sd_mbuf);
! 793: sc->mtd_cdata.mtd_rx_chain[i].sd_mbuf = NULL;
! 794: }
! 795: }
! 796: bzero((char *)&sc->mtd_ldata->mtd_rx_list,
! 797: sizeof(sc->mtd_ldata->mtd_rx_list));
! 798:
! 799: /*
! 800: * Free the TX list buffers.
! 801: */
! 802: for (i = 0; i < MTD_TX_LIST_CNT; i++) {
! 803: if (sc->mtd_cdata.mtd_tx_chain[i].sd_map->dm_nsegs != 0) {
! 804: bus_dmamap_t map = sc->mtd_cdata.mtd_tx_chain[i].sd_map;
! 805:
! 806: bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
! 807: BUS_DMASYNC_POSTWRITE);
! 808: bus_dmamap_unload(sc->sc_dmat, map);
! 809: }
! 810: if (sc->mtd_cdata.mtd_tx_chain[i].sd_mbuf != NULL) {
! 811: m_freem(sc->mtd_cdata.mtd_tx_chain[i].sd_mbuf);
! 812: sc->mtd_cdata.mtd_tx_chain[i].sd_mbuf = NULL;
! 813: }
! 814: }
! 815:
! 816: bzero((char *)&sc->mtd_ldata->mtd_tx_list,
! 817: sizeof(sc->mtd_ldata->mtd_tx_list));
! 818:
! 819: }
! 820:
! 821:
! 822: static void
! 823: mtd_watchdog(struct ifnet *ifp)
! 824: {
! 825: struct mtd_softc *sc = ifp->if_softc;
! 826:
! 827: ifp->if_oerrors++;
! 828: printf("%s: watchdog timeout\n", sc->sc_dev.dv_xname);
! 829:
! 830: mtd_stop(ifp);
! 831: mtd_reset(sc);
! 832: mtd_init(ifp);
! 833:
! 834: if (!IFQ_IS_EMPTY(&ifp->if_snd))
! 835: mtd_start(ifp);
! 836: }
! 837:
! 838:
! 839: int
! 840: mtd_intr(void *xsc)
! 841: {
! 842: struct mtd_softc *sc = xsc;
! 843: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 844: u_int32_t status;
! 845: int claimed = 0;
! 846:
! 847: /* Supress unwanted interrupts */
! 848: if (!(ifp->if_flags & IFF_RUNNING)) {
! 849: if (CSR_READ_4(MTD_ISR) & ISR_INTRS)
! 850: mtd_stop(ifp);
! 851: return (claimed);
! 852: }
! 853:
! 854: /* Disable interrupts. */
! 855: CSR_WRITE_4(MTD_IMR, 0);
! 856:
! 857: while((status = CSR_READ_4(MTD_ISR)) & ISR_INTRS) {
! 858: claimed = 1;
! 859:
! 860: CSR_WRITE_4(MTD_ISR, status);
! 861:
! 862: /* RX interrupt. */
! 863: if (status & ISR_RI) {
! 864: int curpkts = ifp->if_ipackets;
! 865:
! 866: mtd_rxeof(sc);
! 867: if (curpkts == ifp->if_ipackets)
! 868: while(mtd_rx_resync(sc))
! 869: mtd_rxeof(sc);
! 870: }
! 871:
! 872: /* RX error interrupt. */
! 873: if (status & (ISR_RXERI | ISR_RBU))
! 874: ifp->if_ierrors++;
! 875:
! 876: /* TX interrupt. */
! 877: if (status & (ISR_TI | ISR_ETI | ISR_TBU))
! 878: mtd_txeof(sc);
! 879:
! 880: /* Fatal bus error interrupt. */
! 881: if (status & ISR_FBE) {
! 882: mtd_reset(sc);
! 883: mtd_start(ifp);
! 884: }
! 885: }
! 886:
! 887: /* Re-enable interrupts. */
! 888: CSR_WRITE_4(MTD_IMR, IMR_INTRS);
! 889:
! 890: if (!IFQ_IS_EMPTY(&ifp->if_snd))
! 891: mtd_start(ifp);
! 892:
! 893: return (claimed);
! 894: }
! 895:
! 896:
! 897: /*
! 898: * A frame has been uploaded: pass the resulting mbuf chain up to
! 899: * the higher level protocols.
! 900: */
! 901: static void
! 902: mtd_rxeof(struct mtd_softc *sc)
! 903: {
! 904: struct mbuf *m;
! 905: struct ifnet *ifp;
! 906: struct mtd_rx_desc *cur_rx;
! 907: int i, total_len = 0;
! 908: u_int32_t rxstat;
! 909:
! 910: ifp = &sc->sc_arpcom.ac_if;
! 911: i = sc->mtd_cdata.mtd_rx_prod;
! 912:
! 913: while(!(sc->mtd_ldata->mtd_rx_list[i].rd_rsr & htole32(RSR_OWN))) {
! 914: struct mbuf *m0 = NULL;
! 915:
! 916: bus_dmamap_sync(sc->sc_dmat, sc->sc_listmap,
! 917: offsetof(struct mtd_list_data, mtd_rx_list[i]),
! 918: sizeof(struct mtd_rx_desc),
! 919: BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
! 920:
! 921: cur_rx = &sc->mtd_ldata->mtd_rx_list[i];
! 922: rxstat = letoh32(cur_rx->rd_rsr);
! 923: m = sc->mtd_cdata.mtd_rx_chain[i].sd_mbuf;
! 924: total_len = RSR_FLNG_GET(rxstat);
! 925:
! 926: sc->mtd_cdata.mtd_rx_chain[i].sd_mbuf = NULL;
! 927:
! 928: /*
! 929: * If an error occurs, update stats, clear the
! 930: * status word and leave the mbuf cluster in place:
! 931: * it should simply get re-used next time this descriptor
! 932: * comes up in the ring.
! 933: */
! 934: if (rxstat & RSR_RXER) {
! 935: ifp->if_ierrors++;
! 936: mtd_newbuf(sc, i, m);
! 937: if (rxstat & RSR_CRC) {
! 938: i = (i + 1) % MTD_RX_LIST_CNT;
! 939: continue;
! 940: } else {
! 941: mtd_init(ifp);
! 942: return;
! 943: }
! 944: }
! 945:
! 946: /* No errors; receive the packet. */
! 947: total_len -= ETHER_CRC_LEN;
! 948:
! 949: bus_dmamap_sync(sc->sc_dmat, sc->mtd_cdata.mtd_rx_chain[i].sd_map,
! 950: 0, sc->mtd_cdata.mtd_rx_chain[i].sd_map->dm_mapsize,
! 951: BUS_DMASYNC_POSTREAD);
! 952:
! 953: m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, total_len + ETHER_ALIGN,
! 954: 0, ifp, NULL);
! 955: mtd_newbuf(sc, i, m);
! 956: i = (i + 1) % MTD_RX_LIST_CNT;
! 957: if (m0 == NULL) {
! 958: ifp->if_ierrors++;
! 959: continue;
! 960: }
! 961: m_adj(m0, ETHER_ALIGN);
! 962: m = m0;
! 963:
! 964: ifp->if_ipackets++;
! 965:
! 966: #if NBPFILTER > 0
! 967: if (ifp->if_bpf)
! 968: bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
! 969: #endif
! 970: ether_input_mbuf(ifp, m);
! 971: }
! 972:
! 973: sc->mtd_cdata.mtd_rx_prod = i;
! 974: }
! 975:
! 976:
! 977: /*
! 978: * This routine searches the RX ring for dirty descriptors in the
! 979: * event that the rxeof routine falls out of sync with the chip's
! 980: * current descriptor pointer. This may happen sometimes as a result
! 981: * of a "no RX buffer available" condition that happens when the chip
! 982: * consumes all of the RX buffers before the driver has a chance to
! 983: * process the RX ring. This routine may need to be called more than
! 984: * once to bring the driver back in sync with the chip, however we
! 985: * should still be getting RX DONE interrupts to drive the search
! 986: * for new packets in the RX ring, so we should catch up eventually.
! 987: */
! 988: static int
! 989: mtd_rx_resync(sc)
! 990: struct mtd_softc *sc;
! 991: {
! 992: int i, pos;
! 993: struct mtd_rx_desc *cur_rx;
! 994:
! 995: pos = sc->mtd_cdata.mtd_rx_prod;
! 996:
! 997: for (i = 0; i < MTD_RX_LIST_CNT; i++) {
! 998: bus_dmamap_sync(sc->sc_dmat, sc->sc_listmap,
! 999: offsetof(struct mtd_list_data, mtd_rx_list[pos]),
! 1000: sizeof(struct mtd_rx_desc),
! 1001: BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
! 1002:
! 1003: cur_rx = &sc->mtd_ldata->mtd_rx_list[pos];
! 1004: if (!(cur_rx->rd_rsr & htole32(RSR_OWN)))
! 1005: break;
! 1006: pos = (pos + 1) % MTD_RX_LIST_CNT;
! 1007: }
! 1008:
! 1009: /* If the ring really is empty, then just return. */
! 1010: if (i == MTD_RX_LIST_CNT)
! 1011: return (0);
! 1012:
! 1013: /* We've fallen behind the chip: catch it. */
! 1014: sc->mtd_cdata.mtd_rx_prod = pos;
! 1015:
! 1016: return (EAGAIN);
! 1017: }
! 1018:
! 1019:
! 1020: /*
! 1021: * A frame was downloaded to the chip. It's safe for us to clean up
! 1022: * the list buffers.
! 1023: */
! 1024: static void
! 1025: mtd_txeof(struct mtd_softc *sc)
! 1026: {
! 1027: struct mtd_tx_desc *cur_tx = NULL;
! 1028: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
! 1029: int idx;
! 1030:
! 1031: /* Clear the timeout timer. */
! 1032: ifp->if_timer = 0;
! 1033:
! 1034: /*
! 1035: * Go through our tx list and free mbufs for those
! 1036: * frames that have been transmitted.
! 1037: */
! 1038: idx = sc->mtd_cdata.mtd_tx_cons;
! 1039: while(idx != sc->mtd_cdata.mtd_tx_prod) {
! 1040: u_int32_t txstat;
! 1041:
! 1042: bus_dmamap_sync(sc->sc_dmat, sc->sc_listmap,
! 1043: offsetof(struct mtd_list_data, mtd_tx_list[idx]),
! 1044: sizeof(struct mtd_tx_desc),
! 1045: BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
! 1046:
! 1047: cur_tx = &sc->mtd_ldata->mtd_tx_list[idx];
! 1048: txstat = letoh32(cur_tx->td_tsw);
! 1049:
! 1050: if (txstat & TSW_OWN || txstat == TSW_UNSENT)
! 1051: break;
! 1052:
! 1053: if (!(cur_tx->td_tcw & htole32(TCW_LD))) {
! 1054: sc->mtd_cdata.mtd_tx_cnt--;
! 1055: idx = (idx + 1) % MTD_TX_LIST_CNT;
! 1056: continue;
! 1057: }
! 1058:
! 1059: if (CSR_READ_4(MTD_TCRRCR) & TCR_ENHANCED)
! 1060: ifp->if_collisions += TSR_NCR_GET(CSR_READ_4(MTD_TSR));
! 1061: else {
! 1062: if (txstat & TSW_TXERR) {
! 1063: ifp->if_oerrors++;
! 1064: if (txstat & TSW_EC)
! 1065: ifp->if_collisions++;
! 1066: if (txstat & TSW_LC)
! 1067: ifp->if_collisions++;
! 1068: }
! 1069: ifp->if_collisions += TSW_NCR_GET(txstat);
! 1070: }
! 1071:
! 1072: ifp->if_opackets++;
! 1073: if (sc->mtd_cdata.mtd_tx_chain[idx].sd_map->dm_nsegs != 0) {
! 1074: bus_dmamap_t map =
! 1075: sc->mtd_cdata.mtd_tx_chain[idx].sd_map;
! 1076: bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
! 1077: BUS_DMASYNC_POSTWRITE);
! 1078: bus_dmamap_unload(sc->sc_dmat, map);
! 1079: }
! 1080: if (sc->mtd_cdata.mtd_tx_chain[idx].sd_mbuf != NULL) {
! 1081: m_freem(sc->mtd_cdata.mtd_tx_chain[idx].sd_mbuf);
! 1082: sc->mtd_cdata.mtd_tx_chain[idx].sd_mbuf = NULL;
! 1083: }
! 1084: sc->mtd_cdata.mtd_tx_cnt--;
! 1085: idx = (idx + 1) % MTD_TX_LIST_CNT;
! 1086: }
! 1087:
! 1088: if (cur_tx != NULL) {
! 1089: ifp->if_flags &= ~IFF_OACTIVE;
! 1090: sc->mtd_cdata.mtd_tx_cons = idx;
! 1091: } else
! 1092: if (sc->mtd_ldata->mtd_tx_list[idx].td_tsw ==
! 1093: htole32(TSW_UNSENT)) {
! 1094: sc->mtd_ldata->mtd_tx_list[idx].td_tsw =
! 1095: htole32(TSW_OWN);
! 1096: ifp->if_timer = 5;
! 1097: CSR_WRITE_4(MTD_TXPDR, 0xffffffff);
! 1098: }
! 1099: }
! 1100:
! 1101: struct cfdriver mtd_cd = {
! 1102: 0, "mtd", DV_IFNET
! 1103: };
CVSweb