diff options
Diffstat (limited to 'sys/arch/sgi/hpc/if_sq.c')
-rw-r--r-- | sys/arch/sgi/hpc/if_sq.c | 1348 |
1 files changed, 1348 insertions, 0 deletions
diff --git a/sys/arch/sgi/hpc/if_sq.c b/sys/arch/sgi/hpc/if_sq.c new file mode 100644 index 00000000000..44521062c38 --- /dev/null +++ b/sys/arch/sgi/hpc/if_sq.c @@ -0,0 +1,1348 @@ +/* $OpenBSD: if_sq.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: if_sq.c,v 1.42 2011/07/01 18:53:47 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Portions of this code are derived from software contributed to The + * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/timeout.h> +#include <sys/mbuf.h> +#include <sys/pool.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> + +#include <uvm/uvm_extern.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <netinet/if_ether.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> /* guarded_read_4 */ +#include <machine/intr.h> +#include <mips64/arcbios.h> /* bios_enaddr */ +#include <sgi/localbus/intvar.h> + +#include <dev/ic/seeq8003reg.h> + +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/if_sqvar.h> + +/* + * Short TODO list: + * (1) Do counters for bad-RX packets. + * (2) Allow multi-segment transmits, instead of copying to a single, + * contiguous mbuf. + * (3) Verify sq_stop() turns off enough stuff; I was still getting + * seeq interrupts after sq_stop(). + * (4) Implement EDLC modes: especially packet auto-pad and simplex + * mode. + * (5) Should the driver filter out its own transmissions in non-EDLC + * mode? + * (6) Multicast support -- multicast filter, address management, ... + * (7) Deal with RB0 (recv buffer overflow) on reception. Will need + * to figure out if RB0 is read-only as stated in one spot in the + * HPC spec or read-write (ie, is the 'write a one to clear it') + * the correct thing? + */ + +#if defined(SQ_DEBUG) +int sq_debug = 0; +#define SQ_DPRINTF(x) do { if (sq_debug) printf x; } while (0) +#else +#define SQ_DPRINTF(x) do { } while (0) +#endif + +int sq_match(struct device *, void *, void *); +void sq_attach(struct device *, struct device *, void *); +int sq_init(struct ifnet *); +void sq_start(struct ifnet *); +void sq_stop(struct ifnet *); +void sq_watchdog(struct ifnet *); +int sq_ioctl(struct ifnet *, u_long, caddr_t); + +void sq_set_filter(struct sq_softc *); +int sq_intr(void *); +void sq_rxintr(struct sq_softc *); +void sq_txintr(struct sq_softc *); +void sq_txring_hpc1(struct sq_softc *); +void sq_txring_hpc3(struct sq_softc *); +void sq_reset(struct sq_softc *); +int sq_add_rxbuf(struct sq_softc *, int); +#ifdef SQ_DEBUG +void sq_trace_dump(struct sq_softc *); +#endif + +const struct cfattach sq_ca = { + sizeof(struct sq_softc), sq_match, sq_attach +}; + +struct cfdriver sq_cd = { + NULL, "sq", DV_IFNET +}; + +/* XXX these values should be moved to <net/if_ether.h> ? */ +#define ETHER_PAD_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN) + +#define sq_seeq_read(sc, off) \ + bus_space_read_1(sc->sc_regt, sc->sc_regh, ((off) << 2) | 3) +#define sq_seeq_write(sc, off, val) \ + bus_space_write_1(sc->sc_regt, sc->sc_regh, ((off) << 2) | 3, val) + +#define sq_hpc_read(sc, off) \ + bus_space_read_4(sc->sc_hpct, sc->sc_hpch, off) +#define sq_hpc_write(sc, off, val) \ + bus_space_write_4(sc->sc_hpct, sc->sc_hpch, off, val) + +/* MAC address offset for non-onboard implementations */ +#define SQ_HPC_EEPROM_ENADDR 250 + +#define SGI_OUI_0 0x08 +#define SGI_OUI_1 0x00 +#define SGI_OUI_2 0x69 + +int +sq_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *ha = aux; + struct cfdata *cf = vcf; + vaddr_t reset, txstat; + uint32_t dummy; + + if (strcmp(ha->ha_name, cf->cf_driver->cd_name) != 0) + return 0; + + reset = PHYS_TO_XKPHYS(ha->ha_sh + ha->ha_dmaoff + + ha->hpc_regs->enetr_reset, CCA_NC); + txstat = PHYS_TO_XKPHYS(ha->ha_sh + ha->ha_devoff + (SEEQ_TXSTAT << 2), + CCA_NC); + + if (guarded_read_4(reset, &dummy) != 0) + return 0; + + *(volatile uint32_t *)reset = 0x1; + delay(20); + *(volatile uint32_t *)reset = 0x0; + + if (guarded_read_4(txstat, &dummy) != 0) + return 0; + + if ((*(volatile uint32_t *)txstat & 0xff) != TXSTAT_OLDNEW) + return 0; + + return 1; +} + +void +sq_attach(struct device *parent, struct device *self, void *aux) +{ + struct sq_softc *sc = (struct sq_softc *)self; + struct hpc_attach_args *haa = aux; + struct ifnet *ifp = &sc->sc_ac.ac_if; + int i, rc; + + sc->sc_hpct = haa->ha_st; + sc->hpc_regs = haa->hpc_regs; /* HPC register definitions */ + + if ((rc = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_dmaoff, sc->hpc_regs->enet_regs_size, + &sc->sc_hpch)) != 0) { + printf(": can't HPC DMA registers, error = %d\n", rc); + goto fail_0; + } + + sc->sc_regt = haa->ha_st; + if ((rc = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff, sc->hpc_regs->enet_devregs_size, + &sc->sc_regh)) != 0) { + printf(": can't map Seeq registers, error = %d\n", rc); + goto fail_0; + } + + sc->sc_dmat = haa->ha_dmat; + + if ((rc = bus_dmamem_alloc(sc->sc_dmat, sizeof(struct sq_control), + sc->hpc_regs->enet_dma_boundary, + sc->hpc_regs->enet_dma_boundary, &sc->sc_cdseg, 1, + &sc->sc_ncdseg, BUS_DMA_NOWAIT)) != 0) { + printf(": unable to allocate control data, error = %d\n", rc); + goto fail_0; + } + + if ((rc = bus_dmamem_map(sc->sc_dmat, &sc->sc_cdseg, sc->sc_ncdseg, + sizeof(struct sq_control), (caddr_t *)&sc->sc_control, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { + printf(": unable to map control data, error = %d\n", rc); + goto fail_1; + } + + if ((rc = bus_dmamap_create(sc->sc_dmat, + sizeof(struct sq_control), 1, sizeof(struct sq_control), + sc->hpc_regs->enet_dma_boundary, BUS_DMA_NOWAIT, + &sc->sc_cdmap)) != 0) { + printf(": unable to create DMA map for control data, error " + "= %d\n", rc); + goto fail_2; + } + + if ((rc = bus_dmamap_load(sc->sc_dmat, sc->sc_cdmap, + sc->sc_control, sizeof(struct sq_control), NULL, + BUS_DMA_NOWAIT)) != 0) { + printf(": unable to load DMA map for control data, error " + "= %d\n", rc); + goto fail_3; + } + + memset(sc->sc_control, 0, sizeof(struct sq_control)); + + /* Create transmit buffer DMA maps */ + for (i = 0; i < SQ_NTXDESC; i++) { + if ((rc = bus_dmamap_create(sc->sc_dmat, + MCLBYTES, 1, MCLBYTES, 0, + BUS_DMA_NOWAIT, &sc->sc_txmap[i])) != 0) { + printf(": unable to create tx DMA map %d, error = %d\n", + i, rc); + goto fail_4; + } + } + + /* Create receive buffer DMA maps */ + for (i = 0; i < SQ_NRXDESC; i++) { + if ((rc = bus_dmamap_create(sc->sc_dmat, + MCLBYTES, 1, MCLBYTES, 0, + BUS_DMA_NOWAIT, &sc->sc_rxmap[i])) != 0) { + printf(": unable to create rx DMA map %d, error = %d\n", + i, rc); + goto fail_5; + } + } + + /* Pre-allocate the receive buffers. */ + for (i = 0; i < SQ_NRXDESC; i++) { + if ((rc = sq_add_rxbuf(sc, i)) != 0) { + printf(": unable to allocate or map rx buffer %d\n," + " error = %d\n", i, rc); + goto fail_6; + } + } + + bcopy(&haa->hpc_eeprom[SQ_HPC_EEPROM_ENADDR], sc->sc_ac.ac_enaddr, + ETHER_ADDR_LEN); + + /* + * If our mac address is bogus, obtain it from ARCBIOS. This will + * be true of the onboard HPC3 on IP22, since there is no eeprom, + * but rather the DS1386 RTC's battery-backed ram is used. + */ + if (sc->sc_ac.ac_enaddr[0] != SGI_OUI_0 || + sc->sc_ac.ac_enaddr[1] != SGI_OUI_1 || + sc->sc_ac.ac_enaddr[2] != SGI_OUI_2) + enaddr_aton(bios_enaddr, sc->sc_ac.ac_enaddr); + + if ((int2_intr_establish(haa->ha_irq, IPL_NET, sq_intr, sc, + self->dv_xname)) == NULL) { + printf(": unable to establish interrupt!\n"); + goto fail_6; + } + + /* Reset the chip to a known state. */ + sq_reset(sc); + + /* + * Determine if we're an 8003 or 80c03 by setting the first + * MAC address register to non-zero, and then reading it back. + * If it's zero, we have an 80c03, because we will have read + * the TxCollLSB register. + */ + sq_seeq_write(sc, SEEQ_TXCOLLS0, 0xa5); + if (sq_seeq_read(sc, SEEQ_TXCOLLS0) == 0) + sc->sc_type = SQ_TYPE_80C03; + else + sc->sc_type = SQ_TYPE_8003; + sq_seeq_write(sc, SEEQ_TXCOLLS0, 0x00); + + printf(": Seeq %s, address %s\n", + sc->sc_type == SQ_TYPE_80C03 ? "80c03" : "8003", + ether_sprintf(sc->sc_ac.ac_enaddr)); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_mtu = ETHERMTU; + ifp->if_start = sq_start; + ifp->if_ioctl = sq_ioctl; + ifp->if_watchdog = sq_watchdog; + ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_MULTICAST; + IFQ_SET_READY(&ifp->if_snd); + + if_attach(ifp); + IFQ_SET_MAXLEN(&ifp->if_snd, SQ_NTXDESC - 1); + ether_ifattach(ifp); + + /* Done! */ + return; + + /* + * Free any resources we've allocated during the failed attach + * attempt. Do this in reverse order and fall through. + */ + fail_6: + for (i = 0; i < SQ_NRXDESC; i++) { + if (sc->sc_rxmbuf[i] != NULL) { + bus_dmamap_unload(sc->sc_dmat, sc->sc_rxmap[i]); + m_freem(sc->sc_rxmbuf[i]); + } + } + fail_5: + for (i = 0; i < SQ_NRXDESC; i++) { + if (sc->sc_rxmap[i] != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->sc_rxmap[i]); + } + fail_4: + for (i = 0; i < SQ_NTXDESC; i++) { + if (sc->sc_txmap[i] != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->sc_txmap[i]); + } + bus_dmamap_unload(sc->sc_dmat, sc->sc_cdmap); + fail_3: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_cdmap); + fail_2: + bus_dmamem_unmap(sc->sc_dmat, + (void *)sc->sc_control, sizeof(struct sq_control)); + fail_1: + bus_dmamem_free(sc->sc_dmat, &sc->sc_cdseg, sc->sc_ncdseg); + fail_0: + return; +} + +/* Set up data to get the interface up and running. */ +int +sq_init(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + int i; + + /* Cancel any in-progress I/O */ + sq_stop(ifp); + + sc->sc_nextrx = 0; + + sc->sc_nfreetx = SQ_NTXDESC; + sc->sc_nexttx = sc->sc_prevtx = 0; + + SQ_TRACE(SQ_RESET, sc, 0, 0); + + /* Set into 8003 mode, bank 0 to program ethernet address */ + sq_seeq_write(sc, SEEQ_TXCMD, TXCMD_BANK0); + + /* Now write the address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + sq_seeq_write(sc, i, sc->sc_ac.ac_enaddr[i]); + + sc->sc_rxcmd = + RXCMD_IE_CRC | + RXCMD_IE_DRIB | + RXCMD_IE_SHORT | + RXCMD_IE_END | + RXCMD_IE_GOOD; + + /* + * Set the receive filter -- this will add some bits to the + * prototype RXCMD register. Do this before setting the + * transmit config register, since we might need to switch + * banks. + */ + sq_set_filter(sc); + + /* Set up Seeq transmit command register */ + sq_seeq_write(sc, SEEQ_TXCMD, + TXCMD_IE_UFLOW | TXCMD_IE_COLL | TXCMD_IE_16COLL | TXCMD_IE_GOOD); + + /* Now write the receive command register. */ + sq_seeq_write(sc, SEEQ_RXCMD, sc->sc_rxcmd); + + /* + * Set up HPC ethernet PIO and DMA configurations. + * + * The PROM appears to do most of this for the onboard HPC3, but + * not for the Challenge S's IOPLUS chip. We copy how the onboard + * chip is configured and assume that it's correct for both. + */ + if (sc->hpc_regs->revision == 3) { + uint32_t dmareg, pioreg; + + pioreg = + HPC3_ENETR_PIOCFG_P1(1) | + HPC3_ENETR_PIOCFG_P2(6) | + HPC3_ENETR_PIOCFG_P3(1); + + dmareg = + HPC3_ENETR_DMACFG_D1(6) | + HPC3_ENETR_DMACFG_D2(2) | + HPC3_ENETR_DMACFG_D3(0) | + HPC3_ENETR_DMACFG_FIX_RXDC | + HPC3_ENETR_DMACFG_FIX_INTR | + HPC3_ENETR_DMACFG_FIX_EOP | + HPC3_ENETR_DMACFG_TIMEOUT; + + sq_hpc_write(sc, HPC3_ENETR_PIOCFG, pioreg); + sq_hpc_write(sc, HPC3_ENETR_DMACFG, dmareg); + } + + /* Pass the start of the receive ring to the HPC */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ndbp, SQ_CDRXADDR(sc, 0)); + + /* And turn on the HPC ethernet receive channel */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, + sc->hpc_regs->enetr_ctl_active); + + /* + * Turn off delayed receive interrupts on HPC1. + * (see Hollywood HPC Specification 2.1.4.3) + */ + if (sc->hpc_regs->revision != 3) + sq_hpc_write(sc, HPC1_ENET_INTDELAY, HPC1_ENET_INTDELAY_OFF); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + sq_start(ifp); + + return 0; +} + +void +sq_set_filter(struct sq_softc *sc) +{ + struct arpcom *ac = &sc->sc_ac; + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ether_multi *enm; + struct ether_multistep step; + + /* + * Check for promiscuous mode. Also implies + * all-multicast. + */ + if (ifp->if_flags & IFF_PROMISC) { + sc->sc_rxcmd |= RXCMD_REC_ALL; + ifp->if_flags |= IFF_ALLMULTI; + return; + } + + /* + * The 8003 has no hash table. If we have any multicast + * addresses on the list, enable reception of all multicast + * frames. + * + * XXX The 80c03 has a hash table. We should use it. + */ + + ETHER_FIRST_MULTI(step, ac, enm); + + if (enm == NULL) { + sc->sc_rxcmd &= ~RXCMD_REC_MASK; + sc->sc_rxcmd |= RXCMD_REC_BROAD; + + ifp->if_flags &= ~IFF_ALLMULTI; + return; + } + + sc->sc_rxcmd |= RXCMD_REC_MULTI; + ifp->if_flags |= IFF_ALLMULTI; +} + +int +sq_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct sq_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; + + SQ_TRACE(SQ_IOCTL, sc, 0, 0); + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + if (!(ifp->if_flags & IFF_RUNNING)) + sq_init(ifp); +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&sc->sc_ac, ifa); +#endif + break; + + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) + error = ENETRESET; + else + sq_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + sq_stop(ifp); + } + break; + + default: + error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); + break; + } + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + if (ifp->if_flags & IFF_RUNNING) + error = sq_init(ifp); + else + error = 0; + } + + splx(s); + return error; +} + +void +sq_start(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + uint32_t status; + struct mbuf *m0, *m; + bus_dmamap_t dmamap; + int err, len, totlen, nexttx, firsttx, lasttx = -1, ofree, seg; + + if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING) + return; + + /* + * Remember the previous number of free descriptors and + * the first descriptor we'll use. + */ + ofree = sc->sc_nfreetx; + firsttx = sc->sc_nexttx; + + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + while (sc->sc_nfreetx != 0) { + /* + * Grab a packet off the queue. + */ + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + m = NULL; + + dmamap = sc->sc_txmap[sc->sc_nexttx]; + + /* + * Load the DMA map. If this fails, the packet either + * didn't fit in the alloted number of segments, or we were + * short on resources. In this case, we'll copy and try + * again. + * Also copy it if we need to pad, so that we are sure there + * is room for the pad buffer. + * XXX the right way of doing this is to use a static buffer + * for padding and adding it to the transmit descriptor (see + * sys/dev/pci/if_tl.c for example). We can't do this here yet + * because we can't send packets with more than one fragment. + */ + len = m0->m_pkthdr.len; + if (len < ETHER_PAD_LEN || + bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, + BUS_DMA_NOWAIT) != 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("%s: unable to allocate Tx mbuf\n", + sc->sc_dev.dv_xname); + break; + } + if (len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + printf("%s: unable to allocate Tx " + "cluster\n", + sc->sc_dev.dv_xname); + m_freem(m); + break; + } + } + + m_copydata(m0, 0, len, mtod(m, void *)); + if (len < ETHER_PAD_LEN) { + memset(mtod(m, char *) + len, 0, + ETHER_PAD_LEN - len); + len = ETHER_PAD_LEN; + } + m->m_pkthdr.len = m->m_len = len; + + if ((err = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, + m, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load Tx buffer, " + "error = %d\n", + sc->sc_dev.dv_xname, err); + break; + } + } + + /* + * Ensure we have enough descriptors free to describe + * the packet. + */ + if (dmamap->dm_nsegs > sc->sc_nfreetx) { + /* + * Not enough free descriptors to transmit this + * packet. We haven't committed to anything yet, + * so just unload the DMA map, put the packet + * back on the queue, and punt. Notify the upper + * layer that there are no more slots left. + * + * XXX We could allocate an mbuf and copy, but + * XXX it is worth it? + */ + ifp->if_flags |= IFF_OACTIVE; + bus_dmamap_unload(sc->sc_dmat, dmamap); + if (m != NULL) + m_freem(m); + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m0); +#if NBPFILTER > 0 + /* + * Pass the packet to any BPF listeners. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT); +#endif + if (m != NULL) { + m_freem(m0); + m0 = m; + } + + /* + * WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET. + */ + + SQ_TRACE(SQ_ENQUEUE, sc, sc->sc_nexttx, 0); + + /* Sync the DMA map. */ + bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + /* + * Initialize the transmit descriptors. + */ + for (nexttx = sc->sc_nexttx, seg = 0, totlen = 0; + seg < dmamap->dm_nsegs; + seg++, nexttx = SQ_NEXTTX(nexttx)) { + if (sc->hpc_regs->revision == 3) { + sc->sc_txdesc[nexttx].hpc3_hdd_bufptr = + dmamap->dm_segs[seg].ds_addr; + sc->sc_txdesc[nexttx].hpc3_hdd_ctl = + dmamap->dm_segs[seg].ds_len; + } else { + sc->sc_txdesc[nexttx].hpc1_hdd_bufptr = + dmamap->dm_segs[seg].ds_addr; + sc->sc_txdesc[nexttx].hpc1_hdd_ctl = + dmamap->dm_segs[seg].ds_len; + } + sc->sc_txdesc[nexttx].hdd_descptr = + SQ_CDTXADDR(sc, SQ_NEXTTX(nexttx)); + lasttx = nexttx; + totlen += dmamap->dm_segs[seg].ds_len; + } + + /* Last descriptor gets end-of-packet */ + KASSERT(lasttx != -1); + if (sc->hpc_regs->revision == 3) + sc->sc_txdesc[lasttx].hpc3_hdd_ctl |= + HPC3_HDD_CTL_EOPACKET; + else + sc->sc_txdesc[lasttx].hpc1_hdd_ctl |= + HPC1_HDD_CTL_EOPACKET; + + SQ_DPRINTF(("%s: transmit %d-%d, len %d\n", + sc->sc_dev.dv_xname, sc->sc_nexttx, lasttx, totlen)); + + if (ifp->if_flags & IFF_DEBUG) { + printf(" transmit chain:\n"); + for (seg = sc->sc_nexttx;; seg = SQ_NEXTTX(seg)) { + printf(" descriptor %d:\n", seg); + printf(" hdd_bufptr: 0x%08x\n", + (sc->hpc_regs->revision == 3) ? + sc->sc_txdesc[seg].hpc3_hdd_bufptr : + sc->sc_txdesc[seg].hpc1_hdd_bufptr); + printf(" hdd_ctl: 0x%08x\n", + (sc->hpc_regs->revision == 3) ? + sc->sc_txdesc[seg].hpc3_hdd_ctl: + sc->sc_txdesc[seg].hpc1_hdd_ctl); + printf(" hdd_descptr: 0x%08x\n", + sc->sc_txdesc[seg].hdd_descptr); + + if (seg == lasttx) + break; + } + } + + /* Sync the descriptors we're using. */ + SQ_CDTXSYNC(sc, sc->sc_nexttx, dmamap->dm_nsegs, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* Store a pointer to the packet so we can free it later */ + sc->sc_txmbuf[sc->sc_nexttx] = m0; + + /* Advance the tx pointer. */ + sc->sc_nfreetx -= dmamap->dm_nsegs; + sc->sc_nexttx = nexttx; + } + + /* All transmit descriptors used up, let upper layers know */ + if (sc->sc_nfreetx == 0) + ifp->if_flags |= IFF_OACTIVE; + + if (sc->sc_nfreetx != ofree) { + SQ_DPRINTF(("%s: %d packets enqueued, first %d, INTR on %d\n", + sc->sc_dev.dv_xname, lasttx - firsttx + 1, + firsttx, lasttx)); + + /* + * Cause a transmit interrupt to happen on the + * last packet we enqueued, mark it as the last + * descriptor. + * + * HPC1_HDD_CTL_INTR will generate an interrupt on + * HPC1. HPC3 requires HPC3_HDD_CTL_EOPACKET in + * addition to HPC3_HDD_CTL_INTR to interrupt. + */ + KASSERT(lasttx != -1); + if (sc->hpc_regs->revision == 3) { + sc->sc_txdesc[lasttx].hpc3_hdd_ctl |= + HPC3_HDD_CTL_INTR | HPC3_HDD_CTL_EOCHAIN; + } else { + sc->sc_txdesc[lasttx].hpc1_hdd_ctl |= HPC1_HDD_CTL_INTR; + sc->sc_txdesc[lasttx].hpc1_hdd_bufptr |= + HPC1_HDD_CTL_EOCHAIN; + } + + SQ_CDTXSYNC(sc, lasttx, 1, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* + * There is a potential race condition here if the HPC + * DMA channel is active and we try and either update + * the 'next descriptor' pointer in the HPC PIO space + * or the 'next descriptor' pointer in a previous desc- + * riptor. + * + * To avoid this, if the channel is active, we rely on + * the transmit interrupt routine noticing that there + * are more packets to send and restarting the HPC DMA + * engine, rather than mucking with the DMA state here. + */ + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl); + + if ((status & sc->hpc_regs->enetx_ctl_active) != 0) { + SQ_TRACE(SQ_ADD_TO_DMA, sc, firsttx, status); + + /* + * NB: hpc3_hdd_ctl == hpc1_hdd_bufptr, and + * HPC1_HDD_CTL_EOCHAIN == HPC3_HDD_CTL_EOCHAIN + */ + sc->sc_txdesc[SQ_PREVTX(firsttx)].hpc3_hdd_ctl &= + ~HPC3_HDD_CTL_EOCHAIN; + + if (sc->hpc_regs->revision != 3) + sc->sc_txdesc[SQ_PREVTX(firsttx)].hpc1_hdd_ctl + &= ~HPC1_HDD_CTL_INTR; + + SQ_CDTXSYNC(sc, SQ_PREVTX(firsttx), 1, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } else if (sc->hpc_regs->revision == 3) { + SQ_TRACE(SQ_START_DMA, sc, firsttx, status); + + sq_hpc_write(sc, HPC3_ENETX_NDBP, SQ_CDTXADDR(sc, + firsttx)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC3_ENETX_CTL, HPC3_ENETX_CTL_ACTIVE); + } else { + /* + * In the HPC1 case where transmit DMA is + * inactive, we can either kick off if + * the ring was previously empty, or call + * our transmit interrupt handler to + * figure out if the ring stopped short + * and restart at the right place. + */ + if (ofree == SQ_NTXDESC) { + SQ_TRACE(SQ_START_DMA, sc, firsttx, status); + + sq_hpc_write(sc, HPC1_ENETX_NDBP, + SQ_CDTXADDR(sc, firsttx)); + sq_hpc_write(sc, HPC1_ENETX_CFXBP, + SQ_CDTXADDR(sc, firsttx)); + sq_hpc_write(sc, HPC1_ENETX_CBP, + SQ_CDTXADDR(sc, firsttx)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC1_ENETX_CTL, + HPC1_ENETX_CTL_ACTIVE); + } else + sq_txring_hpc1(sc); + } + + /* Set a watchdog timer in case the chip flakes out. */ + ifp->if_timer = 5; + } +} + +void +sq_stop(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + int i; + + ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + for (i = 0; i < SQ_NTXDESC; i++) { + if (sc->sc_txmbuf[i] != NULL) { + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + } + } + + /* Clear Seeq transmit/receive command registers */ + sq_seeq_write(sc, SEEQ_TXCMD, 0); + sq_seeq_write(sc, SEEQ_RXCMD, 0); + + sq_reset(sc); +} + +/* Device timeout/watchdog routine. */ +void +sq_watchdog(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + uint32_t status; + + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl); + log(LOG_ERR, "%s: device timeout (prev %d, next %d, free %d, " + "status %08x)\n", sc->sc_dev.dv_xname, sc->sc_prevtx, + sc->sc_nexttx, sc->sc_nfreetx, status); + +#ifdef SQ_DEBUG + sq_trace_dump(sc); +#endif + + ++ifp->if_oerrors; + + sq_init(ifp); +} + +#ifdef SQ_DEBUG +void +sq_trace_dump(struct sq_softc *sc) +{ + int i; + const char *act; + + for (i = 0; i < sc->sq_trace_idx; i++) { + switch (sc->sq_trace[i].action) { + case SQ_RESET: act = "SQ_RESET"; break; + case SQ_ADD_TO_DMA: act = "SQ_ADD_TO_DMA"; break; + case SQ_START_DMA: act = "SQ_START_DMA"; break; + case SQ_DONE_DMA: act = "SQ_DONE_DMA"; break; + case SQ_RESTART_DMA: act = "SQ_RESTART_DMA"; break; + case SQ_TXINTR_ENTER: act = "SQ_TXINTR_ENTER"; break; + case SQ_TXINTR_EXIT: act = "SQ_TXINTR_EXIT"; break; + case SQ_TXINTR_BUSY: act = "SQ_TXINTR_BUSY"; break; + case SQ_IOCTL: act = "SQ_IOCTL"; break; + case SQ_ENQUEUE: act = "SQ_ENQUEUE"; break; + default: act = "UNKNOWN"; + } + + printf("%s: [%03d] action %-16s buf %03d free %03d " + "status %08x line %d\n", sc->sc_dev.dv_xname, i, act, + sc->sq_trace[i].bufno, sc->sq_trace[i].freebuf, + sc->sq_trace[i].status, sc->sq_trace[i].line); + } + + memset(&sc->sq_trace, 0, sizeof(sc->sq_trace)); + sc->sq_trace_idx = 0; +} +#endif + +int +sq_intr(void *arg) +{ + struct sq_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint32_t stat; + + stat = sq_hpc_read(sc, sc->hpc_regs->enetr_reset); + + if ((stat & 2) == 0) { + SQ_DPRINTF(("%s: Unexpected interrupt!\n", + sc->sc_dev.dv_xname)); + } else + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, (stat | 2)); + + /* + * If the interface isn't running, the interrupt couldn't + * possibly have come from us. + */ + if ((ifp->if_flags & IFF_RUNNING) == 0) + return 0; + + /* Always check for received packets */ + sq_rxintr(sc); + + /* Only handle transmit interrupts if we actually sent something */ + if (sc->sc_nfreetx < SQ_NTXDESC) + sq_txintr(sc); + + /* + * XXX Always claim the interrupt, even if we did nothing. + * XXX There seem to be extra interrupts when the receiver becomes + * XXX idle. + */ + return 1; +} + +void +sq_rxintr(struct sq_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct mbuf* m; + int i, framelen; + uint8_t pktstat; + uint32_t status; + uint32_t ctl_reg; + int new_end, orig_end; + + for (i = sc->sc_nextrx; ; i = SQ_NEXTRX(i)) { + SQ_CDRXSYNC(sc, i, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* + * If this is a CPU-owned buffer, we're at the end of the list. + */ + if (sc->hpc_regs->revision == 3) + ctl_reg = + sc->sc_rxdesc[i].hpc3_hdd_ctl & HPC3_HDD_CTL_OWN; + else + ctl_reg = + sc->sc_rxdesc[i].hpc1_hdd_ctl & HPC1_HDD_CTL_OWN; + + if (ctl_reg) { +#if defined(SQ_DEBUG) + uint32_t reg; + + reg = sq_hpc_read(sc, sc->hpc_regs->enetr_ctl); + SQ_DPRINTF(("%s: rxintr: done at %d (ctl %08x)\n", + sc->sc_dev.dv_xname, i, reg)); +#endif + break; + } + + m = sc->sc_rxmbuf[i]; + framelen = m->m_ext.ext_size - 3; + if (sc->hpc_regs->revision == 3) + framelen -= + HPC3_HDD_CTL_BYTECNT(sc->sc_rxdesc[i].hpc3_hdd_ctl); + else + framelen -= + HPC1_HDD_CTL_BYTECNT(sc->sc_rxdesc[i].hpc1_hdd_ctl); + + /* Now sync the actual packet data */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_POSTREAD); + + pktstat = *((uint8_t *)m->m_data + framelen + 2); + + if ((pktstat & RXSTAT_GOOD) == 0) { + ifp->if_ierrors++; + + if (pktstat & RXSTAT_OFLOW) + printf("%s: receive FIFO overflow\n", + sc->sc_dev.dv_xname); + + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_PREREAD); + SQ_INIT_RXDESC(sc, i); + SQ_DPRINTF(("%s: sq_rxintr: buf %d no RXSTAT_GOOD\n", + sc->sc_dev.dv_xname, i)); + continue; + } + + if (sq_add_rxbuf(sc, i) != 0) { + ifp->if_ierrors++; + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_PREREAD); + SQ_INIT_RXDESC(sc, i); + SQ_DPRINTF(("%s: sq_rxintr: buf %d sq_add_rxbuf() " + "failed\n", sc->sc_dev.dv_xname, i)); + continue; + } + + + m->m_data += 2; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = framelen; + + ifp->if_ipackets++; + + SQ_DPRINTF(("%s: sq_rxintr: buf %d len %d\n", + sc->sc_dev.dv_xname, i, framelen)); + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); +#endif + + ether_input_mbuf(ifp, m); + } + + + /* If anything happened, move ring start/end pointers to new spot */ + if (i != sc->sc_nextrx) { + /* + * NB: hpc3_hdd_ctl == hpc1_hdd_bufptr, and + * HPC1_HDD_CTL_EOCHAIN == HPC3_HDD_CTL_EOCHAIN + */ + + new_end = SQ_PREVRX(i); + sc->sc_rxdesc[new_end].hpc3_hdd_ctl |= HPC3_HDD_CTL_EOCHAIN; + SQ_CDRXSYNC(sc, new_end, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + orig_end = SQ_PREVRX(sc->sc_nextrx); + sc->sc_rxdesc[orig_end].hpc3_hdd_ctl &= ~HPC3_HDD_CTL_EOCHAIN; + SQ_CDRXSYNC(sc, orig_end, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + sc->sc_nextrx = i; + } + + status = sq_hpc_read(sc, sc->hpc_regs->enetr_ctl); + + /* If receive channel is stopped, restart it... */ + if ((status & sc->hpc_regs->enetr_ctl_active) == 0) { + /* Pass the start of the receive ring to the HPC */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ndbp, + SQ_CDRXADDR(sc, sc->sc_nextrx)); + + /* And turn on the HPC ethernet receive channel */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, + sc->hpc_regs->enetr_ctl_active); + } +} + +void +sq_txintr(struct sq_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint shift = 0; + uint32_t status, tmp; + + if (sc->hpc_regs->revision != 3) + shift = 16; + + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl) >> shift; + + SQ_TRACE(SQ_TXINTR_ENTER, sc, sc->sc_prevtx, status); + + tmp = (sc->hpc_regs->enetx_ctl_active >> shift) | TXSTAT_GOOD; + if ((status & tmp) == 0) { + if (status & TXSTAT_COLL) + ifp->if_collisions++; + + if (status & TXSTAT_UFLOW) { + printf("%s: transmit underflow\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; +#ifdef SQ_DEBUG + sq_trace_dump(sc); +#endif + sq_init(ifp); + return; + } + + if (status & TXSTAT_16COLL) { + printf("%s: max collisions reached\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; + ifp->if_collisions += 16; + } + } + + /* prevtx now points to next xmit packet not yet finished */ + if (sc->hpc_regs->revision == 3) + sq_txring_hpc3(sc); + else + sq_txring_hpc1(sc); + + /* If we have buffers free, let upper layers know */ + if (sc->sc_nfreetx > 0) + ifp->if_flags &= ~IFF_OACTIVE; + + /* If all packets have left the coop, cancel watchdog */ + if (sc->sc_nfreetx == SQ_NTXDESC) + ifp->if_timer = 0; + + SQ_TRACE(SQ_TXINTR_EXIT, sc, sc->sc_prevtx, status); + sq_start(ifp); +} + +/* + * Reclaim used transmit descriptors and restart the transmit DMA + * engine if necessary. + */ +void +sq_txring_hpc1(struct sq_softc *sc) +{ + /* + * HPC1 doesn't tag transmitted descriptors, however, + * the NDBP register points to the next descriptor that + * has not yet been processed. If DMA is not in progress, + * we can safely reclaim all descriptors up to NDBP, and, + * if necessary, restart DMA at NDBP. Otherwise, if DMA + * is active, we can only safely reclaim up to CBP. + * + * For now, we'll only reclaim on inactive DMA and assume + * that a sufficiently large ring keeps us out of trouble. + */ + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint32_t reclaimto, status; + int reclaimall, i = sc->sc_prevtx; + + status = sq_hpc_read(sc, HPC1_ENETX_CTL); + if (status & HPC1_ENETX_CTL_ACTIVE) { + SQ_TRACE(SQ_TXINTR_BUSY, sc, i, status); + return; + } else + reclaimto = sq_hpc_read(sc, HPC1_ENETX_NDBP); + + if (sc->sc_nfreetx == 0 && SQ_CDTXADDR(sc, i) == reclaimto) + reclaimall = 1; + else + reclaimall = 0; + + while (sc->sc_nfreetx < SQ_NTXDESC) { + if (SQ_CDTXADDR(sc, i) == reclaimto && !reclaimall) + break; + + SQ_CDTXSYNC(sc, i, sc->sc_txmap[i]->dm_nsegs, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* Sync the packet data, unload DMA map, free mbuf */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_txmap[i], + 0, sc->sc_txmap[i]->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + + ifp->if_opackets++; + sc->sc_nfreetx++; + + SQ_TRACE(SQ_DONE_DMA, sc, i, status); + + i = SQ_NEXTTX(i); + } + + if (sc->sc_nfreetx < SQ_NTXDESC) { + SQ_TRACE(SQ_RESTART_DMA, sc, i, status); + + KASSERT(reclaimto == SQ_CDTXADDR(sc, i)); + + sq_hpc_write(sc, HPC1_ENETX_CFXBP, reclaimto); + sq_hpc_write(sc, HPC1_ENETX_CBP, reclaimto); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC1_ENETX_CTL, HPC1_ENETX_CTL_ACTIVE); + + /* + * Set a watchdog timer in case the chip + * flakes out. + */ + ifp->if_timer = 5; + } + + sc->sc_prevtx = i; +} + +/* + * Reclaim used transmit descriptors and restart the transmit DMA + * engine if necessary. + */ +void +sq_txring_hpc3(struct sq_softc *sc) +{ + /* + * HPC3 tags descriptors with a bit once they've been + * transmitted. We need only free each XMITDONE'd + * descriptor, and restart the DMA engine if any + * descriptors are left over. + */ + struct ifnet *ifp = &sc->sc_ac.ac_if; + int i; + uint32_t status = 0; + + i = sc->sc_prevtx; + while (sc->sc_nfreetx < SQ_NTXDESC) { + /* + * Check status first so we don't end up with a case of + * the buffer not being finished while the DMA channel + * has gone idle. + */ + status = sq_hpc_read(sc, HPC3_ENETX_CTL); + + SQ_CDTXSYNC(sc, i, sc->sc_txmap[i]->dm_nsegs, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* Check for used descriptor and restart DMA chain if needed */ + if ((sc->sc_txdesc[i].hpc3_hdd_ctl & + HPC3_HDD_CTL_XMITDONE) == 0) { + if ((status & HPC3_ENETX_CTL_ACTIVE) == 0) { + SQ_TRACE(SQ_RESTART_DMA, sc, i, status); + + sq_hpc_write(sc, HPC3_ENETX_NDBP, + SQ_CDTXADDR(sc, i)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC3_ENETX_CTL, + HPC3_ENETX_CTL_ACTIVE); + + /* + * Set a watchdog timer in case the chip + * flakes out. + */ + ifp->if_timer = 5; + } else + SQ_TRACE(SQ_TXINTR_BUSY, sc, i, status); + break; + } + + /* Sync the packet data, unload DMA map, free mbuf */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_txmap[i], + 0, sc->sc_txmap[i]->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + + ifp->if_opackets++; + sc->sc_nfreetx++; + + SQ_TRACE(SQ_DONE_DMA, sc, i, status); + i = SQ_NEXTTX(i); + } + + sc->sc_prevtx = i; +} + +void +sq_reset(struct sq_softc *sc) +{ + /* Stop HPC dma channels */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, 0); + sq_hpc_write(sc, sc->hpc_regs->enetx_ctl, 0); + + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, 3); + delay(20); + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, 0); +} + +/* sq_add_rxbuf: Add a receive buffer to the indicated descriptor. */ +int +sq_add_rxbuf(struct sq_softc *sc, int idx) +{ + int err; + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOBUFS; + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return ENOBUFS; + } + + if (sc->sc_rxmbuf[idx] != NULL) + bus_dmamap_unload(sc->sc_dmat, sc->sc_rxmap[idx]); + + sc->sc_rxmbuf[idx] = m; + + if ((err = bus_dmamap_load(sc->sc_dmat, sc->sc_rxmap[idx], + m->m_ext.ext_buf, m->m_ext.ext_size, NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s: can't load rx DMA map %d, error = %d\n", + sc->sc_dev.dv_xname, idx, err); + panic("sq_add_rxbuf"); /* XXX */ + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[idx], + 0, sc->sc_rxmap[idx]->dm_mapsize, BUS_DMASYNC_PREREAD); + + SQ_INIT_RXDESC(sc, idx); + + return 0; +} |