diff options
| author | thorpej <thorpej@NetBSD.org> | 1998-06-02 01:29:41 +0000 |
|---|---|---|
| committer | thorpej <thorpej@NetBSD.org> | 1998-06-02 01:29:41 +0000 |
| commit | 5852faaf84a0ef6ea5c065ff9e21dccb5e9e0bde (patch) | |
| tree | 0e5dcf67741c505017b1969095677b530b515be0 /sys/dev | |
| parent | b8ce6ef7648b100e2792552d8afbb8b4348c2d70 (diff) | |
Device driver for the SMC 83c170 Ethernet PCI Integrated Controller (EPIC/100)
used in the SMC EtherPower II.
Media control isn't yet supported, due to some MII infrastructure
problems which I hope to address soon. This isn't a huge deal, since
the PHY defaults to auto-negotiate mode.
Also, the device just programs the multicast hash table to accept all
multicast, to avoid a hardware bug that causes the multicast address
filter to lose in 10Mb/s mode. This bug will be fixed in a more sane
way once the media control issues are dealt with.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/ic/smc83c170.c | 1293 | ||||
| -rw-r--r-- | sys/dev/ic/smc83c170reg.h | 492 | ||||
| -rw-r--r-- | sys/dev/ic/smc83c170var.h | 133 | ||||
| -rw-r--r-- | sys/dev/pci/files.pci | 6 | ||||
| -rw-r--r-- | sys/dev/pci/if_epic_pci.c | 193 |
5 files changed, 2116 insertions, 1 deletions
diff --git a/sys/dev/ic/smc83c170.c b/sys/dev/ic/smc83c170.c new file mode 100644 index 00000000000..79c47df495c --- /dev/null +++ b/sys/dev/ic/smc83c170.c @@ -0,0 +1,1293 @@ +/* $NetBSD: smc83c170.c,v 1.1 1998/06/02 01:29:42 thorpej Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/* + * Device driver for the Standard Microsystems Corp. 83C170 + * Ethernet PCI Integrated Controller (EPIC/100). + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_ether.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_inarp.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/ic/smc83c170reg.h> +#include <dev/ic/smc83c170var.h> + +void epic_start __P((struct ifnet *)); +void epic_watchdog __P((struct ifnet *)); +int epic_ioctl __P((struct ifnet *, u_long, caddr_t)); + +void epic_shutdown __P((void *)); + +void epic_reset __P((struct epic_softc *)); +void epic_init __P((struct epic_softc *)); +void epic_stop __P((struct epic_softc *)); +int epic_add_rxbuf __P((struct epic_softc *, int)); +void epic_read_eeprom __P((struct epic_softc *, int, int, u_int16_t *)); +void epic_set_mchash __P((struct epic_softc *)); + +/* + * Fudge the incoming packets by this much, to ensure the data after + * the Ethernet header is aligned. + */ +#define RX_ALIGNMENT_FUDGE 2 + +/* XXX Should be somewhere else. */ +#define ETHER_MIN_LEN 60 + +#define INTMASK (INTSTAT_FATAL_INT | INTSTAT_TXU | \ + INTSTAT_TXC | INTSTAT_RQE | INTSTAT_RCC) + +/* + * Attach an EPIC interface to the system. + */ +void +epic_attach(sc) + struct epic_softc *sc; +{ + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + int i, rseg, error, attach_stage; + bus_dma_segment_t seg; + u_int8_t enaddr[ETHER_ADDR_LEN], devname[12 + 1]; + u_int16_t myea[ETHER_ADDR_LEN / 2], mydevname[6]; + + attach_stage = 0; + + /* + * Allocate the control data structures, and create and load the + * DMA map for it. + */ + if ((error = bus_dmamem_alloc(sc->sc_dmat, + sizeof(struct epic_control_data), NBPG, 0, &seg, 1, &rseg, + BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to allocate control data, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail; + } + + attach_stage = 1; + + if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, + sizeof(struct epic_control_data), (caddr_t *)&sc->sc_control_data, + BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) { + printf("%s: unable to map control data, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail; + } + + attach_stage = 2; + + if ((error = bus_dmamap_create(sc->sc_dmat, + sizeof(struct epic_control_data), 1, + sizeof(struct epic_control_data), 0, BUS_DMA_NOWAIT, + &sc->sc_cddmamap)) != 0) { + printf("%s: unable to create control data DMA map, " + "error = %d\n", sc->sc_dev.dv_xname, error); + goto fail; + } + + attach_stage = 3; + + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_cddmamap, + sc->sc_control_data, sizeof(struct epic_control_data), NULL, + BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load control data DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail; + } + + attach_stage = 4; + + /* + * Create the transmit buffer DMA maps. + */ + for (i = 0; i < EPIC_NTXDESC; i++) { + if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, + EPIC_NFRAGS, MCLBYTES, 0, BUS_DMA_NOWAIT, + &sc->sc_txsoft[i].ds_dmamap)) != 0) { + printf("%s: unable to create tx DMA map %d, " + "error = %d\n", sc->sc_dev.dv_xname, i, error); + goto fail; + } + } + + attach_stage = 5; + + /* + * Create the recieve buffer DMA maps. + */ + for (i = 0; i < EPIC_NRXDESC; i++) { + if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, + MCLBYTES, 0, BUS_DMA_NOWAIT, + &sc->sc_rxsoft[i].ds_dmamap)) != 0) { + printf("%s: unable to create rx DMA map %d, " + "error = %d\n", sc->sc_dev.dv_xname, i, error); + goto fail; + } + } + + attach_stage = 6; + + /* + * Pre-allocate the receive buffers. + */ + for (i = 0; i < EPIC_NRXDESC; i++) { + if ((error = epic_add_rxbuf(sc, i)) != 0) { + printf("%s: unable to allocate or map rx buffer %d\n," + " error = %d\n", sc->sc_dev.dv_xname, i, error); + goto fail; + } + } + + attach_stage = 7; + + /* + * Bring the chip out of low-power mode and reset it to a known state. + */ + bus_space_write_4(st, sh, EPIC_GENCTL, 0); + epic_reset(sc); + + /* + * Read the Ethernet address from the EEPROM. + */ + epic_read_eeprom(sc, 0, (sizeof(myea) / sizeof(myea[0])), myea); + bcopy(myea, enaddr, sizeof(myea)); + + /* + * ...and the device name. + */ + epic_read_eeprom(sc, 0x2c, (sizeof(mydevname) / sizeof(mydevname[0])), + mydevname); + bcopy(mydevname, devname, sizeof(mydevname)); + devname[sizeof(mydevname)] = '\0'; + for (i = sizeof(mydevname) - 1; i >= 0; i--) { + if (devname[i] == ' ') + devname[i] = '\0'; + else + break; + } + + printf("%s: %s, Ethernet address %s\n", sc->sc_dev.dv_xname, + devname, ether_sprintf(enaddr)); + + ifp = &sc->sc_ethercom.ec_if; + strcpy(ifp->if_xname, sc->sc_dev.dv_xname); + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = epic_ioctl; + ifp->if_start = epic_start; + ifp->if_watchdog = epic_watchdog; + + /* + * Attach the interface. + */ + if_attach(ifp); + ether_ifattach(ifp, enaddr); +#if NBPFILTER > 0 + bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); +#endif + + /* + * Make sure the interface is shutdown during reboot. + */ + sc->sc_sdhook = shutdownhook_establish(epic_shutdown, sc); + if (sc->sc_sdhook == NULL) + printf("%s: WARNING: unable to establish shutdown hook\n", + sc->sc_dev.dv_xname); + return; + + fail: + /* + * Free any resources we've allocated during the failed attach + * attempt. Do this in reverse order and fall through. + */ + switch (attach_stage) { + case 7: + for (i = 0; i < EPIC_NRXDESC; i++) { + if (sc->sc_rxsoft[i].ds_mbuf != NULL) { + bus_dmamap_unload(sc->sc_dmat, + sc->sc_rxsoft[i].ds_dmamap); + m_freem(sc->sc_rxsoft[i].ds_mbuf); + } + } + /* FALLTHROUGH */ + + case 6: + for (i = 0; i < EPIC_NRXDESC; i++) + bus_dmamap_destroy(sc->sc_dmat, + sc->sc_rxsoft[i].ds_dmamap); + /* FALLTHROUGH */ + + case 5: + for (i = 0; i < EPIC_NTXDESC; i++) + bus_dmamap_destroy(sc->sc_dmat, + sc->sc_txsoft[i].ds_dmamap); + /* FALLTHROUGH */ + + case 4: + bus_dmamap_unload(sc->sc_dmat, sc->sc_cddmamap); + /* FALLTHROUGH */ + + case 3: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_cddmamap); + /* FALLTHROUGH */ + + case 2: + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_control_data, + sizeof(struct epic_control_data)); + /* FALLTHROUGH */ + + case 1: + bus_dmamem_free(sc->sc_dmat, &seg, rseg); + break; + } +} + +/* + * Shutdown hook. Make sure the interface is stopped at reboot. + */ +void +epic_shutdown(arg) + void *arg; +{ + struct epic_softc *sc = arg; + + epic_stop(sc); +} + +/* + * Start packet transmission on the interface. + * [ifnet interface function] + */ +void +epic_start(ifp) + struct ifnet *ifp; +{ + struct epic_softc *sc = ifp->if_softc; + struct epic_txdesc *txd; + struct epic_descsoft *ds; + struct epic_fraglist *fr; + bus_dmamap_t dmamap; + struct mbuf *m0; + int nexttx, seg, error, txqueued; + + txqueued = 0; + + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + while (ifp->if_snd.ifq_head != NULL && + sc->sc_txpending < EPIC_NTXDESC) { + /* + * Grab a packet off the queue. + */ + IF_DEQUEUE(&ifp->if_snd, m0); + + /* + * Get the last and next available transmit descriptor. + */ + nexttx = EPIC_NEXTTX(sc->sc_txlast); + txd = &sc->sc_control_data->ecd_txdescs[nexttx]; + fr = &sc->sc_control_data->ecd_txfrags[nexttx]; + ds = &sc->sc_txsoft[nexttx]; + dmamap = ds->ds_dmamap; + + loadmap: + /* + * Load the DMA map with the packet. + */ + error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, + BUS_DMA_NOWAIT); + switch (error) { + case 0: + /* Success. */ + break; + + case EFBIG: + { + struct mbuf *mn; + + /* + * We ran out of segments. We have to recopy this + * mbuf chain first. Bail out if we can't get the + * new buffers. + */ + printf("%s: too many segments, ", sc->sc_dev.dv_xname); + + MGETHDR(mn, M_DONTWAIT, MT_DATA); + if (mn == NULL) { + m_freem(m0); + printf("aborting\n"); + goto out; + } + if (m0->m_pkthdr.len > MHLEN) { + MCLGET(mn, M_DONTWAIT); + if ((mn->m_flags & M_EXT) == 0) { + m_freem(mn); + m_freem(m0); + printf("aborting\n"); + goto out; + } + } + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(mn, caddr_t)); + mn->m_pkthdr.len = mn->m_len = m0->m_pkthdr.len; + m_freem(m0); + m0 = mn; + printf("retrying\n"); + goto loadmap; + } + + default: + /* + * Some other problem; report it. + */ + printf("%s: can't load mbuf chain, error = %d\n", + sc->sc_dev.dv_xname, error); + m_freem(m0); + goto out; + } + + /* + * Initialize the fraglist. + */ + fr->ef_nfrags = dmamap->dm_nsegs; + for (seg = 0; seg < dmamap->dm_nsegs; seg++) { + fr->ef_frags[seg].ef_addr = + dmamap->dm_segs[seg].ds_addr; + fr->ef_frags[seg].ef_length = + dmamap->dm_segs[seg].ds_len; + } + + bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + /* + * Store a pointer to the packet so we can free it later. + */ + ds->ds_mbuf = m0; + + /* + * Finish setting up the new transmit descriptor: set the + * packet length and give it to the EPIC. + */ + txd->et_txlength = max(m0->m_pkthdr.len, ETHER_MIN_LEN); + txd->et_txstatus = ET_TXSTAT_OWNER; + + /* + * Committed; advance the lasttx pointer. If nothing was + * previously queued, reset the dirty pointer. + */ + sc->sc_txlast = nexttx; + if (sc->sc_txpending == 0) + sc->sc_txdirty = nexttx; + + sc->sc_txpending++; + + txqueued = 1; + +#if NBPFILTER > 0 + /* + * Pass the packet to any BPF listeners. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0); +#endif + } + + out: + /* + * We're finished. If we added more packets, make sure the + * transmit DMA engine is running. + */ + if (txqueued) { + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_COMMAND, + COMMAND_TXQUEUED); + + /* + * Set a 5 second watchdog timer. + */ + ifp->if_timer = 5; + } +} + +/* + * Watchdog timer handler. + * [ifnet interface function] + */ +void +epic_watchdog(ifp) + struct ifnet *ifp; +{ + struct epic_softc *sc = ifp->if_softc; + + printf("%s: device timeout\n", sc->sc_dev.dv_xname); + ifp->if_oerrors++; + + epic_init(sc); +} + +/* + * Handle control requests from the operator. + * [ifnet interface function] + */ +int +epic_ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct epic_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; + + s = splimp(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + epic_init(sc); + arp_ifinit(ifp, ifa); + break; +#endif /* INET */ +#ifdef NS + case AF_NS: + { + struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; + + if (ns_nullhost(*ina)) + ina->x_host = *(union ns_host *) + LLADDR(ifp->if_sadl); + else + bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl), + ifp->if_addrlen); + /* Set new address. */ + epic_init(sc); + break; + } +#endif /* NS */ + default: + epic_init(sc); + break; + } + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + epic_stop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interfase it marked up and it is stopped, then + * start it. + */ + epic_init(sc); + } else { + /* + * Reset the interface to pick up changes in any other + * flags that affect the hardware state. + */ + epic_init(sc); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_ethercom) : + ether_delmulti(ifr, &sc->sc_ethercom); + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + epic_init(sc); + error = 0; + } + break; + + default: + error = EINVAL; + break; + } + + splx(s); + return (error); +} + +/* + * Interrupt handler. + */ +int +epic_intr(arg) + void *arg; +{ + struct epic_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct ether_header *eh; + struct epic_rxdesc *rxd; + struct epic_txdesc *txd; + struct epic_descsoft *ds; + struct mbuf *m; + u_int32_t intstat; + int i, len, claimed = 0, error; + + top: + /* + * Get the interrupt status from the EPIC. + */ + intstat = bus_space_read_4(sc->sc_st, sc->sc_sh, EPIC_INTSTAT); + if ((intstat & INTSTAT_INT_ACTV) == 0) + return (claimed); + + claimed = 1; + + /* + * Acknowledge the interrupt. + */ + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_INTSTAT, + intstat & INTMASK); + + /* + * Check for receive interrupts. + */ + if (intstat & (INTSTAT_RCC | INTSTAT_RQE)) { + for (i = sc->sc_rxptr;; i = EPIC_NEXTRX(i)) { + rxd = &sc->sc_control_data->ecd_rxdescs[i]; + ds = &sc->sc_rxsoft[i]; + m = ds->ds_mbuf; + error = 0; + + if (rxd->er_rxstatus & ER_RXSTAT_OWNER) { + /* + * We have processed all of the + * receive buffers. + */ + break; + } + + bus_dmamap_sync(sc->sc_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + + /* + * Make sure the packet arrived intact. + */ + if ((rxd->er_rxstatus & ER_RXSTAT_PKTINTACT) == 0) { +#if 1 + if (rxd->er_rxstatus & ER_RXSTAT_CRCERROR) + printf("%s: CRC error\n", + sc->sc_dev.dv_xname); + if (rxd->er_rxstatus & ER_RXSTAT_ALIGNERROR) + printf("%s: alignment error\n", + sc->sc_dev.dv_xname); +#endif + ifp->if_ierrors++; + error = 1; + } + + /* + * Add a new buffer to the receive chain. If this + * fails, the old buffer is recycled. + */ + if (epic_add_rxbuf(sc, i) == 0) { + /* + * We wanted to reset the buffer, but + * didn't want to pass it on up. + */ + if (error) { + m_freem(m); + continue; + } + + len = rxd->er_buflength; + if (len < sizeof(struct ether_header)) { + m_freem(m); + continue; + } + + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + eh = mtod(m, struct ether_header *); +#if NBPFILTER > 0 + /* + * Pass this up to any BPF listeners. + */ + if (ifp->if_bpf) { + bpf_mtap(ifp->if_bpf, m); + + /* + * Only pass this up the stack + * if it's for us. + */ + if ((ifp->if_flags & IFF_PROMISC) && + bcmp(LLADDR(ifp->if_sadl), + eh->ether_dhost, + ETHER_ADDR_LEN) != 0 && + (rxd->er_rxstatus & + (ER_RXSTAT_BCAST|ER_RXSTAT_MCAST)) + == 0) { + m_freem(m); + continue; + } + } +#endif /* NPBFILTER > 0 */ + m->m_data += sizeof(struct ether_header); + m->m_len -= sizeof(struct ether_header); + m->m_pkthdr.len = m->m_len; + ether_input(ifp, eh, m); + } + } + + /* + * Update the recieve pointer. + */ + sc->sc_rxptr = i; + + /* + * Check for receive queue underflow. + */ + if (intstat & INTSTAT_RQE) { + printf("%s: receiver queue empty\n", + sc->sc_dev.dv_xname); + /* + * Ring is already built; just restart the + * receiver. + */ + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_PRCDAR, + sc->sc_cddma + EPIC_CDOFF(ecd_rxdescs[0])); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_COMMAND, + COMMAND_RXQUEUED | COMMAND_START_RX); + } + } + + /* + * Check for transmission complete interrupts. + */ + if (intstat & (INTSTAT_TXC | INTSTAT_TXU)) { + for (i = sc->sc_txdirty;; i = EPIC_NEXTTX(i)) { + txd = &sc->sc_control_data->ecd_txdescs[i]; + ds = &sc->sc_txsoft[i]; + + if (sc->sc_txpending == 0 || + (txd->et_txstatus & ET_TXSTAT_OWNER) != 0) + break; + + if (ds->ds_mbuf != NULL) { + bus_dmamap_sync(sc->sc_dmat, ds->ds_dmamap, + 0, ds->ds_dmamap->dm_mapsize, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; + } + sc->sc_txpending--; + + /* + * Check for errors and collisions. + */ + if ((txd->et_txstatus & ET_TXSTAT_PACKETTX) == 0) + ifp->if_oerrors++; + ifp->if_collisions += + TXSTAT_COLLISIONS(txd->et_txstatus); + if (txd->et_txstatus & ET_TXSTAT_CARSENSELOST) { +#if 1 + printf("%s: lost carrier\n", + sc->sc_dev.dv_xname); +#endif + /* XXX clear "active" but in media data */ + } + } + + /* + * Update the dirty transmit buffer pointer. + */ + sc->sc_txdirty = i; + + /* + * Cancel the watchdog timer if there are no pending + * transmissions. + */ + if (sc->sc_txpending == 0) + ifp->if_timer = 0; + + /* + * Kick the transmitter after a DMA underrun. + */ + if (intstat & INTSTAT_TXU) { + printf("%s: transmit underrun\n", sc->sc_dev.dv_xname); + bus_space_write_4(sc->sc_st, sc->sc_sh, + EPIC_COMMAND, COMMAND_TXUGO); + if (sc->sc_txpending) + bus_space_write_4(sc->sc_st, sc->sc_sh, + EPIC_COMMAND, COMMAND_TXQUEUED); + } + + /* + * Try to get more packets going. + */ + epic_start(ifp); + } + + /* + * Check for fatal interrupts. + */ + if (intstat & INTSTAT_FATAL_INT) { + printf("%s: fatal error, resetting\n", sc->sc_dev.dv_xname); + epic_init(sc); + } + + /* + * Check for more interrupts. + */ + goto top; +} + +/* + * Perform a soft reset on the EPIC. + */ +void +epic_reset(sc) + struct epic_softc *sc; +{ + + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_GENCTL, 0); + delay(100); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_GENCTL, GENCTL_SOFTRESET); + delay(100); +} + +/* + * Initialize the interface. Must be called at splimp(). + */ +void +epic_init(sc) + struct epic_softc *sc; +{ + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + u_int8_t *enaddr = LLADDR(ifp->if_sadl); + struct epic_txdesc *txd; + struct epic_rxdesc *rxd; + u_int32_t genctl, reg0; + int i; + + /* + * Cancel any pending I/O. + */ + epic_stop(sc); + + /* + * Reset the EPIC to a known state. + */ + epic_reset(sc); + + /* + * Magical mystery initialization. + */ + bus_space_write_4(st, sh, EPIC_TEST, TEST_INIT); + bus_space_write_4(st, sh, EPIC_TXTEST, 0); + + /* + * Initialize the EPIC genctl register: + * + * - 64 byte receive FIFO threshold + * - automatic advance to next receive frame + */ + genctl = GENCTL_RX_FIFO_THRESH0 | GENCTL_ONECOPY; + bus_space_write_4(st, sh, EPIC_GENCTL, genctl); + + /* + * Reset the MII bus and PHY. + */ + reg0 = bus_space_read_4(st, sh, EPIC_NVCTL); + bus_space_write_4(st, sh, EPIC_NVCTL, reg0 | NVCTL_GPIO1 | NVCTL_GPOE1); + bus_space_write_4(st, sh, EPIC_MIICFG, MIICFG_ENASER); + bus_space_write_4(st, sh, EPIC_GENCTL, genctl | GENCTL_RESET_PHY); + delay(100); + bus_space_write_4(st, sh, EPIC_GENCTL, genctl); + delay(100); + bus_space_write_4(st, sh, EPIC_NVCTL, reg0); + + /* + * Initialize Ethernet address. + */ + reg0 = enaddr[1] << 8 | enaddr[0]; + bus_space_write_4(st, sh, EPIC_LAN0, reg0); + reg0 = enaddr[3] << 8 | enaddr[2]; + bus_space_write_4(st, sh, EPIC_LAN1, reg0); + reg0 = enaddr[5] << 8 | enaddr[4]; + bus_space_write_4(st, sh, EPIC_LAN2, reg0); + + /* + * Set up the multicast hash table. + */ + epic_set_mchash(sc); + + /* + * Initialize receive control. Remember the external buffer + * size setting. + */ + reg0 = bus_space_read_4(st, sh, EPIC_RXCON) & + (RXCON_EXTBUFSIZESEL1 | RXCON_EXTBUFSIZESEL0); + reg0 |= (RXCON_RXMULTICAST | RXCON_RXBROADCAST); + if (ifp->if_flags & IFF_PROMISC) + reg0 |= RXCON_PROMISCMODE; + bus_space_write_4(st, sh, EPIC_RXCON, reg0); + + /* + * XXX Media (full-duplex in TXCON). + */ + + /* + * Initialize the transmit descriptors. + */ + txd = sc->sc_control_data->ecd_txdescs; + bzero(txd, sizeof(sc->sc_control_data->ecd_txdescs)); + for (i = 0; i < EPIC_NTXDESC; i++) { + txd[i].et_control = ET_TXCTL_LASTDESC | ET_TXCTL_IAF | + ET_TXCTL_FRAGLIST; + txd[i].et_bufaddr = sc->sc_cddma + EPIC_CDOFF(ecd_txfrags[i]); + txd[i].et_nextdesc = sc->sc_cddma + + EPIC_CDOFF(ecd_txdescs[(i + 1) & EPIC_NTXDESC_MASK]); + } + + /* + * Initialize the receive descriptors. Note the buffers + * and control word have already been initialized; we only + * need to initialize the ring. + */ + rxd = sc->sc_control_data->ecd_rxdescs; + for (i = 0; i < EPIC_NRXDESC; i++) { + rxd[i].er_nextdesc = sc->sc_cddma + + EPIC_CDOFF(ecd_rxdescs[(i + 1) & EPIC_NRXDESC_MASK]); + } + + /* + * Initialize the interrupt mask and enable interrupts. + */ + bus_space_write_4(st, sh, EPIC_INTMASK, INTMASK); + bus_space_write_4(st, sh, EPIC_GENCTL, genctl | GENCTL_INTENA); + + /* + * Give the transmit and receive rings to the EPIC. + */ + bus_space_write_4(st, sh, EPIC_PTCDAR, + sc->sc_cddma + EPIC_CDOFF(ecd_txdescs[0])); + bus_space_write_4(st, sh, EPIC_PRCDAR, + sc->sc_cddma + EPIC_CDOFF(ecd_rxdescs[0])); + + /* + * Initialize our ring pointers. txlast it initialized to + * the end of the list so that it will wrap around to the + * first descriptor when the first packet is transmitted. + */ + sc->sc_txpending = 0; + sc->sc_txdirty = 0; + sc->sc_txlast = EPIC_NTXDESC - 1; + + sc->sc_rxptr = 0; + + /* + * Set the EPIC in motion. + */ + bus_space_write_4(st, sh, EPIC_COMMAND, + COMMAND_RXQUEUED | COMMAND_START_RX); + + /* + * ...all done! + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; +} + +/* + * Stop transmission on the interface. + */ +void +epic_stop(sc) + struct epic_softc *sc; +{ + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct epic_descsoft *ds; + u_int32_t reg; + int i; + + /* + * Disable interrupts. + */ + reg = bus_space_read_4(st, sh, EPIC_GENCTL); + bus_space_write_4(st, sh, EPIC_GENCTL, reg & ~GENCTL_INTENA); + bus_space_write_4(st, sh, EPIC_INTMASK, 0); + + /* + * Stop the DMA engine and take the receiver off-line. + */ + bus_space_write_4(st, sh, EPIC_COMMAND, COMMAND_STOP_RDMA | + COMMAND_STOP_TDMA | COMMAND_STOP_RX); + + /* + * Release any queued transmit buffers. + */ + for (i = 0; i < EPIC_NTXDESC; i++) { + ds = &sc->sc_txsoft[i]; + if (ds->ds_mbuf != NULL) { + bus_dmamap_unload(sc->sc_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; + } + } + sc->sc_txpending = 0; + + /* + * Release the receive buffers, then reallocate/reinitialize. + */ + for (i = 0; i < EPIC_NRXDESC; i++) { + ds = &sc->sc_rxsoft[i]; + if (ds->ds_mbuf != NULL) { + bus_dmamap_unload(sc->sc_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; + } + if (epic_add_rxbuf(sc, i) != 0) { + /* + * This "can't happen" - we're at splimp() + * and we just freed the buffer we need + * above. + */ + panic("epic_stop: no buffers!"); + } + } + + /* + * Mark the interface down and cancel the watchdog timer. + */ + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; +} + +/* + * Read the EPIC Serial EEPROM. + */ +void +epic_read_eeprom(sc, word, wordcnt, data) + struct epic_softc *sc; + int word, wordcnt; + u_int16_t *data; +{ + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + u_int16_t reg; + int i, x; + +#define EEPROM_WAIT_READY(st, sh) \ + while ((bus_space_read_4((st), (sh), EPIC_EECTL) & EECTL_EERDY) == 0) \ + /* nothing */ + + /* + * Enable the EEPROM. + */ + bus_space_write_4(st, sh, EPIC_EECTL, EECTL_ENABLE); + EEPROM_WAIT_READY(st, sh); + + for (i = 0; i < wordcnt; i++) { + /* Send CHIP SELECT for one clock tick. */ + bus_space_write_4(st, sh, EPIC_EECTL, EECTL_ENABLE|EECTL_EECS); + EEPROM_WAIT_READY(st, sh); + + /* Shift in the READ opcode. */ + for (x = 3; x > 0; x--) { + reg = EECTL_ENABLE|EECTL_EECS; + if (EPIC_EEPROM_OPC_READ & (1 << (x - 1))) + reg |= EECTL_EEDI; + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + bus_space_write_4(st, sh, EPIC_EECTL, reg|EECTL_EESK); + EEPROM_WAIT_READY(st, sh); + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + } + + /* Shift in address. */ + for (x = 6; x > 0; x--) { + reg = EECTL_ENABLE|EECTL_EECS; + if ((word + i) & (1 << (x - 1))) + reg |= EECTL_EEDI; + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + bus_space_write_4(st, sh, EPIC_EECTL, reg|EECTL_EESK); + EEPROM_WAIT_READY(st, sh); + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + } + + /* Shift out data. */ + reg = EECTL_ENABLE|EECTL_EECS; + data[i] = 0; + for (x = 16; x > 0; x--) { + bus_space_write_4(st, sh, EPIC_EECTL, reg|EECTL_EESK); + EEPROM_WAIT_READY(st, sh); + if (bus_space_read_4(st, sh, EPIC_EECTL) & EECTL_EEDO) + data[i] |= (1 << (x - 1)); + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + } + + /* Clear CHIP SELECT. */ + bus_space_write_4(st, sh, EPIC_EECTL, EECTL_ENABLE); + EEPROM_WAIT_READY(st, sh); + } + + /* + * Disable the EEPROM. + */ + bus_space_write_4(st, sh, EPIC_EECTL, 0); + +#undef EEPROM_WAIT_READY +} + +/* + * Add a receive buffer to the indicated descriptor. + */ +int +epic_add_rxbuf(sc, idx) + struct epic_softc *sc; + int idx; +{ + struct epic_rxdesc *rxd = &sc->sc_control_data->ecd_rxdescs[idx]; + struct epic_descsoft *ds = &sc->sc_rxsoft[idx]; + struct mbuf *m, *oldm; + int error = 0; + + oldm = ds->ds_mbuf; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m != NULL) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + error = ENOMEM; + m_freem(m); + if (oldm == NULL) + return (error); + m = oldm; + m->m_data = m->m_ext.ext_buf; + } + } else { + error = ENOMEM; + if (oldm == NULL) + return (error); + m = oldm; + m->m_data = m->m_ext.ext_buf; + } + + ds->ds_mbuf = m; + + /* + * Set up the DMA map for this receive buffer. + */ + if (m != oldm) { + if (oldm != NULL) + bus_dmamap_unload(sc->sc_dmat, ds->ds_dmamap); + error = bus_dmamap_load(sc->sc_dmat, ds->ds_dmamap, + m->m_ext.ext_buf, m->m_ext.ext_size, NULL, BUS_DMA_NOWAIT); + if (error) { + printf("%s: can't load rx buffer, error = %d\n", + sc->sc_dev.dv_xname, error); + panic("epic_add_rxbuf"); /* XXX */ + } + } + + bus_dmamap_sync(sc->sc_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); + + /* + * Move the data pointer up so that the incoming packet + * will be 32-bit aligned. + */ + m->m_data += RX_ALIGNMENT_FUDGE; + + /* + * Initialize the receive descriptor. + */ + rxd->er_bufaddr = ds->ds_dmamap->dm_segs[0].ds_addr + + RX_ALIGNMENT_FUDGE; + rxd->er_buflength = m->m_ext.ext_size - RX_ALIGNMENT_FUDGE; + rxd->er_control = 0; + rxd->er_rxstatus = ER_RXSTAT_OWNER; + + return (error); +} + +/* + * Set the EPIC multicast hash table. + */ +void +epic_set_mchash(sc) + struct epic_softc *sc; +{ + struct ethercom *ec = &sc->sc_ethercom; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct ether_multi *enm; + struct ether_multistep step; + u_int8_t *cp; + u_int32_t crc, mchash[4]; + int len; + static const u_int32_t crctab[] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; + + /* + * Set up the multicast address filter by passing all multicast + * addresses through a CRC generator, and then using the high-order + * 6 bits as an index into the 64 bit multicast hash table (only + * the lower 16 bits of each 32 bit multicast hash register are + * valid). The high order bit selects the register, while the + * rest of the bits select the bit within the register. + */ + + if (ifp->if_flags & IFF_PROMISC) + goto allmulti; + +#if 1 /* XXX thorpej - hardware bug in 10Mb mode */ + goto allmulti; +#endif + + mchash[0] = mchash[1] = mchash[2] = mchash[3] = 0; + + ETHER_FIRST_MULTI(step, ec, enm); + while (enm != NULL) { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { + /* + * We must listen to a range of multicast addresses. + * For now, just accept all multicasts, rather than + * trying to set only those filter bits needed to match + * the range. (At this time, the only use of address + * ranges is for IP multicast routing, for which the + * range is big enough to require all bits set.) + */ + goto allmulti; + } + + cp = enm->enm_addrlo; + crc = 0xffffffff; + for (len = sizeof(enm->enm_addrlo); --len >= 0;) { + crc ^= *cp++; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + } + /* Just want the 6 most significant bits. */ + crc >>= 26; + + /* Set the corresponding bit in the hash table. */ + mchash[crc >> 4] |= 1 << (crc & 0xf); + + ETHER_NEXT_MULTI(step, enm); + } + + ifp->if_flags &= ~IFF_ALLMULTI; + goto sethash; + + allmulti: + ifp->if_flags |= IFF_ALLMULTI; + mchash[0] = mchash[1] = mchash[2] = mchash[3] = 0xffff; + + sethash: + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MC0, mchash[0]); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MC1, mchash[1]); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MC2, mchash[2]); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MC3, mchash[3]); +} diff --git a/sys/dev/ic/smc83c170reg.h b/sys/dev/ic/smc83c170reg.h new file mode 100644 index 00000000000..475dfa54edc --- /dev/null +++ b/sys/dev/ic/smc83c170reg.h @@ -0,0 +1,492 @@ +/* $NetBSD: smc83c170reg.h,v 1.1 1998/06/02 01:29:42 thorpej Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +#ifndef _DEV_IC_SMC83C170REG_H_ +#define _DEV_IC_SMC83C170REG_H_ + +/* + * Register description for the Standard Microsystems Corp. 83C170 + * Ethernet PCI Integrated Controller (EPIC/100). + */ + +/* + * EPIC transmit descriptor. Must be 4-byte aligned. + */ +struct epic_txdesc { +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t et_txlength; /* transmit length */ + u_int16_t et_txstatus; /* transmit status; see below */ +#else + u_int16_t et_txstatus; /* transmit status; see below */ + u_int16_t et_txlength; /* transmit length */ +#endif + u_int32_t et_bufaddr; /* buffer address */ +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t et_control; /* control word; see below */ + u_int16_t et_buflength; /* buffer length */ +#else + u_int16_t et_buflength; /* buffer length */ + u_int16_t et_control; /* control word; see below */ +#endif + u_int32_t et_nextdesc; /* next descriptor pointer */ +}; + +/* et_txstatus */ +#define ET_TXSTAT_OWNER 0x8000 /* NIC owns descriptor */ +#define ET_TXSTAT_COLLMASK 0x1f00 /* collisions */ +#define ET_TXSTAT_DEFERRING 0x0080 /* deferring due to jabber */ +#define ET_TXSTAT_OOWCOLL 0x0040 /* out of window collision */ +#define ET_TXSTAT_CDHB 0x0020 /* collision detect heartbeat */ +#define ET_TXSTAT_UNDERRUN 0x0010 /* DMA underrun */ +#define ET_TXSTAT_CARSENSELOST 0x0008 /* carrier lost */ +#define ET_TXSTAT_TXWITHCOLL 0x0004 /* encountered collisions during tx */ +#define ET_TXSTAT_NONDEFERRED 0x0002 /* transmitted without deferring */ +#define ET_TXSTAT_PACKETTX 0x0001 /* packet transmitted successfully */ + +#define TXSTAT_COLLISIONS(x) (((x) & ET_TXSTAT_COLLMASK) >> 8) + +/* et_control */ +#define ET_TXCTL_LASTDESC 0x0010 /* last descriptor in frame */ +#define ET_TXCTL_NOCRC 0x0008 /* disable CRC generation */ +#define ET_TXCTL_IAF 0x0004 /* interrupt after frame */ +#define ET_TXCTL_LFFORM 0x0002 /* alternate fraglist format */ +#define ET_TXCTL_FRAGLIST 0x0001 /* descriptor points to fraglist */ + +/* + * EPIC receive descriptor. Must be 4-byte aligned. + */ +struct epic_rxdesc { +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t er_rxlength; /* receive frame length */ + u_int16_t er_rxstatus; /* receive status; see below */ +#else + u_int16_t er_rxstatus; /* receive status; see below */ + u_int16_t er_rxlength; /* receive frame length */ +#endif + u_int32_t er_bufaddr; /* buffer address */ +#if BYTE_ORDER == BIG_ENDIAN + u_int16_t er_control; /* control word; see below */ + u_int16_t er_buflength; /* buffer length */ +#else + u_int16_t er_buflength; /* buffer length */ + u_int16_t er_control; /* control word; see below */ +#endif + u_int32_t er_nextdesc; /* next descriptor pointer */ +}; + +/* er_rxstatus */ +#define ER_RXSTAT_OWNER 0x8000 /* NIC owns descriptor */ +#define ER_RXSTAT_HDRCOPIED 0x4000 /* rx status posted after hdr copy */ +#define ER_RXSTAT_FRAGLISTERR 0x2000 /* ran out of frags to copy frame */ +#define ER_RXSTAT_NETSTATVALID 0x1000 /* length and status are valid */ +#define ER_RXSTAT_RCVRDIS 0x0040 /* receiver disabled */ +#define ER_RXSTAT_BCAST 0x0020 /* broadcast address recognized */ +#define ER_RXSTAT_MCAST 0x0010 /* multicast address recognized */ +#define ER_RXSTAT_MISSEDPKT 0x0008 /* missed packet */ +#define ER_RXSTAT_CRCERROR 0x0004 /* EPIC or MII asserted CRC error */ +#define ER_RXSTAT_ALIGNERROR 0x0002 /* frame not byte-aligned */ +#define ER_RXSTAT_PKTINTACT 0x0001 /* packet received without error */ + +/* er_control */ +#define ER_RXCTL_HEADER 0x0004 /* descriptor is for hdr copy */ +#define ER_RXCTL_LFFORM 0x0002 /* alternate fraglist format */ +#define ER_RXCTL_FRAGLIST 0x0001 /* descriptor points to fraglist */ + +#define EPIC_NFRAGS 63 /* maximum number of frags in list */ + +/* + * EPIC fraglist descriptor. + */ +struct epic_fraglist { + u_int32_t ef_nfrags; /* number of frags in list */ + struct { + u_int32_t ef_addr; /* address of frag */ + u_int32_t ef_length; /* length of frag */ + } ef_frags[EPIC_NFRAGS]; +}; + +/* + * EPIC control registers. + */ + +#define EPIC_COMMAND 0x00 /* COMMAND */ +#define COMMAND_TXUGO 0x00000080 /* start tx after underrun */ +#define COMMAND_STOP_RDMA 0x00000040 /* stop rx dma */ +#define COMMAND_STOP_TDMA 0x00000020 /* stop tx dma */ +#define COMMAND_NEXTFRAME 0x00000010 /* move onto next rx frame */ +#define COMMAND_RXQUEUED 0x00000008 /* queue a rx descriptor */ +#define COMMAND_TXQUEUED 0x00000004 /* queue a tx descriptor */ +#define COMMAND_START_RX 0x00000002 /* start receiver */ +#define COMMAND_STOP_RX 0x00000001 /* stop receiver */ + +#define EPIC_INTSTAT 0x04 /* INTERRUPT STATUS */ +#define INTSTAT_PTA 0x08000000 /* PCI target abort */ +#define INTSTAT_PMA 0x04000000 /* PCI master abort */ +#define INTSTAT_APE 0x02000000 /* PCI address parity error */ +#define INTSTAT_DPE 0x01000000 /* PCI data parity error */ +#define INTSTAT_RSV 0x00800000 /* rx status valid */ +#define INTSTAT_RCTS 0x00400000 /* rx copy threshold status */ +#define INTSTAT_RBE 0x00200000 /* rx buffers empty */ +#define INTSTAT_TCIP 0x00100000 /* tx copy in progress */ +#define INTSTAT_RCIP 0x00080000 /* rx copy in progress */ +#define INTSTAT_TXIDLE 0x00040000 /* transmit idle */ +#define INTSTAT_RXIDLE 0x00020000 /* receive idle */ +#define INTSTAT_INT_ACTV 0x00010000 /* interrupt active */ +#define INTSTAT_GP2_INT 0x00008000 /* gpio2 low (PHY event) */ +#define INTSTAT_FATAL_INT 0x00001000 /* fatal error occured */ +#define INTSTAT_RCT 0x00000800 /* rx copy threshold crossed */ +#define INTSTAT_PREI 0x00000400 /* preemptive interrupt */ +#define INTSTAT_CNT 0x00000200 /* counter overflow */ +#define INTSTAT_TXU 0x00000100 /* transmit underrun */ +#define INTSTAT_TQE 0x00000080 /* transmit queue empty */ +#define INTSTAT_TCC 0x00000040 /* transmit chain complete */ +#define INTSTAT_TXC 0x00000020 /* transmit complete */ +#define INTSTAT_RXE 0x00000010 /* receive error */ +#define INTSTAT_OVW 0x00000008 /* rx buffer overflow */ +#define INTSTAT_RQE 0x00000004 /* receive queue empty */ +#define INTSTAT_HCC 0x00000002 /* header copy complete */ +#define INTSTAT_RCC 0x00000001 /* receive copy complete */ + +#define EPIC_INTMASK 0x08 /* INTERRUPT MASK */ + /* Bits 0-15 enable the corresponding interrupt in INTSTAT. */ + +#define EPIC_GENCTL 0x0c /* GENERAL CONTROL */ +#define GENCTL_RESET_PHY 0x00004000 /* reset PHY */ +#define GENCTL_SOFT1 0x00002000 /* software use */ +#define GENCTL_SOFT0 0x00001000 /* software use */ +#define GENCTL_MEM_READ_CTL1 0x00000800 /* PCI memory control */ +#define GENCTL_MEM_READ_CTL0 0x00000400 /* (see below) */ +#define GENCTL_RX_FIFO_THRESH1 0x00000200 /* rx fifo thresh */ +#define GENCTL_RX_FIFO_THRESH0 0x00000100 /* (see below) */ +#define GENCTL_BIG_ENDIAN 0x00000020 /* big endian mode */ +#define GENCTL_ONECOPY 0x00000010 /* auto-NEXTFRAME */ +#define GENCTL_POWERDOWN 0x00000008 /* powersave sleep mode */ +#define GENCTL_SOFTINT 0x00000004 /* software-generated intr */ +#define GENCTL_INTENA 0x00000002 /* interrupt enable */ +#define GENCTL_SOFTRESET 0x00000001 /* initialize EPIC */ + +/* + * Explanation of MEMORY READ CONTROL: + * + * These bits control which PCI command the transmit DMA will use when + * bursting data over the PCI bus. When CTL1 is set, the transmit DMA + * will use the PCI "memory read line" command. When CTL0 is set, the + * transmit DMA will use the PCI "memory read multiple" command. When + * neither bit is set, the transmit DMA will use the "memory read" command. + * Use of "memory read line" or "memory read multiple" may enhance + * performance on some systems. + */ + +/* + * Explanation of RECEIVE FIFO THRESHOLD: + * + * Controls the level at which the PCI burst state machine begins to + * empty the receive FIFO. Default is "1/2 full" (0,1). + * + * 0,0 1/4 full 32 bytes + * 0,1 1/2 full 64 bytes + * 1,0 3/4 full 96 bytes + * 1,1 full 128 bytes + */ + +#define EPIC_NVCTL 0x10 /* NON-VOLATILE CONTROL */ +#define NVCTL_IPG_DLY_MASK 0x00000780 /* interpacket delay gap */ +#define NVCTL_CB_MODE 0x00000040 /* CardBus mode */ +#define NVCTL_GPIO2 0x00000020 /* general purpose i/o */ +#define NVCTL_GPIO1 0x00000010 /* ... */ +#define NVCTL_GPOE2 0x00000008 /* general purpose output ena */ +#define NVCTL_GPOE1 0x00000004 /* ... */ +#define NVCTL_CLKRUNSUPP 0x00000002 /* clock run supported */ +#define NVCTL_ENAMEMMAP 0x00000001 /* enable memory map */ + +#define NVCTL_IPG_DLY(x) (((x) & NVCTL_IPG_DLY_MASK) >> 7) + +#define EPIC_EECTL 0x14 /* EEPROM CONTROL */ +#define EECTL_EEPROMSIZE 0x00000040 /* eeprom size; see below */ +#define EECTL_EERDY 0x00000020 /* eeprom ready */ +#define EECTL_EEDO 0x00000010 /* eeprom data out (from) */ +#define EECTL_EEDI 0x00000008 /* eeprom data in (to) */ +#define EECTL_EESK 0x00000004 /* eeprom clock */ +#define EECTL_EECS 0x00000002 /* eeprom chip select */ +#define EECTL_ENABLE 0x00000001 /* eeprom enable */ + +/* + * Explanation of EEPROM SIZE: + * + * Indicates the size of the serial EEPROM: + * + * 1 16x16 or 64x16 + * 0 128x16 or 256x16 + */ + +/* + * Serial EEPROM opcodes, including start bit: + */ +#define EPIC_EEPROM_OPC_WRITE 0x05 +#define EPIC_EEPROM_OPC_READ 0x06 + +#define EPIC_PBLCNT 0x18 /* PBLCNT */ +#define PBLCNT_MASK 0x0000003f /* programmable burst length */ + +#define EPIC_TEST 0x1c /* TEST */ +#define TEST_INIT 0x00000008 + +#define EPIC_CRCCNT 0x20 /* CRC ERROR COUNTER */ +#define CRCCNT_MASK 0x0000000f /* crc errs since last read */ + +#define EPIC_ALICNT 0x24 /* FRAME ALIGNMENT ERROR COUNTER */ +#define ALICNT_MASK 0x0000000f /* align errs since last read */ + +#define EPIC_MPCNT 0x28 /* MISSED PACKET COUNTER */ +#define MPCNT_MASK 0x0000000f /* miss. pkts since last read */ + +#define EPIC_RXFIFO 0x2c + +#define EPIC_MMCTL 0x30 /* MII MANAGEMENT INTERFACE CONTROL */ +#define MMCTL_PHY_ADDR_MASK 0x00003e00 /* phy address field */ +#define MMCTL_PHY_REG_ADDR_MASK 0x000001f0 /* phy register address field */ +#define MMCTL_RESPONDER 0x00000008 /* phy responder */ +#define MMCTL_WRITE 0x00000002 /* write to phy */ +#define MMCTL_READ 0x00000001 /* read from phy */ + +#define EPIC_MMDATA 0x34 /* MII MANAGEMENT INTERFACE DATA */ +#define MMDATA_MASK 0x0000ffff /* MII frame data */ + +#define EPIC_MIICFG 0x38 /* MII CONFIGURATION */ +#define MIICFG_ALTDIR 0x00000080 /* alternate direction */ +#define MIICFG_ALTDATA 0x00000040 /* alternate data */ +#define MIICFG_ALTCLOCK 0x00000020 /* alternate clock source */ +#define MIICFG_ENASER 0x00000010 /* enable serial manag intf */ +#define MIICFG_PHYPRESENT 0x00000008 /* phy present on MII */ +#define MIICFG_LINKSTATUS 0x00000004 /* 694 link status */ +#define MIICFG_ENABLE 0x00000002 /* enable 694 */ +#define MIICFG_SERMODEENA 0x00000001 /* serial mode enable */ + +#define EPIC_IPG 0x3c /* INTERPACKET GAP */ +#define IPG_INTERFRAME_MASK 0x00007f00 /* interframe gap time */ +#define IPG_INTERPKT_MASK 0x000000ff /* interpacket gap time */ + +#define EPIC_LAN0 0x40 /* LAN ADDRESS */ + +#define EPIC_LAN1 0x44 + +#define EPIC_LAN2 0x48 + +#define LANn_MASK 0x0000ffff + +/* + * Explanation of LAN ADDRESS registers: + * + * LAN address is described as: + * + * 0000 [n1][n0][n3][n2] | 0000 [n5][n4][n7][n6] | 0000 [n9][n8][n11][n10] + * + * n == one nibble, mapped as follows: + * + * LAN0 [15-12] n3 + * LAN0 [11-8] n2 + * LAN0 [7-4] n1 + * LAN0 [3-0] n0 + * LAN1 [15-12] n7 + * LAN1 [11-8] n6 + * LAN1 [7-4] n5 + * LAN1 [3-0] n4 + * LAN2 [15-12] n11 + * LAN2 [11-8] n10 + * LAN2 [7-4] n9 + * LAN2 [3-0] n8 + * + * The LAN address is automatically recalled from the EEPROM after a + * hard reseet. + */ + +#define EPIC_IDCHK 0x4c /* BOARD ID/CHECKSUM */ +#define IDCHK_ID_MASK 0x0000ff00 /* board ID */ +#define IDCHK_CKSUM_MASK 0x000000ff /* checksum (should be 0xff) */ + +#define EPIC_MC0 0x50 /* MULTICAST ADDDRESS HASH TABLE */ + +#define EPIC_MC1 0x54 + +#define EPIC_MC2 0x58 + +#define EPIC_MC3 0x5c + +/* + * Explanation of MULTICAST ADDRESS HASH TABLE registers: + * + * Bits in the hash table are encoded as follows: + * + * MC0 [15-0] + * MC1 [31-16] + * MC2 [47-32] + * MC3 [53-48] + */ + +#define EPIC_RXCON 0x60 /* RECEIVE CONTROL */ +#define RXCON_EXTBUFSIZESEL1 0x00000200 /* ext buf size; see below */ +#define RXCON_EXTBUFSIZESEL0 0x00000100 /* ... */ +#define RXCON_EARLYRXENABLE 0x00000080 /* early receive enable */ +#define RXCON_MONITORMODE 0x00000040 /* monitor mode */ +#define RXCON_PROMISCMODE 0x00000020 /* promiscuous mode */ +#define RXCON_RXINVADDR 0x00000010 /* rx inv individual addr */ +#define RXCON_RXMULTICAST 0x00000008 /* receive multicast */ +#define RXCON_RXBROADCAST 0x00000004 /* receive broadcast */ +#define RXCON_RXRUNT 0x00000002 /* receive runt frames */ +#define RXCON_SAVEERRPKTS 0x00000001 /* save errored packets */ + +/* + * Explanation of EXTERNAL BUFFER SIZE SELECT: + * + * 0,0 external buffer access is disabled + * 0,1 16k + * 1,0 32k + * 1,1 128k + */ + +#define EPIC_RXSTAT 0x64 /* RECEIVE STATUS */ + +#define EPIC_RXCNT 0x68 + +#define EPIC_RXTEST 0x6c + +#define EPIC_TXCON 0x70 /* TRANSMIT CONTROL */ +#define TXCON_SLOTTIME_MASK 0x000000f8 /* slot time */ +#define TXCON_LOOPBACK_D2 0x00000004 /* loopback mode bit 2 */ +#define TXCON_LOOPBACK_D1 0x00000002 /* loopback mode bit 1 */ +#define TXCON_EARLYTX_ENABLE 0x00000001 /* early transmit enable */ + +/* + * Explanation of LOOPBACK MODE BIT: + * + * 0,0 normal operation + * 0,1 internal loopback (before PHY) + * 1,0 external loopback (after PHY) + * 1,1 full duplex - decouples transmit and receive blocks + */ + +#define EPIC_TXSTAT 0x74 /* TRANSMIT STATUS */ + +#define EPIC_TDPAR 0x78 + +#define EPIC_TXTEST 0x7c + +#define EPIC_PRFDAR 0x80 + +#define EPIC_PRCDAR 0x84 /* PCI RECEIVE CURRENT DESCRIPTOR ADDR */ + +#define EPIC_PRHDAR 0x88 + +#define EPIC_PRFLAR 0x8c + +#define EPIC_PRDLGTH 0x90 + +#define EPIC_PRFCNT 0x94 + +#define EPIC_PRLCAR 0x98 + +#define EPIC_PRLPAR 0x9c + +#define EPIC_PREFAR 0xa0 + +#define EPIC_PRSTAT 0xa4 /* PCI RECEIVE DMA STATUS */ + +#define EPIC_PRBUF 0xa8 + +#define EPIC_RDNCAR 0xac + +#define EPIC_PRCPTHR 0xb0 /* PCI RECEIVE COPY THRESHOLD */ + +#define EPIC_ROMDATA 0xb4 + +#define EPIC_PREEMPR 0xbc + +#define EPIC_PTFDAR 0xc0 + +#define EPIC_PTCDAR 0xc4 /* PCI TRANSMIT CURRENT DESCRIPTOR ADDR */ + +#define EPIC_PTHDAR 0xc8 + +#define EPIC_PTFLAR 0xcc + +#define EPIC_PTDLGTH 0xd0 + +#define EPIC_PTFCNT 0xd4 + +#define EPIC_PTLCAR 0xd8 + +#define EPIC_ETXTHR 0xdc /* EARLY TRANSMIT THRESHOLD */ + +#define EPIC_PTETXC 0xe0 + +#define EPIC_PTSTAT 0xe4 + +#define EPIC_PTBUF 0xe8 + +#define EPIC_PTFDAR2 0xec + +#define EPIC_FEVTR 0xf0 /* FEVTR (CardBus) */ + +#define EPIC_FEVTRMSKR 0xf4 /* FEVTRMSKR (CardBus) */ + +#define EPIC_FPRSTSTR 0xf8 /* FPRSTR (CardBus) */ + +#define EPIC_FFRCEVTR 0xfc /* PPRCEVTR (CardBus) */ + +/* + * EEPROM format: + * + * Word Bits Description + * ---- ---- ----------- + * 0 7-0 LAN Address Byte 0 + * 0 15-8 LAN Address Byte 1 + * 1 7-0 LAN Address Byte 2 + * 1 15-8 LAN Address Byte 3 + * 2 7-0 LAN Address Byte 4 + * 2 15-8 LAN Address Byte 5 + * 3 7-0 Board ID + * 3 15-8 Checksum + * 4 5-0 Non-Volatile Control Register Contents + * 5 7-0 PCI Minimum Grant Desired Setting + * 5 15-8 PCI Maximum Latency Desired Setting + * 6 15-0 Subsystem Vendor ID + * 7 14-0 Subsystem ID + */ + +#endif /* _DEV_IC_SMC83C170REG_H_ */ diff --git a/sys/dev/ic/smc83c170var.h b/sys/dev/ic/smc83c170var.h new file mode 100644 index 00000000000..e386d9927e2 --- /dev/null +++ b/sys/dev/ic/smc83c170var.h @@ -0,0 +1,133 @@ +/* $NetBSD: smc83c170var.h,v 1.1 1998/06/02 01:29:42 thorpej Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +#ifndef _DEV_IC_SMC83C170VAR_H_ +#define _DEV_IC_SMC83C170VAR_H_ + +/* + * Misc. definitions for the Standard Microsystems Corp. 83C170 + * Ethernet PCI Integrated Controller (EPIC/100) driver. + */ + +/* + * Transmit descriptor list size. + */ +#define EPIC_NTXDESC 128 +#define EPIC_NTXDESC_MASK (EPIC_NTXDESC - 1) +#define EPIC_NEXTTX(x) ((x + 1) & EPIC_NTXDESC_MASK) + +/* + * Receive descriptor list size. + */ +#define EPIC_NRXDESC 64 +#define EPIC_NRXDESC_MASK (EPIC_NRXDESC - 1) +#define EPIC_NEXTRX(x) ((x + 1) & EPIC_NRXDESC_MASK) + +/* + * Control structures are DMA'd to the EPIC chip. We allocate them in + * a single clump that maps to a single DMA segment to make several things + * easier. + */ +struct epic_control_data { + /* + * The transmit descriptors. + */ + struct epic_txdesc ecd_txdescs[EPIC_NTXDESC]; + + /* + * The receive descriptors. + */ + struct epic_rxdesc ecd_rxdescs[EPIC_NRXDESC]; + + /* + * The transmit fraglists. + */ + struct epic_fraglist ecd_txfrags[EPIC_NTXDESC]; +}; + +#define EPIC_CDOFF(x) offsetof(struct epic_control_data, x) + +/* + * Software state for transmit and receive desciptors. + */ +struct epic_descsoft { + struct mbuf *ds_mbuf; /* head of mbuf chain */ + bus_dmamap_t ds_dmamap; /* our DMA map */ +}; + +/* + * Software state per device. + */ +struct epic_softc { + struct device sc_dev; /* generic device information */ + 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 */ + struct ethercom sc_ethercom; /* ethernet common data */ + void *sc_sdhook; /* shutdown hook */ + + struct ifmedia sc_media; /* media information */ + + bus_dmamap_t sc_cddmamap; /* control data DMA map */ +#define sc_cddma sc_cddmamap->dm_segs[0].ds_addr + + /* + * Software state for transmit and receive descriptors. + */ + struct epic_descsoft sc_txsoft[EPIC_NTXDESC]; + struct epic_descsoft sc_rxsoft[EPIC_NRXDESC]; + + /* + * Control data structures. + */ + struct epic_control_data *sc_control_data; + + int sc_txpending; /* number of TX requests pending */ + int sc_txdirty; /* first dirty TX descriptor */ + int sc_txlast; /* last used TX descriptor */ + + int sc_rxptr; /* next ready RX descriptor */ +}; + +#ifdef _KERNEL +void epic_attach __P((struct epic_softc *)); +int epic_intr __P((void *)); +#endif /* _KERNEL */ + +#endif /* _DEV_IC_SMC83C170VAR_H_ */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 18f82d5513c..f65d49d1b91 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.38 1998/05/31 06:03:44 cgd Exp $ +# $NetBSD: files.pci,v 1.39 1998/06/02 01:29:41 thorpej Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be @@ -114,3 +114,7 @@ file dev/pci/tga_bt485.c tga device eap: audio, auconv, mulaw attach eap at pci file dev/pci/eap.c eap + +# SMC EPIC/100 Fast Ethernet on PCI +attach epic at pci with epic_pci +file dev/pci/if_epic_pci.c epic_pci diff --git a/sys/dev/pci/if_epic_pci.c b/sys/dev/pci/if_epic_pci.c new file mode 100644 index 00000000000..3e88e1b970c --- /dev/null +++ b/sys/dev/pci/if_epic_pci.c @@ -0,0 +1,193 @@ +/* $NetBSD: if_epic_pci.c,v 1.1 1998/06/02 01:29:41 thorpej Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/* + * PCI bus front-end for the Standard Microsystems Corp. 83C170 + * Ethernet PCI Integrated Controller (EPIC/100) driver. + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_ether.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_inarp.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/ic/smc83c170reg.h> +#include <dev/ic/smc83c170var.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +/* + * PCI configuration space registers used by the EPIC. + */ +#define EPIC_PCI_IOBA 0x10 /* i/o mapped base */ +#define EPIC_PCI_MMBA 0x14 /* memory mapped base */ + +struct epic_pci_softc { + struct epic_softc sc_epic; /* real EPIC softc */ + + /* PCI-specific goo. */ + void *sc_ih; /* interrupt handle */ +}; + +#ifdef __BROKEN_INDIRECT_CONFIG +int epic_pci_match __P((struct device *, void *, void *)); +#else +int epic_pci_match __P((struct device *, struct cfdata *, void *)); +#endif +void epic_pci_attach __P((struct device *, struct device *, void *)); + +struct cfattach epic_pci_ca = { + sizeof(struct epic_pci_softc), epic_pci_match, epic_pci_attach, +}; + +int +epic_pci_match(parent, match, aux) + struct device *parent; +#ifdef __BROKEN_INDIRECT_CONFIG + void *match; +#else + struct cfdata *match; +#endif + void *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SMC && + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SMC_83C170) + return (1); + + return (0); +} + +void +epic_pci_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct epic_pci_softc *psc = (struct epic_pci_softc *)self; + struct epic_softc *sc = &psc->sc_epic; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pci_intr_handle_t ih; + const char *intrstr = NULL; + bus_space_tag_t iot, memt; + bus_space_handle_t ioh, memh; + int ioh_valid, memh_valid; + + /* + * Map the device. + */ + ioh_valid = (pci_mapreg_map(pa, EPIC_PCI_IOBA, + PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL) == 0); + memh_valid = (pci_mapreg_map(pa, EPIC_PCI_MMBA, + PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, + &memt, &memh, NULL, NULL) == 0); + + if (memh_valid) { + sc->sc_st = memt; + sc->sc_sh = memh; + } else if (ioh_valid) { + sc->sc_st = iot; + sc->sc_sh = ioh; + } else { + printf(": unable to map device registers\n"); + return; + } + + sc->sc_dmat = pa->pa_dmat; + + printf(": SMC EPIC/100 Fast Ethernet\n"); + + /* + * Map and establish our interrupt. + */ + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("%s: unable to map interrupt\n", sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + psc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, epic_intr, sc); + if (psc->sc_ih == NULL) { + printf("%s: unable to establish interrupt", + sc->sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr); + + /* + * Finish off the attach. + */ + epic_attach(sc); +} |
