From 6a9db57002c18f39bcd957bf9ca46304f4dfa7e8 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Wed, 19 Sep 2001 17:38:01 +0000 Subject: 2001-09-19 Chris Johns * network/README.i82586, network/i82586.c, network/i82586reg.h, network/i82586var.h: New files. Network driver for Intel i82586. --- c/src/libchip/ChangeLog | 6 + c/src/libchip/network/Makefile.am | 8 +- c/src/libchip/network/README.i82586 | 5 + c/src/libchip/network/i82586.c | 2195 +++++++++++++++++++++++++++++++++++ c/src/libchip/network/i82586reg.h | 436 +++++++ c/src/libchip/network/i82586var.h | 319 +++++ 6 files changed, 2965 insertions(+), 4 deletions(-) create mode 100644 c/src/libchip/network/README.i82586 create mode 100644 c/src/libchip/network/i82586.c create mode 100644 c/src/libchip/network/i82586reg.h create mode 100644 c/src/libchip/network/i82586var.h (limited to 'c') diff --git a/c/src/libchip/ChangeLog b/c/src/libchip/ChangeLog index a76f9ffe3f..cbfce62249 100644 --- a/c/src/libchip/ChangeLog +++ b/c/src/libchip/ChangeLog @@ -1,3 +1,9 @@ +2001-09-19 Chris Johns + + * network/README.i82586, network/i82586.c, network/i82586reg.h, + network/i82586var.h: New files. Network driver for Intel + i82586. + 2001-04-27 Ralf Corsepius * configure.in: Add [-ansi -fasm] to RTEMS_PROG_CC_FOR_TARGET. diff --git a/c/src/libchip/network/Makefile.am b/c/src/libchip/network/Makefile.am index 68adba9e43..f8ced4bf2e 100644 --- a/c/src/libchip/network/Makefile.am +++ b/c/src/libchip/network/Makefile.am @@ -10,10 +10,10 @@ LIBNAME = libnetchip LIB = $(ARCH)/$(LIBNAME).a # add cs8900.c to work with it and make it compile -C_FILES = dec21140.c sonic.c +C_FILES = dec21140.c i82586.h sonic.c C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o) -include_libchip_HEADERS = cs8900.h sonic.h +include_libchip_HEADERS = cs8900.h i82586var.h sonic.h OBJS = $(C_O_FILES) @@ -52,7 +52,7 @@ endif .PRECIOUS: $(LIB) -EXTRA_DIST = README README.cs8900 README.dec21140 README.sonic \ - cs8900.c dec21140.c sonic.c +EXTRA_DIST = README README.cs8900 README.dec21140 README.i82586 README.sonic \ + cs8900.c dec21140.c i82586.c sonic.c include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/libchip/network/README.i82586 b/c/src/libchip/network/README.i82586 new file mode 100644 index 0000000000..01c6522d9c --- /dev/null +++ b/c/src/libchip/network/README.i82586 @@ -0,0 +1,5 @@ +# +# $Id$ +# + +TBD diff --git a/c/src/libchip/network/i82586.c b/c/src/libchip/network/i82586.c new file mode 100644 index 0000000000..6d0006272b --- /dev/null +++ b/c/src/libchip/network/i82586.c @@ -0,0 +1,2195 @@ +/* $NetBSD: i82586.c,v 1.38 2001/07/07 05:35:39 thorpej Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg and Charles M. Hannum. + * + * 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. + */ + +/*- + * Copyright (c) 1997 Paul Kranenburg. + * Copyright (c) 1992, 1993, University of Vermont and State + * Agricultural College. + * Copyright (c) 1992, 1993, Garrett A. Wollman. + * + * Portions: + * Copyright (c) 1994, 1995, Rafal K. Boni + * Copyright (c) 1990, 1991, William F. Jolitz + * Copyright (c) 1990, The Regents of the University of California + * + * RTEMS: + * Copyright (c) 2001, Chris Johns, Cybertec Pty Ltd, + * http://www.cybertec.com.au/. + * + * All rights reserved. + * + * 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 University of Vermont + * and State Agricultural College and Garrett A. Wollman, by William F. + * Jolitz, and by the University of California, Berkeley, Lawrence + * Berkeley Laboratory, and its contributors. + * 4. Neither the names of the Universities nor the names of the authors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR AUTHORS 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. + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Original StarLAN driver written by Garrett Wollman with reference to the + * Clarkson Packet Driver code for this chip written by Russ Nelson and others. + * + * BPF support code taken from hpdev/if_le.c, supplied with tcpdump. + * + * 3C507 support is loosely based on code donated to NetBSD by Rafal Boni. + * + * Majorly cleaned up and 3C507 code merged by Charles Hannum. + * + * Converted to SUN ie driver by Charles D. Cranor, + * October 1994, January 1995. + * This sun version based on i386 version 1.30. + */ + +/* + * The i82586 is a very painful chip, found in sun3's, sun-4/100's + * sun-4/200's, and VME based suns. The byte order is all wrong for a + * SUN, making life difficult. Programming this chip is mostly the same, + * but certain details differ from system to system. This driver is + * written so that different "ie" interfaces can be controled by the same + * driver. + */ + +/* +Mode of operation: + + We run the 82586 in a standard Ethernet mode. We keep NFRAMES + received frame descriptors around for the receiver to use, and + NRXBUF associated receive buffer descriptors, both in a circular + list. Whenever a frame is received, we rotate both lists as + necessary. (The 586 treats both lists as a simple queue.) We also + keep a transmit command around so that packets can be sent off + quickly. + + We configure the adapter in AL-LOC = 1 mode, which means that the + Ethernet/802.3 MAC header is placed at the beginning of the receive + buffer rather than being split off into various fields in the RFD. + This also means that we must include this header in the transmit + buffer as well. + + By convention, all transmit commands, and only transmit commands, + shall have the I (IE_CMD_INTR) bit set in the command. This way, + when an interrupt arrives at i82586_intr(), it is immediately possible + to tell what precisely caused it. ANY OTHER command-sending + routines should run at splnet(), and should post an acknowledgement + to every interrupt they generate. + + To save the expense of shipping a command to 82586 every time we + want to send a frame, we use a linked list of commands consisting + of alternate XMIT and NOP commands. The links of these elements + are manipulated (in iexmit()) such that the NOP command loops back + to itself whenever the following XMIT command is not yet ready to + go. Whenever an XMIT is ready, the preceding NOP link is pointed + at it, while its own link field points to the following NOP command. + Thus, a single transmit command sets off an interlocked traversal + of the xmit command chain, with the host processor in control of + the synchronization. +*/ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "i82586reg.h" +#include "i82586var.h" + +/* + * A global way to change all async cmd requests at once. For RTEMS and running + * as tasks I wanted to see if the tx race condition is effected by this. + */ +#define ASYNC_OPTION (1) + +void i82586_reset (struct ie_softc *, int); +void i82586_watchdog (struct ifnet *); +void i82586_init (void *); +int i82586_ioctl (struct ifnet *, int cmd, caddr_t data); +void i82586_start (struct ifnet *); + +void i82586_stop (struct ifnet *, int); +int i82586_rint (struct ie_softc *, int); +int i82586_tint (struct ie_softc *, int); + +int i82586_mediachange (struct ifnet *); +void i82586_mediastatus (struct ifnet *, struct ifmediareq *); + +static void i82586_tx_task(void *arg); +static void i82586_start_tx(struct ie_softc *sc); + +static int ie_readframe (struct ie_softc *, int); +static struct mbuf *ieget (struct ie_softc *, int, int); +static int i82586_get_rbd_list (struct ie_softc *, u_int16_t *, + u_int16_t *, int *); +static void i82586_release_rbd_list (struct ie_softc *, + u_int16_t, u_int16_t); +static int i82586_drop_frames (struct ie_softc *); +static int i82586_chk_rx_ring (struct ie_softc *); + +static __inline__ void ie_ack (struct ie_softc *, u_int); +static __inline__ void iexmit (struct ie_softc *); +static void i82586_start_transceiver (struct ie_softc *); + +static void i82586_count_errors (struct ie_softc *); +static void i82586_rx_errors (struct ie_softc *, int, int); +static void i82586_setup_bufs (struct ie_softc *); +static void setup_simple_command (struct ie_softc *, int, int); +static int ie_cfg_setup (struct ie_softc *, int, int, int); +static int ie_ia_setup (struct ie_softc *, int); +static void ie_run_tdr (struct ie_softc *, int); +static int ie_mc_setup (struct ie_softc *, int); +static void ie_mc_reset (struct ie_softc *); +static int i82586_start_cmd (struct ie_softc *, int, int, int, int); +static int i82586_cmd_wait (struct ie_softc *); + +#if I82586_DEBUG +static void print_softie(struct ie_softc *sc); +static void print_rbd (struct ie_softc *, int); +#endif + +#define min(l,r) ((l) < (r) ? (l) : (r)) +#define max(l,r) ((l) > (r) ? (l) : (r)) + +#define delay(p) rtems_task_wake_after (TOD_MICROSECONDS_TO_TICKS (p)) + +#define i82586_WAKE_EVENT RTEMS_EVENT_1 +#define i82586_TX_EVENT RTEMS_EVENT_2 + +char *bitmask_snprintf(unsigned long value, const char *format, char *buf, int blen) +{ + char *b = buf; + int bit = 31; + + while (bit-- > *format) + value <<= 1; + + format++; + + while (*format) + { + if (value & 0x80000000) + while (isalnum(*format)) + *b++ = *format; + else + *b++ = '0'; + + *b++ = ','; + + while (bit-- > *format) + value <<= 1; + + format++; + } + + *b = '\0'; + return buf; +} + +char *ether_sprintf(unsigned char *addr) +{ + static char buf[32]; + char *b = buf; + int i; + + for (i = 0; i < ETHER_ADDR_LEN; i++) + { + sprintf(b, "%02x:", *addr++); + b += 3; + } + b--; + b = "\0"; + return buf; +} + +/* + * Front-ends call this function to attach to the MI driver. + * + * The front-end has responsibility for managing the ICP and ISCP + * structures. Both of these are opaque to us. Also, the front-end + * chooses a location for the SCB which is expected to be addressable + * (through `sc->scb') as an offset against the shared-memory bus handle. + * + * The following MD interface function must be setup by the front-end + * before calling here: + * + * hwreset - board dependent reset + * hwinit - board dependent initialization + * chan_attn - channel attention + * intrhook - board dependent interrupt processing + * memcopyin - shared memory copy: board to KVA + * memcopyout - shared memory copy: KVA to board + * ie_bus_read16 - read a sixteen-bit i82586 pointer + * ie_bus_write16 - write a sixteen-bit i82586 pointer + * ie_bus_write24 - write a twenty-four-bit i82586 pointer + * + */ +int +i82586_attach(struct rtems_bsdnet_ifconfig *config, int attaching) +{ + struct ie_softc *sc; + struct ifnet *ifp; + char *name; + int unit; + int mtu; + + /* + * Parse driver name + */ + + if ((unit = rtems_bsdnet_parse_driver_name (config, &name)) < 0) + return 0; + + sc = config->drv_ctrl; + ifp = &sc->arpcom.ac_if; + +#if I82586_DEBUG + sc->sc_debug = 0; //IED_TINT | IED_XMIT; +#endif + + if (attaching) + { + if (ifp->if_softc) + { + printf ("Driver `%s' already in use.\n", config->name); + return 0; + } + + /* + * Process options + */ + + memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = name; + ifp->if_mtu = mtu; + ifp->if_init = i82586_init; + ifp->if_ioctl = i82586_ioctl; + ifp->if_start = i82586_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* Attach the interface. */ + if_attach(ifp); + ether_ifattach(ifp); + return 1; + } + return 0; +} + + +/* + * Interrupt Acknowledge. Mask in native byte-order. + */ +static __inline__ void +ie_ack(struct ie_softc *sc, u_int mask) +{ + u_int status; + + IE_BUS_BARRIER(sc, 0, 0, BUS_SPACE_BARRIER_READ); + status = sc->ie_bus_read16(sc, IE_SCB_STATUS(sc->scb)); + i82586_start_cmd(sc, status & mask, 0, 0, 0); + if (sc->intrhook) + sc->intrhook(sc, INTR_ACK); +} + + +/* + * Read data off the interface, and turn it into an mbuf chain. + * + * This code is DRAMATICALLY different from the previous version; this + * version tries to allocate the entire mbuf chain up front, given the + * length of the data available. This enables us to allocate mbuf + * clusters in many situations where before we would have had a long + * chain of partially-full mbufs. This should help to speed up the + * operation considerably. (Provided that it works, of course.) + */ +static __inline struct mbuf * +ieget(struct ie_softc *sc, int head, int totlen) +{ + struct mbuf *m, *m0, *newm; + int len, resid; + int thisrboff, thismboff; + struct ether_header eh; + + /* + * Snarf the Ethernet header. + */ + (sc->memcopyin)(sc, &eh, IE_RBUF_ADDR(sc, head), + sizeof(struct ether_header)); + + resid = totlen; + + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 == 0) + return (0); + m0->m_pkthdr.rcvif = &sc->arpcom.ac_if; + m0->m_pkthdr.len = totlen; + len = MHLEN; + m = m0; + + /* + * This loop goes through and allocates mbufs for all the data we will + * be copying in. It does not actually do the copying yet. + */ + while (totlen > 0) { + if (totlen >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) + goto bad; + len = MCLBYTES; + } + + if (m == m0) { + caddr_t newdata = (caddr_t) + ALIGN(m->m_data + sizeof(struct ether_header)) - + sizeof(struct ether_header); + len -= newdata - m->m_data; + m->m_data = newdata; + } + + m->m_len = len = min(totlen, len); + + totlen -= len; + if (totlen > 0) { + MGET(newm, M_DONTWAIT, MT_DATA); + if (newm == 0) + goto bad; + len = MLEN; + m = m->m_next = newm; + } + } + + m = m0; + thismboff = 0; + + /* + * Copy the Ethernet header into the mbuf chain. + */ + memcpy(mtod(m, caddr_t), &eh, sizeof(struct ether_header)); + thismboff = sizeof(struct ether_header); + thisrboff = sizeof(struct ether_header); + resid -= sizeof(struct ether_header); + + /* + * Now we take the mbuf chain (hopefully only one mbuf most of the + * time) and stuff the data into it. There are no possible failures + * at or after this point. + */ + while (resid > 0) { + int thisrblen = IE_RBUF_SIZE - thisrboff, + thismblen = m->m_len - thismboff; + len = min(thisrblen, thismblen); + + (sc->memcopyin)(sc, mtod(m, caddr_t) + thismboff, + IE_RBUF_ADDR(sc,head) + thisrboff, + (u_int)len); + resid -= len; + + if (len == thismblen) { + m = m->m_next; + thismboff = 0; + } else + thismboff += len; + + if (len == thisrblen) { + if (++head == sc->nrxbuf) + head = 0; + thisrboff = 0; + } else + thisrboff += len; + } + + /* + * Unless something changed strangely while we were doing the copy, + * we have now copied everything in from the shared memory. + * This means that we are done. + */ + return (m0); + + bad: + m_freem(m0); + return (0); +} + +/* + * Setup all necessary artifacts for an XMIT command, and then pass the XMIT + * command to the chip to be executed. + */ +static __inline__ void +iexmit(struct ie_softc *sc) +{ + int off; + int cur, prev; + + cur = sc->xctail; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_TX_EMIT, cur); +#endif + +#if I82586_DEBUG + if (sc->sc_debug & IED_XMIT) + printf("%s: xmit buffer %d\n", sc->arpcom.ac_if.if_name, cur); +#endif + + /* + * Setup the transmit command. + */ + sc->ie_bus_write16(sc, IE_CMD_XMIT_DESC(sc->xmit_cmds, cur), + IE_XBD_ADDR(sc->xbds, cur)); + + sc->ie_bus_write16(sc, IE_CMD_XMIT_STATUS(sc->xmit_cmds, cur), 0); + + if (sc->do_xmitnopchain) { + /* + * Gate this XMIT command to the following NOP + */ + sc->ie_bus_write16(sc, IE_CMD_XMIT_LINK(sc->xmit_cmds, cur), + IE_CMD_NOP_ADDR(sc->nop_cmds, cur)); + sc->ie_bus_write16(sc, IE_CMD_XMIT_CMD(sc->xmit_cmds, cur), + IE_CMD_XMIT | IE_CMD_INTR); + + /* + * Loopback at following NOP + */ + sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, cur), 0); + sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, cur), + IE_CMD_NOP_ADDR(sc->nop_cmds, cur)); + + /* + * Gate preceding NOP to this XMIT command + */ + prev = (cur + NTXBUF - 1) % NTXBUF; + sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, prev), 0); + sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, prev), + IE_CMD_XMIT_ADDR(sc->xmit_cmds, cur)); + + off = IE_SCB_STATUS(sc->scb); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + if ((sc->ie_bus_read16(sc, off) & IE_CUS_ACTIVE) == 0) { + printf("iexmit: CU not active\n"); + i82586_start_transceiver(sc); + } + } else { + sc->ie_bus_write16(sc, IE_CMD_XMIT_LINK(sc->xmit_cmds,cur), + 0xffff); + + sc->ie_bus_write16(sc, IE_CMD_XMIT_CMD(sc->xmit_cmds, cur), + IE_CMD_XMIT | IE_CMD_INTR | IE_CMD_LAST); + + off = IE_SCB_CMDLST(sc->scb); + sc->ie_bus_write16(sc, off, IE_CMD_XMIT_ADDR(sc->xmit_cmds, cur)); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + + if (i82586_start_cmd(sc, IE_CUC_START, 0, 0, ASYNC_OPTION)) + printf("%s: iexmit: start xmit command timed out\n", + sc->arpcom.ac_if.if_name); + } + + sc->arpcom.ac_if.if_timer = 5; +} + + +/* + * Device timeout/watchdog routine. + * Entered if the device neglects to generate an interrupt after a + * transmit has been started on it. + */ +void +i82586_watchdog(struct ifnet *ifp) +{ + struct ie_softc *sc = ifp->if_softc; + printf("%s: device timeout\n", ifp->if_name); + ++ifp->if_oerrors; + i82586_reset(sc, 1); +} + +static int +i82586_cmd_wait(struct ie_softc *sc) +{ + /* spin on i82586 command acknowledge; wait at most 0.9 (!) seconds */ + int i, off; + u_int16_t cmd; + + for (i = 0; i < 900000; i++) { + /* Read the command word */ + off = IE_SCB_CMD(sc->scb); + + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + if ((cmd = sc->ie_bus_read16(sc, off)) == 0) + return (0); + delay(1); + } + + off = IE_SCB_STATUS(sc->scb); + printf("i82586_cmd_wait: timo(%ssync): scb status: 0x%x, cmd: 0x%x\n", + sc->async_cmd_inprogress?"a":"", + sc->ie_bus_read16(sc, off), cmd); + + return (1); /* Timeout */ +} + +/* + * Send a command to the controller and wait for it to either complete + * or be accepted, depending on the command. If the command pointer + * is null, then pretend that the command is not an action command. + * If the command pointer is not null, and the command is an action + * command, wait for one of the MASK bits to turn on in the command's + * status field. + * If ASYNC is set, we just call the chip's attention and return. + * We may have to wait for the command's acceptance later though. + */ +static int +i82586_start_cmd(struct ie_softc *sc, int cmd, int iecmdbuf, int mask, int async) +{ + int i; + int off; + + if (sc->async_cmd_inprogress != 0) { + /* + * If previous command was issued asynchronously, wait + * for it now. + */ + if (i82586_cmd_wait(sc) != 0) + return (1); + sc->async_cmd_inprogress = 0; + } + + off = IE_SCB_CMD(sc->scb); + sc->ie_bus_write16(sc, off, cmd); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE); + (sc->chan_attn)(sc, CARD_RESET); + + if (async != 0) { + sc->async_cmd_inprogress = 1; + return (0); + } + + if (IE_ACTION_COMMAND(cmd) && iecmdbuf) { + int status; + /* + * Now spin-lock waiting for status. This is not a very nice + * thing to do, and can kill performance pretty well... + * According to the packet driver, the minimum timeout + * should be .369 seconds. + */ + for (i = 0; i < 369000; i++) { + /* Read the command status */ + off = IE_CMD_COMMON_STATUS(iecmdbuf); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + status = sc->ie_bus_read16(sc, off); + if (status & mask) + return (0); + delay(1); + } + + } else { + /* + * Otherwise, just wait for the command to be accepted. + */ + return (i82586_cmd_wait(sc)); + } + + /* Timeout */ + return (1); +} + + +/* + * Transfer accumulated chip error counters to IF. + */ +static __inline void +i82586_count_errors(struct ie_softc *sc) +{ + int scb = sc->scb; + + sc->arpcom.ac_if.if_ierrors += + sc->ie_bus_read16(sc, IE_SCB_ERRCRC(scb)) + + sc->ie_bus_read16(sc, IE_SCB_ERRALN(scb)) + + sc->ie_bus_read16(sc, IE_SCB_ERRRES(scb)) + + sc->ie_bus_read16(sc, IE_SCB_ERROVR(scb)); + + /* Clear error counters */ + sc->ie_bus_write16(sc, IE_SCB_ERRCRC(scb), 0); + sc->ie_bus_write16(sc, IE_SCB_ERRALN(scb), 0); + sc->ie_bus_write16(sc, IE_SCB_ERRRES(scb), 0); + sc->ie_bus_write16(sc, IE_SCB_ERROVR(scb), 0); +} + + +static void +i82586_rx_errors(struct ie_softc *sc, int fn, int status) +{ + char bits[128]; + + printf("%s: rx error (frame# %d): %s\n", sc->arpcom.ac_if.if_name, fn, + bitmask_snprintf(status, IE_FD_STATUSBITS, bits, sizeof(bits))); +} + +/* + * i82586 interrupt entry point. + */ +rtems_isr +i82586_intr(rtems_vector_number vec, void *arg) +{ + struct ie_softc *sc = arg; + +#if I82586_DEBUG + static unsigned long icnt = 0; + I82586_TRACE(sc, I82586_INTS_REQ, icnt++); +#endif + + /* + * Implementation dependent interrupt handling. It must at least + * disabled interrupts from the i82586. It is hoped this can + * happen somewhere outside the i82586. + */ + if (sc->intrhook) + (sc->intrhook)(sc, INTR_ENTER); + + /* + * Wake the task to handle the interrupt. It will + * enabled the interrupts when it has finished. + */ + rtems_event_send (sc->intr_task, i82586_WAKE_EVENT); +} + +/* + * i82586 interrupt task. The task is actually an extension of the interrupt + * with a context switch in the middle. The RTEMS TCP/IP stack requires a task + * be used to talk to the stack as a network semaphore is claimed. This + * cannot happen during an interrupt. + */ +static void +i82586_intr_task(void *arg) +{ + struct ie_softc *sc = arg; + rtems_event_set events; + u_int status; + int off; + int reset; + + /* + * Not sure this is a good idea but as a out path exists and + * roads lead to it, it seems ok. + */ + for (;;) { + rtems_bsdnet_event_receive (i82586_WAKE_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + 0, &events); + + off = IE_SCB_STATUS(sc->scb); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + status = sc->ie_bus_read16(sc, off) & IE_ST_WHENCE; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_INTS_IN, status); +#endif + + reset = 0; + + while ((status & IE_ST_WHENCE) != 0) { +#if I82586_DEBUG + if (sc->sc_debug) + printf ("%s: -------\n%s: scbstatus=0x%x\n", + sc->arpcom.ac_if.if_name, sc->arpcom.ac_if.if_name, status); +#endif + +#if 1 + /* Ack interrupts FIRST in case we receive more during the ISR. */ + ie_ack(sc, status & IE_ST_WHENCE); +#endif + + i82586_start_cmd(sc, status & IE_ST_WHENCE, 0, 0, ASYNC_OPTION); + + if (status & (IE_ST_FR | IE_ST_RNR)) + if (i82586_rint(sc, status) != 0) { + reset = 1; + break; + } + + if (status & IE_ST_CX) + if (i82586_tint(sc, status) != 0) { + reset = 1; + break; + } + +#if I82586_DEBUG + if ((status & IE_ST_CNA) && (sc->sc_debug & IED_CNA)) + printf("%s: cna; status=0x%x\n", sc->arpcom.ac_if.if_name, status); +#endif + +#if 0 + if (sc->intrhook) + (sc->intrhook)(sc, INTR_LOOP); +#endif + +#if 1 + /* + * Interrupt ACK was posted asynchronously; wait for + * completion here before reading SCB status again. + * + * If ACK fails, try to reset the chip, in hopes that + * it helps. + */ + if (i82586_cmd_wait(sc) != 0) { + reset = 1; + break; + } +#endif + + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + status = sc->ie_bus_read16(sc, off); +#if I82586_DEBUG + I82586_TRACE(sc, I82586_INTS_LOOPS, status); +#endif + } + + if (reset) { +#if I82586_DEBUG + printf("%s: intr reset; status=0x%x\n", sc->arpcom.ac_if.if_name, status); +#endif + i82586_cmd_wait(sc); + i82586_reset(sc, 1); + } + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_INTS_OUT, status); +#endif + + if (sc->intrhook) + (sc->intrhook)(sc, INTR_EXIT); + } +} + +/* + * Process a received-frame interrupt. + */ +int +i82586_rint(struct ie_softc *sc, int scbstatus) +{ + static int timesthru = 1024; + int i, status, off; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_RX_INT, scbstatus); + + if (sc->sc_debug & IED_RINT) + printf("%s: rint: status 0x%x\n", + sc->arpcom.ac_if.if_name, scbstatus); +#endif + + for (;;) { + int drop = 0; + + i = sc->rfhead; + off = IE_RFRAME_STATUS(sc->rframes, i); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + status = sc->ie_bus_read16(sc, off); + +#if I82586_DEBUG + if (sc->sc_debug & IED_RINT) + printf("%s: rint: frame(%d) status 0x%x\n", + sc->arpcom.ac_if.if_name, i, status); +#endif + if ((status & IE_FD_COMPLETE) == 0) { + if ((status & IE_FD_OK) != 0) { + printf("%s: rint: weird: ", + sc->arpcom.ac_if.if_name); + i82586_rx_errors(sc, i, status); +#if I82586_DEBUG + I82586_TRACE(sc, I82586_RX_ERR, status); +#endif + break; + } + if (--timesthru == 0) { + /* Account the accumulated errors */ + i82586_count_errors(sc); + timesthru = 1024; + } + break; + } else if ((status & IE_FD_OK) == 0) { + /* + * If the chip is configured to automatically + * discard bad frames, the only reason we can + * get here is an "out-of-resource" condition. + */ + i82586_rx_errors(sc, i, status); + drop = 1; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_RX_DROP, status); +#endif + } + +#if I82586_DEBUG + if ((status & IE_FD_BUSY) != 0) + printf("%s: rint: frame(%d) busy; status=0x%x\n", + sc->arpcom.ac_if.if_name, i, status); +#endif + + /* + * Advance the RFD list, since we're done with + * this descriptor. + */ + + /* Clear frame status */ + sc->ie_bus_write16(sc, off, 0); + + /* Put fence at this frame (the head) */ + off = IE_RFRAME_LAST(sc->rframes, i); + sc->ie_bus_write16(sc, off, IE_FD_EOL|IE_FD_SUSP); + + /* and clear RBD field */ + off = IE_RFRAME_BUFDESC(sc->rframes, i); + sc->ie_bus_write16(sc, off, 0xffff); + + /* Remove fence from current tail */ + off = IE_RFRAME_LAST(sc->rframes, sc->rftail); + sc->ie_bus_write16(sc, off, 0); + + if (++sc->rftail == sc->nframes) + sc->rftail = 0; + if (++sc->rfhead == sc->nframes) + sc->rfhead = 0; + + /* Pull the frame off the board */ + if (drop) { + i82586_drop_frames(sc); + if ((status & IE_FD_RNR) != 0) + sc->rnr_expect = 1; + sc->arpcom.ac_if.if_ierrors++; + } else if (ie_readframe(sc, i) != 0) + return (1); + } + + if ((scbstatus & IE_ST_RNR) != 0) { + + /* + * Receiver went "Not Ready". We try to figure out + * whether this was an expected event based on past + * frame status values. + */ + + if ((scbstatus & IE_RUS_SUSPEND) != 0) { + /* + * We use the "suspend on last frame" flag. + * Send a RU RESUME command in response, since + * we should have dealt with all completed frames + * by now. + */ + printf("RINT: SUSPENDED; scbstatus=0x%x\n", + scbstatus); + if (i82586_start_cmd(sc, IE_RUC_RESUME, 0, 0, 0) == 0) + return (0); + printf("%s: RU RESUME command timed out\n", + sc->arpcom.ac_if.if_name); + return (1); /* Ask for a reset */ + } + + if (sc->rnr_expect != 0) { + /* + * The RNR condition was announced in the previously + * completed frame. Assume the receive ring is Ok, + * so restart the receiver without further delay. + */ + i82586_start_transceiver(sc); + sc->rnr_expect = 0; + return (0); + + } else if ((scbstatus & IE_RUS_NOSPACE) != 0) { + /* + * We saw no previous IF_FD_RNR flag. + * We check our ring invariants and, if ok, + * just restart the receiver at the current + * point in the ring. + */ + if (i82586_chk_rx_ring(sc) != 0) + return (1); + + i82586_start_transceiver(sc); + sc->arpcom.ac_if.if_ierrors++; + return (0); + } else + printf("%s: receiver not ready; scbstatus=0x%x\n", + sc->arpcom.ac_if.if_name, scbstatus); + + sc->arpcom.ac_if.if_ierrors++; + return (1); /* Ask for a reset */ + } + + return (0); +} + +/* + * Process a command-complete interrupt. These are only generated by the + * transmission of frames. This routine is deceptively simple, since most + * of the real work is done by i82586_start(). + */ +int +i82586_tint(struct ie_softc *sc, int scbstatus) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + int status; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_TX_INT, sc->xmit_busy); +#endif + +#if I82586_DEBUG + if (sc->xmit_busy <= 0) { + printf("i82586_tint: (%d), WEIRD: xmit_busy=%d, xctail=%d, xchead=%d\n", + sc->trace_flow_in / 2, sc->xmit_busy, sc->xctail, sc->xchead); + I82586_TRACE(sc, I82586_TX_BAD, sc->xctail); + return (0); + } +#endif + + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + + status = sc->ie_bus_read16(sc, IE_CMD_XMIT_STATUS(sc->xmit_cmds, + sc->xctail)); + +#if I82586_DEBUG + if (sc->sc_debug & IED_TINT) + printf("%s: tint: SCB status 0x%x; xmit status 0x%x\n", + sc->arpcom.ac_if.if_name, scbstatus, status); +#endif + + if ((status & IE_STAT_COMPL) == 0 || (status & IE_STAT_BUSY)) { + printf("i82586_tint: (%d) command still busy; status=0x%x; tail=%d\n", + sc->trace_flow_in / 2, status, sc->xctail); + printf("iestatus = 0x%x\n", scbstatus); +// sc->sc_debug = IED_ALL; + } + + if (status & IE_STAT_OK) { + ifp->if_opackets++; + ifp->if_collisions += (status & IE_XS_MAXCOLL); + } else { + ifp->if_oerrors++; + /* + * Check SQE and DEFERRED? + * What if more than one bit is set? + */ + if (status & IE_STAT_ABORT) + printf("%s: send aborted\n", sc->arpcom.ac_if.if_name); + else if (status & IE_XS_NOCARRIER) + printf("%s: no carrier\n", sc->arpcom.ac_if.if_name); + else if (status & IE_XS_LOSTCTS) + printf("%s: lost CTS\n", sc->arpcom.ac_if.if_name); + else if (status & IE_XS_UNDERRUN) + printf("%s: DMA underrun\n", sc->arpcom.ac_if.if_name); + else if (status & IE_XS_EXCMAX) { + printf("%s: too many collisions\n", + sc->arpcom.ac_if.if_name); + sc->arpcom.ac_if.if_collisions += 16; + } + } + + /* + * If multicast addresses were added or deleted while transmitting, + * ie_mc_reset() set the want_mcsetup flag indicating that we + * should do it. + */ + if (sc->want_mcsetup) { + ie_mc_setup(sc, IE_XBUF_ADDR(sc, sc->xctail)); + sc->want_mcsetup = 0; + } + + /* Done with the buffer. */ + sc->xmit_busy--; + sc->xctail = (sc->xctail + 1) % NTXBUF; + + /* Start the next packet, if any, transmitting. */ + if (sc->xmit_busy > 0) + iexmit(sc); + + i82586_start_tx(sc); + return (0); +} + +/* + * Get a range of receive buffer descriptors that represent one packet. + */ +static int +i82586_get_rbd_list(struct ie_softc *sc, u_int16_t *start, u_int16_t *end, int *pktlen) +{ + int off, rbbase = sc->rbds; + int rbindex, count = 0; + int plen = 0; + int rbdstatus; + + *start = rbindex = sc->rbhead; + + do { + off = IE_RBD_STATUS(rbbase, rbindex); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + rbdstatus = sc->ie_bus_read16(sc, off); + if ((rbdstatus & IE_RBD_USED) == 0) { + /* + * This means we are somehow out of sync. So, we + * reset the adapter. + */ +#if I82586_DEBUG + print_rbd(sc, rbindex); +#endif + printf("%s: receive descriptors out of sync at %d\n", + sc->arpcom.ac_if.if_name, rbindex); + return (0); + } + plen += (rbdstatus & IE_RBD_CNTMASK); + + if (++rbindex == sc->nrxbuf) + rbindex = 0; + + ++count; + } while ((rbdstatus & IE_RBD_LAST) == 0); + *end = rbindex; + *pktlen = plen; + return (count); +} + + +/* + * Release a range of receive buffer descriptors after we've copied the packet. + */ +static void +i82586_release_rbd_list(struct ie_softc *sc, u_int16_t start, u_int16_t end) +{ + int off, rbbase = sc->rbds; + int rbindex = start; + + do { + /* Clear buffer status */ + off = IE_RBD_STATUS(rbbase, rbindex); + sc->ie_bus_write16(sc, off, 0); + if (++rbindex == sc->nrxbuf) + rbindex = 0; + } while (rbindex != end); + + /* Mark EOL at new tail */ + rbindex = ((rbindex == 0) ? sc->nrxbuf : rbindex) - 1; + off = IE_RBD_BUFLEN(rbbase, rbindex); + sc->ie_bus_write16(sc, off, IE_RBUF_SIZE|IE_RBD_EOL); + + /* Remove EOL from current tail */ + off = IE_RBD_BUFLEN(rbbase, sc->rbtail); + sc->ie_bus_write16(sc, off, IE_RBUF_SIZE); + + /* New head & tail pointer */ +/* hmm, why have both? head is always (tail + 1) % NRXBUF */ + sc->rbhead = end; + sc->rbtail = rbindex; +} + +/* + * Drop the packet at the head of the RX buffer ring. + * Called if the frame descriptor reports an error on this packet. + * Returns 1 if the buffer descriptor ring appears to be corrupt; + * and 0 otherwise. + */ +static int +i82586_drop_frames(struct ie_softc *sc) +{ + u_int16_t bstart, bend; + int pktlen; + + if (i82586_get_rbd_list(sc, &bstart, &bend, &pktlen) == 0) + return (1); + i82586_release_rbd_list(sc, bstart, bend); + return (0); +} + +/* + * Check the RX frame & buffer descriptor lists for our invariants, + * i.e.: EOL bit set iff. it is pointed at by the r*tail pointer. + * + * Called when the receive unit has stopped unexpectedly. + * Returns 1 if an inconsistency is detected; 0 otherwise. + * + * The Receive Unit is expected to be NOT RUNNING. + */ +static int +i82586_chk_rx_ring(struct ie_softc *sc) +{ + int n, off, val; + + for (n = 0; n < sc->nrxbuf; n++) { + off = IE_RBD_BUFLEN(sc->rbds, n); + val = sc->ie_bus_read16(sc, off); + if ((n == sc->rbtail) ^ ((val & IE_RBD_EOL) != 0)) { + /* `rbtail' and EOL flag out of sync */ + printf("%s: rx buffer descriptors out of sync at %d\n", + sc->arpcom.ac_if.if_name, n); + return (1); + } + + /* Take the opportunity to clear the status fields here ? */ + } + + for (n = 0; n < sc->nframes; n++) { + off = IE_RFRAME_LAST(sc->rframes, n); + val = sc->ie_bus_read16(sc, off); + if ((n == sc->rftail) ^ ((val & (IE_FD_EOL|IE_FD_SUSP)) != 0)) { + /* `rftail' and EOL flag out of sync */ + printf("%s: rx frame list out of sync at %d\n", + sc->arpcom.ac_if.if_name, n); + return (1); + } + } + + return (0); +} + + +/* + * Read frame NUM from unit UNIT (pre-cached as IE). + * + * This routine reads the RFD at NUM, and copies in the buffers from the list + * of RBD, then rotates the RBD list so that the receiver doesn't start + * complaining. Trailers are DROPPED---there's no point in wasting time + * on confusing code to deal with them. Hopefully, this machine will + * never ARP for trailers anyway. + */ +static int +ie_readframe(struct ie_softc *sc, int num) /* frame number to read */ +{ + struct ether_header *eh; + struct mbuf *m; + u_int16_t bstart, bend; + int pktlen; + + if (i82586_get_rbd_list(sc, &bstart, &bend, &pktlen) == 0) { + sc->arpcom.ac_if.if_ierrors++; + return (1); + } + + m = ieget(sc, bstart, pktlen); + i82586_release_rbd_list(sc, bstart, bend); + + if (m == 0) { + sc->arpcom.ac_if.if_ierrors++; + return (0); + } + + eh = mtod(m, struct ether_header *); + m->m_data += sizeof (struct ether_header); + +#if I82586_DEBUG + if (sc->sc_debug & IED_READFRAME) { + + printf("%s: frame from ether %s type 0x%x len %d\n", + sc->arpcom.ac_if.if_name, + ether_sprintf(eh->ether_shost), + (u_int)ntohs(eh->ether_type), + pktlen); + } +#endif + +#if NBPFILTER > 0 + /* Check for a BPF filter; if so, hand it up. */ + if (sc->arpcom.ac_if.if_bpf != 0) + /* Pass it up. */ + bpf_mtap(sc->arpcom.ac_if.if_bpf, m); +#endif /* NBPFILTER > 0 */ + + /* + * Finally pass this packet up to higher layers. + */ + ether_input (&sc->arpcom.ac_if, eh, m); + sc->arpcom.ac_if.if_ipackets++; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_RX_OK, sc->arpcom.ac_if.if_ipackets); +#endif + + return (0); +} + +/* + * Start transmission on an interface. + */ +void +i82586_start(struct ifnet *ifp) +{ + struct ie_softc *sc = ifp->if_softc; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_TX_REQ, sc->xmit_busy); +#endif + + rtems_event_send (sc->tx_task, i82586_TX_EVENT); +} + +static void +i82586_tx_task(void *arg) +{ + struct ie_softc *sc = arg; + rtems_event_set events; + + for (;;) { + rtems_bsdnet_event_receive (i82586_TX_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + 0, &events); + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_TX_EVT, sc->xmit_busy); + + if (sc->sc_debug) + printf ("%s: =======\n", sc->arpcom.ac_if.if_name); +#endif + + i82586_start_tx(sc); + } +} + +static void +i82586_start_tx(struct ie_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m0, *m; + int buffer, head, xbase; + u_short len; + int s; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_START_TX, sc->xmit_busy); +#endif + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + { +#if I82586_DEBUG + I82586_TRACE(sc, I82586_TX_ACTIVE, ifp->if_snd.ifq_len); +#endif + return; + } + + for (;;) { + if (sc->xmit_busy == NTXBUF) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + head = sc->xchead; + xbase = sc->xbds; + + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == 0) + break; + + /* We need to use m->m_pkthdr.len, so require the header */ + if ((m0->m_flags & M_PKTHDR) == 0) + panic("i82586_start: no header mbuf"); + +#if NBPFILTER > 0 + /* Tap off here if there is a BPF listener. */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0); +#endif + +#if I82586_DEBUG + if (sc->sc_debug & IED_ENQ) + printf("%s: fill buffer %d\n", sc->arpcom.ac_if.if_name, + sc->xchead); +#endif + + if (m0->m_pkthdr.len > IE_TBUF_SIZE) + printf("%s: tbuf overflow\n", sc->arpcom.ac_if.if_name); + + buffer = IE_XBUF_ADDR(sc, head); + for (m = m0; m != 0; m = m->m_next) { + (sc->memcopyout)(sc, mtod(m,caddr_t), buffer, m->m_len); + buffer += m->m_len; + } + + len = max(m0->m_pkthdr.len, ETHER_MIN_LEN); + m_freem(m0); + + /* + * Setup the transmit buffer descriptor here, while we + * know the packet's length. + */ + sc->ie_bus_write16(sc, IE_XBD_FLAGS(xbase, head), + len | IE_TBD_EOL); + sc->ie_bus_write16(sc, IE_XBD_NEXT(xbase, head), 0xffff); + sc->ie_bus_write24(sc, IE_XBD_BUF(xbase, head), + IE_XBUF_ADDR(sc, head)); + + if (++head == NTXBUF) + head = 0; + sc->xchead = head; + +#if I82586_DEBUG + I82586_TRACE(sc, I82586_TX_START, sc->xmit_busy); +#endif + + s = splnet(); + /* Start the first packet transmitting. */ + if (sc->xmit_busy == 0) + iexmit(sc); + + sc->xmit_busy++; + + splx(s); + } +} + +/* + * Probe IE's ram setup [ Move all this into MD front-end!? ] + * Use only if SCP and ISCP represent offsets into shared ram space. + */ +int +i82586_proberam(struct ie_softc *sc) +{ + int result, off; + + /* Put in 16-bit mode */ + off = IE_SCP_BUS_USE(sc->scp); + sc->ie_bus_write16(sc, off, 0); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE); + + /* Set the ISCP `busy' bit */ + off = IE_ISCP_BUSY(sc->iscp); + sc->ie_bus_write16(sc, off, 1); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE); + + if (sc->hwreset) + (sc->hwreset)(sc, CHIP_PROBE); + + (sc->chan_attn) (sc, CHIP_PROBE); + + delay(100); /* wait a while... */ + + /* Read back the ISCP `busy' bit; it should be clear by now */ + off = IE_ISCP_BUSY(sc->iscp); + IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ); + result = sc->ie_bus_read16(sc, off) == 0; + + /* Acknowledge any interrupts we may have caused. */ + ie_ack(sc, IE_ST_WHENCE); + + return (result); +} + +void +i82586_reset(struct ie_softc *sc, int hard) +{ + int s = splnet(); + + if (hard) + printf("%s: reset\n", sc->arpcom.ac_if.if_name); + + /* Clear OACTIVE in case we're called from watchdog (frozen xmit). */ + sc->arpcom.ac_if.if_timer = 0; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + /* + * Stop i82586 dead in its tracks. + */ + if (i82586_start_cmd(sc, IE_RUC_ABORT | IE_CUC_ABORT, 0, 0, 0)) + printf("%s: abort commands timed out\n", sc->arpcom.ac_if.if_name); + + /* + * This can really slow down the i82586_reset() on some cards, but it's + * necessary to unwedge other ones (eg, the Sun VME ones) from certain + * lockups. + */ + if (hard && sc->hwreset) + (sc->hwreset)(sc, CARD_RESET); + + delay(100); + ie_ack(sc, IE_ST_WHENCE); + + if ((sc->arpcom.ac_if.if_flags & IFF_UP) != 0) { + i82586_init(&sc->arpcom.ac_if); + } + + splx(s); +} + +static void +setup_simple_command(struct ie_softc *sc, int cmd, int cmdbuf) +{ + /* Setup a simple command */ + sc->ie_bus_write16(sc, IE_CMD_COMMON_STATUS(cmdbuf), 0); + sc->ie_bus_write16(sc, IE_CMD_COMMON_CMD(cmdbuf), cmd | IE_CMD_LAST); + sc->ie_bus_write16(sc, IE_CMD_COMMON_LINK(cmdbuf), 0xffff); + + /* Assign the command buffer to the SCB command list */ + sc->ie_bus_write16(sc, IE_SCB_CMDLST(sc->scb), cmdbuf); +} + +/* + * Run the time-domain reflectometer. + */ +static void +ie_run_tdr(struct ie_softc *sc, int cmd) +{ + int result; + + setup_simple_command(sc, IE_CMD_TDR, cmd); + sc->ie_bus_write16(sc, IE_CMD_TDR_TIME(cmd), 0); + + if (i82586_start_cmd(sc, IE_CUC_START, cmd, IE_STAT_COMPL, 0) || + (sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmd)) & IE_STAT_OK) == 0) + result = 0x10000; /* XXX */ + else + result = sc->ie_bus_read16(sc, IE_CMD_TDR_TIME(cmd)); + + /* Squash any pending interrupts */ + ie_ack(sc, IE_ST_WHENCE); + + if (result & IE_TDR_SUCCESS) + return; + + if (result & 0x10000) + printf("%s: TDR command failed\n", sc->arpcom.ac_if.if_name); + else if (result & IE_TDR_XCVR) + printf("%s: transceiver problem\n", sc->arpcom.ac_if.if_name); + else if (result & IE_TDR_OPEN) + printf("%s: TDR detected incorrect termination %d clocks away\n", + sc->arpcom.ac_if.if_name, result & IE_TDR_TIME); + else if (result & IE_TDR_SHORT) + printf("%s: TDR detected a short circuit %d clocks away\n", + sc->arpcom.ac_if.if_name, result & IE_TDR_TIME); + else + printf("%s: TDR returned unknown status 0x%x\n", + sc->arpcom.ac_if.if_name, result); +} + + +/* + * i82586_setup_bufs: set up the buffers + * + * We have a block of KVA at sc->buf_area which is of size sc->buf_area_sz. + * this is to be used for the buffers. The chip indexs its control data + * structures with 16 bit offsets, and it indexes actual buffers with + * 24 bit addresses. So we should allocate control buffers first so that + * we don't overflow the 16 bit offset field. The number of transmit + * buffers is fixed at compile time. + * + */ +static void +i82586_setup_bufs(struct ie_softc *sc) +{ + int ptr = sc->buf_area; /* memory pool */ + int n, r; + + /* + * step 0: zero memory and figure out how many recv buffers and + * frames we can have. + */ + ptr = (ptr + 3) & ~3; /* set alignment and stick with it */ + + + /* + * step 1: lay out data structures in the shared-memory area + */ + + /* The no-op commands; used if "nop-chaining" is in effect */ + sc->nop_cmds = ptr; + ptr += NTXBUF * IE_CMD_NOP_SZ; + + /* The transmit commands */ + sc->xmit_cmds = ptr; + ptr += NTXBUF * IE_CMD_XMIT_SZ; + + /* The transmit buffers descriptors */ + sc->xbds = ptr; + ptr += NTXBUF * IE_XBD_SZ; + + /* The transmit buffers */ + sc->xbufs = ptr; + ptr += NTXBUF * IE_TBUF_SIZE; + + ptr = (ptr + 3) & ~3; /* re-align.. just in case */ + + /* Compute free space for RECV stuff */ + n = sc->buf_area_sz - (ptr - sc->buf_area); + + /* Compute size of one RECV frame */ + r = IE_RFRAME_SZ + ((IE_RBD_SZ + IE_RBUF_SIZE) * B_PER_F); + + sc->nframes = n / r; + + if (sc->nframes <= 0) + panic("ie: bogus buffer calc\n"); + + sc->nrxbuf = sc->nframes * B_PER_F; + + /* The receice frame descriptors */ + sc->rframes = ptr; + ptr += sc->nframes * IE_RFRAME_SZ; + + /* The receive buffer descriptors */ + sc->rbds = ptr; + ptr += sc->nrxbuf * IE_RBD_SZ; + + /* The receive buffers */ + sc->rbufs = ptr; + ptr += sc->nrxbuf * IE_RBUF_SIZE; + +#if I82586_DEBUG + printf("%s: %d frames %d bufs\n", sc->arpcom.ac_if.if_name, sc->nframes, + sc->nrxbuf); +#endif + + /* + * step 2: link together the recv frames and set EOL on last one + */ + for (n = 0; n < sc->nframes; n++) { + int m = (n == sc->nframes - 1) ? 0 : n + 1; + + /* Clear status */ + sc->ie_bus_write16(sc, IE_RFRAME_STATUS(sc->rframes,n), 0); + + /* RBD link = NULL */ + sc->ie_bus_write16(sc, IE_RFRAME_BUFDESC(sc->rframes,n), + 0xffff); + + /* Make a circular list */ + sc->ie_bus_write16(sc, IE_RFRAME_NEXT(sc->rframes,n), + IE_RFRAME_ADDR(sc->rframes,m)); + + /* Mark last as EOL */ + sc->ie_bus_write16(sc, IE_RFRAME_LAST(sc->rframes,n), + ((m==0)? (IE_FD_EOL|IE_FD_SUSP) : 0)); + } + + /* + * step 3: link the RBDs and set EOL on last one + */ + for (n = 0; n < sc->nrxbuf; n++) { + int m = (n == sc->nrxbuf - 1) ? 0 : n + 1; + + /* Clear status */ + sc->ie_bus_write16(sc, IE_RBD_STATUS(sc->rbds,n), 0); + + /* Make a circular list */ + sc->ie_bus_write16(sc, IE_RBD_NEXT(sc->rbds,n), + IE_RBD_ADDR(sc->rbds,m)); + + /* Link to data buffers */ + sc->ie_bus_write24(sc, IE_RBD_BUFADDR(sc->rbds, n), + IE_RBUF_ADDR(sc, n)); + sc->ie_bus_write16(sc, IE_RBD_BUFLEN(sc->rbds,n), + IE_RBUF_SIZE | ((m==0)?IE_RBD_EOL:0)); + } + + /* + * step 4: all xmit no-op commands loopback onto themselves + */ + for (n = 0; n < NTXBUF; n++) { + sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, n), 0); + + sc->ie_bus_write16(sc, IE_CMD_NOP_CMD(sc->nop_cmds, n), + IE_CMD_NOP); + + sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, n), + IE_CMD_NOP_ADDR(sc->nop_cmds, n)); + } + + + /* + * step 6: set the head and tail pointers on receive to keep track of + * the order in which RFDs and RBDs are used. + */ + + /* Pointers to last packet sent and next available transmit buffer. */ + sc->xchead = sc->xctail = 0; + + /* Clear transmit-busy flag and set number of free transmit buffers. */ + sc->xmit_busy = 0; + + /* + * Pointers to first and last receive frame. + * The RFD pointed to by rftail is the only one that has EOL set. + */ + sc->rfhead = 0; + sc->rftail = sc->nframes - 1; + + /* + * Pointers to first and last receive descriptor buffer. + * The RBD pointed to by rbtail is the only one that has EOL set. + */ + sc->rbhead = 0; + sc->rbtail = sc->nrxbuf - 1; + +/* link in recv frames * and buffer into the scb. */ +#if I82586_DEBUG + printf("%s: reserved %d bytes\n", + sc->arpcom.ac_if.if_name, ptr - sc->buf_area); +#endif +} + +static int +ie_cfg_setup(struct ie_softc *sc, int cmd, int promiscuous, int manchester) +{ + int cmdresult, status; + u_int8_t buf[IE_CMD_CFG_SZ]; /* XXX malloc? */ + + *IE_CMD_CFG_CNT(buf) = 0x0c; + *IE_CMD_CFG_FIFO(buf) = 8; + *IE_CMD_CFG_SAVEBAD(buf) = 0x40; + *IE_CMD_CFG_ADDRLEN(buf) = 0x2e; + *IE_CMD_CFG_PRIORITY(buf) = 0; + *IE_CMD_CFG_IFS(buf) = 0x60; + *IE_CMD_CFG_SLOT_LOW(buf) = 0; + *IE_CMD_CFG_SLOT_HIGH(buf) = 0xf2; + *IE_CMD_CFG_PROMISC(buf) = !!promiscuous | manchester << 2; + *IE_CMD_CFG_CRSCDT(buf) = 0; + *IE_CMD_CFG_MINLEN(buf) = 64; + *IE_CMD_CFG_JUNK(buf) = 0xff; + sc->memcopyout(sc, buf, cmd, IE_CMD_CFG_SZ); + setup_simple_command(sc, IE_CMD_CONFIG, cmd); + IE_BUS_BARRIER(sc, cmd, IE_CMD_CFG_SZ, BUS_SPACE_BARRIER_WRITE); + + cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmd, IE_STAT_COMPL, 0); + status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmd)); + if (cmdresult != 0) { + printf("%s: configure command timed out; status %x\n", + sc->arpcom.ac_if.if_name, status); + return (0); + } + if ((status & IE_STAT_OK) == 0) { + printf("%s: configure command failed; status %x\n", + sc->arpcom.ac_if.if_name, status); + return (0); + } + + /* Squash any pending interrupts */ + ie_ack(sc, IE_ST_WHENCE); + return (1); +} + +static int +ie_ia_setup(struct ie_softc *sc, int cmdbuf) +{ + int cmdresult, status; + + setup_simple_command(sc, IE_CMD_IASETUP, cmdbuf); + + (sc->memcopyout)(sc, sc->arpcom.ac_enaddr, + IE_CMD_IAS_EADDR(cmdbuf), ETHER_ADDR_LEN); + + cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmdbuf, IE_STAT_COMPL, 0); + status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmdbuf)); + if (cmdresult != 0) { + printf("%s: individual address command timed out; status %x\n", + sc->arpcom.ac_if.if_name, status); + return (0); + } + if ((status & IE_STAT_OK) == 0) { + printf("%s: individual address command failed; status %x\n", + sc->arpcom.ac_if.if_name, status); + return (0); + } + + /* Squash any pending interrupts */ + ie_ack(sc, IE_ST_WHENCE); + return (1); +} + +/* + * Run the multicast setup command. + * Called at splnet(). + */ +static int +ie_mc_setup(struct ie_softc *sc, int cmdbuf) +{ + int cmdresult, status; + + if (sc->mcast_count == 0) + return (1); + + setup_simple_command(sc, IE_CMD_MCAST, cmdbuf); + + (sc->memcopyout)(sc, (caddr_t)sc->mcast_addrs, + IE_CMD_MCAST_MADDR(cmdbuf), + sc->mcast_count * ETHER_ADDR_LEN); + + sc->ie_bus_write16(sc, IE_CMD_MCAST_BYTES(cmdbuf), + sc->mcast_count * ETHER_ADDR_LEN); + + /* Start the command */ + cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmdbuf, IE_STAT_COMPL, 0); + status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmdbuf)); + if (cmdresult != 0) { + printf("%s: multicast setup command timed out; status %x\n", + sc->arpcom.ac_if.if_name, status); + return (0); + } + if ((status & IE_STAT_OK) == 0) { + printf("%s: multicast setup command failed; status %x\n", + sc->arpcom.ac_if.if_name, status); + return (0); + } + + /* Squash any pending interrupts */ + ie_ack(sc, IE_ST_WHENCE); + return (1); +} + +/* + * This routine takes the environment generated by check_ie_present() and adds + * to it all the other structures we need to operate the adapter. This + * includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, starting + * the receiver unit, and clearing interrupts. + * + * THIS ROUTINE MUST BE CALLED AT splnet() OR HIGHER. + */ +void +i82586_init(void *arg) +{ + struct ie_softc *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + int cmd; + + sc->async_cmd_inprogress = 0; + sc->xmit_busy = 0; + +#if I82586_DEBUG + memset(sc->trace_flow, 0, sizeof(sc->trace_flow)); + sc->trace_flow_wrap = 0; +#endif + sc->trace_flow_in = 0; + + cmd = sc->buf_area; + +#if I82586_DEBUG + printf ("%s: sc_debug at 0x%08x\n", sc->arpcom.ac_if.if_name, (unsigned int) &sc->sc_debug); +#endif + + /* + * Send the configure command first. + */ + if (ie_cfg_setup(sc, cmd, sc->promisc, 0) == 0) + return; + + /* + * Send the Individual Address Setup command. + */ + if (ie_ia_setup(sc, cmd) == 0) + return; + + /* + * Run the time-domain reflectometer. + */ + ie_run_tdr(sc, cmd); + + /* + * Set the multi-cast filter, if any + */ + if (ie_mc_setup(sc, cmd) == 0) + return; + + /* + * If no tasks exist, create them. Need to add something to allow + * different names for the different devices. + */ + if (sc->intr_task == 0) + sc->intr_task = rtems_bsdnet_newproc ("IEi0", 2048, i82586_intr_task, sc); + if (sc->tx_task == 0) + sc->tx_task = rtems_bsdnet_newproc ("IEt0", 2048, i82586_tx_task, sc); + + + /* + * Acknowledge any interrupts we have generated thus far. + */ + ie_ack(sc, IE_ST_WHENCE); + + /* + * Set up the transmit and recv buffers. + */ + i82586_setup_bufs(sc); + + if (sc->hwinit) + (sc->hwinit)(sc); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + if (NTXBUF < 2) + sc->do_xmitnopchain = 0; + + i82586_start_transceiver(sc); +} + +/* + * Start the RU and possibly the CU unit + */ +static void +i82586_start_transceiver(struct ie_softc *sc) +{ +#if I82586_DEBUG + I82586_TRACE(sc, I82586_RX_START, 0); +#endif + + /* + * Start RU at current position in frame & RBD lists. + */ + sc->ie_bus_write16(sc, IE_RFRAME_BUFDESC(sc->rframes,sc->rfhead), + IE_RBD_ADDR(sc->rbds, sc->rbhead)); + + sc->ie_bus_write16(sc, IE_SCB_RCVLST(sc->scb), + IE_RFRAME_ADDR(sc->rframes,sc->rfhead)); + + if (sc->do_xmitnopchain) { + /* Stop transmit command chain */ + if (i82586_start_cmd(sc, IE_CUC_SUSPEND|IE_RUC_SUSPEND, 0, 0, 0)) + printf("%s: CU/RU stop command timed out\n", + sc->arpcom.ac_if.if_name); + + /* Start the receiver & transmitter chain */ + /* sc->scb->ie_command_list = + IEADDR(sc->nop_cmds[(sc->xctail+NTXBUF-1) % NTXBUF]);*/ + sc->ie_bus_write16(sc, IE_SCB_CMDLST(sc->scb), + IE_CMD_NOP_ADDR( + sc->nop_cmds, + (sc->xctail + NTXBUF - 1) % NTXBUF)); + + if (i82586_start_cmd(sc, IE_CUC_START|IE_RUC_START, 0, 0, 0)) + printf("%s: CU/RU command timed out\n", + sc->arpcom.ac_if.if_name); + } else { + if (i82586_start_cmd(sc, IE_RUC_START, 0, 0, 0)) + printf("%s: RU command timed out\n", + sc->arpcom.ac_if.if_name); + } +} + +void +i82586_stop(struct ifnet *ifp, int disable) +{ + struct ie_softc *sc = ifp->if_softc; + + if (i82586_start_cmd(sc, IE_RUC_SUSPEND | IE_CUC_SUSPEND, 0, 0, 0)) + printf("%s: iestop: disable commands timed out\n", + sc->arpcom.ac_if.if_name); +} + +int +i82586_ioctl(struct ifnet *ifp, int cmd, caddr_t data) +{ + struct ie_softc *sc = ifp->if_softc; +// struct ifreq *ifr = (struct ifreq *)data; + int s; + int error = 0; + + s = splnet(); + switch(cmd) { + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + break; + case SIO_RTEMS_SHOW_STATS: +#if I82586_DEBUG + print_softie(sc); +#endif + break; + default: + error = ether_ioctl(ifp, cmd, data); + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + ie_mc_reset(sc); + error = 0; + } + break; + } +#if I82586_DEBUG + if (cmd == SIOCSIFFLAGS) + sc->sc_debug = (ifp->if_flags & IFF_DEBUG) ? IED_ALL : 0; +#endif + splx(s); + return (error); +} + +static void +ie_mc_reset(struct ie_softc *sc) +{ + struct ether_multi *enm; + struct ether_multistep step; + int size; + + /* + * Step through the list of addresses. + */ + again: + size = 0; + sc->mcast_count = 0; + ETHER_FIRST_MULTI(step, &sc->arpcom, enm); + while (enm) { + size += 6; + if (sc->mcast_count >= IE_MAXMCAST || + memcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { + sc->arpcom.ac_if.if_flags |= IFF_ALLMULTI; + i82586_ioctl(&sc->arpcom.ac_if, + SIOCSIFFLAGS, (void *)0); + return; + } + ETHER_NEXT_MULTI(step, enm); + } + + if (size > sc->mcast_addrs_size) { + /* Need to allocate more space */ + if (sc->mcast_addrs_size) + free(sc->mcast_addrs, M_IPMADDR); + sc->mcast_addrs = (char *) + malloc(size, M_IPMADDR, M_WAITOK); + sc->mcast_addrs_size = size; + } + + /* + * We've got the space; now copy the addresses + */ + ETHER_FIRST_MULTI(step, &sc->arpcom, enm); + while (enm) { + if (sc->mcast_count >= IE_MAXMCAST) + goto again; /* Just in case */ + + memcpy(&sc->mcast_addrs[sc->mcast_count], enm->enm_addrlo, 6); + sc->mcast_count++; + ETHER_NEXT_MULTI(step, enm); + } + sc->want_mcsetup = 1; +} + +/* + * Media change callback. + */ +int +i82586_mediachange(struct ifnet *ifp) +{ + struct ie_softc *sc = ifp->if_softc; + + if (sc->sc_mediachange) + return ((*sc->sc_mediachange)(sc)); + return (0); +} + +/* + * Media status callback. + */ +void +i82586_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct ie_softc *sc = ifp->if_softc; + + if (sc->sc_mediastatus) + (*sc->sc_mediastatus)(sc, ifmr); +} + +#if I82586_DEBUG +static void +print_softie(struct ie_softc *sc) +{ + static char *trace_labels[] = { + "INTS_REQ", + "INTS_IN", + "INTS_LOOPS", + "INTS_OUT", + "RX_INT", + "RX_DROP", + "RX_ERR", + "RX_OK", + "RX_START", + "START_TX", + "TX_START", + "TX_INT", + "TX_REQ", + "TX_EVT", + "TX_EMIT", + "TX_BAD", + "TX_ACTIVE", + "TRACE_CNT" + }; + + int i; + + printf("i82586 %s:\n", sc->arpcom.ac_if.if_name); + + printf(" iobase=%p\n", sc->sc_iobase); + + printf(" scp=0x%08x\t\tiscp=0x%08x\t\tscb=0x%08x\n", + sc->scp, sc->iscp, sc->scb); + printf(" buf_area=0x%08x\tbuf_area_sz=0x%08x\n", + sc->buf_area, sc->buf_area_sz); + printf(" rframes=0x%08x\trbds=0x%08x\t\trbufs=0x%08x\n", + sc->rframes, sc->rbds, sc->rbufs); + printf(" nop_cmds=0x%08x\txmit_cmds=0x%08x\n", + sc->nop_cmds, sc->xmit_cmds); + printf(" xbds=0x%08x\txbufs=0x%08x\n\n", + sc->xbds, sc->xbufs); + printf(" rfhead=%d\trftail=%d\n", + sc->rfhead, sc->rftail); + printf(" rbhead=%d\trbtail=%d\n", + sc->rbhead, sc->rbtail); + printf(" nframes=%d\tnrxbuf=%d\trnr_expect=%d\n", + sc->nframes, sc->nrxbuf, sc->rnr_expect); + printf(" xchead=%d\txctail=%d\n", + sc->xchead, sc->xctail); + printf(" xmit_busy=%d\txmit_req=%d\tdo_xmitnopchain=%d\n", + sc->xmit_busy, sc->xmit_req, sc->do_xmitnopchain); + printf(" promisc=%d\tasync_cmd_inprogress=%d\n\n", + sc->promisc, sc->async_cmd_inprogress); + + { + int cnt; + int in; + int lfdone = 0; + char *tabs; + + if (!sc->trace_flow_wrap) { + cnt = sc->trace_flow_in; + in = 0; + } + else { + cnt = I82586_TRACE_FLOW; + in = sc->trace_flow_in; + } + + sc->trace_flow_in = sc->trace_flow_wrap = 0; + + cnt /= 2; + + for (i = 0; i < cnt; i++) { + if (!lfdone) { + switch (sc->trace_flow[in]) { + case I82586_INTS_REQ: + case I82586_INTS_IN: + printf("\n"); + } + } + + lfdone = 0; + + if (strlen(trace_labels[sc->trace_flow[in]]) < 8) + tabs = "\t\t"; + else + tabs = "\t"; + + printf(" %d\t%s%s0x%08x (%d)\n", + i, trace_labels[sc->trace_flow[in]], tabs, + sc->trace_flow[in + 1], sc->trace_flow[in + 1]); + + switch (sc->trace_flow[in]) { + case I82586_INTS_REQ: + case I82586_INTS_OUT: + lfdone = 1; + printf("\n"); + } + + in += 2; + + if (in >= I82586_TRACE_FLOW) + in = 0; + } + } +} + +static void +print_rbd(struct ie_softc *sc, int n) +{ + printf("RBD at %08x:\n status %04x, next %04x, buffer %lx\n" + "length/EOL %04x\n", IE_RBD_ADDR(sc->rbds,n), + sc->ie_bus_read16(sc, IE_RBD_STATUS(sc->rbds,n)), + sc->ie_bus_read16(sc, IE_RBD_NEXT(sc->rbds,n)), + (u_long)0,/*bus_space_read_4(sc->bt, sc->bh, IE_RBD_BUFADDR(sc->rbds,n)),-* XXX */ + sc->ie_bus_read16(sc, IE_RBD_BUFLEN(sc->rbds,n))); +} + +#endif diff --git a/c/src/libchip/network/i82586reg.h b/c/src/libchip/network/i82586reg.h new file mode 100644 index 0000000000..bd5024563c --- /dev/null +++ b/c/src/libchip/network/i82586reg.h @@ -0,0 +1,436 @@ +/* $NetBSD: i82586reg.h,v 1.7 1998/02/28 01:07:45 pk Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg. + * + * 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. + */ + +/*- + * Copyright (c) 1992, University of Vermont and State Agricultural College. + * Copyright (c) 1992, Garrett A. Wollman. + * All rights reserved. + * + * 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 University of + * Vermont and State Agricultural College and Garrett A. Wollman. + * 4. Neither the name of the University nor the name of the author + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR 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. + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Written by GAW with reference to the Clarkson Packet Driver code for this + * chip written by Russ Nelson and others. + */ + +/* + * NOTE, the structure definitions in here are for reference only. + * We use integer offsets exclusively to access the i82586 data structures. + */ + + +/* + * This is the master configuration block. + * It tells the hardware where all the rest of the stuff is. + *- +struct __ie_sys_conf_ptr { + u_int16_t mbz; // must be zero + u_int8_t ie_bus_use; // true if 8-bit only + u_int8_t mbz2[5]; // must be zero + u_int32_t ie_iscp_ptr; // 24-bit physaddr of ISCP +}; + */ +#define IE_SCP_SZ 12 +#define IE_SCP_BUS_USE(base) ((base) + 2) +#define IE_SCP_ISCP(base) ((base) + 8) + +/* + * Note that this is wired in hardware; the SCP is always located here, no + * matter what. + */ +#define IE_SCP_ADDR 0xfffff4 + +/* + * The tells the hardware where all the rest of the stuff is, too. + * FIXME: some of these should be re-commented after we figure out their + * REAL function. + *- +struct __ie_int_sys_conf_ptr { + u_int8_t ie_busy; // zeroed after init + u_int8_t mbz; + u_int16_t ie_scb_offset; // 16-bit physaddr of next struct + caddr_t ie_base; // 24-bit physaddr for all 16-bit vars +}; + */ +#define IE_ISCP_SZ 8 +#define IE_ISCP_BUSY(base) ((base) + 0) +#define IE_ISCP_SCB(base) ((base) + 2) +#define IE_ISCP_BASE(base) ((base) + 4) + +/* + * This FINALLY tells the hardware what to do and where to put it. + *- +struct __ie_sys_ctl_block { + u_int16_t ie_status; // status word + u_int16_t ie_command; // command word + u_int16_t ie_command_list; // 16-pointer to command block list + u_int16_t ie_recv_list; // 16-pointer to receive frame list + u_int16_t ie_err_crc; // CRC errors + u_int16_t ie_err_align; // Alignment errors + u_int16_t ie_err_resource; // Resource errors + u_int16_t ie_err_overrun; // Overrun errors +}; + */ +#define IE_SCB_SZ 16 +#define IE_SCB_STATUS(base) ((base) + 0) +#define IE_SCB_CMD(base) ((base) + 2) +#define IE_SCB_CMDLST(base) ((base) + 4) +#define IE_SCB_RCVLST(base) ((base) + 6) +#define IE_SCB_ERRCRC(base) ((base) + 8) +#define IE_SCB_ERRALN(base) ((base) + 10) +#define IE_SCB_ERRRES(base) ((base) + 12) +#define IE_SCB_ERROVR(base) ((base) + 14) + +/* Command values */ +#define IE_RUC_MASK 0x0070 /* mask for RU command */ +#define IE_RUC_NOP 0 /* for completeness */ +#define IE_RUC_START 0x0010 /* start receive unit command */ +#define IE_RUC_RESUME 0x0020 /* resume a suspended receiver command */ +#define IE_RUC_SUSPEND 0x0030 /* suspend receiver command */ +#define IE_RUC_ABORT 0x0040 /* abort current receive operation */ + +#define IE_CUC_MASK 0x0700 /* mask for CU command */ +#define IE_CUC_NOP 0 /* included for completeness */ +#define IE_CUC_START 0x0100 /* do-command command */ +#define IE_CUC_RESUME 0x0200 /* resume a suspended cmd list */ +#define IE_CUC_SUSPEND 0x0300 /* suspend current command */ +#define IE_CUC_ABORT 0x0400 /* abort current command */ + +#define IE_ACK_COMMAND 0xf000 /* mask for ACK command */ +#define IE_ACK_CX 0x8000 /* ack IE_ST_CX */ +#define IE_ACK_FR 0x4000 /* ack IE_ST_FR */ +#define IE_ACK_CNA 0x2000 /* ack IE_ST_CNA */ +#define IE_ACK_RNR 0x1000 /* ack IE_ST_RNR */ + +#define IE_ACTION_COMMAND(x) (((x) & IE_CUC_MASK) == IE_CUC_START) + /* is this command an action command? */ + +/* Status values */ +#define IE_ST_WHENCE 0xf000 /* mask for cause of interrupt */ +#define IE_ST_CX 0x8000 /* command with I bit completed */ +#define IE_ST_FR 0x4000 /* frame received */ +#define IE_ST_CNA 0x2000 /* all commands completed */ +#define IE_ST_RNR 0x1000 /* receive not ready */ + +#define IE_CUS_MASK 0x0700 /* mask for command unit status */ +#define IE_CUS_ACTIVE 0x0200 /* command unit is active */ +#define IE_CUS_SUSPEND 0x0100 /* command unit is suspended */ + +#define IE_RUS_MASK 0x0070 /* mask for receiver unit status */ +#define IE_RUS_SUSPEND 0x0010 /* receiver is suspended */ +#define IE_RUS_NOSPACE 0x0020 /* receiver has no resources */ +#define IE_RUS_READY 0x0040 /* receiver is ready */ + +/* + * This is filled in partially by the chip, partially by us. + *- +struct __ie_recv_frame_desc { + u_int16_t ie_fd_status; // status for this frame + u_int16_t ie_fd_last; // end of frame list flag + u_int16_t ie_fd_next; // 16-pointer to next RFD + u_int16_t ie_fd_buf_desc; // 16-pointer to list of buffer descs + struct __ie_en_addr dest; // destination ether + struct __ie_en_addr src; // source ether + u_int16_t ie_length; // 802 length/Ether type + u_short mbz; // must be zero +}; + */ +#define IE_RFRAME_SZ 24 +#define IE_RFRAME_ADDR(base,i) ((base) + (i) * IE_RFRAME_SZ) +#define IE_RFRAME_STATUS(b,i) (IE_RFRAME_ADDR(b,i) + 0) +#define IE_RFRAME_LAST(b,i) (IE_RFRAME_ADDR(b,i) + 2) +#define IE_RFRAME_NEXT(b,i) (IE_RFRAME_ADDR(b,i) + 4) +#define IE_RFRAME_BUFDESC(b,i) (IE_RFRAME_ADDR(b,i) + 6) +#define IE_RFRAME_EDST(b,i) (IE_RFRAME_ADDR(b,i) + 8) +#define IE_RFRAME_ESRC(b,i) (IE_RFRAME_ADDR(b,i) + 14) +#define IE_RFRAME_ELEN(b,i) (IE_RFRAME_ADDR(b,i) + 20) + +/* "last" bits */ +#define IE_FD_EOL 0x8000 /* last rfd in list */ +#define IE_FD_SUSP 0x4000 /* suspend RU after receipt */ + +/* status field bits */ +#define IE_FD_COMPLETE 0x8000 /* frame is complete */ +#define IE_FD_BUSY 0x4000 /* frame is busy */ +#define IE_FD_OK 0x2000 /* frame is ok */ +#define IE_FD_CRC 0x0800 /* CRC error */ +#define IE_FD_ALGN 0x0400 /* Alignment error */ +#define IE_FD_RNR 0x0200 /* receiver out of resources here */ +#define IE_FD_OVR 0x0100 /* DMA overrun */ +#define IE_FD_SHORT 0x0080 /* Short frame */ +#define IE_FD_NOEOF 0x0040 /* no EOF (?) */ +#define IE_FD_ERRMASK /* all error bits */ \ + (IE_FD_CRC|IE_FD_ALGN|IE_FD_RNR|IE_FD_OVR|IE_FD_SHORT|IE_FD_NOEOF) +#define IE_FD_STATUSBITS \ + "\20\20COMPLT\17BUSY\16OK\14CRC\13ALGN\12RNR\11OVR\10SHORT\7NOEOF" + +/* + * linked list of buffers... + *- +struct __ie_recv_buf_desc { + u_int16_t ie_rbd_status; // status for this buffer + u_int16_t ie_rbd_next; // 16-pointer to next RBD + caddr_t ie_rbd_buffer; // 24-pointer to buffer for this RBD + u_int16_t ie_rbd_length; // length of the buffer + u_int16_t mbz; // must be zero +}; + */ +#define IE_RBD_SZ 12 +#define IE_RBD_ADDR(base,i) ((base) + (i) * IE_RBD_SZ) +#define IE_RBD_STATUS(b,i) (IE_RBD_ADDR(b,i) + 0) +#define IE_RBD_NEXT(b,i) (IE_RBD_ADDR(b,i) + 2) +#define IE_RBD_BUFADDR(b,i) (IE_RBD_ADDR(b,i) + 4) +#define IE_RBD_BUFLEN(b,i) (IE_RBD_ADDR(b,i) + 8) + +/* RBD status fields */ +#define IE_RBD_LAST 0x8000 /* last buffer */ +#define IE_RBD_USED 0x4000 /* this buffer has data */ +#define IE_RBD_CNTMASK 0x3fff /* byte count of buffer data */ + +/* RDB `End Of List' flag; encoded in `buffer length' field */ +#define IE_RBD_EOL 0x8000 /* last buffer */ + + +/* + * All commands share this in common. + *- +struct __ie_cmd_common { + u_int16_t ie_cmd_status; // status of this command + u_int16_t ie_cmd_cmd; // command word + u_int16_t ie_cmd_link; // link to next command +}; + */ +#define IE_CMD_COMMON_SZ 6 +#define IE_CMD_COMMON_STATUS(base) ((base) + 0) +#define IE_CMD_COMMON_CMD(base) ((base) + 2) +#define IE_CMD_COMMON_LINK(base) ((base) + 4) + +#define IE_STAT_COMPL 0x8000 /* command is completed */ +#define IE_STAT_BUSY 0x4000 /* command is running now */ +#define IE_STAT_OK 0x2000 /* command completed successfully */ +#define IE_STAT_ABORT 0x1000 /* command was aborted */ + +#define IE_CMD_NOP 0x0000 /* NOP */ +#define IE_CMD_IASETUP 0x0001 /* initial address setup */ +#define IE_CMD_CONFIG 0x0002 /* configure command */ +#define IE_CMD_MCAST 0x0003 /* multicast setup command */ +#define IE_CMD_XMIT 0x0004 /* transmit command */ +#define IE_CMD_TDR 0x0005 /* time-domain reflectometer command */ +#define IE_CMD_DUMP 0x0006 /* dump command */ +#define IE_CMD_DIAGNOSE 0x0007 /* diagnostics command */ + +#define IE_CMD_LAST 0x8000 /* this is the last command in the list */ +#define IE_CMD_SUSPEND 0x4000 /* suspend CU after this command */ +#define IE_CMD_INTR 0x2000 /* post an interrupt after completion */ + +/* + * No-op commands; just like COMMON but "indexable" + */ +#define IE_CMD_NOP_SZ IE_CMD_COMMON_SZ +#define IE_CMD_NOP_ADDR(base,i) ((base) + (i) * IE_CMD_NOP_SZ) +#define IE_CMD_NOP_STATUS(b,i) (IE_CMD_NOP_ADDR(b,i) + 0) +#define IE_CMD_NOP_CMD(b,i) (IE_CMD_NOP_ADDR(b,i) + 2) +#define IE_CMD_NOP_LINK(b,i) (IE_CMD_NOP_ADDR(b,i) + 4) + + +/* + * This is the command to transmit a frame. + *- +struct __ie_xmit_cmd { + struct __ie_cmd_common com; // common part +#define __ie_xmit_status com.ie_cmd_status + + u_int16_t ie_xmit_desc; // pointer to buffer descriptor + struct __ie_en_addr ie_xmit_addr; // destination address + u_int16_t ie_xmit_length; // 802.3 length/Ether type field +}; + */ +#define IE_CMD_XMIT_SZ (IE_CMD_COMMON_SZ + 10) +#define IE_CMD_XMIT_ADDR(base,i) ((base) + (i) * IE_CMD_XMIT_SZ) +#define IE_CMD_XMIT_STATUS(b,i) \ + (IE_CMD_XMIT_ADDR(b,i) + 0) /* == CMD_COMMON_STATUS */ +#define IE_CMD_XMIT_CMD(b,i) \ + (IE_CMD_XMIT_ADDR(b,i) + 2) /* == CMD_COMMON_CMD */ +#define IE_CMD_XMIT_LINK(b,i) \ + (IE_CMD_XMIT_ADDR(b,i) + 4) /* == CMD_COMMON_LINK */ +#define IE_CMD_XMIT_DESC(b,i) \ + (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 0) +#define IE_CMD_XMIT_EADDR(b,i) \ + (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 2) +#define IE_CMD_XMIT_LEN(b,i) \ + (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 8) + +#define IE_XS_MAXCOLL 0x000f /* number of collisions during transmit */ +#define IE_XS_EXCMAX 0x0020 /* exceeded maximum number of collisions */ +#define IE_XS_SQE 0x0040 /* SQE positive */ +#define IE_XS_DEFERRED 0x0080 /* transmission deferred */ +#define IE_XS_UNDERRUN 0x0100 /* DMA underrun */ +#define IE_XS_LOSTCTS 0x0200 /* Lost CTS */ +#define IE_XS_NOCARRIER 0x0400 /* No Carrier */ +#define IE_XS_LATECOLL 0x0800 /* Late collision */ + +/* + * This is a buffer descriptor for a frame to be transmitted. + *- +struct __ie_xmit_buf { + u_int16_t ie_xmit_flags; // see below + u_int16_t ie_xmit_next; // 16-pointer to next desc + caddr_t ie_xmit_buf; // 24-pointer to the actual buffer +}; + */ +#define IE_XBD_SZ 8 +#define IE_XBD_ADDR(base,i) ((base) + (i) * IE_XBD_SZ) +#define IE_XBD_FLAGS(b,i) (IE_XBD_ADDR(b,i) + 0) +#define IE_XBD_NEXT(b,i) (IE_XBD_ADDR(b,i) + 2) +#define IE_XBD_BUF(b,i) (IE_XBD_ADDR(b,i) + 4) + +#define IE_TBD_EOL 0x8000 /* this TBD is the last one */ +#define IE_TBD_CNTMASK 0x3fff /* The rest of the `flags' word + is actually the length. */ + + +/* + * Multicast setup command. + *- +struct __ie_mcast_cmd { + struct __ie_cmd_common com; // common part +#define ie_mcast_status com.ie_cmd_status + + // size (in bytes) of multicast addresses + u_short ie_mcast_bytes; + struct __ie_en_addr ie_mcast_addrs[IE_MAXMCAST + 1];// space for them +}; + */ +#define IE_CMD_MCAST_SZ (IE_CMD_COMMON_SZ + 2 /* + XXX */) +#define IE_CMD_MCAST_BYTES(base) ((base) + IE_CMD_COMMON_SZ + 0) +#define IE_CMD_MCAST_MADDR(base) ((base) + IE_CMD_COMMON_SZ + 2) + +/* + * Time Domain Reflectometer command. + *- +struct __ie_tdr_cmd { + struct __ie_cmd_common com; // common part +#define ie_tdr_status com.ie_cmd_status + u_short ie_tdr_time; // error bits and time +}; + */ +#define IE_CMD_TDR_SZ (IE_CMD_COMMON_SZ + 2) +#define IE_CMD_TDR_TIME(base) ((base) + IE_CMD_COMMON_SZ + 0) + +#define IE_TDR_SUCCESS 0x8000 /* TDR succeeded without error */ +#define IE_TDR_XCVR 0x4000 /* detected a transceiver problem */ +#define IE_TDR_OPEN 0x2000 /* detected an incorrect termination ("open") */ +#define IE_TDR_SHORT 0x1000 /* TDR detected a short circuit */ +#define IE_TDR_TIME 0x07ff /* mask for reflection time */ + +/* + * Initial Address Setup command + *- +struct __ie_iasetup_cmd { + struct __ie_cmd_common com; +#define ie_iasetup_status com.ie_cmd_status + struct __ie_en_addr ie_address; +}; + */ +#define IE_CMD_IAS_SZ (IE_CMD_COMMON_SZ + 6) +#define IE_CMD_IAS_EADDR(base) ((base) + IE_CMD_COMMON_SZ + 0) + +/* + * Configuration command + *- +struct __ie_config_cmd { + struct __ie_cmd_common com; // common part +#define ie_config_status com.ie_cmd_status + + u_int8_t ie_config_count; // byte count (0x0c) + u_int8_t ie_fifo; // fifo (8) + u_int8_t ie_save_bad; // save bad frames (0x40) + u_int8_t ie_addr_len; // address length (0x2e) (AL-LOC == 1) + u_int8_t ie_priority; // priority and backoff (0x0) + u_int8_t ie_ifs; // inter-frame spacing (0x60) + u_int8_t ie_slot_low; // slot time, LSB (0x0) + u_int8_t ie_slot_high; // slot time, MSN, and retries (0xf2) + u_int8_t ie_promisc; // 1 if promiscuous, else 0 + u_int8_t ie_crs_cdt; // CSMA/CD parameters (0x0) + u_int8_t ie_min_len; // min frame length (0x40) + u_int8_t ie_junk; // stuff for 82596 (0xff) +}; + */ +#define IE_CMD_CFG_SZ (IE_CMD_COMMON_SZ + 12) +#define IE_CMD_CFG_CNT(base) ((base) + IE_CMD_COMMON_SZ + 0) +#define IE_CMD_CFG_FIFO(base) ((base) + IE_CMD_COMMON_SZ + 1) +#define IE_CMD_CFG_SAVEBAD(base) ((base) + IE_CMD_COMMON_SZ + 2) +#define IE_CMD_CFG_ADDRLEN(base) ((base) + IE_CMD_COMMON_SZ + 3) +#define IE_CMD_CFG_PRIORITY(base) ((base) + IE_CMD_COMMON_SZ + 4) +#define IE_CMD_CFG_IFS(base) ((base) + IE_CMD_COMMON_SZ + 5) +#define IE_CMD_CFG_SLOT_LOW(base) ((base) + IE_CMD_COMMON_SZ + 6) +#define IE_CMD_CFG_SLOT_HIGH(base) ((base) + IE_CMD_COMMON_SZ + 7) +#define IE_CMD_CFG_PROMISC(base) ((base) + IE_CMD_COMMON_SZ + 8) +#define IE_CMD_CFG_CRSCDT(base) ((base) + IE_CMD_COMMON_SZ + 9) +#define IE_CMD_CFG_MINLEN(base) ((base) + IE_CMD_COMMON_SZ + 10) +#define IE_CMD_CFG_JUNK(base) ((base) + IE_CMD_COMMON_SZ + 11) diff --git a/c/src/libchip/network/i82586var.h b/c/src/libchip/network/i82586var.h new file mode 100644 index 0000000000..f250fe03d0 --- /dev/null +++ b/c/src/libchip/network/i82586var.h @@ -0,0 +1,319 @@ +/* $NetBSD: i82586var.h,v 1.15 2001/01/22 22:28:45 bjh21 Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg and Charles M. Hannum. + * + * 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. + */ + +/*- + * Copyright (c) 1992, 1993, University of Vermont and State + * Agricultural College. + * Copyright (c) 1992, 1993, Garrett A. Wollman. + * + * Portions: + * Copyright (c) 1994, 1995, Rafal K. Boni + * Copyright (c) 1990, 1991, William F. Jolitz + * Copyright (c) 1990, The Regents of the University of California + * + * All rights reserved. + * + * 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 University of Vermont + * and State Agricultural College and Garrett A. Wollman, by William F. + * Jolitz, and by the University of California, Berkeley, Lawrence + * Berkeley Laboratory, and its contributors. + * 4. Neither the names of the Universities nor the names of the authors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR AUTHORS 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. + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Original StarLAN driver written by Garrett Wollman with reference to the + * Clarkson Packet Driver code for this chip written by Russ Nelson and others. + * + * BPF support code taken from hpdev/if_le.c, supplied with tcpdump. + * + * 3C507 support is loosely based on code donated to NetBSD by Rafal Boni. + * + * Majorly cleaned up and 3C507 code merged by Charles Hannum. + * + * Converted to SUN ie driver by Charles D. Cranor, + * October 1994, January 1995. + * This sun version based on i386 version 1.30. + */ + +#ifndef I82586_DEBUG +#define I82586_DEBUG 0 +#endif + +/* Debug elements */ +#define IED_RINT 0x01 +#define IED_TINT 0x02 +#define IED_RNR 0x04 +#define IED_CNA 0x08 +#define IED_READFRAME 0x10 +#define IED_ENQ 0x20 +#define IED_XMIT 0x40 +#define IED_ALL 0x7f + +#define B_PER_F 3 /* recv buffers per frame */ +#define IE_RBUF_SIZE 256 /* size of each receive buffer; + MUST BE POWER OF TWO */ +#define NTXBUF 2 /* number of transmit commands */ +#define IE_TBUF_SIZE ETHER_MAX_LEN /* length of transmit buffer */ + +#define IE_MAXMCAST (IE_TBUF_SIZE/6)/* must fit in transmit buffer */ + + +#define INTR_ENTER 0 /* intr hook called on ISR entry */ +#define INTR_EXIT 1 /* intr hook called on ISR exit */ +#define INTR_LOOP 2 /* intr hook called on ISR loop */ +#define INTR_ACK 3 /* intr hook called on ie_ack */ + +#define CHIP_PROBE 0 /* reset called from chip probe */ +#define CARD_RESET 1 /* reset called from card reset */ + +#if I82586_DEBUG +#define I82586_INTS_REQ 0 +#define I82586_INTS_IN 1 +#define I82586_INTS_LOOPS 2 +#define I82586_INTS_OUT 3 +#define I82586_RX_INT 4 +#define I82586_RX_DROP 5 +#define I82586_RX_ERR 6 +#define I82586_RX_OK 7 +#define I82586_RX_START 8 +#define I82586_START_TX 9 +#define I82586_TX_START 10 +#define I82586_TX_INT 11 +#define I82586_TX_REQ 12 +#define I82586_TX_EVT 13 +#define I82586_TX_EMIT 14 +#define I82586_TX_BAD 15 +#define I82586_TX_ACTIVE 16 +#define I82586_TRACE_CNT 17 + +#define I82586_TRACE_FLOW (10000) +#endif + +/* + * Ethernet status, per interface. + * + * The chip uses two types of pointers: 16 bit and 24 bit + * 24 bit pointers cover the board's memory. + * 16 bit pointers are offsets from the ISCP's `ie_base' + * + * The board's memory is represented by the bus handle `bh'. The MI + * i82586 driver deals exclusively with offsets relative to the + * board memory bus handle. The `ie_softc' fields below that are marked + * `MD' are in the domain of the front-end driver; they opaque to the + * MI driver part. + * + * The front-end is required to manage the SCP and ISCP structures. i.e. + * allocate room for them on the board's memory, and arrange to point the + * chip at the SCB stucture, the offset of which is passed to the MI + * driver in `sc_scb'. + * + * The following functions provide the glue necessary to deal with + * host and bus idiosyncracies: + * + * hwreset - board reset + * hwinit - board initialization + * chan_attn - get chip to look at prepared commands + * intrhook - board dependent interrupt processing + * + * All of the following shared-memory access function use an offset + * relative to the bus handle to indicate the shared memory location. + * The bus_{read/write}N function take or return offset into the + * shared memory in the host's byte-order. + * + * memcopyin - copy device memory: board to KVA + * memcopyout - copy device memory: KVA to board + * bus_read16 - read a 16-bit i82586 pointer + `offset' argument will be 16-bit aligned + * bus_write16 - write a 16-bit i82586 pointer + `offset' argument will be 16-bit aligned + * bus_write24 - write a 24-bit i82586 pointer + `offset' argument will be 32-bit aligned + * bus_barrier - perform a bus barrier operation, forcing + all outstanding reads/writes to complete + * + */ + +struct ie_softc { + struct arpcom arpcom; + + /* + * For RTEMS we run the tx and rx handlers under a task due to the + * network semaphore stuff. + */ + + rtems_id intr_task; + rtems_id tx_task; + + void *sc_iobase; /* (MD) KVA of base of 24 bit addr space */ + void *sc_maddr; /* (MD) KVA of base of chip's RAM + (16bit addr space) */ + u_int sc_msize; /* (MD) how much RAM we have/use */ + + /* Bus glue */ + void (*hwreset) (struct ie_softc *, int); + void (*hwinit) (struct ie_softc *); + void (*chan_attn) (struct ie_softc *, int); + int (*intrhook) (struct ie_softc *, int where); + + void (*memcopyin) (struct ie_softc *, void *, int, size_t); + void (*memcopyout) (struct ie_softc *, const void *, + int, size_t); + u_int16_t (*ie_bus_read16) (struct ie_softc *, int offset); + void (*ie_bus_write16) (struct ie_softc *, int offset, + u_int16_t value); + void (*ie_bus_write24) (struct ie_softc *, int offset, + int addr); + void (*ie_bus_barrier) (struct ie_softc *, int offset, + int length, int flags); + + /* Media management */ + int (*sc_mediachange) (struct ie_softc *); + /* card dependent media change */ + void (*sc_mediastatus) (struct ie_softc *, struct ifmediareq *); + /* card dependent media status */ + + /* + * Offsets (relative to bus handle) of the i82586 SYSTEM structures. + */ + int scp; /* Offset to the SCP (set by front-end) */ + int iscp; /* Offset to the ISCP (set by front-end) */ + int scb; /* Offset to SCB (set by front-end) */ + + /* + * Offset and size of a block of board memory where the buffers + * are to be allocated from (initialized by front-end). + */ + int buf_area; /* Start of descriptors and buffers */ + int buf_area_sz; /* Size of above */ + + /* + * The buffers & descriptors (recv and xmit) + */ + int rframes; /* Offset to `nrxbuf' frame descriptors */ + int rbds; /* Offset to `nrxbuf' buffer descriptors */ + int rbufs; /* Offset to `nrxbuf' receive buffers */ +#define IE_RBUF_ADDR(sc, i) (sc->rbufs + ((i) * IE_RBUF_SIZE)) + int rfhead, rftail; + int rbhead, rbtail; + int nframes; /* number of frames in use */ + int nrxbuf; /* number of recv buffs in use */ + int rnr_expect; /* XXX - expect a RCVR not ready interrupt */ + + int nop_cmds; /* Offset to NTXBUF no-op commands */ + int xmit_cmds; /* Offset to NTXBUF transmit commands */ + int xbds; /* Offset to NTXBUF buffer descriptors */ + int xbufs; /* Offset to NTXBUF transmit buffers */ +#define IE_XBUF_ADDR(sc, i) (sc->xbufs + ((i) * IE_TBUF_SIZE)) + + int xchead, xctail; + int xmit_busy; + int do_xmitnopchain; /* Controls use of xmit NOP chains */ + int xmit_req; + + /* Multicast addresses */ + char *mcast_addrs; /* Current MC filter addresses */ + int mcast_addrs_size; /* Current size of MC buffer */ + int mcast_count; /* Current # of addrs in buffer */ + int want_mcsetup; /* run mcsetup at next opportunity */ + + int promisc; /* are we in promisc mode? */ + int async_cmd_inprogress; /* we didn't wait for 586 to accept + a command */ + +#if I82586_DEBUG +#define I82586_TRACE(s, e, d) \ +do { rtems_interrupt_level level; rtems_interrupt_disable (level); \ + (s)->trace_flow[(s)->trace_flow_in++] = (e); \ + (s)->trace_flow[(s)->trace_flow_in++] = (unsigned int)(d); \ + if ((s)->trace_flow_in >= I82586_TRACE_FLOW) { \ + (s)->trace_flow_in = 0; \ + (s)->trace_flow_wrap = 1; \ + } \ + rtems_interrupt_enable (level); \ + } while (0) + + int sc_debug; + unsigned int trace_flow[I82586_TRACE_FLOW * 2]; + unsigned int trace_flow_wrap; +#endif + unsigned int trace_flow_in; +}; + +/* Exported functions */ +rtems_isr i82586_intr (rtems_vector_number , void *); +int i82586_proberam (struct ie_softc *); +int i82586_attach (struct rtems_bsdnet_ifconfig *config, int attaching); + +/* Shortcut macros to optional (driver uses default if unspecified) callbacks */ +#define xIE_BUS_BARRIER(sc, offset, length, flags) \ +do { \ + if ((sc)->ie_bus_barrier) \ + ((sc)->ie_bus_barrier)((sc), (offset), (length), (flags));\ + else \ + bus_space_barrier((sc)->bt, (sc)->bh, (offset), (length), \ + (flags)); \ +} while (0) + +#define IE_BUS_BARRIER(sc, offset, length, flags) -- cgit v1.2.3