diff options
Diffstat (limited to 'sys/arch/sgi/hpc/if_sq.c')
-rw-r--r-- | sys/arch/sgi/hpc/if_sq.c | 1546 |
1 files changed, 0 insertions, 1546 deletions
diff --git a/sys/arch/sgi/hpc/if_sq.c b/sys/arch/sgi/hpc/if_sq.c deleted file mode 100644 index 42395c3be6d..00000000000 --- a/sys/arch/sgi/hpc/if_sq.c +++ /dev/null @@ -1,1546 +0,0 @@ -/* $OpenBSD: if_sq.c,v 1.31 2021/03/11 11:17:00 jsg 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_media.h> - -#if NBPFILTER > 0 -#include <net/bpf.h> -#endif - -#include <netinet/in.h> -#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/sgi/ip22.h> - -#include <dev/ic/seeq8003reg.h> - -#include <sgi/hpc/hpcvar.h> -#include <sgi/hpc/hpcreg.h> -#include <sgi/hpc/iocreg.h> /* IOC_READ / IOC_WRITE */ -#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(). - * (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? - * - * Note that it is no use to implement EDLC auto-padding: the HPC glue will - * not start a packet transfer until it has been fed 64 bytes, which defeats - * the auto-padding purpose. - */ - -#ifdef 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 - -int sq_ifmedia_change_ip22(struct ifnet *); -int sq_ifmedia_change_singlemedia(struct ifnet *); -void sq_ifmedia_status_ip22(struct ifnet *, struct ifmediareq *); -void sq_ifmedia_status_singlemedia(struct ifnet *, struct ifmediareq *); - -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; - uint64_t media; - int i, rc; - - sc->sc_hpct = haa->ha_st; - sc->sc_hpcbh = haa->ha_sh; - 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 map 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), - 0, 0, &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 | (ip22_ecc ? 0 : 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), - 0, 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 ((hpc_intr_establish(haa->ha_irq, IPL_NET, sq_intr, sc, - self->dv_xname)) == NULL) { - printf(": unable to establish interrupt!\n"); - goto fail_6; - } - - /* - * 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 (haa->hpc_regs->revision == 3 && - sys_config.system_subtype != IP22_INDIGO2) { - uint32_t dmareg, pioreg; - - if (haa->ha_giofast) { - pioreg = - HPC3_ENETR_PIOCFG_P1(1) | - HPC3_ENETR_PIOCFG_P2(5) | - HPC3_ENETR_PIOCFG_P3(0); - dmareg = - HPC3_ENETR_DMACFG_D1(5) | - HPC3_ENETR_DMACFG_D2(1) | - HPC3_ENETR_DMACFG_D3(0); - } else { - 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); - } - dmareg |= 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); - } - - /* 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_start = sq_start; - ifp->if_ioctl = sq_ioctl; - ifp->if_watchdog = sq_watchdog; - ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST; - - if_attach(ifp); - ifq_set_maxlen(&ifp->if_snd, SQ_NTXDESC - 1); - ether_ifattach(ifp); - - if (haa->hpc_regs->revision == 3) { - uint8_t mask, set; - if (/* sys_config.system_type != SGI_IP20 && */ /* implied */ - sys_config.system_subtype == IP22_CHALLS) { - /* - * Challenge S: onboard has AUI connector only, - * IO+ has TP connector only. - */ - if (haa->ha_base == HPC_BASE_ADDRESS_0) { - ifmedia_init(&sc->sc_ifmedia, 0, - sq_ifmedia_change_singlemedia, - sq_ifmedia_status_singlemedia); - /* - * Force 10Base5. - */ - media = IFM_ETHER | IFM_10_5; - mask = IOC_WRITE_ENET_AUTO; - set = IOC_WRITE_ENET_AUI; - } else { - ifmedia_init(&sc->sc_ifmedia, 0, - sq_ifmedia_change_singlemedia, - sq_ifmedia_status_singlemedia); - /* - * Force 10BaseT, and set the 10BaseT port - * to use UTP cable. - */ - media = IFM_ETHER | IFM_10_T; - mask = set = 0; - } - } else { - /* - * Indy, Indigo 2: onboard has AUI and TP connectors. - */ - ifmedia_init(&sc->sc_ifmedia, 0, - sq_ifmedia_change_ip22, sq_ifmedia_status_ip22); - ifmedia_add(&sc->sc_ifmedia, - IFM_ETHER | IFM_10_5, 0, NULL); - ifmedia_add(&sc->sc_ifmedia, - IFM_ETHER | IFM_10_T, 0, NULL); - - /* - * Force autoselect, and set the 10BaseT port - * to use UTP cable. - */ - media = IFM_ETHER | IFM_AUTO; - mask = IOC_WRITE_ENET_AUI; - set = IOC_WRITE_ENET_AUTO | IOC_WRITE_ENET_UTP; - } - - if (haa->ha_base == HPC_BASE_ADDRESS_0) { - bus_space_write_4(haa->ha_st, haa->ha_sh, - IOC_BASE + IOC_WRITE, - (bus_space_read_4(haa->ha_st, haa->ha_sh, - IOC_BASE + IOC_WRITE) & ~mask) | set); - bus_space_barrier(haa->ha_st, haa->ha_sh, - IOC_BASE + IOC_WRITE, 4, - BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); - } - } else { - /* - * HPC1/1.5: IP20 on-board, or E++: AUI connector only, - * and career information unreliable. - */ - ifmedia_init(&sc->sc_ifmedia, 0, - sq_ifmedia_change_singlemedia, - sq_ifmedia_status_singlemedia); - media = IFM_ETHER | IFM_10_5; - sc->sc_flags |= SQF_NOLINKDOWN; - } - - ifmedia_add(&sc->sc_ifmedia, media, 0, NULL); - ifmedia_set(&sc->sc_ifmedia, media); - - /* supposedly connected, until TX says otherwise */ - sc->sc_flags |= SQF_LINKUP; - - /* 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 or 80C03 mode, bank 0 to program Ethernet address */ - if (sc->sc_type == SQ_TYPE_80C03) - sq_seeq_write(sc, SEEQ_TXCMD, TXCMD_ENABLE_C); - 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); - - if (sc->sc_type == SQ_TYPE_80C03) { - sq_seeq_write(sc, SEEQ_TXCMD, TXCMD_BANK2); - sq_seeq_write(sc, SEEQ_TXCTRL, 0); - sq_seeq_write(sc, SEEQ_TXCTRL, TXCTRL_SQE | TXCTRL_NOCARR); -#if 0 /* HPC expects a minimal packet size of ETHER_MIN_LEN anyway */ - sq_seeq_write(sc, SEEQ_CFG, CFG_TX_AUTOPAD); -#endif - sq_seeq_write(sc, SEEQ_TXCMD, TXCMD_BANK0); - } - - /* Set up Seeq transmit command register */ - sc->sc_txcmd = - TXCMD_IE_UFLOW | TXCMD_IE_COLL | TXCMD_IE_16COLL | TXCMD_IE_GOOD; - sq_seeq_write(sc, SEEQ_TXCMD, sc->sc_txcmd); - - /* Now write the receive command register. */ - sq_seeq_write(sc, SEEQ_RXCMD, sc->sc_rxcmd); - - /* 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; - ifq_clr_oactive(&ifp->if_snd); - 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; - - sc->sc_rxcmd &= ~RXCMD_REC_MASK; - ifp->if_flags &= ~IFF_ALLMULTI; - - /* - * 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. - */ - if (ifp->if_flags & IFF_PROMISC || ac->ac_multicnt > 0) { - ifp->if_flags |= IFF_ALLMULTI; - if (ifp->if_flags & IFF_PROMISC) - sc->sc_rxcmd |= RXCMD_REC_ALL; - else - sc->sc_rxcmd |= RXCMD_REC_MULTI; - } - - /* - * Unless otherwise specified, always accept broadcast frames. - */ - if ((sc->sc_rxcmd & RXCMD_REC_MASK) == RXCMD_REC_NONE) - sc->sc_rxcmd |= RXCMD_REC_BROAD; -} - -int -sq_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct sq_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *)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); - break; - - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd); - 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; - struct mbuf *m0, *m; - struct hpc_dma_desc *txd, *active, store; - bus_dmamap_t dmamap; - uint32_t status; - int err, len, totlen, nexttx, firsttx, lasttx = -1, ofree, seg; - - if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) - 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. - */ - m0 = ifq_deq_begin(&ifp->if_snd); - 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 allotted 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) { - ifq_deq_rollback(&ifp->if_snd, m0); - 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) { - ifq_deq_rollback(&ifp->if_snd, m0); - 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 /* && - sc->sc_type != SQ_TYPE_80C03 */) { - 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) { - ifq_deq_rollback(&ifp->if_snd, m0); - 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) { - ifq_deq_rollback(&ifp->if_snd, m0); - /* - * 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? - */ - ifq_set_oactive(&ifp->if_snd); - bus_dmamap_unload(sc->sc_dmat, dmamap); - m_freem(m); - break; - } - - ifq_deq_commit(&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)) { - txd = sc->sc_txdesc + nexttx; - active = hpc_sync_dma_desc(txd, &store); - if (sc->hpc_regs->revision == 3) { - active->hpc3_hdd_bufptr = - dmamap->dm_segs[seg].ds_addr; - active->hpc3_hdd_ctl = - dmamap->dm_segs[seg].ds_len; - } else { - active->hpc1_hdd_bufptr = - dmamap->dm_segs[seg].ds_addr; - active->hpc1_hdd_ctl = - dmamap->dm_segs[seg].ds_len; - } - active->hdd_descptr = SQ_CDTXADDR(sc, SQ_NEXTTX(nexttx)); - hpc_update_dma_desc(txd, active); - lasttx = nexttx; - totlen += dmamap->dm_segs[seg].ds_len; - } - - /* Last descriptor gets end-of-packet */ - KASSERT(lasttx != -1); - txd = sc->sc_txdesc + lasttx; - active = hpc_sync_dma_desc(txd, &store); - if (sc->hpc_regs->revision == 3) - active->hpc3_hdd_ctl |= HPC3_HDD_CTL_EOPACKET; - else - active->hpc1_hdd_ctl |= HPC1_HDD_CTL_EOPACKET; - hpc_update_dma_desc(txd, active); - - 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)) { - active = hpc_sync_dma_desc(&sc->sc_txdesc[seg], - &store); - printf(" descriptor %d:\n", seg); - printf(" hdd_bufptr: 0x%08x\n", - (sc->hpc_regs->revision == 3) ? - active->hpc3_hdd_bufptr : - active->hpc1_hdd_bufptr); - printf(" hdd_ctl: 0x%08x\n", - (sc->hpc_regs->revision == 3) ? - active->hpc3_hdd_ctl: - active->hpc1_hdd_ctl); - printf(" hdd_descptr: 0x%08x\n", - active->hdd_descptr); - - if (seg == lasttx) - break; - } - } - - /* 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) - ifq_set_oactive(&ifp->if_snd); - - 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_EOCHAIN in - * addition to HPC3_HDD_CTL_INTR to interrupt. - */ - KASSERT(lasttx != -1); - txd = sc->sc_txdesc + lasttx; - active = hpc_sync_dma_desc(txd, &store); - if (sc->hpc_regs->revision == 3) { - active->hpc3_hdd_ctl |= - HPC3_HDD_CTL_INTR | HPC3_HDD_CTL_EOCHAIN; - } else { - active->hpc1_hdd_ctl |= HPC1_HDD_CTL_INTR; - active->hpc1_hdd_bufptr |= HPC1_HDD_CTL_EOCHAIN; - } - hpc_update_dma_desc(txd, active); - - /* - * 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); - - txd = sc->sc_txdesc + SQ_PREVTX(firsttx); - active = hpc_sync_dma_desc(txd, &store); - /* - * NB: hpc3_hdd_ctl == hpc1_hdd_bufptr, and - * HPC1_HDD_CTL_EOCHAIN == HPC3_HDD_CTL_EOCHAIN - */ - active->hpc3_hdd_ctl &= ~HPC3_HDD_CTL_EOCHAIN; - if (sc->hpc_regs->revision != 3) - active->hpc1_hdd_ctl &= ~HPC1_HDD_CTL_INTR; - - hpc_update_dma_desc(txd, active); - } 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; - ifq_clr_oactive(&ifp->if_snd); - - 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 */ - sc->sc_txcmd = 0; - 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; - int oldlink = sc->sc_flags & SQF_LINKUP; - uint32_t stat; - uint8_t sqe; - - 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; - - /* - * Check for loss of carrier detected during transmission if we - * can detect it. - * Unfortunately, this does not work on IP20 and E++ designs. - */ - if (sc->sc_type == SQ_TYPE_80C03 && - !ISSET(sc->sc_flags, SQF_NOLINKDOWN)) { - sqe = sq_seeq_read(sc, SEEQ_SQE) & (SQE_FLAG | SQE_NOCARR); - if (sqe != 0) { - sq_seeq_write(sc, SEEQ_TXCMD, - TXCMD_BANK2 | sc->sc_txcmd); - /* reset counters */ - sq_seeq_write(sc, SEEQ_TXCTRL, 0); - sq_seeq_write(sc, SEEQ_TXCTRL, - TXCTRL_SQE | TXCTRL_NOCARR); - sq_seeq_write(sc, SEEQ_TXCMD, - TXCMD_BANK0 | sc->sc_txcmd); - if (sqe == (SQE_FLAG | SQE_NOCARR)) - sc->sc_flags &= ~SQF_LINKUP; - } - } - - /* 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); - - /* Notify link status change */ - if (oldlink != (sc->sc_flags & SQF_LINKUP)) { - if (oldlink != 0) { - ifp->if_link_state = LINK_STATE_DOWN; - ifp->if_baudrate = 0; - } else { - ifp->if_link_state = LINK_STATE_UP; - ifp->if_baudrate = IF_Mbps(10); - } - if_link_state_change(ifp); - } - - /* - * 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_list ml = MBUF_LIST_INITIALIZER(); - struct mbuf* m; - struct hpc_dma_desc *rxd, *active, store; - 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)) { - rxd = sc->sc_rxdesc + i; - active = hpc_sync_dma_desc(rxd, &store); - /* - * If this is a CPU-owned buffer, we're at the end of the list. - */ - if (sc->hpc_regs->revision == 3) - ctl_reg = active->hpc3_hdd_ctl & HPC3_HDD_CTL_OWN; - else - ctl_reg = active->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(active->hpc3_hdd_ctl); - else - framelen -= - HPC1_HDD_CTL_BYTECNT(active->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; - } - - /* Link must be good if we have received data. */ - sc->sc_flags |= SQF_LINKUP; - - 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.len = m->m_len = framelen; - - SQ_DPRINTF(("%s: sq_rxintr: buf %d len %d\n", - sc->sc_dev.dv_xname, i, framelen)); - - ml_enqueue(&ml, m); - } - - if_input(ifp, &ml); - - /* If anything happened, move ring start/end pointers to new spot */ - if (i != sc->sc_nextrx) { - new_end = SQ_PREVRX(i); - rxd = sc->sc_rxdesc + new_end; - active = hpc_sync_dma_desc(rxd, &store); - /* - * NB: hpc3_hdd_ctl == hpc1_hdd_bufptr, and - * HPC1_HDD_CTL_EOCHAIN == HPC3_HDD_CTL_EOCHAIN - */ - active->hpc3_hdd_ctl |= HPC3_HDD_CTL_EOCHAIN; - hpc_update_dma_desc(rxd, active); - - orig_end = SQ_PREVRX(sc->sc_nextrx); - rxd = sc->sc_rxdesc + orig_end; - active = hpc_sync_dma_desc(rxd, &store); - active->hpc3_hdd_ctl &= ~HPC3_HDD_CTL_EOCHAIN; - hpc_update_dma_desc(rxd, active); - - 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) { - if (ifp->if_flags & IFF_DEBUG) - 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) - ifq_clr_oactive(&ifp->if_snd); - - /* 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; - - /* 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; - - 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; - struct hpc_dma_desc *txd, *active, store; - 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); - - txd = sc->sc_txdesc + i; - active = hpc_sync_dma_desc(txd, &store); - - /* Check for used descriptor and restart DMA chain if needed */ - if ((active->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; - - 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; -} - -/* - * Media handling - */ - -int -sq_ifmedia_change_ip22(struct ifnet *ifp) -{ - struct sq_softc *sc = ifp->if_softc; - struct ifmedia *ifm = &sc->sc_ifmedia; - uint32_t iocw; - - iocw = - bus_space_read_4(sc->sc_hpct, sc->sc_hpcbh, IOC_BASE + IOC_WRITE); - - switch (IFM_SUBTYPE(ifm->ifm_media)) { - case IFM_10_5: - iocw &= ~IOC_WRITE_ENET_AUTO; - iocw |= IOC_WRITE_ENET_AUI; - break; - case IFM_10_T: - iocw &= ~(IOC_WRITE_ENET_AUTO | IOC_WRITE_ENET_AUI); - iocw |= IOC_WRITE_ENET_UTP; /* in case it cleared */ - break; - default: - case IFM_AUTO: - iocw |= IOC_WRITE_ENET_AUTO; - break; - } - - bus_space_write_4(sc->sc_hpct, sc->sc_hpcbh, IOC_BASE + IOC_WRITE, - iocw); - bus_space_barrier(sc->sc_hpct, sc->sc_hpcbh, IOC_BASE + IOC_WRITE, 4, - BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); - return 0; -} - -int -sq_ifmedia_change_singlemedia(struct ifnet *ifp) -{ - return 0; -} - -void -sq_ifmedia_status_ip22(struct ifnet *ifp, struct ifmediareq *req) -{ - struct sq_softc *sc = ifp->if_softc; - uint32_t iocr, iocw; - - iocw = - bus_space_read_4(sc->sc_hpct, sc->sc_hpcbh, IOC_BASE + IOC_WRITE); - - req->ifm_status = IFM_AVALID; - if (sc->sc_flags & SQF_LINKUP) - req->ifm_status |= IFM_ACTIVE; - if ((iocw & IOC_WRITE_ENET_AUTO) != 0) { - iocr = bus_space_read_4(sc->sc_hpct, sc->sc_hpcbh, - IOC_BASE + IOC_READ); - if ((iocr & IOC_READ_ENET_LINK) != 0) - req->ifm_active = IFM_10_5 | IFM_ETHER; - else - req->ifm_active = IFM_10_T | IFM_ETHER; - } else { - if ((iocw & IOC_WRITE_ENET_AUI) != 0) - req->ifm_active = IFM_10_5 | IFM_ETHER; - else - req->ifm_active = IFM_10_T | IFM_ETHER; - } -} - -void -sq_ifmedia_status_singlemedia(struct ifnet *ifp, struct ifmediareq *req) -{ - struct sq_softc *sc = ifp->if_softc; - struct ifmedia *ifm = &sc->sc_ifmedia; - - req->ifm_status = IFM_AVALID; - if (sc->sc_flags & SQF_LINKUP) - req->ifm_status |= IFM_ACTIVE; - req->ifm_active = ifm->ifm_media; -} |