Index: sys/arch/sgimips/conf/files.sgimips =================================================================== RCS file: /cvsroot/src/sys/arch/sgimips/conf/files.sgimips,v retrieving revision 1.41 diff -u -r1.41 files.sgimips --- sys/arch/sgimips/conf/files.sgimips 16 Sep 2006 08:50:27 -0000 1.41 +++ sys/arch/sgimips/conf/files.sgimips 22 Aug 2008 15:56:03 -0000 @@ -37,6 +37,7 @@ # Machine-dependent drivers include "arch/sgimips/ioc/files.ioc" include "arch/sgimips/dev/files.dev" +defflag opt_sgimace.h MACE_NEEDS_DELAYS include "arch/sgimips/mace/files.mace" # Machine-independent drivers. Index: sys/arch/sgimips/mace/if_mec.c =================================================================== RCS file: /cvsroot/src/sys/arch/sgimips/mace/if_mec.c,v retrieving revision 1.8 diff -u -r1.8 if_mec.c --- sys/arch/sgimips/mace/if_mec.c 2 Apr 2006 11:20:46 -0000 1.8 +++ sys/arch/sgimips/mace/if_mec.c 22 Aug 2008 15:56:04 -0000 @@ -1,8 +1,7 @@ /* $NetBSD: if_mec.c,v 1.8 2006/04/02 11:20:46 tsutsui Exp $ */ -/* - * Copyright (c) 2004 Izumi Tsutsui. - * All rights reserved. +/*- + * Copyright (c) 2004, 2008 Izumi Tsutsui. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -12,8 +11,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -60,7 +57,7 @@ */ /* - * MACE MAC-110 ethernet driver + * MACE MAC-110 Ethernet driver */ #include @@ -116,18 +113,29 @@ #define MEC_DEBUG_INTR 0x08 #define MEC_DEBUG_RXINTR 0x10 #define MEC_DEBUG_TXINTR 0x20 +#define MEC_DEBUG_TXSEGS 0x40 uint32_t mec_debug = 0; #define DPRINTF(x, y) if (mec_debug & (x)) printf y #else #define DPRINTF(x, y) /* nothing */ #endif +#define MEC_EVENT_COUNTERS + +#ifdef MEC_EVENT_COUNTERS +#define MEC_EVCNT_INCR(ev) (ev)->ev_count++ +#else +#define MEC_EVCNT_INCR(ev) do {} while (/* CONSTCOND */ 0) +#endif + /* * Transmit descriptor list size */ #define MEC_NTXDESC 64 #define MEC_NTXDESC_MASK (MEC_NTXDESC - 1) #define MEC_NEXTTX(x) (((x) + 1) & MEC_NTXDESC_MASK) +#define MEC_NTXDESC_RSVD 4 +#define MEC_NTXDESC_INTR 8 /* * software state for TX @@ -137,8 +145,7 @@ bus_dmamap_t txs_dmamap; /* our DMA map */ uint32_t txs_flags; #define MEC_TXS_BUFLEN_MASK 0x0000007f /* data len in txd_buf */ -#define MEC_TXS_TXDBUF 0x00000080 /* txd_buf is used */ -#define MEC_TXS_TXDPTR1 0x00000100 /* txd_ptr[0] is used */ +#define MEC_TXS_TXDPTR 0x00000080 /* concat txd_ptr is used */ }; /* @@ -146,13 +153,17 @@ */ #define MEC_TXDESCSIZE 128 #define MEC_NTXPTR 3 -#define MEC_TXD_BUFOFFSET \ - (sizeof(uint64_t) + MEC_NTXPTR * sizeof(uint64_t)) +#define MEC_TXD_BUFOFFSET sizeof(uint64_t) +#define MEC_TXD_BUFOFFSET1 \ + (sizeof(uint64_t) + sizeof(uint64_t) * MEC_NTXPTR) #define MEC_TXD_BUFSIZE (MEC_TXDESCSIZE - MEC_TXD_BUFOFFSET) +#define MEC_TXD_BUFSIZE1 (MEC_TXDESCSIZE - MEC_TXD_BUFOFFSET1) #define MEC_TXD_BUFSTART(len) (MEC_TXD_BUFSIZE - (len)) #define MEC_TXD_ALIGN 8 +#define MEC_TXD_ALIGNMASK (MEC_TXD_ALIGN - 1) #define MEC_TXD_ROUNDUP(addr) \ - (((addr) + (MEC_TXD_ALIGN - 1)) & ~((uint64_t)MEC_TXD_ALIGN - 1)) + (((addr) + MEC_TXD_ALIGNMASK) & ~(uint64_t)MEC_TXD_ALIGNMASK) +#define MEC_NTXSEG 16 struct mec_txdesc { volatile uint64_t txd_cmd; @@ -182,14 +193,18 @@ #define MEC_TXSTAT_UNUSED 0x7fffffffe0000000ULL /* should be zero */ #define MEC_TXSTAT_SENT 0x8000000000000000ULL /* packet sent */ - uint64_t txd_ptr[MEC_NTXPTR]; + union { + uint64_t txptr[MEC_NTXPTR]; #define MEC_TXPTR_UNUSED2 0x0000000000000007 /* should be zero */ #define MEC_TXPTR_DMAADDR 0x00000000fffffff8 /* TX DMA address */ #define MEC_TXPTR_LEN 0x0000ffff00000000ULL /* buffer length */ #define TXPTR_LEN(x) ((uint64_t)(x) << 32) #define MEC_TXPTR_UNUSED1 0xffff000000000000ULL /* should be zero */ - uint8_t txd_buf[MEC_TXD_BUFSIZE]; + uint8_t txbuf[MEC_TXD_BUFSIZE]; + } txd_data; +#define txd_ptr txd_data.txptr +#define txd_buf txd_data.txbuf }; /* @@ -271,7 +286,7 @@ bus_space_tag_t sc_st; /* bus_space tag */ bus_space_handle_t sc_sh; /* bus_space handle */ bus_dma_tag_t sc_dmat; /* bus_dma tag */ - void *sc_sdhook; /* shoutdown hook */ + void *sc_sdhook; /* shutdown hook */ struct ethercom sc_ethercom; /* Ethernet common part */ @@ -284,7 +299,7 @@ bus_dmamap_t sc_cddmamap; /* bus_dma map for control data */ #define sc_cddma sc_cddmamap->dm_segs[0].ds_addr - /* pointer to allocalted control data */ + /* pointer to allocated control data */ struct mec_control_data *sc_control_data; #define sc_txdesc sc_control_data->mcd_txdesc #define sc_rxdesc sc_control_data->mcd_rxdesc @@ -301,6 +316,52 @@ #if NRND > 0 rndsource_element_t sc_rnd_source; /* random source */ #endif +#ifdef MEC_EVENT_COUNTERS + struct evcnt sc_ev_txpkts; /* TX packets queued total */ + struct evcnt sc_ev_txdpad; /* TX packets padded in txdesc buf */ + struct evcnt sc_ev_txdbuf; /* TX packets copied to txdesc buf */ + struct evcnt sc_ev_txptr1; /* TX packets using concat ptr1 */ + struct evcnt sc_ev_txptr1a; /* TX packets w/ptr1 ~160bytes */ + struct evcnt sc_ev_txptr1b; /* TX packets w/ptr1 ~256bytes */ + struct evcnt sc_ev_txptr1c; /* TX packets w/ptr1 ~512bytes */ + struct evcnt sc_ev_txptr1d; /* TX packets w/ptr1 ~1024bytes */ + struct evcnt sc_ev_txptr1e; /* TX packets w/ptr1 >1024bytes */ + struct evcnt sc_ev_txptr2; /* TX packets using concat ptr1,2 */ + struct evcnt sc_ev_txptr2a; /* TX packets w/ptr2 ~160bytes */ + struct evcnt sc_ev_txptr2b; /* TX packets w/ptr2 ~256bytes */ + struct evcnt sc_ev_txptr2c; /* TX packets w/ptr2 ~512bytes */ + struct evcnt sc_ev_txptr2d; /* TX packets w/ptr2 ~1024bytes */ + struct evcnt sc_ev_txptr2e; /* TX packets w/ptr2 >1024bytes */ + struct evcnt sc_ev_txptr3; /* TX packets using concat ptr1,2,3 */ + struct evcnt sc_ev_txptr3a; /* TX packets w/ptr3 ~160bytes */ + struct evcnt sc_ev_txptr3b; /* TX packets w/ptr3 ~256bytes */ + struct evcnt sc_ev_txptr3c; /* TX packets w/ptr3 ~512bytes */ + struct evcnt sc_ev_txptr3d; /* TX packets w/ptr3 ~1024bytes */ + struct evcnt sc_ev_txptr3e; /* TX packets w/ptr3 >1024bytes */ + struct evcnt sc_ev_txmbuf; /* TX packets copied to new mbufs */ + struct evcnt sc_ev_txmbufa; /* TX packets w/mbuf ~160bytes */ + struct evcnt sc_ev_txmbufb; /* TX packets w/mbuf ~256bytes */ + struct evcnt sc_ev_txmbufc; /* TX packets w/mbuf ~512bytes */ + struct evcnt sc_ev_txmbufd; /* TX packets w/mbuf ~1024bytes */ + struct evcnt sc_ev_txmbufe; /* TX packets w/mbuf >1024bytes */ + struct evcnt sc_ev_txptrs; /* TX packets using ptrs total */ + struct evcnt sc_ev_txptrc0; /* TX packets w/ptrs no hdr chain */ + struct evcnt sc_ev_txptrc1; /* TX packets w/ptrs 1 hdr chain */ + struct evcnt sc_ev_txptrc2; /* TX packets w/ptrs 2 hdr chains */ + struct evcnt sc_ev_txptrc3; /* TX packets w/ptrs 3 hdr chains */ + struct evcnt sc_ev_txptrc4; /* TX packets w/ptrs 4 hdr chains */ + struct evcnt sc_ev_txptrc5; /* TX packets w/ptrs 5 hdr chains */ + struct evcnt sc_ev_txptrc6; /* TX packets w/ptrs >5 hdr chains */ + struct evcnt sc_ev_txptrh0; /* TX packets w/ptrs ~8bytes hdr */ + struct evcnt sc_ev_txptrh1; /* TX packets w/ptrs ~16bytes hdr */ + struct evcnt sc_ev_txptrh2; /* TX packets w/ptrs ~32bytes hdr */ + struct evcnt sc_ev_txptrh3; /* TX packets w/ptrs ~64bytes hdr */ + struct evcnt sc_ev_txptrh4; /* TX packets w/ptrs ~80bytes hdr */ + struct evcnt sc_ev_txptrh5; /* TX packets w/ptrs ~96bytes hdr */ + struct evcnt sc_ev_txdstall; /* TX stalled due to no txdesc */ + struct evcnt sc_ev_txempty; /* TX empty interrupts */ + struct evcnt sc_ev_txsent; /* TX sent interrupts */ +#endif }; #define MEC_CDTXADDR(sc, x) ((sc)->sc_cddma + MEC_CDTXOFF(x)) @@ -353,7 +414,7 @@ STATIC int mec_intr(void *arg); STATIC void mec_stop(struct ifnet *, int); STATIC void mec_rxintr(struct mec_softc *); -STATIC void mec_txintr(struct mec_softc *); +STATIC void mec_txintr(struct mec_softc *, uint32_t); STATIC void mec_shutdown(void *); CFATTACH_DECL(mec, sizeof(struct mec_softc), @@ -379,11 +440,12 @@ struct mec_softc *sc = (void *)self; struct mace_attach_args *maa = aux; struct ifnet *ifp = &sc->sc_ethercom.ec_if; - uint32_t command; + uint64_t address, command; const char *macaddr; struct mii_softc *child; bus_dma_segment_t seg; int i, err, rseg; + bool mac_is_fake; sc->sc_st = maa->maa_st; if (bus_space_subregion(sc->sc_st, maa->maa_sh, @@ -411,7 +473,7 @@ * BUS_DMA_COHERENT (which disables cache) may cause some performance * issue on copying data from the RX buffer to mbuf on normal memory, * though we have to make sure all bus_dmamap_sync(9) ops are called - * proprely in that case. + * properly in that case. */ if ((err = bus_dmamem_map(sc->sc_dmat, &seg, rseg, sizeof(struct mec_control_data), @@ -439,7 +501,7 @@ /* create TX buffer DMA maps */ for (i = 0; i < MEC_NTXDESC; i++) { if ((err = bus_dmamap_create(sc->sc_dmat, - MCLBYTES, 1, MCLBYTES, 0, 0, + MCLBYTES, MEC_NTXSEG, MCLBYTES, PAGE_SIZE, 0, &sc->sc_txsoft[i].txs_dmamap)) != 0) { printf(": unable to create tx DMA map %d, error = %d\n", i, err); @@ -449,21 +511,69 @@ callout_init(&sc->sc_tick_ch); - /* get ethernet address from ARCBIOS */ + /* get Ethernet address from ARCBIOS */ if ((macaddr = ARCBIOS->GetEnvironmentVariable("eaddr")) == NULL) { printf(": unable to get MAC address!\n"); goto fail_4; } - enaddr_aton(macaddr, sc->sc_enaddr); + /* + * On some machines the DS2502 chip storing the serial number/ + * mac address is on the pci riser board - if this board is + * missing, ARCBIOS will not know a good ethernet address (but + * otherwise the machine will work fine). + */ + mac_is_fake = false; + if (strcmp(macaddr, "ff:ff:ff:ff:ff:ff") == 0) { + uint32_t ui = 0; + const char * netaddr = + ARCBIOS->GetEnvironmentVariable("netaddr"); + + /* + * Create a MAC address by abusing the "netaddr" env var + */ + sc->sc_enaddr[0] = 0xf2; + sc->sc_enaddr[1] = 0x0b; + sc->sc_enaddr[2] = 0xa4; + if (netaddr) { + mac_is_fake = true; + while (*netaddr) { + int v = 0; + while (*netaddr && *netaddr != '.') { + if (*netaddr >= '0' && *netaddr <= '9') + v = v*10 + (*netaddr - '0'); + netaddr++; + } + ui <<= 8; + ui |= v; + if (*netaddr == '.') + netaddr++; + } + } + memcpy(sc->sc_enaddr+3, ((uint8_t *)&ui)+1, 3); + } + if (!mac_is_fake) + enaddr_aton(macaddr, sc->sc_enaddr); + + /* set the Ethernet address */ + address = 0; + for (i = 0; i < ETHER_ADDR_LEN; i++) { + address = address << 8; + address |= sc->sc_enaddr[i]; + } + bus_space_write_8(sc->sc_st, sc->sc_sh, MEC_STATION, address); /* reset device */ mec_reset(sc); command = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_MAC_CONTROL); - printf(": MAC-110 Ethernet, rev %d\n", - (command & MEC_MAC_REVISION) >> MEC_MAC_REVISION_SHIFT); + printf(": MAC-110 Ethernet, rev %u\n", + (u_int)((command & MEC_MAC_REVISION) >> MEC_MAC_REVISION_SHIFT)); + if (mac_is_fake) + printf("%s: could not get ethernet address from firmware" + " - generated one from the \"netaddr\" environment" + " variable\n", sc->sc_dev.dv_xname); printf("%s: Ethernet address %s\n", sc->sc_dev.dv_xname, ether_sprintf(sc->sc_enaddr)); @@ -513,6 +623,97 @@ RND_TYPE_NET, 0); #endif +#ifdef MEC_EVENT_COUNTERS + evcnt_attach_dynamic(&sc->sc_ev_txpkts , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts queued total"); + evcnt_attach_dynamic(&sc->sc_ev_txdpad , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts padded in txdesc buf"); + evcnt_attach_dynamic(&sc->sc_ev_txdbuf , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts copied to txdesc buf"); + evcnt_attach_dynamic(&sc->sc_ev_txptr1 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts using concat ptr1"); + evcnt_attach_dynamic(&sc->sc_ev_txptr1a , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr1 ~160bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr1b , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr1 ~256bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr1c , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr1 ~512bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr1d , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr1 ~1024bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr1e , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr1 >1024bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr2 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts using concat ptr1,2"); + evcnt_attach_dynamic(&sc->sc_ev_txptr2a , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr2 ~160bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr2b , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr2 ~256bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr2c , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr2 ~512bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr2d , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr2 ~1024bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr2e , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr2 >1024bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr3 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts using concat ptr1,2,3"); + evcnt_attach_dynamic(&sc->sc_ev_txptr3a , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr3 ~160bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr3b , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr3 ~256bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr3c , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr3 ~512bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr3d , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr3 ~1024bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptr3e , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptr3 >1024bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txmbuf , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts copied to new mbufs"); + evcnt_attach_dynamic(&sc->sc_ev_txmbufa , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/mbuf ~160bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txmbufb , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/mbuf ~256bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txmbufc , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/mbuf ~512bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txmbufd , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/mbuf ~1024bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txmbufe , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/mbuf >1024bytes"); + evcnt_attach_dynamic(&sc->sc_ev_txptrs , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts using ptrs total"); + evcnt_attach_dynamic(&sc->sc_ev_txptrc0 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs no hdr chain"); + evcnt_attach_dynamic(&sc->sc_ev_txptrc1 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs 1 hdr chain"); + evcnt_attach_dynamic(&sc->sc_ev_txptrc2 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs 2 hdr chains"); + evcnt_attach_dynamic(&sc->sc_ev_txptrc3 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs 3 hdr chains"); + evcnt_attach_dynamic(&sc->sc_ev_txptrc4 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs 4 hdr chains"); + evcnt_attach_dynamic(&sc->sc_ev_txptrc5 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs 5 hdr chains"); + evcnt_attach_dynamic(&sc->sc_ev_txptrc6 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs >5 hdr chains"); + evcnt_attach_dynamic(&sc->sc_ev_txptrh0 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs ~8bytes hdr"); + evcnt_attach_dynamic(&sc->sc_ev_txptrh1 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs ~16bytes hdr"); + evcnt_attach_dynamic(&sc->sc_ev_txptrh2 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs ~32bytes hdr"); + evcnt_attach_dynamic(&sc->sc_ev_txptrh3 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs ~64bytes hdr"); + evcnt_attach_dynamic(&sc->sc_ev_txptrh4 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs ~80bytes hdr"); + evcnt_attach_dynamic(&sc->sc_ev_txptrh5 , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX pkts w/ptrs ~96bytes hdr"); + evcnt_attach_dynamic(&sc->sc_ev_txdstall , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX stalled due to no txdesc"); + evcnt_attach_dynamic(&sc->sc_ev_txempty , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX empty interrupts"); + evcnt_attach_dynamic(&sc->sc_ev_txsent , EVCNT_TYPE_MISC, + NULL, device_xname(self), "TX sent interrupts"); +#endif + /* set shutdown hook to reset interface on powerdown */ sc->sc_sdhook = shutdownhook_establish(mec_shutdown, sc); @@ -554,8 +755,10 @@ bus_space_write_8(st, sh, MEC_PHY_ADDRESS, (phy << MEC_PHY_ADDR_DEVSHIFT) | (reg & MEC_PHY_ADDR_REGISTER)); + delay(25); bus_space_write_8(st, sh, MEC_PHY_READ_INITIATE, 1); delay(25); + mec_mii_wait(sc); for (i = 0; i < 20; i++) { delay(30); @@ -607,8 +810,10 @@ if ((busy & MEC_PHY_DATA_BUSY) == 0) return 0; +#if 0 if (busy == 0xffff) /* XXX ? */ return 0; +#endif } printf("%s: MII timed out\n", sc->sc_dev.dv_xname); @@ -757,8 +962,10 @@ { bus_space_tag_t st = sc->sc_st; bus_space_handle_t sh = sc->sc_sh; - uint64_t address, control; - int i; + uint64_t control; + + /* stop DMA first */ + bus_space_write_8(st, sh, MEC_DMA_CONTROL, 0); /* reset chip */ bus_space_write_8(st, sh, MEC_MAC_CONTROL, MEC_MAC_CORE_RESET); @@ -766,19 +973,12 @@ bus_space_write_8(st, sh, MEC_MAC_CONTROL, 0); delay(1000); - /* set ethernet address */ - address = 0; - for (i = 0; i < ETHER_ADDR_LEN; i++) { - address = address << 8; - address += sc->sc_enaddr[i]; - } - bus_space_write_8(st, sh, MEC_STATION, address); - - /* Default to 100/half and let autonegotiation work its magic */ + /* Default to 100/half and let auto-negotiation work its magic */ control = MEC_MAC_SPEED_SELECT | MEC_MAC_FILTER_MATCHMULTI | MEC_MAC_IPG_DEFAULT; bus_space_write_8(st, sh, MEC_MAC_CONTROL, control); + /* stop DMA again for sanity */ bus_space_write_8(st, sh, MEC_DMA_CONTROL, 0); DPRINTF(MEC_DEBUG_RESET, ("mec: control now %llx\n", @@ -795,9 +995,9 @@ bus_dmamap_t dmamap; bus_space_tag_t st = sc->sc_st; bus_space_handle_t sh = sc->sc_sh; - uint64_t txdaddr; int error, firsttx, nexttx, opending; - int len, bufoff, buflen, unaligned, txdlen; + int len, bufoff, buflen, nsegs, align, resid, pseg, nptr, slen, i; + uint32_t txdcmd; if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING) return; @@ -811,79 +1011,172 @@ DPRINTF(MEC_DEBUG_START, ("mec_start: opending = %d, firsttx = %d\n", opending, firsttx)); - for (;;) { + while (sc->sc_txpending < MEC_NTXDESC - 1) { /* Grab a packet off the queue. */ IFQ_POLL(&ifp->if_snd, m0); if (m0 == NULL) break; m = NULL; - if (sc->sc_txpending == MEC_NTXDESC) { - break; - } - /* * Get the next available transmit descriptor. */ nexttx = MEC_NEXTTX(sc->sc_txlast); txd = &sc->sc_txdesc[nexttx]; txs = &sc->sc_txsoft[nexttx]; + dmamap = txs->txs_dmamap; + txs->txs_flags = 0; buflen = 0; bufoff = 0; - txdaddr = 0; /* XXX gcc */ - txdlen = 0; /* XXX gcc */ + resid = 0; + nptr = 0; /* XXX gcc */ + pseg = 0; /* XXX gcc */ len = m0->m_pkthdr.len; DPRINTF(MEC_DEBUG_START, - ("mec_start: len = %d, nexttx = %d\n", len, nexttx)); + ("mec_start: len = %d, nexttx = %d, txpending = %d\n", + len, nexttx, sc->sc_txpending)); - if (len < ETHER_PAD_LEN) { + + if (len <= MEC_TXD_BUFSIZE) { /* - * I don't know if MEC chip does auto padding, - * so if the packet is small enough, - * just copy it to the buffer in txdesc. - * Maybe this is the simple way. + * If a TX packet will fit into small txdesc buffer, + * just copy it into there. Maybe it's faster than + * checking alignment and calling bus_dma(9) etc. */ DPRINTF(MEC_DEBUG_START, ("mec_start: short packet\n")); - IFQ_DEQUEUE(&ifp->if_snd, m0); - bufoff = MEC_TXD_BUFSTART(ETHER_PAD_LEN); - m_copydata(m0, 0, m0->m_pkthdr.len, - txd->txd_buf + bufoff); - memset(txd->txd_buf + bufoff + len, 0, - ETHER_PAD_LEN - len); - len = buflen = ETHER_PAD_LEN; - txs->txs_flags = MEC_TXS_TXDBUF | buflen; + /* + * I don't know if MEC chip does auto padding, + * but do it manually for safety. + */ + if (len < ETHER_PAD_LEN) { + MEC_EVCNT_INCR(&sc->sc_ev_txdpad); + bufoff = MEC_TXD_BUFSTART(ETHER_PAD_LEN); + m_copydata(m0, 0, len, txd->txd_buf + bufoff); + memset(txd->txd_buf + bufoff + len, 0, + ETHER_PAD_LEN - len); + len = buflen = ETHER_PAD_LEN; + } else { + MEC_EVCNT_INCR(&sc->sc_ev_txdbuf); + bufoff = MEC_TXD_BUFSTART(len); + m_copydata(m0, 0, len, txd->txd_buf + bufoff); + buflen = len; + } } else { /* - * If the packet won't fit the buffer in txdesc, - * we have to use concatinate pointer to handle it. - * While MEC can handle up to three segments to - * concatinate, MEC requires that both the second and - * third segments have to be 8 byte aligned. - * Since it's unlikely for mbuf clusters, we use - * only the first concatinate pointer. If the packet - * doesn't fit in one DMA segment, allocate new mbuf - * and copy the packet to it. - * - * Besides, if the start address of the first segments - * is not 8 byte aligned, such part have to be copied - * to the txdesc buffer. (XXX see below comments) - */ + * If the packet won't fit the static buffer in txdesc, + * we have to use the concatenate pointers to handle it. + */ DPRINTF(MEC_DEBUG_START, ("mec_start: long packet\n")); + txs->txs_flags = MEC_TXS_TXDPTR; - dmamap = txs->txs_dmamap; - if (bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, - BUS_DMA_WRITE | BUS_DMA_NOWAIT) != 0) { - DPRINTF(MEC_DEBUG_START, + /* + * Call bus_dmamap_load_mbuf(9) first to see + * how many chains the TX mbuf has. + */ + error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, + BUS_DMA_WRITE | BUS_DMA_NOWAIT); + if (error == 0) { + /* + * Check chains which might contain headers. + * They might be so much fragmented and + * it's better to copy them into txdesc buffer + * since they would be small enough. + */ + nsegs = dmamap->dm_nsegs; + for (pseg = 0; pseg < nsegs; pseg++) { + slen = dmamap->dm_segs[pseg].ds_len; + if (buflen + slen > + MEC_TXD_BUFSIZE1 - MEC_TXD_ALIGN) + break; + buflen += slen; + } + /* + * Check if the rest chains can be fit into + * the concatinate pointers. + */ + align = dmamap->dm_segs[pseg].ds_addr & + MEC_TXD_ALIGNMASK; + if (align > 0) { + /* + * If the first chain isn't uint64_t + * aligned, append the unaligned part + * into txdesc buffer too. + */ + resid = MEC_TXD_ALIGN - align; + buflen += resid; + for (; pseg < nsegs; pseg++) { + slen = + dmamap->dm_segs[pseg].ds_len; + if (slen > resid) + break; + resid -= slen; + } + } else if (pseg == 0) { + /* + * In this case, the first chain is + * uint64_t aligned but it's too long + * to put into txdesc buf. + * We have to put some data into + * txdesc buf even in this case, + * so put MEC_TXD_ALIGN bytes there. + */ + buflen = resid = MEC_TXD_ALIGN; + } + nptr = nsegs - pseg; + if (nptr <= MEC_NTXPTR) { + bufoff = MEC_TXD_BUFSTART(buflen); + + /* + * Check if all the rest chains are + * uint64_t aligned. + */ + align = 0; + for (i = pseg + 1; i < nsegs; i++) + align |= + dmamap->dm_segs[i].ds_addr + & MEC_TXD_ALIGNMASK; + if (align != 0) { + /* chains are not aligned */ + error = -1; + } + } else { + /* The TX mbuf chains doesn't fit. */ + error = -1; + } + if (error == -1) + bus_dmamap_unload(sc->sc_dmat, dmamap); + } + if (error != 0) { + /* + * The TX mbuf chains can't be put into + * the concatinate buffers. In this case, + * we have to allocate a new contiguous mbuf + * and copy data into it. + * + * Even in this case, the Ethernet header in + * the TX mbuf might be unaligned and trailing + * data might be word aligned, so put 2 byte + * (MEC_ETHER_ALIGN) padding at the top of the + * allocated mbuf and copy TX packets. + * 6 bytes (MEC_ALIGN_BYTES - MEC_ETHER_ALIGN) + * at the top of the new mbuf won't be uint64_t + * alignd, but we have to put some data into + * txdesc buffer anyway even if the buffer + * is uint64_t aligned. + */ + DPRINTF(MEC_DEBUG_START|MEC_DEBUG_TXSEGS, ("mec_start: re-allocating mbuf\n")); + MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { printf("%s: unable to allocate " - "TX mbuf\n", sc->sc_dev.dv_xname); + "TX mbuf\n", + device_xname(&sc->sc_dev)); break; } if (len > (MHLEN - MEC_ETHER_ALIGN)) { @@ -891,95 +1184,157 @@ if ((m->m_flags & M_EXT) == 0) { printf("%s: unable to allocate " "TX cluster\n", - sc->sc_dev.dv_xname); + device_xname(&sc->sc_dev)); m_freem(m); break; } } + m->m_data += MEC_ETHER_ALIGN; + /* - * Each packet has the Ethernet header, so - * in many case the header isn't 4-byte aligned - * and data after the header is 4-byte aligned. - * Thus adding 2-byte offset before copying to - * new mbuf avoids unaligned copy and this may - * improve some performance. - * As noted above, unaligned part has to be - * copied to txdesc buffer so this may cause - * extra copy ops, but for now MEC always - * requires some data in txdesc buffer, - * so we always have to copy some data anyway. + * Copy whole data (including unaligned part) + * for following bpf_mtap(). */ - m->m_data += MEC_ETHER_ALIGN; - m_copydata(m0, 0, len, mtod(m, caddr_t)); + m_copydata(m0, 0, len, mtod(m, void *)); m->m_pkthdr.len = m->m_len = len; error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m, BUS_DMA_WRITE | BUS_DMA_NOWAIT); - if (error) { + if (dmamap->dm_nsegs > 1) { + /* should not happen, but for sanity */ + bus_dmamap_unload(sc->sc_dmat, dmamap); + error = -1; + } + if (error != 0) { printf("%s: unable to load TX buffer, " "error = %d\n", - sc->sc_dev.dv_xname, error); + device_xname(&sc->sc_dev), error); + m_freem(m); break; } - } - IFQ_DEQUEUE(&ifp->if_snd, m0); - if (m != NULL) { - m_freem(m0); - m0 = m; - } + /* + * Only the first segment should be put into + * the concatinate pointer in this case. + */ + pseg = 0; + nptr = 1; - /* handle unaligned part */ - txdaddr = MEC_TXD_ROUNDUP(dmamap->dm_segs[0].ds_addr); - txs->txs_flags = MEC_TXS_TXDPTR1; - unaligned = - dmamap->dm_segs[0].ds_addr & (MEC_TXD_ALIGN - 1); - DPRINTF(MEC_DEBUG_START, - ("mec_start: ds_addr = 0x%08x, unaligned = %d\n", - (u_int)dmamap->dm_segs[0].ds_addr, unaligned)); - if (unaligned != 0) { - buflen = MEC_TXD_ALIGN - unaligned; - bufoff = MEC_TXD_BUFSTART(buflen); - DPRINTF(MEC_DEBUG_START, - ("mec_start: unaligned, " - "buflen = %d, bufoff = %d\n", - buflen, bufoff)); - memcpy(txd->txd_buf + bufoff, - mtod(m0, caddr_t), buflen); - txs->txs_flags |= MEC_TXS_TXDBUF | buflen; - } -#if 1 - else { /* - * XXX needs hardware info XXX - * It seems MEC always requires some data - * in txd_buf[] even if buffer is - * 8-byte aligned otherwise DMA abort error - * occurs later... + * Set lenght of unaligned part which will be + * copied into txdesc buffer. */ - buflen = MEC_TXD_ALIGN; + buflen = MEC_TXD_ALIGN - MEC_ETHER_ALIGN; bufoff = MEC_TXD_BUFSTART(buflen); - memcpy(txd->txd_buf + bufoff, - mtod(m0, caddr_t), buflen); - DPRINTF(MEC_DEBUG_START, - ("mec_start: aligned, " - "buflen = %d, bufoff = %d\n", - buflen, bufoff)); - txs->txs_flags |= MEC_TXS_TXDBUF | buflen; - txdaddr += MEC_TXD_ALIGN; + resid = buflen; +#ifdef MEC_EVENT_COUNTERS + MEC_EVCNT_INCR(&sc->sc_ev_txmbuf); + if (len <= 160) + MEC_EVCNT_INCR(&sc->sc_ev_txmbufa); + else if (len <= 256) + MEC_EVCNT_INCR(&sc->sc_ev_txmbufb); + else if (len <= 512) + MEC_EVCNT_INCR(&sc->sc_ev_txmbufc); + else if (len <= 1024) + MEC_EVCNT_INCR(&sc->sc_ev_txmbufd); + else + MEC_EVCNT_INCR(&sc->sc_ev_txmbufe); +#endif + } +#ifdef MEC_EVENT_COUNTERS + else { + MEC_EVCNT_INCR(&sc->sc_ev_txptrs); + if (nptr == 1) { + MEC_EVCNT_INCR(&sc->sc_ev_txptr1); + if (len <= 160) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr1a); + else if (len <= 256) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr1b); + else if (len <= 512) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr1c); + else if (len <= 1024) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr1d); + else + MEC_EVCNT_INCR( + &sc->sc_ev_txptr1e); + } else if (nptr == 2) { + MEC_EVCNT_INCR(&sc->sc_ev_txptr2); + if (len <= 160) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr2a); + else if (len <= 256) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr2b); + else if (len <= 512) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr2c); + else if (len <= 1024) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr2d); + else + MEC_EVCNT_INCR( + &sc->sc_ev_txptr2e); + } else if (nptr == 3) { + MEC_EVCNT_INCR(&sc->sc_ev_txptr3); + if (len <= 160) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr3a); + else if (len <= 256) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr3b); + else if (len <= 512) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr3c); + else if (len <= 1024) + MEC_EVCNT_INCR( + &sc->sc_ev_txptr3d); + else + MEC_EVCNT_INCR( + &sc->sc_ev_txptr3e); + } + if (pseg == 0) + MEC_EVCNT_INCR(&sc->sc_ev_txptrc0); + else if (pseg == 1) + MEC_EVCNT_INCR(&sc->sc_ev_txptrc1); + else if (pseg == 2) + MEC_EVCNT_INCR(&sc->sc_ev_txptrc2); + else if (pseg == 3) + MEC_EVCNT_INCR(&sc->sc_ev_txptrc3); + else if (pseg == 4) + MEC_EVCNT_INCR(&sc->sc_ev_txptrc4); + else if (pseg == 5) + MEC_EVCNT_INCR(&sc->sc_ev_txptrc5); + else + MEC_EVCNT_INCR(&sc->sc_ev_txptrc6); + if (buflen <= 8) + MEC_EVCNT_INCR(&sc->sc_ev_txptrh0); + else if (buflen <= 16) + MEC_EVCNT_INCR(&sc->sc_ev_txptrh1); + else if (buflen <= 32) + MEC_EVCNT_INCR(&sc->sc_ev_txptrh2); + else if (buflen <= 64) + MEC_EVCNT_INCR(&sc->sc_ev_txptrh3); + else if (buflen <= 80) + MEC_EVCNT_INCR(&sc->sc_ev_txptrh4); + else + MEC_EVCNT_INCR(&sc->sc_ev_txptrh5); } #endif - txdlen = len - buflen; - DPRINTF(MEC_DEBUG_START, - ("mec_start: txdaddr = 0x%08llx, txdlen = %d\n", - txdaddr, txdlen)); + m_copydata(m0, 0, buflen, txd->txd_buf + bufoff); + + IFQ_DEQUEUE(&ifp->if_snd, m0); + if (m != NULL) { + m_freem(m0); + m0 = m; + } /* * sync the DMA map for TX mbuf - * - * XXX unaligned part doesn't have to be sync'ed, - * but it's harmless... */ - bus_dmamap_sync(sc->sc_dmat, dmamap, 0, - dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, dmamap, buflen, + len - buflen, BUS_DMASYNC_PREWRITE); } #if NBPFILTER > 0 @@ -989,39 +1344,80 @@ if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m0); #endif + MEC_EVCNT_INCR(&sc->sc_ev_txpkts); /* * setup the transmit descriptor. */ + txdcmd = TXCMD_BUFSTART(MEC_TXDESCSIZE - buflen) | (len - 1); - /* TXINT bit will be set later on the last packet */ - txd->txd_cmd = (len - 1); - /* but also set TXINT bit on a half of TXDESC */ - if (sc->sc_txpending == (MEC_NTXDESC / 2)) - txd->txd_cmd |= MEC_TXCMD_TXINT; - - if (txs->txs_flags & MEC_TXS_TXDBUF) - txd->txd_cmd |= TXCMD_BUFSTART(MEC_TXDESCSIZE - buflen); - if (txs->txs_flags & MEC_TXS_TXDPTR1) { - txd->txd_cmd |= MEC_TXCMD_PTR1; - txd->txd_ptr[0] = TXPTR_LEN(txdlen - 1) | txdaddr; + /* + * Set MEC_TXCMD_TXINT every MEC_NTXDESC_INTR packets + * if more than half txdescs have been queued + * because TX_EMPTY interrupts will rarely happen + * if TX queue is so stacked. + */ + if (sc->sc_txpending > (MEC_NTXDESC / 2) && + (nexttx & (MEC_NTXDESC_INTR - 1)) == 0) + txdcmd |= MEC_TXCMD_TXINT; + + if ((txs->txs_flags & MEC_TXS_TXDPTR) != 0) { + bus_dma_segment_t *segs = dmamap->dm_segs; + + DPRINTF(MEC_DEBUG_TXSEGS, + ("mec_start: nsegs = %d, pseg = %d, nptr = %d\n", + dmamap->dm_nsegs, pseg, nptr)); + + switch (nptr) { + case 3: + KASSERT((segs[pseg + 2].ds_addr & + MEC_TXD_ALIGNMASK) == 0); + txdcmd |= MEC_TXCMD_PTR3; + txd->txd_ptr[2] = + TXPTR_LEN(segs[pseg + 2].ds_len - 1) | + segs[pseg + 2].ds_addr; + /* FALLTHROUGH */ + case 2: + KASSERT((segs[pseg + 1].ds_addr & + MEC_TXD_ALIGNMASK) == 0); + txdcmd |= MEC_TXCMD_PTR2; + txd->txd_ptr[1] = + TXPTR_LEN(segs[pseg + 1].ds_len - 1) | + segs[pseg + 1].ds_addr; + /* FALLTHROUGH */ + case 1: + txdcmd |= MEC_TXCMD_PTR1; + txd->txd_ptr[0] = + TXPTR_LEN(segs[pseg].ds_len - resid - 1) | + (segs[pseg].ds_addr + resid); + break; + default: + panic("%s: impossible nptr in %s", + device_xname(&sc->sc_dev), __func__); + /* NOTREACHED */ + } /* * Store a pointer to the packet so we can * free it later. */ txs->txs_mbuf = m0; } else { - txd->txd_ptr[0] = 0; /* * In this case all data are copied to buffer in txdesc, * we can free TX mbuf here. */ m_freem(m0); } + txd->txd_cmd = txdcmd; DPRINTF(MEC_DEBUG_START, - ("mec_start: txd_cmd = 0x%016llx, txd_ptr = 0x%016llx\n", - txd->txd_cmd, txd->txd_ptr[0])); + ("mec_start: txd_cmd = 0x%016llx\n", txd->txd_cmd)); + DPRINTF(MEC_DEBUG_START, + ("mec_start: txd_ptr[0] = 0x%016llx\n", txd->txd_ptr[0])); + DPRINTF(MEC_DEBUG_START, + ("mec_start: txd_ptr[1] = 0x%016llx\n", txd->txd_ptr[1])); + DPRINTF(MEC_DEBUG_START, + ("mec_start: txd_ptr[2] = 0x%016llx\n", txd->txd_ptr[2])); DPRINTF(MEC_DEBUG_START, ("mec_start: len = %d (0x%04x), buflen = %d (0x%02x)\n", len, len, buflen, buflen)); @@ -1030,32 +1426,24 @@ MEC_TXDESCSYNC(sc, nexttx, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + /* start TX */ + bus_space_write_8(st, sh, MEC_TX_RING_PTR, MEC_NEXTTX(nexttx)); + /* advance the TX pointer. */ sc->sc_txpending++; sc->sc_txlast = nexttx; } - if (sc->sc_txpending == MEC_NTXDESC) { + if (sc->sc_txpending == MEC_NTXDESC - 1) { /* No more slots; notify upper layer. */ + MEC_EVCNT_INCR(&sc->sc_ev_txdstall); ifp->if_flags |= IFF_OACTIVE; } if (sc->sc_txpending != opending) { /* - * Cause a TX interrupt to happen on the last packet - * we enqueued. - */ - sc->sc_txdesc[sc->sc_txlast].txd_cmd |= MEC_TXCMD_TXINT; - MEC_TXCMDSYNC(sc, sc->sc_txlast, - BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); - - /* start TX */ - bus_space_write_8(st, sh, MEC_TX_RING_PTR, - MEC_NEXTTX(sc->sc_txlast)); - - /* * If the transmitter was idle, - * reset the txdirty pointer and reenable TX interrupt. + * reset the txdirty pointer and re-enable TX interrupt. */ if (opending == 0) { sc->sc_txdirty = firsttx; @@ -1086,7 +1474,7 @@ /* release any TX buffers */ for (i = 0; i < MEC_NTXDESC; i++) { txs = &sc->sc_txsoft[i]; - if ((txs->txs_flags & MEC_TXS_TXDPTR1) != 0) { + if ((txs->txs_flags & MEC_TXS_TXDPTR) != 0) { bus_dmamap_unload(sc->sc_dmat, txs->txs_dmamap); m_freem(txs->txs_mbuf); txs->txs_mbuf = NULL; @@ -1215,7 +1603,7 @@ bus_space_tag_t st = sc->sc_st; bus_space_handle_t sh = sc->sc_sh; struct ifnet *ifp = &sc->sc_ethercom.ec_if; - uint32_t statreg, statack, dmac; + uint32_t statreg, statack, txptr; int handled, sent; DPRINTF(MEC_DEBUG_INTR, ("mec_intr: called\n")); @@ -1241,18 +1629,15 @@ mec_rxintr(sc); } - dmac = bus_space_read_8(st, sh, MEC_DMA_CONTROL); - DPRINTF(MEC_DEBUG_INTR, - ("mec_intr: DMA_CONT = 0x%08x\n", dmac)); - if (statack & (MEC_INT_TX_EMPTY | MEC_INT_TX_PACKET_SENT | MEC_INT_TX_ABORT)) { - mec_txintr(sc); + txptr = (statreg & MEC_INT_TX_RING_BUFFER_ALIAS) + >> MEC_INT_TX_RING_BUFFER_SHIFT; + mec_txintr(sc, txptr); sent = 1; - if ((statack & MEC_INT_TX_EMPTY) != 0 && - (dmac & MEC_DMA_TX_INT_ENABLE) != 0) { + if ((statack & MEC_INT_TX_EMPTY) != 0) { /* * disable TX interrupt to stop * TX empty interrupt @@ -1261,6 +1646,12 @@ DPRINTF(MEC_DEBUG_INTR, ("mec_intr: disable TX_INT\n")); } +#ifdef MEC_EVENT_COUNTERS + if ((statack & MEC_INT_TX_EMPTY) != 0) + MEC_EVCNT_INCR(&sc->sc_ev_txempty); + if ((statack & MEC_INT_TX_PACKET_SENT) != 0) + MEC_EVCNT_INCR(&sc->sc_ev_txsent); +#endif } if (statack & @@ -1271,10 +1662,12 @@ MEC_INT_RX_DMA_UNDERFLOW)) { printf("%s: mec_intr: interrupt status = 0x%08x\n", sc->sc_dev.dv_xname, statreg); + mec_init(ifp); + break; } } - if (sent) { + if (sent && !IFQ_IS_EMPTY(&ifp->if_snd)) { /* try to get more packets going */ mec_start(ifp); } @@ -1321,7 +1714,7 @@ len = rxstat & MEC_RXSTAT_LEN; if (len < ETHER_MIN_LEN || - len > ETHER_MAX_LEN) { + len > (MCLBYTES - MEC_ETHER_ALIGN)) { /* invalid length packet; drop it. */ DPRINTF(MEC_DEBUG_RXINTR, ("mec_rxintr: wrong packet\n")); @@ -1394,7 +1787,7 @@ #if NBPFILTER > 0 /* * Pass this up to any BPF listeners, but only - * pass it up the stack it its for us. + * pass it up the stack if it's for us. */ if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m); @@ -1409,7 +1802,7 @@ } STATIC void -mec_txintr(struct mec_softc *sc) +mec_txintr(struct mec_softc *sc, uint32_t txptr) { struct ifnet *ifp = &sc->sc_ethercom.ec_if; struct mec_txdesc *txd; @@ -1419,15 +1812,13 @@ int i; u_int col; - ifp->if_flags &= ~IFF_OACTIVE; - DPRINTF(MEC_DEBUG_TXINTR, ("mec_txintr: called\n")); - for (i = sc->sc_txdirty; sc->sc_txpending != 0; + for (i = sc->sc_txdirty; i != txptr && sc->sc_txpending != 0; i = MEC_NEXTTX(i), sc->sc_txpending--) { txd = &sc->sc_txdesc[i]; - MEC_TXDESCSYNC(sc, i, + MEC_TXCMDSYNC(sc, i, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); txstat = txd->txd_stat; @@ -1439,15 +1830,8 @@ break; } - if ((txstat & MEC_TXSTAT_SUCCESS) == 0) { - printf("%s: TX error: txstat = 0x%016llx\n", - sc->sc_dev.dv_xname, txstat); - ifp->if_oerrors++; - continue; - } - txs = &sc->sc_txsoft[i]; - if ((txs->txs_flags & MEC_TXS_TXDPTR1) != 0) { + if ((txs->txs_flags & MEC_TXS_TXDPTR) != 0) { dmamap = txs->txs_dmamap; bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE); @@ -1458,7 +1842,13 @@ col = (txstat & MEC_TXSTAT_COLCNT) >> MEC_TXSTAT_COLCNT_SHIFT; ifp->if_collisions += col; - ifp->if_opackets++; + + if ((txstat & MEC_TXSTAT_SUCCESS) == 0) { + printf("%s: TX error: txstat = 0x%016llx\n", + sc->sc_dev.dv_xname, txstat); + ifp->if_oerrors++; + } else + ifp->if_opackets++; } /* update the dirty TX buffer pointer */ @@ -1470,6 +1860,8 @@ /* cancel the watchdog timer if there are no pending TX packets */ if (sc->sc_txpending == 0) ifp->if_timer = 0; + if (sc->sc_txpending < MEC_NTXDESC - MEC_NTXDESC_RSVD) + ifp->if_flags &= ~IFF_OACTIVE; } STATIC void @@ -1478,4 +1870,6 @@ struct mec_softc *sc = arg; mec_stop(&sc->sc_ethercom.ec_if, 1); + /* make sure to stop DMA etc. */ + mec_reset(sc); } Index: sys/arch/sgimips/mace/if_mecreg.h =================================================================== RCS file: /cvsroot/src/sys/arch/sgimips/mace/if_mecreg.h,v retrieving revision 1.3 diff -u -r1.3 if_mecreg.h --- sys/arch/sgimips/mace/if_mecreg.h 10 Dec 2005 07:05:10 -0000 1.3 +++ sys/arch/sgimips/mace/if_mecreg.h 22 Aug 2008 15:56:04 -0000 @@ -74,7 +74,9 @@ #define MEC_INT_RX_FIFO_UNDERFLOW 0x0000000000000040 #define MEC_INT_RX_DMA_UNDERFLOW 0x0000000000000080 #define MEC_INT_RX_MCL_FIFO_ALIAS 0x0000000000001f00 +#define MEC_INT_RX_MCL_FIFO_SHIFT 8 #define MEC_INT_TX_RING_BUFFER_ALIAS 0x0000000001ff0000 +#define MEC_INT_TX_RING_BUFFER_SHIFT 16 #define MEC_INT_RX_SEQUENCE_NUMBER 0x000000003e000000 #define MEC_INT_MCAST_HASH_OUTPUT 0x0000000040000000 Index: sys/arch/sgimips/sgimips/bus.c =================================================================== RCS file: /cvsroot/src/sys/arch/sgimips/sgimips/bus.c,v retrieving revision 1.46 diff -u -r1.46 bus.c --- sys/arch/sgimips/sgimips/bus.c 8 Jun 2006 19:29:16 -0000 1.46 +++ sys/arch/sgimips/sgimips/bus.c 22 Aug 2008 15:56:04 -0000 @@ -63,6 +63,8 @@ #include +#include "opt_sgimace.h" + static int _bus_dmamap_load_buffer(bus_dmamap_t, void *, bus_size_t, struct vmspace *, int, vaddr_t *, int *, int); @@ -194,15 +196,22 @@ bus_space_read_4(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t o) { u_int32_t reg; +#ifdef MACE_NEEDS_DELAYS int s; +#endif switch (tag) { case SGIMIPS_BUS_SPACE_MACE: +#ifdef MACE_NEEDS_DELAYS s = splhigh(); delay(10); +#endif + wbflush(); reg = (*(volatile u_int32_t *)(bsh + o)); +#ifdef MACE_NEEDS_DELAYS delay(10); splx(s); +#endif break; default: wbflush(); @@ -217,15 +226,22 @@ bus_space_write_4(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t o, u_int32_t v) { +#ifdef MACE_NEEDS_DELAYS int s; +#endif switch (tag) { case SGIMIPS_BUS_SPACE_MACE: +#ifdef MACE_NEEDS_DELAYS s = splhigh(); delay(10); +#endif *(volatile u_int32_t *)((bsh) + (o)) = (v); + wbflush(); +#ifdef MACE_NEEDS_DELAYS delay(10); splx(s); +#endif break; default: *(volatile u_int32_t *)((bsh) + (o)) = (v); @@ -260,15 +276,22 @@ bus_size_t o) { u_int32_t reg; +#ifdef MACE_NEEDS_DELAYS int s; +#endif switch (t) { case SGIMIPS_BUS_SPACE_MACE: +#ifdef MACE_NEEDS_DELAYS s = splhigh(); delay(10); +#endif + wbflush(); reg = (*(volatile u_int32_t *)(bsh + o)); +#ifdef MACE_NEEDS_DELAYS delay(10); splx(s); +#endif break; case SGIMIPS_BUS_SPACE_MEM: case SGIMIPS_BUS_SPACE_IO: @@ -311,15 +334,22 @@ bus_space_write_stream_4(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t o, u_int32_t v) { +#ifdef MACE_NEEDS_DELAYS int s; +#endif switch (tag) { case SGIMIPS_BUS_SPACE_MACE: +#ifdef MACE_NEEDS_DELAYS s = splhigh(); delay(10); +#endif *(volatile u_int32_t *)((bsh) + (o)) = (v); + wbflush(); +#ifdef MACE_NEEDS_DELAYS delay(10); splx(s); +#endif break; case SGIMIPS_BUS_SPACE_IO: case SGIMIPS_BUS_SPACE_MEM: @@ -339,15 +369,24 @@ bus_space_read_8(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t o) { u_int64_t reg; +#ifdef MACE_NEEDS_DELAYS int s; +#endif + + /* see if we're properly aligned */ + KASSERT((o & 7) == 0); switch (tag) { case SGIMIPS_BUS_SPACE_MACE: +#ifdef MACE_NEEDS_DELAYS s = splhigh(); delay(10); +#endif reg = mips3_ld( (u_int64_t *)(bsh + o)); +#ifdef MACE_NEEDS_DELAYS delay(10); splx(s); +#endif break; default: reg = mips3_ld( (u_int64_t *)(bsh + o)); @@ -359,15 +398,24 @@ void bus_space_write_8(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t o, u_int64_t v) { +#ifdef MACE_NEEDS_DELAYS int s; +#endif + + /* see if we're properly aligned */ + KASSERT((o & 7) == 0); switch (tag) { case SGIMIPS_BUS_SPACE_MACE: +#ifdef MACE_NEEDS_DELAYS s = splhigh(); delay(10); +#endif mips3_sd( (u_int64_t *)(bsh + o), v); +#ifdef MACE_NEEDS_DELAYS delay(10); splx(s); +#endif break; default: mips3_sd( (u_int64_t *)(bsh + o), v);